Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6d79778
Move tracking opt-in to general settings and onboarding wizard
superdav42 Jan 25, 2026
c101dc4
better error logging and handling of user accounts unable to connect …
superdav42 Jan 25, 2026
0f94b89
add recommended html attr
superdav42 Jan 25, 2026
a1df46f
allow kses to be extended
superdav42 Jan 25, 2026
d0bc595
add settings api
superdav42 Jan 25, 2026
58d27e5
Fix problems with choosing country and state
superdav42 Jan 25, 2026
9998c00
Fix login redirect
superdav42 Jan 25, 2026
a5b944d
Add support for pay what you want prices
superdav42 Jan 29, 2026
ab41443
Add support for pay what you want prices
superdav42 Jan 29, 2026
e2bc76d
Add support for pay what you want prices
superdav42 Jan 29, 2026
a808f21
set duration for manually created memberships
superdav42 Jan 29, 2026
30f1ac2
add duration restrictions for discount codes
superdav42 Jan 29, 2026
af6276c
make yoursite translatable
superdav42 Jan 29, 2026
a8b1edb
let users change their email
superdav42 Jan 29, 2026
28692a2
let networks login
superdav42 Jan 29, 2026
d905829
fix error in multinetwork
superdav42 Jan 29, 2026
76e4874
fix extra chars
superdav42 Jan 29, 2026
f4ca4ec
Apply suggestion from @superdav42
superdav42 Jan 30, 2026
93dd859
Fix product tabs
superdav42 Feb 2, 2026
eb12793
Skip billing requirements for free trials, fix null product handling,…
superdav42 Feb 3, 2026
a3b1bfd
Skip billing validation when payment not required, code style fixes
superdav42 Feb 3, 2026
d181e94
Apply suggestion from @coderabbitai[bot]
superdav42 Feb 3, 2026
1d66ab8
Address CodeRabbit review feedback and refactor Settings API
superdav42 Feb 3, 2026
e4537be
Merge branch 'main' into tweask-again
superdav42 Feb 3, 2026
c65339c
Enforce a strong password
superdav42 Feb 3, 2026
a76cee6
Merge branch 'tweask-again' of github.com:Multisite-Ultimate/ultimate…
superdav42 Feb 3, 2026
e95460b
Restructure E2E tests with programmatic setup and working checkout flows
superdav42 Feb 3, 2026
4655401
Fix PHPCS violations in E2E PHP fixtures
superdav42 Feb 3, 2026
08f14d0
Fix e2e workflow: correct spec names, remove invalid CLI flags, fix e…
superdav42 Feb 4, 2026
0ba2b71
fix searches
superdav42 Feb 4, 2026
01cbcb6
Add the doc
superdav42 Feb 4, 2026
3a09c64
fix legacy
superdav42 Feb 4, 2026
7dda86b
Use correct logic
superdav42 Feb 4, 2026
98ef974
Update test matrix to require PHP 8.2 minimum
superdav42 Feb 4, 2026
b50502b
Merge branch 'main' into tweask-again
superdav42 Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
439 changes: 439 additions & 0 deletions inc/apis/class-settings-endpoint.php

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion inc/class-addon-repository.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<?php
/**
* Handle access to addons.
*/

namespace WP_Ultimo;

use Psr\Log\LogLevel;

