--- a/widget-options/includes/admin/settings/migration-page.php
+++ b/widget-options/includes/admin/settings/migration-page.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Display Logic Migration Page
+ *
+ * Provides a UI for reviewing and batch-migrating legacy display logic
+ * snippets to the new snippet-based system.
+ *
+ * @copyright Copyright (c) 2024, Widget Options Team
+ * @since 5.1
+ */
+
+// Exit if accessed directly
+if (!defined('ABSPATH')) exit;
+?>
+<div class="wrap" id="widgetopts-migration-wrap">
+ <h1><?php esc_html_e('Display Logic Migration', 'widget-options'); ?></h1>
+
+ <div id="widgetopts-migration-notices"></div>
+
+ <div id="widgetopts-migration-loading" style="padding: 20px; text-align: center;">
+ <span class="spinner is-active" style="float: none;"></span>
+ <span><?php esc_html_e('Scanning website for legacy display logic...', 'widget-options'); ?></span>
+ </div>
+
+ <div id="widgetopts-migration-empty" style="display:none; padding: 20px; background: #f0f6fc; border-left: 4px solid #72aee6;">
+ <p><strong><?php esc_html_e('No legacy display logic found.', 'widget-options'); ?></strong></p>
+ <p><?php esc_html_e('All widgets are using the new snippet-based system.', 'widget-options'); ?></p>
+ </div>
+
+ <div id="widgetopts-migration-content" style="display:none;">
+ <div style="margin-bottom: 15px; display: flex; gap: 8px; align-items: center;">
+ <button type="button" class="button button-primary" id="widgetopts-migrate-all">
+ <?php esc_html_e('Migrate All', 'widget-options'); ?>
+ </button>
+ <button type="button" class="button" id="widgetopts-migrate-selected">
+ <?php esc_html_e('Migrate Selected', 'widget-options'); ?>
+ </button>
+ <label style="margin-left: 10px;">
+ <input type="checkbox" id="widgetopts-select-all" />
+ <?php esc_html_e('Select All', 'widget-options'); ?>
+ </label>
+ </div>
+
+ <table class="wp-list-table widefat fixed striped" id="widgetopts-migration-table">
+ <thead>
+ <tr>
+ <td class="manage-column column-cb check-column" style="width: 40px;">
+ <span class="screen-reader-text"><?php esc_html_e('Select', 'widget-options'); ?></span>
+ </td>
+ <th class="manage-column" style="width: 35%;"><?php esc_html_e('Code Snippet', 'widget-options'); ?></th>
+ <th class="manage-column" style="width: 20%;"><?php esc_html_e('Name', 'widget-options'); ?></th>
+ <th class="manage-column" style="width: 35%;"><?php esc_html_e('Locations', 'widget-options'); ?></th>
+ <th class="manage-column" style="width: 10%;"><?php esc_html_e('Actions', 'widget-options'); ?></th>
+ </tr>
+ </thead>
+ <tbody id="widgetopts-migration-tbody">
+ </tbody>
+ </table>
+ </div>
+</div>
+
+<style>
+ #widgetopts-migration-table .widgetopts-code-preview {
+ font-family: 'Courier New', Consolas, Monaco, monospace;
+ font-size: 12px;
+ line-height: 1.4;
+ background: #f9f2f4;
+ color: #c7254e;
+ padding: 8px 10px;
+ border-radius: 3px;
+ max-height: 120px;
+ overflow-y: auto;
+ white-space: pre-wrap;
+ word-break: break-all;
+ display: block;
+ }
+ #widgetopts-migration-table .widgetopts-name-input {
+ width: 100%;
+ padding: 4px 8px;
+ }
+ #widgetopts-migration-table .widgetopts-locations-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ font-size: 12px;
+ }
+ #widgetopts-migration-table .widgetopts-locations-list li {
+ padding: 2px 0;
+ color: #50575e;
+ }
+ #widgetopts-migration-table .widgetopts-locations-list .widgetopts-loc-type {
+ display: inline-block;
+ padding: 1px 6px;
+ border-radius: 3px;
+ font-size: 10px;
+ font-weight: 600;
+ text-transform: uppercase;
+ margin-right: 4px;
+ color: #fff;
+ }
+ .widgetopts-loc-type-classic_widget { background: #0073aa; }
+ .widgetopts-loc-type-gutenberg { background: #1e1e1e; }
+ .widgetopts-loc-type-elementor { background: #92003B; }
+ .widgetopts-loc-type-beaver { background: #6bc04b; }
+ .widgetopts-loc-type-siteorigin { background: #2ea2cc; }
+ .widgetopts-loc-count {
+ display: inline-block;
+ font-size: 11px;
+ font-weight: 600;
+ color: #787c82;
+ }
+ #widgetopts-migration-table .widgetopts-locations-list {
+ max-height: 150px;
+ overflow-y: auto;
+ }
+ #widgetopts-migration-table .button-delete {
+ color: #b32d2e;
+ border-color: #b32d2e;
+ }
+ #widgetopts-migration-table .button-delete:hover {
+ background: #b32d2e;
+ color: #fff;
+ }
+ .widgetopts-migration-notice {
+ padding: 10px 15px;
+ margin: 10px 0;
+ border-left: 4px solid;
+ background: #fff;
+ }
+ .widgetopts-migration-notice.success {
+ border-color: #00a32a;
+ background: #f0f6e8;
+ }
+ .widgetopts-migration-notice.error {
+ border-color: #d63638;
+ background: #fcf0f1;
+ }
+ .widgetopts-row-disabled {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+</style>
--- a/widget-options/includes/admin/settings/modules/logic.php
+++ b/widget-options/includes/admin/settings/modules/logic.php
@@ -31,9 +31,24 @@
<?php if( $widget_options['logic'] == 'activate' ){ ?>
<button class="button button-secondary widgetopts-toggle-settings"><?php _e( 'Configure Settings', 'widget-options' );?></button>
<button class="button button-secondary widgetopts-toggle-activation"><?php _e( 'Disable', 'widget-options' );?></button>
+ <?php if( current_user_can( 'manage_options' ) ){ ?>
+ <a href="<?php echo admin_url('edit.php?post_type=widgetopts_snippet'); ?>" class="button button-secondary widgetopts-manage-snippets-btn" style="margin-left: 5px;">
+ <?php _e( 'Manage Snippets', 'widget-options' );?>
+ </a>
+ <?php } ?>
+ <?php if( current_user_can( WIDGETOPTS_MIGRATION_PERMISSIONS ) ){ ?>
+ <a href="<?php echo admin_url('options-general.php?page=widgetopts_migration'); ?>" class="button button-secondary" style="margin-left: 5px;">
+ <?php _e( 'Migrate', 'widget-options' );?>
+ </a>
+ <?php } ?>
<?php }else{ ?>
<button class="button button-secondary widgetopts-toggle-settings"><?php _e( 'Learn More', 'widget-options' );?></button>
<button class="button button-primary widgetopts-toggle-activation"><?php _e( 'Enable', 'widget-options' );?></button>
+ <?php if( current_user_can( 'manage_options' ) ){ ?>
+ <a href="<?php echo admin_url('edit.php?post_type=widgetopts_snippet'); ?>" class="button button-secondary widgetopts-manage-snippets-btn" style="margin-left: 5px; display: none;">
+ <?php _e( 'Manage Snippets', 'widget-options' );?>
+ </a>
+ <?php } ?>
<?php } ?>
</div>
@@ -62,6 +77,19 @@
</p>
</td>
</tr>
+ <tr>
+ <th scope="row">
+ <label><?php _e( 'Logic Snippets', 'widget-options' );?></label>
+ </th>
+ <td>
+ <a href="<?php echo admin_url('edit.php?post_type=widgetopts_snippet'); ?>" class="button button-secondary">
+ <?php _e( 'Manage Logic Snippets', 'widget-options' );?>
+ </a>
+ <p class="description">
+ <?php _e( 'Create and manage reusable display logic snippets for your widgets.', 'widget-options' );?>
+ </p>
+ </td>
+ </tr>
</table>
<?php widgetopts_modal_end( $widget_options['logic'] ); ?>
--- a/widget-options/includes/extras.php
+++ b/widget-options/includes/extras.php
@@ -609,23 +609,9 @@
'wordwrap',
// Array Manipulation
- 'array_merge',
- 'array_diff',
- 'array_filter',
- 'array_map',
- 'array_keys',
- 'array_values',
'in_array',
'count',
'sizeof',
- 'array_slice',
- 'array_push',
- 'array_pop',
- 'array_reduce',
- 'array_intersect',
- 'array_unique',
- 'array_column',
- 'array_reverse',
// Math Functions
'abs',
@@ -909,5 +895,15 @@
return true;
}
+ // Check if Beaver Builder is in edit mode
+ if (class_exists('FLBuilderModel') && FLBuilderModel::is_builder_active()) {
+ return true;
+ }
+
+ // Check if Elementor is in edit mode
+ if (class_exists('ElementorPlugin') && ElementorPlugin::$instance->editor->is_edit_mode()) {
+ return true;
+ }
+
return false;
}
--- a/widget-options/includes/install.php
+++ b/widget-options/includes/install.php
@@ -45,10 +45,21 @@
if (!function_exists('widgetopts_register_defaults')) {
register_activation_hook(WIDGETOPTS_PLUGIN_FILE, function () {
add_option('Activated_Plugin', WIDGETOPTS_PLUGIN_FILE);
+ update_option('widgetopts_force_migration_rescan', 1);
widgetopts_register_defaults();
});
add_action('admin_init', function () {
+ if (is_admin() && get_option('widgetopts_force_migration_rescan')) {
+ delete_option('widgetopts_force_migration_rescan');
+ if (class_exists('WidgetOpts_Snippets_Migration')) {
+ WidgetOpts_Snippets_Migration::rescan_and_update_flag();
+ } elseif (class_exists('WidgetOpts_Snippets_CPT')) {
+ delete_option('wopts_display_logic_migration_required');
+ WidgetOpts_Snippets_CPT::scan_for_legacy_logic();
+ }
+ }
+
if (is_admin() && get_option('Activated_Plugin') == WIDGETOPTS_PLUGIN_FILE) {
delete_option('Activated_Plugin');
exit(wp_redirect(admin_url('options-general.php?page=widgetopts_plugin_settings')));
--- a/widget-options/includes/pagebuilders/beaver/beaver.php
+++ b/widget-options/includes/pagebuilders/beaver/beaver.php
@@ -44,6 +44,7 @@
// add_action( 'admin_notices', array( &$this, 'widgetopts_plugin_check' ) );
add_filter('fl_builder_is_node_visible', array(&$this, 'widgetopts_beaver_is_node_visible'), 10, 2);
+ add_action('fl_builder_control_widgetopts-beaver-legacy', array(&$this, 'fl_widgetopts_beaver_legacy_control'), 1, 4);
}
function widgetopts_beaver_settings($form, $id)
@@ -210,10 +211,48 @@
$settings_fld = array();
if (isset($widget_options['logic']) && 'activate' == $widget_options['logic']) {
+ // Get available snippets for dropdown
+ $snippet_options = array('' => __('— No Logic (Always Show) —', 'widget-options'));
+ if (class_exists('WidgetOpts_Snippets_CPT')) {
+ $snippets = WidgetOpts_Snippets_CPT::get_all_snippets();
+ foreach ($snippets as $snippet) {
+ $snippet_options[$snippet['id']] = $snippet['title'];
+ }
+ }
+
+ $snippet_description = __('Select a logic snippet to control when this widget is displayed.', 'widget-options');
+ if (current_user_can('manage_options')) {
+ $snippet_description .= '<br><a href="' . admin_url('edit.php?post_type=widgetopts_snippet') . '" target="_blank" style="display:inline-block;margin-top:5px;padding:3px 8px;background:#0073aa;color:#fff;text-decoration:none;border-radius:3px;font-size:11px;">' . __('Manage Snippets', 'widget-options') . ' →</a>';
+ }
+
+ // Hidden field to ensure BB includes legacy logic value in settings
$settings_fld['widgetopts_settings_logic'] = array(
- 'type' => 'textarea',
- 'label' => __('Display Logic', 'widget-options'),
- 'description' => __('<small>PLEASE NOTE that the display logic you introduce is EVAL'd directly. Anyone who has access to edit widget appearance will have the right to add any code, including malicious and possibly destructive functions. There is an optional filter "widget_options_logic_override" which you can use to bypass the EVAL with your own code if needed.</small>', 'widget-options')
+ 'type' => 'hidden',
+ 'default' => '',
+ );
+
+ // Hidden flag: set to '1' when user clicks Clear button
+ $settings_fld['widgetopts_logic_cleared'] = array(
+ 'type' => 'hidden',
+ 'default' => '',
+ );
+
+ // Widget Options version stamp
+ $settings_fld['widgetopts_wopt_version'] = array(
+ 'type' => 'hidden',
+ 'default' => '',
+ );
+
+ // Per-node legacy logic display (shown only when this node has legacy code)
+ $settings_fld['widgetopts_legacy_logic_display'] = array(
+ 'type' => 'widgetopts-beaver-legacy',
+ );
+
+ $settings_fld['widgetopts_logic_snippet_id'] = array(
+ 'type' => 'widgetopts-select2',
+ 'label' => __('Display Logic Snippet', 'widget-options'),
+ 'options' => $snippet_options,
+ 'description' => $snippet_description
);
}
@@ -313,7 +352,7 @@
<?php if (isset($widget_options['visibility']) && 'activate' == $widget_options['visibility']) { ?>
<a onclick="widgetoptsBeaverModule.navClick(event)" href="#fl-builder-settings-section-widgetopts-visibility" class="widgetopts-s-active"><span class="dashicons dashicons-visibility"></span><?php _e('Visibility', 'widget-options'); ?></a>
<?php } ?>
- <?php if (isset($widget_options['logic']) && 'activate' == $widget_options['logic'] && current_user_can('administrator')) { ?>
+ <?php if (isset($widget_options['logic']) && 'activate' == $widget_options['logic']) { ?>
<a onclick="widgetoptsBeaverModule.navClick(event)" href="#fl-builder-settings-section-widgetopts-settings" class=""><span class="dashicons dashicons-admin-generic"></span><?php _e('Settings', 'widget-options'); ?></a>
<?php } ?>
<a onclick="widgetoptsBeaverModule.navClick(event)" href="#fl-builder-settings-section-widgetopts-upgrade"><span class="dashicons dashicons-plus"></span><?php _e('More', 'widget-options'); ?></a>
@@ -401,7 +440,7 @@
}
}
- if (!empty($options) && $value) {
+ if (!empty($options) && $value && is_array($value)) {
uksort($options, function ($key1, $key2) use ($value) {
return array_search($key1, $value) <=> array_search($key2, $value);
});
@@ -414,11 +453,15 @@
?>
<select name="<?php echo $name;
if (isset($field['multi-select'])) echo '[]'; ?>" class="widgetopts-select2 <?php echo $field['class']; ?>" <?php if (isset($field['multi-select'])) echo 'multiple ';
- echo $attributes; ?> placeholder="<?php _e('Click to search or select', 'widget-options'); ?>">
- <option></option>
+ echo $attributes; ?> placeholder="<?php _e('Click to search or select', 'widget-options'); ?>">
+ <option value=""><?php echo isset($options['']) ? esc_html(is_array($options['']) ? $options['']['label'] : $options['']) : ''; ?></option>
<?php
foreach ($options as $option_key => $option_val) :
+ if ($option_key === '') {
+ continue;
+ }
+
if (is_array($option_val) && isset($option_val['premium']) && $option_val['premium'] && true === FL_BUILDER_LITE) {
continue;
}
@@ -439,6 +482,54 @@
</select>
<?php }
+ /**
+ * Custom control: show legacy logic readonly textarea + warning per-node
+ */
+ function fl_widgetopts_beaver_legacy_control($name, $value, $field, $settings)
+ {
+ // Don't show if snippet_id is already set (migration done)
+ if (!empty($settings->widgetopts_logic_snippet_id)) {
+ return;
+ }
+
+ // Only render if this specific node has legacy logic code
+ $legacy_code = '';
+ if (isset($settings->widgetopts_settings_logic) && !empty($settings->widgetopts_settings_logic)) {
+ $legacy_code = $settings->widgetopts_settings_logic;
+ } elseif (isset($settings->widgetopts_settings_logic_backup) && !empty($settings->widgetopts_settings_logic_backup)) {
+ $legacy_code = $settings->widgetopts_settings_logic_backup;
+ }
+
+ if (empty($legacy_code)) {
+ return;
+ }
+
+ $migration_url = admin_url('options-general.php?page=widgetopts_migration');
+ ?>
+ <div class="widgetopts-legacy-logic-wrap" style="margin-bottom:15px;">
+ <p><strong><?php _e('Legacy Display Logic Code', 'widget-options'); ?></strong></p>
+ <textarea readonly="readonly" rows="4" style="width:100%;background:#f9f2f4;color:#c7254e;font-family:monospace;font-size:12px;cursor:not-allowed;resize:vertical;"><?php echo esc_textarea($legacy_code); ?></textarea>
+ <p style="margin-top:8px;padding:8px 12px;background:#fff3cd;border-left:4px solid #ffc107;color:#856404;font-size:12px;">
+ <?php _e('This module uses legacy inline display logic. Please migrate it to the new snippet-based system.', 'widget-options'); ?>
+ <?php if (current_user_can(WIDGETOPTS_MIGRATION_PERMISSIONS)) : ?>
+ <br><a href="<?php echo esc_url($migration_url); ?>" target="_blank"><?php _e('Go to Migration Page', 'widget-options'); ?> →</a>
+ <?php endif; ?>
+ </p>
+ <button type="button" class="fl-builder-button fl-builder-button-primary widgetopts-bb-clear-legacy" style="margin-top:8px;padding:5px 15px;background:#dc3545;color:#fff;border:none;border-radius:3px;cursor:pointer;font-size:12px;" onclick="(function(btn){var form=jQuery(btn).closest('form,.fl-builder-settings');if(form.length){form.find('input[name=widgetopts_settings_logic]').val('');form.find('input[name=widgetopts_logic_cleared]').val('1');jQuery(btn).closest('.widgetopts-legacy-logic-wrap').slideUp();form.find('#fl-field-widgetopts_logic_snippet_id').show();}})( this)">
+ <?php _e('Clear Legacy Logic', 'widget-options'); ?>
+ </button>
+ </div>
+ <script>
+ jQuery(function(){
+ var wrap = jQuery('.widgetopts-legacy-logic-wrap');
+ if (wrap.length && wrap.is(':visible')) {
+ wrap.closest('form,.fl-builder-settings').find('#fl-field-widgetopts_logic_snippet_id').hide();
+ }
+ });
+ </script>
+ <?php
+ }
+
function fl_widgetopts_upgrade($name, $value, $field, $settings)
{ ?>
<div class="extended-widget-opts-tabcontent extended-widget-opts-tabcontent-gopro">
@@ -808,8 +899,23 @@
//widget logic
if (isset($widget_options['logic']) && 'activate' == $widget_options['logic']) {
- if (isset($settings->widgetopts_settings_logic) && !empty($settings->widgetopts_settings_logic)) {
- //do widget logic
+ // New snippet-based system
+ if (isset($settings->widgetopts_logic_snippet_id) && !empty($settings->widgetopts_logic_snippet_id)) {
+ $snippet_id = $settings->widgetopts_logic_snippet_id;
+ if (class_exists('WidgetOpts_Snippets_API')) {
+ $result = WidgetOpts_Snippets_API::execute_snippet($snippet_id);
+ if ($result === false) {
+ return false;
+ }
+ }
+ }
+ // Legacy support for old inline logic
+ elseif (isset($settings->widgetopts_settings_logic) && !empty($settings->widgetopts_settings_logic)) {
+ // Flag that legacy migration is needed
+ if (!get_option('wopts_display_logic_migration_required', false)) {
+ update_option('wopts_display_logic_migration_required', true);
+ }
+
$display_logic = stripslashes(trim($settings->widgetopts_settings_logic));
$display_logic = apply_filters('widget_options_logic_override', $display_logic);
$display_logic = apply_filters('extended_widget_options_logic_override', $display_logic);
@@ -819,9 +925,6 @@
if ($display_logic === true) {
return true;
}
- // if (stristr($display_logic, "return") === false) {
- // $display_logic = "return (" . $display_logic . ");";
- // }
$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
try {
if (!widgetopts_safe_eval($display_logic)) {
@@ -850,4 +953,84 @@
add_action('plugins_loaded', array('WP_Widget_Options_Beaver', 'init'));
// new WP_Widget_Options_Beaver();
+ /**
+ * Protect legacy display logic from modification/injection on BB save.
+ * Always restores the DB value for widgetopts_settings_logic per node.
+ * Prevents code injection via console AND data loss before migration.
+ *
+ * @since 5.1
+ */
+ add_filter('fl_builder_before_save_layout', function($data) {
+ if (!is_array($data) || !class_exists('FLBuilderModel')) {
+ return $data;
+ }
+
+ $post_id = FLBuilderModel::get_post_id();
+ if (!$post_id) {
+ return $data;
+ }
+
+ // Build map: node_id => old widgetopts_settings_logic from DB
+ $old_logic = array();
+ foreach (array('_fl_builder_data', '_fl_builder_draft') as $mk) {
+ $old_nodes = get_post_meta($post_id, $mk, true);
+ if (is_array($old_nodes)) {
+ foreach ($old_nodes as $nid => $node) {
+ if (is_object($node) && isset($node->settings) && is_object($node->settings)
+ && !empty($node->settings->widgetopts_settings_logic)) {
+ $old_logic[$nid] = $node->settings->widgetopts_settings_logic;
+ }
+ }
+ }
+ }
+
+ // Protect each node
+ foreach ($data as $nid => &$node) {
+ if (!is_object($node) || !isset($node->settings) || !is_object($node->settings)) {
+ continue;
+ }
+
+ $wopt_ver = isset($node->settings->widgetopts_wopt_version) ? $node->settings->widgetopts_wopt_version : '';
+ $has_snippet = !empty($node->settings->widgetopts_logic_snippet_id);
+ $has_legacy = !empty($node->settings->widgetopts_settings_logic);
+
+ // If wopt_version >= 4.2: legacy logic is obsolete — clear it
+ if ($wopt_ver !== '' && version_compare($wopt_ver, '4.2', '>=')) {
+ if ($has_legacy) {
+ $node->settings->widgetopts_settings_logic = '';
+ }
+ continue;
+ }
+
+ // Auto-clear legacy logic if snippet_id is already set (migration done)
+ if ($has_snippet && $has_legacy) {
+ $node->settings->widgetopts_settings_logic = '';
+ $node->settings->widgetopts_wopt_version = WIDGETOPTS_VERSION;
+ continue;
+ }
+
+ // User intentionally cleared legacy logic via Clear button
+ if (!empty($node->settings->widgetopts_logic_cleared)) {
+ $node->settings->widgetopts_settings_logic = '';
+ unset($node->settings->widgetopts_logic_cleared);
+ $node->settings->widgetopts_wopt_version = WIDGETOPTS_VERSION;
+ continue;
+ }
+
+ $old_val = isset($old_logic[$nid]) ? $old_logic[$nid] : '';
+ $new_val = isset($node->settings->widgetopts_settings_logic) ? $node->settings->widgetopts_settings_logic : '';
+
+ if ($new_val !== $old_val) {
+ if ($old_val !== '') {
+ $node->settings->widgetopts_settings_logic = $old_val;
+ } else {
+ unset($node->settings->widgetopts_settings_logic);
+ }
+ }
+
+ }
+
+ return $data;
+ }, 10, 1);
+
endif;
--- a/widget-options/includes/pagebuilders/elementor/elementor.php
+++ b/widget-options/includes/pagebuilders/elementor/elementor.php
@@ -68,6 +68,14 @@
//filter by the section_ids above
if (in_array($section_id, $widgetopts_elementor_section_id)) {
+ // Prevent duplicate control registration for the same element
+ static $processed_elements = [];
+ $el_key = spl_object_id($element);
+ if (isset($processed_elements[$el_key])) {
+ return;
+ }
+ $processed_elements[$el_key] = true;
+
$element->start_controls_section(
'widgetopts_section',
[
@@ -91,7 +99,7 @@
widgetopts_elementor_tab_state($element, $section_id, $args);
}
- if (current_user_can('administrator') && ('activate' == $widget_options['logic'] || (isset($widget_options['sliding']) && 'activate' == $widget_options['sliding'] && in_array($element->get_name(), array('button', 'button_plus', 'eael-creative-button', 'cta'))))) {
+ if ('activate' == $widget_options['logic'] || (isset($widget_options['sliding']) && 'activate' == $widget_options['sliding'] && in_array($element->get_name(), array('button', 'button_plus', 'eael-creative-button', 'cta')))) {
widgetopts_elementor_tab_settings($element, $section_id, $args);
}
@@ -375,15 +383,96 @@
);
}
- if ('activate' == $widget_options['logic'] && current_user_can('administrator')) {
-
+ if ('activate' == $widget_options['logic']) {
+ // Hidden control to store legacy logic value (avoids self-referencing condition)
$element->add_control(
'widgetopts_logic',
[
- 'type' => ElementorControls_Manager::TEXTAREA,
- 'label' => __('Display Widget Logic', 'widget-options'),
- 'description' => __('Add your PHP Conditional Tags. Please note that this will be EVAL'd directly.', 'widget-options'),
- // 'separator' => 'none',
+ 'type' => ElementorControls_Manager::HIDDEN,
+ 'default' => '',
+ ],
+ [
+ 'overwrite' => true
+ ]
+ );
+
+ // Hidden flag: set to '1' when user clicks Clear button
+ $element->add_control(
+ 'widgetopts_logic_cleared',
+ [
+ 'type' => ElementorControls_Manager::HIDDEN,
+ 'default' => '',
+ ],
+ [
+ 'overwrite' => true
+ ]
+ );
+
+ // Widget Options version stamp — used to determine if legacy logic should be used
+ $element->add_control(
+ 'widgetopts_wopt_version',
+ [
+ 'type' => ElementorControls_Manager::HIDDEN,
+ 'default' => '',
+ ],
+ [
+ 'overwrite' => true
+ ]
+ );
+
+ // RAW_HTML warning + Clear button — shown only when legacy logic exists and no snippet assigned
+ $migration_url = admin_url('options-general.php?page=widgetopts_migration');
+ $migration_link = current_user_can(WIDGETOPTS_MIGRATION_PERMISSIONS)
+ ? '<br><a href="' . esc_url($migration_url) . '" target="_blank">' . __('Go to Migration Page', 'widget-options') . ' →</a>'
+ : '';
+ $element->add_control(
+ 'widgetopts_legacy_warning',
+ [
+ 'type' => ElementorControls_Manager::RAW_HTML,
+ 'raw' => '<div style="margin-bottom:10px;">'
+ . '<p style="margin:0 0 5px;"><strong>' . __('Legacy Display Logic Code', 'widget-options') . '</strong></p>'
+ . '<p style="margin:0;padding:8px 12px;background:#fff3cd;border-left:4px solid #ffc107;color:#856404;font-size:12px;">'
+ . __('This element uses legacy inline display logic that needs migration.', 'widget-options')
+ . $migration_link
+ . '</p>'
+ . '<button type="button" class="elementor-button widgetopts-clear-legacy-logic" style="margin-top:8px;padding:5px 15px;background:#dc3545;color:#fff;border:none;border-radius:3px;cursor:pointer;font-size:12px;">' . __('Clear Legacy Logic', 'widget-options') . '</button>'
+ . '</div>',
+ 'condition' => [
+ 'widgetopts_logic!' => '',
+ 'widgetopts_logic_snippet_id' => '',
+ ],
+ ],
+ [
+ 'overwrite' => true
+ ]
+ );
+
+ // Get available snippets for dropdown
+ $snippet_options = array('' => __('— No Logic (Always Show) —', 'widget-options'));
+ if (class_exists('WidgetOpts_Snippets_CPT')) {
+ $snippets = WidgetOpts_Snippets_CPT::get_all_snippets();
+ foreach ($snippets as $snippet) {
+ $snippet_options[$snippet['id']] = $snippet['title'];
+ }
+ }
+
+ $snippet_description = __('Select a logic snippet to control when this widget is displayed.', 'widget-options');
+ if (current_user_can('manage_options')) {
+ $snippet_description .= '<br><a href="' . admin_url('edit.php?post_type=widgetopts_snippet') . '" target="_blank" style="display:inline-block;margin-top:5px;padding:3px 8px;background:#0073aa;color:#fff;text-decoration:none;border-radius:3px;font-size:11px;">' . __('Manage Snippets', 'widget-options') . ' →</a>';
+ }
+
+ $element->add_control(
+ 'widgetopts_logic_snippet_id',
+ [
+ 'type' => ElementorControls_Manager::SELECT2,
+ 'label' => __('Display Logic Snippet', 'widget-options'),
+ 'label_block' => true,
+ 'description' => $snippet_description,
+ 'options' => $snippet_options,
+ 'default' => '',
+ 'condition' => [
+ 'widgetopts_logic' => '',
+ ],
],
[
'overwrite' => true
@@ -526,3 +615,281 @@
return false;
}
}
+
+/**
+ * Protect legacy display logic from modification/injection on Elementor save.
+ * Strategy: capture old DB value before save, restore it after save.
+ * This prevents both code injection via console AND data loss before migration.
+ *
+ * @since 5.1
+ */
+add_action('elementor/document/before_save', function($document) {
+ $post_id = $document->get_main_id();
+ $GLOBALS['_wopts_el_pre_save_' . $post_id] = get_post_meta($post_id, '_elementor_data', true);
+}, 5, 1);
+
+add_action('elementor/document/after_save', function($document) {
+ $post_id = $document->get_main_id();
+ $key = '_wopts_el_pre_save_' . $post_id;
+ $old_raw = isset($GLOBALS[$key]) ? $GLOBALS[$key] : '';
+ unset($GLOBALS[$key]);
+
+ // Build map of element_id => old widgetopts_logic value
+ $old_logic = array();
+ if ($old_raw) {
+ $old_els = is_string($old_raw) ? json_decode($old_raw, true) : $old_raw;
+ if (is_array($old_els)) {
+ $walk_old = function($els) use (&$walk_old, &$old_logic) {
+ foreach ($els as $el) {
+ if (!empty($el['id']) && !empty($el['settings']['widgetopts_logic'])) {
+ $old_logic[$el['id']] = $el['settings']['widgetopts_logic'];
+ }
+ if (!empty($el['elements'])) $walk_old($el['elements']);
+ }
+ };
+ $walk_old($old_els);
+ }
+ }
+
+ // Read the just-saved data and fix it
+ $saved_raw = get_post_meta($post_id, '_elementor_data', true);
+ if (empty($saved_raw)) return;
+ $saved_els = json_decode($saved_raw, true);
+ if (!is_array($saved_els)) return;
+
+ $changed = false;
+ $protect = function(&$els) use (&$protect, &$old_logic, &$changed) {
+ foreach ($els as &$el) {
+ $id = isset($el['id']) ? $el['id'] : '';
+ $wopt_ver = isset($el['settings']['widgetopts_wopt_version']) ? $el['settings']['widgetopts_wopt_version'] : '';
+ $has_snippet = !empty($el['settings']['widgetopts_logic_snippet_id']);
+ $has_legacy = !empty($el['settings']['widgetopts_logic']);
+
+ // If wopt_version >= 4.2: legacy logic is obsolete — clear it
+ if ($wopt_ver !== '' && version_compare($wopt_ver, '4.2', '>=')) {
+ if ($has_legacy) {
+ $el['settings']['widgetopts_logic'] = '';
+ $changed = true;
+ }
+ if (!empty($el['elements'])) $protect($el['elements']);
+ continue;
+ }
+
+ // Auto-clear legacy logic if snippet_id is already set (migration done)
+ if ($has_snippet && $has_legacy) {
+ $el['settings']['widgetopts_logic'] = '';
+ $el['settings']['widgetopts_wopt_version'] = WIDGETOPTS_VERSION;
+ $changed = true;
+ if (!empty($el['elements'])) $protect($el['elements']);
+ continue;
+ }
+
+ // User intentionally cleared legacy logic via Clear button
+ $was_cleared = !empty($el['settings']['widgetopts_logic_cleared']);
+ if ($was_cleared) {
+ $el['settings']['widgetopts_logic'] = '';
+ unset($el['settings']['widgetopts_logic_cleared']);
+ $el['settings']['widgetopts_wopt_version'] = WIDGETOPTS_VERSION;
+ $changed = true;
+ if (!empty($el['elements'])) $protect($el['elements']);
+ continue;
+ }
+
+ $old_val = ($id && isset($old_logic[$id])) ? $old_logic[$id] : '';
+ $new_val = isset($el['settings']['widgetopts_logic']) ? $el['settings']['widgetopts_logic'] : '';
+ if ($new_val !== $old_val) {
+ if ($old_val !== '') {
+ $el['settings']['widgetopts_logic'] = $old_val;
+ } else {
+ unset($el['settings']['widgetopts_logic']);
+ }
+ $changed = true;
+ }
+
+ if (!empty($el['elements'])) $protect($el['elements']);
+ }
+ };
+ $protect($saved_els);
+
+ if ($changed) {
+ update_post_meta($post_id, '_elementor_data', wp_slash(json_encode($saved_els)));
+ }
+}, 10, 1);
+
+/**
+ * Add script to refresh snippets in Elementor editor
+ *
+ * @since 5.1
+ */
+add_action('elementor/editor/after_enqueue_scripts', function() {
+ ?>
+ <style>
+ /* Make legacy logic textarea readonly via CSS */
+ .elementor-control-widgetopts_logic textarea[data-setting="widgetopts_logic"] {
+ background: #f9f2f4 !important;
+ color: #c7254e !important;
+ font-family: monospace !important;
+ font-size: 12px !important;
+ cursor: not-allowed !important;
+ pointer-events: none !important;
+ }
+ </style>
+ <script>
+ (function() {
+ // Convert Elementor's native Select2 to AJAX mode for snippet dropdown
+ function initElementorSnippetSelect2() {
+ if (typeof jQuery === 'undefined') return;
+ var $select = jQuery('select[data-setting="widgetopts_logic_snippet_id"]');
+ if (!$select.length) return;
+
+ // Destroy Elementor's native Select2 if present
+ if ($select.hasClass('select2-hidden-accessible')) {
+ $select.select2('destroy');
+ }
+
+ // Init Select2 with AJAX transport for dynamic search
+ $select.select2({
+ placeholder: '— No Logic (Always Show) —',
+ allowClear: true,
+ width: '100%',
+ minimumInputLength: 0,
+ dropdownParent: $select.closest('.elementor-control-content'),
+ ajax: {
+ url: ajaxurl,
+ type: 'POST',
+ dataType: 'json',
+ delay: 250,
+ data: function(params) {
+ return {
+ action: 'widgetopts_get_snippets_ajax',
+ search: params.term || ''
+ };
+ },
+ processResults: function(response) {
+ var results = [{id: '', text: '— No Logic (Always Show) —'}];
+ if (response.success && response.data && response.data.snippets) {
+ response.data.snippets.forEach(function(snippet) {
+ results.push({id: String(snippet.id), text: snippet.title, description: snippet.description || ''});
+ });
+ }
+ return { results: results };
+ },
+ cache: true
+ }
+ });
+
+ // Add dynamic description element if not present
+ var $control = $select.closest('.elementor-control-content');
+ var $descEl = $control.find('.widgetopts-snippet-desc');
+ if (!$descEl.length) {
+ $descEl = jQuery('<p class="widgetopts-snippet-desc elementor-control-field-description" style="font-style: italic; color: #666; display:none;"></p>');
+ $control.append($descEl);
+ }
+
+ // Sync Select2 change back to Elementor model + update description
+ $select.off('select2:select.woDesc select2:unselect.woDesc');
+ $select.on('select2:select.woDesc', function(e) {
+ $select.trigger('change');
+ var desc = e.params.data.description || '';
+ if (desc) {
+ $descEl.text(desc).show();
+ } else {
+ $descEl.text('').hide();
+ }
+ });
+ $select.on('select2:unselect.woDesc', function() {
+ $select.trigger('change');
+ $descEl.text('').hide();
+ });
+ }
+
+ // Clear Legacy Logic button handler
+ function initClearLegacyLogicButton() {
+ if (typeof jQuery === 'undefined') return;
+ jQuery(document).off('click.woptsClearLegacy').on('click.woptsClearLegacy', '.widgetopts-clear-legacy-logic', function(e) {
+ e.preventDefault();
+ var btn = this;
+ var elElement = jQuery(btn).closest('.elementor-editor-element-settings-list, .elementor-editor-element-edit').closest('.elementor-element');
+ if (!elElement.length) {
+ // Fallback: find from panel
+ var panelEl = document.getElementById('elementor-panel');
+ if (panelEl && typeof elementor !== 'undefined' && elementor.selection) {
+ var selected = elementor.selection.getElements();
+ if (selected && selected[0]) {
+ var container = selected[0];
+ if (typeof $e !== 'undefined') {
+ $e.run('document/elements/settings', {
+ container: container,
+ settings: { widgetopts_logic: '', widgetopts_logic_cleared: '1' }
+ });
+ }
+ jQuery(btn).closest('.elementor-control-widgetopts_legacy_warning').slideUp();
+ return;
+ }
+ }
+ }
+ var dataId = elElement.attr('data-id');
+ if (dataId && typeof elementor !== 'undefined' && typeof $e !== 'undefined') {
+ var container = elementor.getContainer(dataId);
+ if (container) {
+ $e.run('document/elements/settings', {
+ container: container,
+ settings: { widgetopts_logic: '', widgetopts_logic_cleared: '1' }
+ });
+ }
+ }
+ jQuery(btn).closest('.elementor-control-widgetopts_legacy_warning').slideUp();
+ });
+ }
+
+ // Make legacy logic textarea readonly via MutationObserver
+ var legacyObserver = null;
+ function makeLegacyLogicReadonly() {
+ if (typeof jQuery === 'undefined') return;
+ // Apply to any currently visible
+ jQuery('textarea[data-setting="widgetopts_logic"]').each(function() {
+ if (!this.hasAttribute('readonly')) {
+ this.setAttribute('readonly', 'readonly');
+ }
+ });
+ // Watch for new textareas appearing in the panel
+ if (legacyObserver) legacyObserver.disconnect();
+ var panelEl = document.getElementById('elementor-panel');
+ if (!panelEl) return;
+ legacyObserver = new MutationObserver(function(mutations) {
+ var tas = panelEl.querySelectorAll('textarea[data-setting="widgetopts_logic"]:not([readonly])');
+ tas.forEach(function(ta) {
+ ta.setAttribute('readonly', 'readonly');
+ });
+ });
+ legacyObserver.observe(panelEl, { childList: true, subtree: true });
+ }
+
+ // Wait for elementor to load and register hooks
+ function initElementorHooks() {
+ if (typeof elementor !== 'undefined' && elementor.hooks) {
+ elementor.hooks.addAction('panel/open_editor/widget', function() {
+ setTimeout(initElementorSnippetSelect2, 500);
+ setTimeout(makeLegacyLogicReadonly, 500);
+ initClearLegacyLogicButton();
+ });
+ elementor.hooks.addAction('panel/open_editor/section', function() {
+ setTimeout(initElementorSnippetSelect2, 500);
+ setTimeout(makeLegacyLogicReadonly, 500);
+ initClearLegacyLogicButton();
+ });
+ elementor.hooks.addAction('panel/open_editor/column', function() {
+ setTimeout(initElementorSnippetSelect2, 500);
+ setTimeout(makeLegacyLogicReadonly, 500);
+ initClearLegacyLogicButton();
+ });
+ } else {
+ setTimeout(initElementorHooks, 500);
+ }
+ }
+
+ initElementorHooks();
+ })();
+ </script>
+ <?php
+});
--- a/widget-options/includes/pagebuilders/elementor/render.php
+++ b/widget-options/includes/pagebuilders/elementor/render.php
@@ -346,7 +346,7 @@
}
}
- //widget logic
+ // User state check
if (isset($widget_options['state']) && 'activate' == $widget_options['state']) {
if (isset($settings['widgetopts_roles_state']) && !empty($settings['widgetopts_roles_state'])) {
//do state action here
@@ -360,8 +360,23 @@
//widget logic
if ('activate' == $widget_options['logic']) {
- if (isset($settings['widgetopts_logic']) && !empty($settings['widgetopts_logic'])) {
- //do widget logic
+ // New snippet-based system
+ if (isset($settings['widgetopts_logic_snippet_id']) && !empty($settings['widgetopts_logic_snippet_id'])) {
+ $snippet_id = $settings['widgetopts_logic_snippet_id'];
+ if (class_exists('WidgetOpts_Snippets_API')) {
+ $result = WidgetOpts_Snippets_API::execute_snippet($snippet_id);
+ if ($result === false) {
+ return $placeholder;
+ }
+ }
+ }
+ // Legacy support for old inline logic
+ elseif (isset($settings['widgetopts_logic']) && !empty($settings['widgetopts_logic'])) {
+ // Flag that legacy migration is needed
+ if (!get_option('wopts_display_logic_migration_required', false)) {
+ update_option('wopts_display_logic_migration_required', true);
+ }
+
$display_logic = stripslashes(trim($settings['widgetopts_logic']));
$display_logic = apply_filters('widget_options_logic_override', $display_logic);
$display_logic = apply_filters('extended_widget_options_logic_override', $display_logic);
@@ -371,9 +386,6 @@
if ($display_logic === true) {
return $content;
}
- // if (stristr($display_logic, "return") === false) {
- // $display_logic = "return (" . $display_logic . ");";
- // }
$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
try {
if (!widgetopts_safe_eval($display_logic)) {
--- a/widget-options/includes/pagebuilders/siteorigin.php
+++ b/widget-options/includes/pagebuilders/siteorigin.php
@@ -25,8 +25,23 @@
if (isset($widgets['extended_widget_opts']) && !empty($widgets['extended_widget_opts'])) {
if (isset($panels_data['widgets'][$key]) && 'activate' == $widget_options['logic']) {
- // display widget logic
- if (isset($widgets['extended_widget_opts']['class']) && isset($widgets['extended_widget_opts']['class']['logic']) && !empty($widgets['extended_widget_opts']['class']['logic'])) {
+ // New snippet-based system
+ if (isset($widgets['extended_widget_opts']['class']['logic_snippet_id']) && !empty($widgets['extended_widget_opts']['class']['logic_snippet_id'])) {
+ $snippet_id = $widgets['extended_widget_opts']['class']['logic_snippet_id'];
+ if (class_exists('WidgetOpts_Snippets_API')) {
+ $result = WidgetOpts_Snippets_API::execute_snippet($snippet_id);
+ if ($result === false) {
+ unset($panels_data['widgets'][$key]);
+ }
+ }
+ }
+ // Legacy support for old inline logic
+ elseif (isset($widgets['extended_widget_opts']['class']['logic']) && !empty($widgets['extended_widget_opts']['class']['logic'])) {
+ // Flag that legacy migration is needed
+ if (!get_option('wopts_display_logic_migration_required', false)) {
+ update_option('wopts_display_logic_migration_required', true);
+ }
+
$display_logic = stripslashes(trim($widgets['extended_widget_opts']['class']['logic']));
$display_logic = apply_filters('widget_options_logic_override', $display_logic);
if ($display_logic === false) {
@@ -35,9 +50,6 @@
if ($display_logic === true) {
// return true;
}
- // if (stristr($display_logic, "return") === false) {
- // $display_logic = "return (" . $display_logic . ");";
- // }
$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
try {
if (!widgetopts_safe_eval($display_logic)) {
@@ -56,6 +68,100 @@
}
}
+/**
+ * Protect legacy display logic from modification/injection on SiteOrigin save.
+ * Intercepts panels_data meta save, restores old logic values from DB.
+ *
+ * @since 5.1
+ */
+if (!function_exists('widgetopts_siteorigin_protect_logic_on_save')) {
+ add_filter('update_post_metadata', 'widgetopts_siteorigin_protect_logic_on_save', 10, 5);
+ function widgetopts_siteorigin_protect_logic_on_save($check, $object_id, $meta_key, $meta_value, $prev_value) {
+ if ($meta_key !== 'panels_data') return $check;
+
+ static $processing = false;
+ if ($processing) return $check;
+
+ // Get old panels data from DB
+ $old_data = get_post_meta($object_id, 'panels_data', true);
+ if (!is_array($old_data) || empty($old_data['widgets'])) return $check;
+
+ // Collect known old logic values
+ $old_logic_set = array();
+ foreach ($old_data['widgets'] as $widget) {
+ if (isset($widget['extended_widget_opts']['class']['logic']) && $widget['extended_widget_opts']['class']['logic'] !== '') {
+ $old_logic_set[] = $widget['extended_widget_opts']['class']['logic'];
+ }
+ }
+ if (empty($old_logic_set)) return $check;
+
+ // Check new data
+ $new_data = is_array($meta_value) ? $meta_value : maybe_unserialize($meta_value);
+ if (!is_array($new_data) || empty($new_data['widgets'])) return $check;
+
+ $modified = false;
+ foreach ($new_data['widgets'] as &$widget) {
+ if (!isset($widget['extended_widget_opts']['class'])) continue;
+
+ $cls = &$widget['extended_widget_opts']['class'];
+ $wopt_ver = isset($cls['wopt_version']) ? $cls['wopt_version'] : '';
+ $has_snippet = !empty($cls['logic_snippet_id']);
+ $has_legacy = !empty($cls['logic']);
+
+ // If wopt_version >= 4.2: legacy logic is obsolete — clear it
+ if ($wopt_ver !== '' && version_compare($wopt_ver, '4.2', '>=')) {
+ if ($has_legacy) {
+ $cls['logic'] = '';
+ $modified = true;
+ }
+ unset($cls);
+ continue;
+ }
+
+ // Auto-clear legacy logic if snippet_id is already set (migration done)
+ if ($has_snippet && $has_legacy) {
+ $cls['logic'] = '';
+ $cls['wopt_version'] = WIDGETOPTS_VERSION;
+ $modified = true;
+ unset($cls);
+ continue;
+ }
+
+ // User intentionally cleared legacy logic via Clear button
+ if (!empty($cls['logic_cleared'])) {
+ $cls['logic'] = '';
+ unset($cls['logic_cleared']);
+ $cls['wopt_version'] = WIDGETOPTS_VERSION;
+ $modified = true;
+ unset($cls);
+ continue;
+ }
+
+ if (!isset($cls['logic'])) {
+ unset($cls);
+ continue;
+ }
+ $val = $cls['logic'];
+ if ($val !== '' && !in_array($val, $old_logic_set, true)) {
+ // Injected value not in old data — strip
+ $cls['logic'] = '';
+ $modified = true;
+ }
+
+ unset($cls);
+ }
+
+ if ($modified) {
+ $processing = true;
+ update_post_meta($object_id, 'panels_data', $new_data);
+ $processing = false;
+ return true; // Short-circuit original update
+ }
+
+ return $check;
+ }
+}
+
if (!function_exists('widgetopts_siteorigin_panels_widget_classes')) {
add_filter('siteorigin_panels_widget_classes', 'widgetopts_siteorigin_panels_widget_classes', 10, 4);
function widgetopts_siteorigin_panels_widget_classes($classes, $widget, $instance, $widget_info)
--- a/widget-options/includes/scripts.php
+++ b/widget-options/includes/scripts.php
@@ -191,7 +191,7 @@
// </a>';
// $sidebaropts .= '</div>';
- wp_localize_script('jquery-widgetopts-option-tabs', 'widgetopts10n', array('ajax_url' => admin_url('admin-ajax.php'), 'opts_page' => esc_url(admin_url('options-general.php?page=widgetopts_plugin_settings')), 'search_form' => $form, 'sidebaropts' => $sidebaropts, 'controls' => $btn_controls, 'translation' => array('manage_settings' => __('Manage Widget Options', 'widget-options'), 'search_chooser' => __('Search sidebar…', 'widget-options')), 'validate_expression_nonce' => wp_create_nonce('widgetopts-expression-nonce')));
+ wp_localize_script('jquery-widgetopts-option-tabs', 'widgetopts10n', array('ajax_url' => admin_url('admin-ajax.php'), 'opts_page' => esc_url(admin_url('options-general.php?page=widgetopts_plugin_settings')), 'search_form' => $form, 'sidebaropts' => $sidebaropts, 'controls' => $btn_controls, 'translation' => array('manage_settings' => __('Manage Widget Options', 'widget-options'), 'search_chooser' => __('Search sidebar…', 'widget-options')), 'validate_expression_nonce' => wp_create_nonce('widgetopts-expression-nonce'), 'migration_nonce' => wp_create_nonce('widgetopts_migration_nonce'), 'migration_strings' => array('prompt_title' => __('Enter a name for the new snippet:', 'widget-options'), 'default_title' => __('Migrated Snippet', 'widget-options'), 'converting' => __('Converting...', 'widget-options'), 'success' => __('Converted successfully!', 'widget-options'), 'error' => __('Conversion failed.', 'widget-options'))));
} else {
wp_localize_script('widgetopts-global-script', 'widgetopts10n', array('ajax_url' => admin_url('admin-ajax.php'), 'opts_page' => esc_url(admin_url('options-general.php?page=widgetopts_plugin_settings')), 'translation' => array('manage_settings' => __('Manage Widget Options', 'widget-options'), 'search_chooser' => __('Search sidebar…', 'widget-options')), 'validate_expression_nonce' => wp_create_nonce('widgetopts-expression-nonce')));
}
@@ -342,9 +342,205 @@
});
});
+
+ // Select2 AJAX config for dynamic snippet search
+ var snippetSelect2Config = {
+ placeholder: '<?php echo esc_js(__("— No Logic (Always Show) —", "widget-options")); ?>',
+ allowClear: true,
+ width: '100%',
+ minimumInputLength: 0,
+ ajax: {
+ url: ajaxurl,
+ type: 'POST',
+ dataType: 'json',
+ delay: 250,
+ data: function(params) {
+ return {
+ action: 'widgetopts_get_snippets_ajax',
+ search: params.term || ''
+ };
+ },
+ processResults: function(response) {
+ var results = [{id: '', text: '<?php echo esc_js(__("— No Logic (Always Show) —", "widget-options")); ?>'}];
+ if (response.success && response.data && response.data.snippets) {
+ response.data.snippets.forEach(function(snippet) {
+ results.push({id: String(snippet.id), text: snippet.title, description: snippet.description || ''});
+ });
+ }
+ return { results: results };
+ },
+ cache: true
+ }
+ };
+
+ // Update snippet description text next to the select
+ function updateSnippetDesc($select, description) {
+ var $desc = $select.closest('.widget-opts-logic').find('.widgetopts-snippet-desc');
+ if ($desc.length) {
+ if (description) {
+ $desc.text(description).show();
+ } else {
+ $desc.text('').hide();
+ }
+ }
+ }
+
+ // Init Select2 AJAX on a single select element
+ function initSelect2Ajax($select) {
+ if ($select.hasClass('select2-hidden-accessible')) {
+ $select.select2('destroy');
+ }
+ var config = jQuery.extend({}, snippetSelect2Config, {
+ dropdownParent: $select.closest('.widget-content, .widget-inside, form')
+ });
+ $select.select2(config);
+ $select.off('select2:select.woDesc select2:unselect.woDesc');
+ $select.on('select2:select.woDesc', function(e) {
+ updateSnippetDesc($select, e.params.data.description || '');
+ });
+ $select.on('select2:unselect.woDesc', function() {
+ updateSnippetDesc($select, '');
+ });
+ }
+
+ // Initialize Select2 for all uninitialized snippet dropdowns
+ function initSnippetSelect2() {
+ jQuery('.widgetopts-select2-snippets:not(.select2-hidden-accessible)').each(function() {
+ initSelect2Ajax(jQuery(this));
+ });
+ }
+
+ // Init on page load
+ jQuery(document).ready(function() {
+ setTimeout(initSnippetSelect2, 500);
+ });
+
+ // Re-init when widget is added/updated (WordPress fires widget-updated event)
+ jQuery(document).on('widget-added widget-updated', function(e, widget) {
+ setTimeout(function() {
+ jQuery(widget).find('.widgetopts-select2-snippets').each(function() {
+ initSelect2Ajax(jQuery(this));
+ });
+ }, 300);
+ });
+
+ // Init when widget panel is opened (click on widget title)
+ jQuery(document).on('click', '.widget-title, .widget-top', function() {
+ setTimeout(initSnippetSelect2, 300);
+ });
+
+ // Init when Widget Options tabs are clicked
+ jQuery(document).on('click', '.extended-widget-opts-tabs a, .extended-widget-opts-settings-tabnav-ul a', function() {
+ setTimeout(initSnippetSelect2, 100);
+ });
})();
</script>
<?php }
add_action('admin_footer-widgets.php', 'widgetopts_widgets_footer_additional_script', 999);
}
+
+if (!function_exists('widgetopts_siteorigin_footer_script')) {
+ function widgetopts_siteorigin_footer_script()
+ {
+ global $widget_options;
+ // Only load for SiteOrigin pages
+ if (!isset($widget_options['siteorigin']) || $widget_options['siteorigin'] !== 'activate') {
+ return;
+ }
+ ?>
+ <script>
+ (function($) {
+ // Select2 AJAX config for dynamic snippet search
+ var soSnippetConfig = {
+ placeholder: '<?php echo esc_js(__("— No Logic (Always Show) —", "widget-options")); ?>',
+ allowClear: true,
+ width: '100%',
+ minimumInputLength: 0,
+ ajax: {
+ url: ajaxurl,
+ type: 'P