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
8 changes: 6 additions & 2 deletions inc/admin-pages/class-broadcast-list-admin-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,9 @@ public function render_add_new_broadcast_modal(): void {
],
'step_note' => [
'type' => 'note',
'desc' => sprintf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-show="step === 2" v-on:click.prevent="step = 1">%s</a>', __('&larr; Back to Type Selection', 'ultimate-multisite')),
'desc' => function () {
printf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-show="step === 2" v-on:click.prevent="step = 1">%s</a>', esc_html__('&larr; Back to Type Selection', 'ultimate-multisite'));
},
'wrapper_html_attr' => [
'v-show' => 'step === 2',
],
Expand Down Expand Up @@ -352,7 +354,9 @@ public function render_add_new_broadcast_modal(): void {
],
'step_note_2' => [
'type' => 'note',
'desc' => sprintf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-show="step === 3" v-on:click.prevent="step = 2">%s</a>', __('&larr; Back to Target Selection', 'ultimate-multisite')),
'desc' => function () {
printf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-show="step === 3" v-on:click.prevent="step = 2">%s</a>', esc_html__('&larr; Back to Target Selection', 'ultimate-multisite'));
},
'wrapper_html_attr' => [
'v-show' => 'step === 3',
],
Expand Down
4 changes: 3 additions & 1 deletion inc/admin-pages/class-checkout-form-edit-admin-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,9 @@ public function get_create_field_fields($attributes = []) {
'type_note' => [
'type' => 'note',
'order' => 0,
'desc' => sprintf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-on:click.prevent="type = \'\'">%s</a>', __('&larr; Back to Field Type Selection', 'ultimate-multisite')),
'desc' => function () {
printf('<a href="#" class="wu-no-underline wu-mt-1 wu-uppercase wu-text-2xs wu-font-semibold wu-text-gray-600" v-on:click.prevent="type = \'\'">%s</a>', esc_html__('&larr; Back to Field Type Selection', 'ultimate-multisite'));
},
'wrapper_html_attr' => [
'v-show' => 'type && (!saved && !name)',
'v-cloak' => '1',
Expand Down
14 changes: 8 additions & 6 deletions inc/admin-pages/class-customer-edit-admin-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ public function page_loaded() {
// Handle delete meta field action
if (isset($_GET['delete_meta_key']) && isset($_GET['_wpnonce'])) {
$meta_key = sanitize_key($_GET['delete_meta_key']);
$nonce = sanitize_text_field($_GET['_wpnonce']);
$nonce = sanitize_text_field(wp_unslash($_GET['_wpnonce']));

// Verify nonce for security
if ( ! wp_verify_nonce($nonce, 'delete_customer_meta_' . $meta_key)) {
wp_die(__('Security check failed. Please try again.', 'ultimate-multisite'));
wp_die(esc_html__('Security check failed. Please try again.', 'ultimate-multisite'));
}

$customer = $this->get_object();
Expand Down Expand Up @@ -586,10 +586,12 @@ public function generate_customer_meta_fields() {
'fields' => [
'new_meta_remove' => [
'type' => 'note',
'desc' => sprintf(
'<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600" href="#" @click.prevent="() => new_meta_fields.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>',
__('Remove', 'ultimate-multisite')
),
'desc' => function () {
printf(
'<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600" href="#" v-on:click.prevent="() => new_meta_fields.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>',
esc_html__('Remove', 'ultimate-multisite')
);
},
'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
],
'new_meta_slug' => [
Expand Down
13 changes: 10 additions & 3 deletions inc/admin-pages/class-product-edit-admin-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,14 @@ protected function get_product_option_sections() {
'fields' => [
'price_variations_remove' => [
'type' => 'note',
'desc' => sprintf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => price_variations.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite')),
'desc' => function () {
printf(
'<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => price_variations.splice(index, 1)">
<span class="dashicons-wu-squared-cross"></span>
</a>',
esc_html__('Remove', 'ultimate-multisite')
);
},
'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
],
Comment on lines 755 to 766
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for the field rendering code path that handles 'desc'
rg -n --type=php -C3 "render.*field|render.*note" inc --max-count=50

# Search for where 'desc' is processed in field rendering
rg -n --type=php -B2 -A5 "\['desc'\]" inc | head -n 300

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 9340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the main form field rendering logic
fd -e php . inc | xargs rg -l "desc.*callable|is_callable.*desc" | head -5
fd -e php . inc | xargs rg -l "type.*note|note.*render" | head -5

# Check for callable handling in field rendering
rg -n --type=php "if.*is_callable" inc | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 65


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find field template files, especially for 'note' type
fd -e php . inc -path "*/field*" -o -path "*/template*" | head -20

# Look for note field template
find inc -type f -name "*note*" -o -name "*field*template*" | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 249


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for where fields are rendered from templates
rg -n --type=php "get_template|wu_get_template" inc | grep -i field | head -20

# Look for 'note' type field rendering
rg -n --type=php "'note'" inc | head -30

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 2784


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find field template directories
find inc -type d -name "*template*" 2>/dev/null | head -20

# Look for note related field templates
find inc -type f -name "*.php" | xargs grep -l "note.*template\|template.*note" | head -10

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 562


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the field class to understand rendering
cat -n inc/ui/class-field.php | head -300

# Look for how desc is rendered in fields
rg -n "'desc'" inc/ui/class-field.php -B2 -A2

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 10282


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where field templates are stored
find inc -type d -name "views" -o -type d -name "templates" | head -10

# Look for field template files
find inc -path "*/views/*" -name "*.php" -o -path "*/templates/*" -name "*.php" | head -30

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 63


🏁 Script executed:

#!/bin/bash
set -euso pipefail

# Search for where form fields are rendered, looking for template paths
rg -n "include.*template|require.*template" inc/ui/class-form.php -B2 -A2

# Check class-form.php render_fields method more thoroughly
sed -n '180,250p' inc/ui/class-form.php | cat -n

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1912


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where $this->views is set in class-form.php
rg -n "views" inc/ui/class-form.php | head -20

# Get the full class-form.php to see constructor and views property
sed -n '1,100p' inc/ui/class-form.php | cat -n

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 3206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find field template directories
find inc -type f -name "field-note.php" -o -name "field-note*"

# Search more broadly for field templates
find inc -type f -name "field-*.php" | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 63


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the settings/fields directory
find . -type d -name "fields" | head -20

# Look for any field-*.php files
find . -type f -name "field-*.php" 2>/dev/null | head -30

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1533


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for settings/fields path
find . -path "*/settings/fields*" 2>/dev/null | head -20

# Look for note field template more broadly
find . -type f -name "*.php" | xargs grep -l "type.*note\|'note'" 2>/dev/null | head -15

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1469


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Examine the admin-pages field-note.php template
cat -n ./views/admin-pages/fields/field-note.php

# Also check the settings version for comparison
echo "=== SETTINGS VERSION ==="
cat -n ./views/settings/fields/field-note.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1933


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify the exact behavior by checking wu_kses_allowed_html function
rg -n "wu_kses_allowed_html" inc --type=php -B2 -A10 | head -50

# Check if there's any output buffering happening
rg -n "ob_start|ob_get_clean|ob_get_flush" inc --type=php | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 2174


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the actual file being reviewed to see the full context
sed -n '750,770p' inc/admin-pages/class-product-edit-admin-page.php | cat -n

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1108


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what wu_kses_allowed_html allows - especially for Vue directives
sed -n '326,450p' inc/functions/helper.php | cat -n

# Search for other examples of callable desc with printf/echo
rg -n "desc.*function\s*\(\)" inc/admin-pages --type=php -B2 -A5 | head -40

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 8993


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how the Field class handles the callable return value more carefully
sed -n '267,295p' inc/ui/class-field.php | cat -n

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1064


Use esc_attr__() instead of esc_html__() for the title attribute.

The title attribute requires attribute-context escaping. While esc_html__() works here because the text "Remove" contains no special characters, the correct function for attribute context is esc_attr__():

  'desc' => function () {
      printf(
          '<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => price_variations.splice(index, 1)">
              <span class="dashicons-wu-squared-cross"></span>
          </a>',
-         esc_html__('Remove', 'ultimate-multisite')
+         esc_attr__('Remove', 'ultimate-multisite')
      );
  },

The field system does support callable desc values, and this pattern correctly outputs Vue directives (which are whitelisted in wu_kses_allowed_html()).

πŸ€– Prompt for AI Agents
In inc/admin-pages/class-product-edit-admin-page.php around lines 755 to 766,
the title attribute is escaped with esc_html__() but should use
attribute-context escaping; replace esc_html__('Remove', 'ultimate-multisite')
with esc_attr__('Remove', 'ultimate-multisite') (or wrap the translated string
with esc_attr() if you prefer) so the title value is properly escaped for HTML
attribute context while keeping the rest of the callable desc unchanged.

'price_variations_duration' => [
Expand Down Expand Up @@ -921,13 +928,13 @@ protected function get_product_option_sections() {
* @since 2.0.0
*
* @param \WP_Ultimo\Models\Product $product The product being edited.
* @return string
* @return void
*/
public function get_site_template_selection_list($product) {

$all_templates = wu_get_site_templates();

return wu_get_template_contents(
wu_get_template(
'limitations/site-template-selector',
[
'templates' => $all_templates,
Expand Down
2 changes: 1 addition & 1 deletion inc/admin-pages/class-settings-admin-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ public function default_handler(): void {
if (isset($_POST[ $field ])) { // phpcs:ignore WordPress.Security.NonceVerification
$value = wp_unslash($_POST[ $field ]); // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if (is_array($value)) {
$filtered_data[ $field ] = array_map('sanitize_text_field', $value);
$filtered_data[ $field ] = wu_clean($value);
} elseif ( ! empty($field_data['allow_html'])) {
$filtered_data[ $field ] = sanitize_post_field('post_content', $value, $this->get_id(), 'db');
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ public function get_fields() {
'fields' => [
'period_options_remove' => [
'type' => 'note',
'desc' => sprintf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', __('Remove', 'ultimate-multisite')),
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
Comment on lines +215 to +217
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

Use full v-on:click.prevent syntax instead of shorthand.

According to the PR objectives, the shorthand @click.prevent directive was causing Vue.js rendering issues in delete buttons and should be replaced with the full v-on:click.prevent syntax. This change is inconsistent with the fixes applied in other files (e.g., class-customer-edit-admin-page.php lines 591 and class-broadcast-list-admin-page.php lines 298, 358).

Apply this diff:

 					'desc'            => function () {
-						printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
+						printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
 					},
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => period_options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
πŸ€– Prompt for AI Agents
In inc/checkout/signup-fields/class-signup-field-period-selection.php around
lines 215 to 217, the inline Vue directive uses the shorthand "@click.prevent"
which causes rendering issues; replace it with the full "v-on:click.prevent"
syntax in the generated markup returned by the anonymous 'desc' function so it
matches the fixes in other files (e.g., class-customer-edit-admin-page.php and
class-broadcast-list-admin-page.php), keeping the same handler expression "() =>
period_options.splice(index, 1)" and preserving escaping and the surrounding
printf string.

'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
],
'period_options_duration' => [
Expand Down
4 changes: 3 additions & 1 deletion inc/checkout/signup-fields/class-signup-field-select.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ public function get_fields() {
'fields' => [
'options_remove' => [
'type' => 'note',
'desc' => sprintf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', __('Remove', 'ultimate-multisite')),
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
Comment on lines +178 to +180
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

Critical: Inconsistent Vue directive form.

This code uses @click.prevent (shorthand), but according to the PR objectives, the root cause of issue #294 was that "the shorthand @click.prevent directive inside HTML strings in desc fields was being rendered as text instead of being parsed by Vue." The fix was to use the full v-on:click.prevent form.

Other files in this PR (inc/ui/class-domain-mapping-element.php, inc/ui/class-template-switching-element.php, inc/admin-pages/class-checkout-form-edit-admin-page.php) correctly use v-on:click.prevent, but this file still uses the shorthand form that was identified as problematic.

Apply this diff to use the full form:

 'desc'            => function () {
-    printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
+    printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => options.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
 },
πŸ€– Prompt for AI Agents
In inc/checkout/signup-fields/class-signup-field-select.php around lines
178-180, the HTML string for the 'desc' callback uses the shorthand
@click.prevent which was rendered as text; replace the shorthand with the full
directive v-on:click.prevent inside that string (keeping the existing
quoting/escaping and the click handler body intact) so Vue parses it the same
way as the other files in this PR.

'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
],
'options_key' => [
Expand Down
2 changes: 1 addition & 1 deletion inc/class-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public function add_settings(): void {
'api',
'api_note',
[
'desc' => __('This is your API Key. You cannot change it directly. To reset the API key and secret, use the button "Refresh API credentials" below.', 'ultimate-multisite'),
'desc' => fn() => esc_html__('This is your API Key. You cannot change it directly. To reset the API key and secret, use the button "Refresh API credentials" below.', 'ultimate-multisite'),
'type' => 'note',
'classes' => 'wu-text-gray-700 wu-text-xs',
'wrapper_classes' => 'wu-bg-white sm:wu-border-t-0 sm:wu-mt-0 sm:wu-pt-0',
Expand Down
52 changes: 26 additions & 26 deletions inc/class-orphaned-tables-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,34 +112,36 @@ public function render_orphaned_tables_delete_modal(): void {
return;
}

$table_list = '<div class="wu-max-h-32 wu-overflow-y-auto wu-bg-white wu-p-2 wu-border wu-rounded wu-mb-4">';
foreach ($orphaned_tables as $table) {
$table_list .= '<div class="wu-text-xs wu-font-mono wu-py-1">' . esc_html($table) . '</div>';
}
$table_list .= '</div>';

$fields = [
'confirmation' => [
'type' => 'note',
'desc' => sprintf(
'<div class="wu-p-4 wu-bg-red-100 wu-border wu-border-red-400 wu-text-red-700 wu-rounded">
'desc' => function () use ($orphaned_tables, $table_count) {
printf(
'<div class="wu-p-4 wu-bg-red-100 wu-border wu-border-red-400 wu-text-red-700 wu-rounded">
<h3 class="wu-mt-0 wu-mb-2">%s</h3>
<p class="wu-mb-2">%s</p>
%s
<p class="wu-text-sm wu-mb-4">
<strong>%s</strong> %s
</p>
</div>',
sprintf(
<p class="wu-mb-2">%s</p>',
sprintf(
/* translators: %d: number of orphaned tables */
esc_html(_n('Confirm Deletion of %d Orphaned Table', 'Confirm Deletion of %d Orphaned Tables', $table_count, 'ultimate-multisite')),
$table_count
),
esc_html__('You are about to permanently delete the following database tables:', 'ultimate-multisite'),
$table_list,
esc_html__('Warning:', 'ultimate-multisite'),
esc_html__('This action cannot be undone. Please ensure you have a database backup before proceeding.', 'ultimate-multisite')
),
esc_html(_n('Confirm Deletion of %d Orphaned Table', 'Confirm Deletion of %d Orphaned Tables', $table_count, 'ultimate-multisite')),
esc_html($table_count)
),
esc_html__('You are about to permanently delete the following database tables:', 'ultimate-multisite'),
);

echo '<div class="wu-max-h-32 wu-overflow-y-auto wu-bg-white wu-p-2 wu-border wu-rounded wu-mb-4">';
foreach ($orphaned_tables as $table) {
echo '<div class="wu-text-xs wu-font-mono wu-py-1">' . esc_html($table) . '</div>';
}
echo '</div>';
printf(
'<p class="wu-text-sm wu-mb-4">
<strong>%s</strong> %s
</p>',
esc_html__('Warning:', 'ultimate-multisite'),
esc_html__('This action cannot be undone. Please ensure you have a database backup before proceeding.', 'ultimate-multisite')
);
echo '</div>';
},
'wrapper_classes' => 'wu-w-full',
],
'submit' => [
Expand Down Expand Up @@ -185,9 +187,7 @@ public function handle_orphaned_tables_delete_modal(): void {
wp_die(esc_html__('You do not have the required permissions.', 'ultimate-multisite'));
}

if (empty($orphaned_tables) || ! is_array($orphaned_tables)) {
$orphaned_tables = $this->find_orphaned_tables();
}
$orphaned_tables = $this->find_orphaned_tables();

$deleted_count = $this->delete_orphaned_tables($orphaned_tables);

Expand Down
4 changes: 3 additions & 1 deletion inc/class-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,9 @@ public function default_sections(): void {
'fields' => [
'emulated_post_types_remove' => [
'type' => 'note',
'desc' => sprintf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', __('Remove', 'ultimate-multisite')),
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
Comment on lines +1110 to +1112
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

Critical: Inconsistent Vue directive form.

Similar to inc/checkout/signup-fields/class-signup-field-select.php, this code uses @click.prevent (shorthand) instead of the full v-on:click.prevent form. According to the PR objectives, the shorthand form inside HTML strings in desc fields causes Vue.js rendering issues (issue #294).

Apply this diff to use the full form:

 'desc'            => function () {
-    printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
+    printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
 },
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" @click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
'desc' => function () {
printf('<a title="%s" class="wu-no-underline wu-inline-block wu-text-gray-600 wu-mt-2 wu-mr-2" href="#" v-on:click.prevent="() => emulated_post_types.splice(index, 1)"><span class="dashicons-wu-squared-cross"></span></a>', esc_html__('Remove', 'ultimate-multisite'));
},
πŸ€– Prompt for AI Agents
In inc/class-settings.php around lines 1110 to 1112, the inline HTML string in
the 'desc' callback uses the Vue shorthand directive "@click.prevent" which must
be replaced with the full form "v-on:click.prevent" to avoid Vue rendering
issues; update the string accordingly and ensure quotes and escaping inside the
PHP printf call remain correct (preserve single/double quotes and esc_html__
usage) so the generated HTML contains v-on:click.prevent="() =>
emulated_post_types.splice(index, 1)" instead of the shorthand.

'wrapper_classes' => 'wu-absolute wu-top-0 wu-right-0',
],
'emulated_post_types_slug' => [
Expand Down
20 changes: 20 additions & 0 deletions inc/database/engine/class-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,24 @@ public function get_plural_name() {

return $this->item_name_plural;
}

/**
* Get columns from an array of arguments.
* Copy of the parent method of public access.
*
* @param array $args Arguments to filter columns by.
* @param string $operator Optional. The logical operation to perform.
* @param string $field Optional. A field from the object to place
* instead of the entire object. Default false.
* @return array Array of column.
*/
public function get_columns($args = array(), $operator = 'and', $field = false) {
// Filter columns.
$filter = wp_filter_object_list($this->columns, $args, $operator, $field);

// Return column or false.
return ! empty($filter)
? array_values($filter)
: array();
}
Comment on lines +100 to +118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Fix the inline comment to match actual return behavior.

Line 115's comment states "Return column or false" but the code returns an empty array array() when no columns match, not false. This documentation inconsistency could mislead future maintainers.

Apply this diff to fix the comment:

 	/**
 	 * Get columns from an array of arguments.
 	 * Copy of the parent method of public access.
 	 *
 	 * @param array  $args     Arguments to filter columns by.
 	 * @param string $operator Optional. The logical operation to perform.
 	 * @param string $field    Optional. A field from the object to place
 	 *                         instead of the entire object. Default false.
 	 * @return array Array of column.
 	 */
 	public function get_columns($args = array(), $operator = 'and', $field = false) {
 		// Filter columns.
 		$filter = wp_filter_object_list($this->columns, $args, $operator, $field);
 
-		// Return column or false.
+		// Return columns or empty array.
 		return ! empty($filter)
 			? array_values($filter)
 			: array();
 	}
πŸ€– Prompt for AI Agents
In inc/database/engine/class-query.php around lines 100 to 118, the inline
comment "Return column or false." is incorrect because the method returns an
array of columns or an empty array; update the comment to accurately state the
return behavior (e.g., "Return array of columns or an empty array when no
columns match") and ensure any surrounding wording/pluralization in the docblock
matches that behavior.

}
2 changes: 1 addition & 1 deletion inc/functions/email.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function wu_get_default_system_emails($slug = '') {
* @since 2.0.0
*
* @param string $slug Default system email slug to be create.
* @return array
* @return bool|Email
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

PHPDoc return type is incomplete.

The docblock states @return bool|Email, but Email_Manager::create_system_email() returns Email|false|WP_Error. The annotation should be updated for consistency:

-* @return bool|Email
+* @return Email|false|\WP_Error
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @return bool|Email
* @return Email|false|\WP_Error
πŸ€– Prompt for AI Agents
In inc/functions/email.php around line 103, the PHPDoc return type is incorrect:
it currently says "@return bool|Email" but the function
Email_Manager::create_system_email() can return Email, false, or WP_Error;
update the docblock to "@return Email|false|WP_Error" (or the project's
preferred type order) so the annotation matches the actual possible return types
and improves static analysis and documentation.

*/
function wu_create_default_system_email($slug) {

Expand Down
29 changes: 28 additions & 1 deletion inc/functions/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@

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

Check failure on line 297 in inc/functions/helper.php

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Empty CATCH statement detected

// Ignore it or log it.
}
Expand Down Expand Up @@ -362,6 +362,15 @@
'v-model' => true,
'v-bind' => true,
'v-bind:class' => true,
'v-bind:href' => true,
'v-bind:value' => true,
'v-bind:checked' => true,
'v-bind:disabled' => true,
'v-bind:src' => true,
'v-bind:max' => true,
'v-bind:min' => true,
'v-bind:id' => true,
'v-bind:readonly' => true,
'v-on' => true,
'v-cloak' => true,
'v-pre' => true,
Expand All @@ -370,12 +379,30 @@
// Vue.js shorthand attributes
':class' => true,
':style' => true,
':name' => true,
':id' => true,
':value' => true,
':key' => true,
':data-slug' => true,
':src' => true,
':alt' => true,
':href' => true,
':data-title' => true,
':aria-label' => true,
':colspan' => true,
':list' => true,
':tag' => true,
':headers' => true,
':step-name' => true,
':disabled' => true,
':element' => true,
':template' => true,
'v-on:click' => true,
'v-on:click.prevent' => true,
'v-on:input' => true,
'v-on:change' => true,
'@click' => true,
'@click.prevent' => true,
'v-on:click.prevent' => true,
'@submit' => true,
'@change' => true,
// Common data attributes
Expand Down
2 changes: 1 addition & 1 deletion inc/limits/class-site-template-limits.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function maybe_filter_template_selection_options($attributes) {
*/
public function maybe_force_template_selection($template_id, $membership) {

if ($membership && $membership->get_limitations()->site_templates->get_mode() === Limit_Site_Templates::MODE_ASSIGN_TEMPLATE) {
if ($membership && Limit_Site_Templates::MODE_ASSIGN_TEMPLATE === $membership->get_limitations()->site_templates->get_mode()) {
$template_id = $membership->get_limitations()->site_templates->get_pre_selected_site_template();
}

Expand Down
16 changes: 2 additions & 14 deletions inc/list-tables/class-base-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace WP_Ultimo\List_Tables;

use Closure;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if Closure is used anywhere in the Base_List_Table file
rg -n "Closure" inc/list-tables/class-base-list-table.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Also check the file structure and get more context around line 12
head -50 inc/list-tables/class-base-list-table.php | cat -n

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1424


🏁 Script executed:

#!/bin/bash
# Get total line count and search for any Closure usage in the complete file
wc -l inc/list-tables/class-base-list-table.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 127


🏁 Script executed:

#!/bin/bash
# Search for Closure with context to see if it's used in type hints, instantiation, or callbacks
rg -C 3 "Closure" inc/list-tables/class-base-list-table.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 186


Remove the unused Closure import.

The Closure import at line 12 is not used anywhere in this file and should be removed.

πŸ€– Prompt for AI Agents
In inc/list-tables/class-base-list-table.php around line 12, the file imports
Closure with "use Closure;" but that symbol is unused; remove the unused import
line to clean up the file and avoid linting warnings, keeping any other
use/imports intact.

use WP_Ultimo\Helpers\Hash;

// Exit if accessed directly
Expand Down Expand Up @@ -1262,10 +1263,6 @@ public function get_default_date_filter_options() {
/**
* Returns the columns from the BerlinDB Schema.
*
* Schema columns are protected on BerlinDB, which makes it hard to reference them out context.
* This is the reason for the reflection funkiness going on in here.
* Maybe there's a better way to do it, but it works for now.
*
* @since 2.0.0
*
* @param array $args Key => Value pair to search the return columns. e.g. array('searchable' => true).
Expand All @@ -1274,16 +1271,7 @@ public function get_default_date_filter_options() {
* @return array.
*/
protected function get_schema_columns($args = [], $operator = 'and', $field = false) {

$query_class = new $this->query_class();

$reflector = new \ReflectionObject($query_class);

$method = $reflector->getMethod('get_columns');

$method->setAccessible(true);

return $method->invoke($query_class, $args, $operator, $field);
return (new $this->query_class())->get_columns($args, $operator, $field);
}

/**
Expand Down
Loading
Loading