/**
* Addon Repository class for handling addon downloads and updates.
*
Expand Down Expand Up @@ -141,7 +146,8 @@ public function get_user_data(): array {
$code = wp_remote_retrieve_response_code($request);
$message = wp_remote_retrieve_response_message($request);
if (is_wp_error($request)) {
throw new \Exception(esc_html($request->get_error_message()), (int) $request->get_error_code());
wu_log_add('api-calls', $request->get_error_message(), LogLevel::ERROR);
$this->delete_tokens();
}
if (200 === absint($code) && 'OK' === $message) {
$user = json_decode($body, true);
Expand Down
31 changes: 16 additions & 15 deletions inc/class-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,22 @@ public function default_sections(): void {
120
);

$this->add_field(
'general',
'enable_error_reporting',
[
'title' => __('Help Improve Ultimate Multisite', 'ultimate-multisite'),
'desc' => sprintf(
/* translators: %s is a link to the privacy policy */
__('Allow Ultimate Multisite to collect anonymous usage data and error reports to help us improve the plugin. We collect: PHP version, WordPress version, plugin version, network type (subdomain/subdirectory), aggregate counts (sites, memberships), active gateways, and error logs. We never collect personal data, customer information, or domain names. <a href="%s" target="_blank">Learn more</a>.', 'ultimate-multisite'),
'https://ultimatemultisite.com/privacy-policy/'
),
'type' => 'toggle',
'default' => 0,
],
130
);

/*
* Login & Registration
* This section holds the Login & Registration settings of the Ultimate Multisite Plugin.
Expand Down Expand Up @@ -1730,21 +1746,6 @@ public function default_sections(): void {
]
);

$this->add_field(
'other',
'enable_error_reporting',
[
'title' => __('Help Improve Ultimate Multisite', 'ultimate-multisite'),
'desc' => sprintf(
/* translators: %s is a link to the privacy policy */
__('Allow Ultimate Multisite to collect anonymous usage data and error reports to help us improve the plugin. We collect: PHP version, WordPress version, plugin version, network type (subdomain/subdirectory), aggregate counts (sites, memberships), active gateways, and error logs. We never collect personal data, customer information, or domain names. <a href="%s" target="_blank">Learn more</a>.', 'ultimate-multisite'),
'https://ultimatemultisite.com/privacy-policy/'
),
'type' => 'toggle',
'default' => 0,
]
);

