Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions inc/class-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ public function search_models(): void {

$results = [];

// Merge the caller's query params (search term, etc.) with locally-built params.
$query = array_merge($args['query'], $query ?? []);

if ('user' === $args['model']) {
$results = $this->search_wordpress_users($query);
} elseif ('page' === $args['model']) {
Expand Down
17 changes: 11 additions & 6 deletions inc/objects/class-limitations.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ protected function merge_recursive(array &$array1, array &$array2, $should_sum =
* We use values 0 or '' as unlimited in our limits
*/
continue;
} elseif ($should_sum && is_null($value) && ! is_null($original_value)) {
/*
* Null values should not overwrite existing values in additive mode.
* A null limit means "no restriction configured" and should not
* remove restrictions from previously merged limitations.
*/
continue;
} elseif (isset($array1[ $key ]) && is_numeric($array1[ $key ]) && is_numeric($value) && $should_sum && ! $is_unlimited) {
$array1[ $key ] = ((int) $array1[ $key ]) + $value;
} elseif ('visibility' === $key && isset($array1[ $key ]) && $should_sum) {
Expand Down Expand Up @@ -354,8 +361,6 @@ protected function merge_recursive(array &$array1, array &$array2, $should_sum =

// Avoid change true values
$array1[ $key ] = true !== $original_value || ! $should_sum ? $value : true;

$array1[ $key ] = true !== $original_value || ! $should_sum ? $value : true;
}
}
}
Expand Down Expand Up @@ -463,13 +468,13 @@ public static function remove_limitations($slug, $id): void {
*/
public static function get_empty() {

$limitations = new self();
$modules_data = [];

foreach (array_keys(self::repository()) as $module_name) {
$limitations->{$module_name};
foreach (self::repository() as $module_name => $class_name) {
$modules_data[ $module_name ] = $class_name::default_state();
}

return $limitations;
return new self($modules_data);
}

/**
Expand Down
11 changes: 11 additions & 0 deletions patches/mpdf-mpdf-psr-log-aware-trait-void-return.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- a/src/PsrLogAwareTrait.php
+++ b/src/PsrLogAwareTrait.php
@@ -12,7 +12,7 @@
*/
protected $logger;

- public function setLogger(LoggerInterface $logger)
+ public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
127 changes: 127 additions & 0 deletions tests/WP_Ultimo/Objects/Limitations_Test.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?php

Check failure on line 1 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Missing file doc comment

namespace WP_Ultimo\Objects;

use WP_UnitTestCase;
use WP_Ultimo\Objects\Limitations;

class Limitations_Test extends WP_UnitTestCase {

Check failure on line 8 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Missing doc comment for class Limitations_Test

/**
* Clear limitations cache before each test.
Expand Down Expand Up @@ -71,7 +71,7 @@
];
}

/**

Check failure on line 74 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$expected_modules_count" missing

Check failure on line 74 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$modules_data" missing
* Test constructor with various module data.
*
* @dataProvider constructorDataProvider
Expand Down Expand Up @@ -121,7 +121,7 @@
];
}

/**

Check failure on line 124 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$should_exist" missing

Check failure on line 124 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$module_name" missing
* Test magic getter method.
*
* @dataProvider magicGetterDataProvider
Expand Down Expand Up @@ -196,7 +196,7 @@
];
}

/**

Check failure on line 199 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$expected_count" missing

Check failure on line 199 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$modules_data" missing
* Test build_modules method.
*
* @dataProvider buildModulesDataProvider
Expand Down Expand Up @@ -257,7 +257,7 @@
];
}

/**

Check failure on line 260 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$module_name" missing

Check failure on line 260 in tests/WP_Ultimo/Objects/Limitations_Test.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Doc comment for parameter "$data" missing
* Test static build method.
*
* @dataProvider buildMethodDataProvider
Expand Down Expand Up @@ -783,4 +783,131 @@

$this->assertEquals('force_active', $array1['behavior']);
}

/**
* Test that null limit from addon product does not overwrite plan template list during additive merge.
*
* Regression test: when a plan with configured site templates was merged with an addon product
* that had site_templates.limit = null, the null would overwrite the plan's template list,
* causing "The selected template is not available for this product" validation error.
*/
public function test_merge_null_limit_does_not_overwrite_template_list(): void {

$plan_limitations = new Limitations([
'site_templates' => [
'enabled' => true,
'mode' => 'default',
'limit' => [
'2' => ['behavior' => 'available'],
'3' => ['behavior' => 'pre_selected'],
],
],
]);

$addon_limitations = new Limitations([
'site_templates' => [
'enabled' => true,
'mode' => 'default',
'limit' => null,
],
]);

$merged = $plan_limitations->merge($addon_limitations);

$available = $merged->site_templates->get_available_site_templates();

$this->assertContains(2, $available, 'Template 2 should still be available after merging addon with null limit');
$this->assertContains(3, $available, 'Template 3 should still be available after merging addon with null limit');
}

/**
* Test that null limit does overwrite in override mode.
*/
public function test_merge_null_limit_overwrites_in_override_mode(): void {

$base = new Limitations([
'users' => [
'enabled' => true,
'limit' => 5,
],
]);

$override = new Limitations([
'users' => [
'enabled' => true,
'limit' => null,
],
]);

$merged = $base->merge(true, $override);

$this->assertNull($merged->users->get_limit(), 'Null limit should overwrite in override mode');
}

/**
* Test that get_empty returns proper default data in to_array.
*/
public function test_get_empty_to_array_returns_default_states(): void {

$empty = Limitations::get_empty();

$array = $empty->to_array();

$this->assertNotEmpty($array, 'get_empty()->to_array() should not be empty');
$this->assertArrayHasKey('site_templates', $array);
$this->assertArrayHasKey('plugins', $array);
$this->assertArrayHasKey('users', $array);
}

/**
* Test full checkout scenario: plan templates preserved when merging with addon product.
*
* Simulates the validation rule in class-site-template.php where all product
* limitations are merged together to determine available templates.
*/
public function test_checkout_plan_plus_addon_preserves_templates(): void {

// Plan with specific templates configured
$plan_data = new Limitations([
'site_templates' => [
'enabled' => true,
'mode' => 'choose_available_templates',
'limit' => [
'5' => ['behavior' => 'available'],
'7' => ['behavior' => 'available'],
'9' => ['behavior' => 'not_available'],
],
],
'disk_space' => [
'enabled' => true,
'limit' => 500,
],
]);

// Addon product with no template restrictions but some disk space
$addon_data = new Limitations([
'site_templates' => [
'enabled' => true,
'mode' => 'default',
'limit' => null,
],
'disk_space' => [
'enabled' => true,
'limit' => 100,
],
]);

// Simulate the validation rule merge
$limits = new Limitations([]);
$limits = $limits->merge($plan_data);
$limits = $limits->merge($addon_data);

$available = $limits->site_templates->get_available_site_templates();

$this->assertContains(5, $available, 'Template 5 should be available');
$this->assertContains(7, $available, 'Template 7 should be available');

// Disk space should be additive
$this->assertEquals(600, $limits->disk_space->get_limit(), 'Disk space should be summed');
}
}
Loading