$this->add_field(
'other',
'advanced_header',
Expand Down
5 changes: 5 additions & 0 deletions inc/class-wp-ultimo.php
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,11 @@ protected function load_extra_components(): void {
*/
\WP_Ultimo\API\Register_Endpoint::get_instance();

/*
* Loads API settings endpoint.
*/
\WP_Ultimo\API\Settings_Endpoint::get_instance();

/*
* Loads Documentation
*/
Expand Down
20 changes: 15 additions & 5 deletions inc/functions/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function wu_slugify($term) {
*/
function wu_path($dir): string {

return WP_ULTIMO_PLUGIN_DIR . $dir; // @phpstan-ignore-line
return WP_ULTIMO_PLUGIN_DIR . $dir;
}

/**
Expand All @@ -117,7 +117,7 @@ function wu_path($dir): string {
*/
function wu_url($dir) {

return apply_filters('wp_ultimo_url', WP_ULTIMO_PLUGIN_URL . $dir); // @phpstan-ignore-line
return apply_filters('wp_ultimo_url', WP_ULTIMO_PLUGIN_URL . $dir);
}

/**
Expand Down Expand Up @@ -294,8 +294,7 @@ function wu_ignore_errors($func, $log = false) { // phpcs:ignore Generic.CodeAna

try {
call_user_func($func);
} catch (\Throwable $exception) {

} catch (\Throwable $exception) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// Ignore it or log it.
}
}
Expand Down Expand Up @@ -496,7 +495,7 @@ function wu_kses_allowed_html(): array {
'template' => true,
];

return [
$allowed_tags = [
'svg' => $svg_attributes + [
'width' => true,
'height' => true,
Expand Down Expand Up @@ -613,4 +612,15 @@ function wu_kses_allowed_html(): array {
'height' => true,
],
] + array_merge_recursive($allowed_html, array_fill_keys(array_keys($allowed_html) + ['div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong', 'em', 'b', 'i', 'ul', 'ol', 'li', 'a', 'img', 'input', 'textarea'], $vue_and_data_attributes));

/**
* Filters the allowed HTML tags and attributes.
*
* Allows addons to extend the allowed HTML elements for wp_kses sanitization.
*
* @since 2.5.0
*
* @param array $allowed_tags The allowed HTML tags and attributes.
*/
return apply_filters('wu_kses_allowed_html', $allowed_tags);
}
6 changes: 6 additions & 0 deletions inc/ui/class-login-form-element.php
Original file line number Diff line number Diff line change
Expand Up @@ -771,12 +771,18 @@ public function output($atts, $content = null): void {
'title' => $atts['label_username'],
'placeholder' => $atts['placeholder_username'],
'tooltip' => '',
'html_attr' => [
'autocomplete' => 'username',
],
],
'pwd' => [
'type' => 'password',
'title' => $atts['label_password'],
'placeholder' => $atts['placeholder_password'],
'tooltip' => '',
'html_attr' => [
'autocomplete' => 'current-password',
],
],
];

Expand Down
7 changes: 7 additions & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -240,16 +240,23 @@ We recommend running this in a staging environment before updating your producti

== Changelog ==

Version [2.4.10] - Released on 2026-XX-XX
- New: Settings API
- Fix: Problems with choosing country and state


Version [2.4.10] - Released on 2026-01-23
- New: Configurable minimum password strength setting with Medium, Strong, and Super Strong options.
- New: Super Strong password requirements include 12+ characters, uppercase, lowercase, numbers, and special characters - compatible with WPMU DEV Defender Pro rules.
- New: Real-time password requirement hints during checkout with translatable strings.
- New: Themed password field styling with visibility toggle and color fallbacks for page builders (Elementor, Kadence, Beaver Builder).
- New: Opt-in anonymous usage tracking to help improve the plugin.
- New: Better error page for customers and admins.
- New: Rating reminder notice after 30 days of installation.
- New: WooCommerce Subscriptions compatibility layer for site duplication.
- Improved: JSON response handling for pending site creation in non-FastCGI environments.


Version [2.4.9] - Released on 2025-12-23
- New: Inline login prompt at checkout for existing users - returning customers can sign in directly without leaving the checkout flow.
- New: GitHub Actions workflow for PR builds with WordPress Playground testing - enables one-click browser-based testing of pull requests.
Expand Down
6 changes: 6 additions & 0 deletions views/checkout/fields/field-select.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@

?>

<?php
// Check if Vue is handling the name dynamically to avoid duplicate attributes
$has_vue_name = isset($field->html_attr['v-bind:name']);
?>
<select
class="form-control wu-w-full wu-my-1 <?php echo esc_attr(trim($field->classes)); ?>"
id="field-<?php echo esc_attr($field->id); ?>"
<?php if ( ! $has_vue_name) : ?>
name="<?php echo esc_attr($field->id); ?>"
<?php endif; ?>
value="<?php echo esc_attr($field->value); ?>"
<?php $field->print_html_attributes(); ?>
>
Expand Down
10 changes: 9 additions & 1 deletion views/checkout/fields/field-text.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@

<?php endif; ?>

<input class="form-control wu-w-full wu-my-1 <?php echo esc_attr(trim($field->classes)); ?>" id="field-<?php echo esc_attr($field->id); ?>" name="<?php echo esc_attr($field->id); ?>" type="<?php echo esc_attr($field->type); ?>" placeholder="<?php echo esc_attr($field->placeholder); ?>" value="<?php echo esc_attr($field->value); ?>" <?php $field->print_html_attributes(); ?>>
<?php
// Check if Vue is handling the name dynamically to avoid duplicate attributes
$has_vue_name = isset($field->html_attr['v-bind:name']);
?>
<input class="form-control wu-w-full wu-my-1 <?php echo esc_attr(trim($field->classes)); ?>" id="field-<?php echo esc_attr($field->id); ?>"
<?php
if ( ! $has_vue_name) :
?>
name="<?php echo esc_attr($field->id); ?>" <?php endif; ?>type="<?php echo esc_attr($field->type); ?>" placeholder="<?php echo esc_attr($field->placeholder); ?>" value="<?php echo esc_attr($field->value); ?>" <?php $field->print_html_attributes(); ?>>

<?php if ($field->suffix) : ?>

Expand Down
Loading