PluginProbe ʕ •ᴥ•ʔ
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets / 4.2.3
Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets v4.2.3
4.2.5 4.2.4 trunk 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.2 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8 3.8.1 3.8.10 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.8.9.1 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.5.1 4.0.6 4.0.6.1 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2.0 4.2.1 4.2.2 4.2.3
widget-options / includes / pagebuilders / elementor / elementor.php
widget-options / includes / pagebuilders / elementor Last commit date
elementor.php 2 months ago render.php 2 months ago
elementor.php
896 lines
1 <?php
2
3 /**
4 * Extends funtionality to Elementor Pagebuilder
5 *
6 *
7 * @copyright Copyright (c) 2017, Jeffrey Carandang
8 * @since 4.3
9 */
10 // Exit if accessed directly
11 if (!defined('ABSPATH')) exit;
12
13 if (!function_exists('widgetopts_elementor_section')) {
14 //Add "Widget Options" section to every Elementor Widgets
15 add_action('elementor/element/after_section_end', 'widgetopts_elementor_section', 5, 3);
16 function widgetopts_elementor_section($element, $section_id, $args)
17 {
18 if (widgetopts_is_elementor_edit_mode()) {
19 global $widget_options;
20
21 //filter the elements first to avoid conflicts that can cause pagebuilder not to load
22 if (!in_array($element->get_name(), array('global-settings', 'section', 'page-settings', 'oew-blog-grid'))) {
23
24 //create array of section_id to set Widget Options Section to single section to avoid issues
25 $widgetopts_elementor_section_id = apply_filters(
26 'widgetopts_elementor_section_id',
27 array(
28 'section_image',
29 'section_advanced',
30 'section_title',
31 'section_editor',
32 'section_video',
33 'section_button',
34 'section_divider',
35 'section_spacer',
36 'section_map',
37 'section_icon',
38 'section_gallery',
39 'section_image_carousel',
40 'section_icon_list',
41 'section_counter',
42 'section_testimonial',
43 'section_tabs',
44 'section_toggle',
45 'section_social_icon',
46 'section_alert',
47 'section_audio',
48 'section_shortcode',
49 'section_anchor',
50 'section_sidebar',
51 'section_layout',
52 'section_slides',
53 'section_form_fields',
54 'section_list',
55 'section_header',
56 'section_pricing',
57 'section_countdown',
58 'section_buttons_content',
59 'section_blockquote_content',
60 'section_content',
61 'section_login_content',
62 'text_elements',
63 'section_side_a_content',
64 'section_side_b_content',
65 '_section_style'
66 )
67 );
68
69 //filter by the section_ids above
70 if (in_array($section_id, $widgetopts_elementor_section_id)) {
71 // Prevent duplicate control registration for the same element
72 static $processed_elements = [];
73 $el_key = spl_object_id($element);
74 if (isset($processed_elements[$el_key])) {
75 return;
76 }
77 $processed_elements[$el_key] = true;
78
79 $element->start_controls_section(
80 'widgetopts_section',
81 [
82 'tab' => Elementor\Controls_Manager::TAB_ADVANCED,
83 'label' => __('Widget Options', 'widget-options'),
84 ],
85 [
86 'overwrite' => true
87 ]
88 );
89
90 $element->start_controls_tabs('widgetopts_content_tabs', [
91 'overwrite' => true
92 ]);
93
94 if (isset($widget_options['visibility']) && 'activate' == $widget_options['visibility']) {
95 widgetopts_elementor_tab_visibility($element, $section_id, $args);
96 }
97
98 if (isset($widget_options['state']) && 'activate' == $widget_options['state']) {
99 widgetopts_elementor_tab_state($element, $section_id, $args);
100 }
101
102 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')))) {
103 widgetopts_elementor_tab_settings($element, $section_id, $args);
104 }
105
106 //upsell pro
107 if (!is_plugin_active('extended-widget-options/plugin.php')) {
108 $element->start_controls_tab(
109 'widgetopts_tab_upsell',
110 [
111 'label' => __('<i class="dashicons dashicons-plus"></i>', 'widget-options')
112 ],
113 [
114 'overwrite' => true
115 ]
116 );
117 $upgrade_link = apply_filters('widget_options_site_url', trailingslashit(WIDGETOPTS_PLUGIN_WEBSITE) . '?utm_source=elementor&utm_medium=upgrade&utm_campaign=upgradebtn');
118 $element->add_control(
119 'widgetopts_pro',
120 [
121 'type' => Elementor\Controls_Manager::RAW_HTML,
122 'raw' => '<div class="elementor-panel-nerd-box">
123 <i class="elementor-panel-nerd-box-icon dashicons dashicons-lock"></i>
124 <div class="elementor-panel-nerd-box-title">' .
125 __('Unlock All Widget Options', 'widget-options') .
126 '</div>
127 <div class="elementor-panel-nerd-box-message">' .
128 __('Upgrade to Widget Options Extended to unlock all options to easily control and manage each Elementor widget.', 'widget-options') .
129 '</div>
130 <a href="' . $upgrade_link . '" class="elementor-panel-nerd-box-link elementor-button elementor-button-default elementor-go-pro" target="_blank">' .
131 __('Upgrade Now!', 'widget-options') .
132 '</a>
133 </div>',
134 ],
135 [
136 'overwrite' => true
137 ]
138 );
139 $element->end_controls_tab();
140 }
141 $element->end_controls_tabs();
142
143 $element->end_controls_section();
144 }
145 }
146 }
147 }
148 }
149
150 if (!function_exists('widgetopts_elementor_tab_visibility')) {
151 function widgetopts_elementor_tab_visibility($element, $section_id, $args)
152 {
153 global $widget_options, $widgetopts_taxonomies, $widgetopts_pages, $widgetopts_types, $widgetopts_categories;
154
155 $pages = (!empty($widgetopts_pages)) ? $widgetopts_pages : array();
156 $taxonomies = (!empty($widgetopts_taxonomies)) ? $widgetopts_taxonomies : array();
157 $types = (!empty($widgetopts_types)) ? $widgetopts_types : array();
158 $categories = (!empty($widgetopts_categories)) ? $widgetopts_categories : array();
159
160 // print_r( $get_terms['community-category'] );
161
162 $element->start_controls_tab(
163 'widgetopts_tab_visibility',
164 [
165 'label' => __('<span class="dashicons dashicons-visibility"></span>', 'widget-options')
166 ],
167 [
168 'overwrite' => true
169 ]
170 );
171
172 $element->add_control(
173 'widgetopts_visibility',
174 [
175 'label' => __('Show/Hide', 'widget-options'),
176 'type' => Elementor\Controls_Manager::SELECT,
177 'default' => 'hide',
178 'options' => [
179 'show' => __('Show on Selected Pages'),
180 'hide' => __('Hide on Selected Pages')
181 ],
182 // 'separator' => 'none'
183 ],
184 [
185 'overwrite' => true
186 ]
187 );
188
189 if (isset($widget_options['settings']['visibility']) && isset($widget_options['settings']['visibility']['post_type']) && '1' == $widget_options['settings']['visibility']['post_type']) {
190 $pages_array = array();
191 if (!empty($pages)) {
192 foreach ($pages as $page) {
193 $pages_array[$page->ID] = $page->post_title;
194 }
195
196 $element->add_control(
197 'widgetopts_pages',
198 [
199 'label' => __('Pages', 'widget-options'),
200 'type' => Elementor\Controls_Manager::SELECT2,
201 'multiple' => true,
202 'label_block' => true,
203 'separator' => 'before',
204 'options' => $pages_array,
205 'render_type' => 'none',
206 'description' => __('Click on the field to search and select pages', 'widget-options')
207 ],
208 [
209 'overwrite' => true
210 ]
211 );
212 }
213
214 if (!empty($types)) {
215 $types_array = array();
216 foreach ($types as $ptype => $type) {
217 $types_array[$ptype] = $type->labels->name;
218 }
219
220 $element->add_control(
221 'widgetopts_types',
222 [
223 'label' => __('Post Types', 'widget-options'),
224 'type' => Elementor\Controls_Manager::SELECT2,
225 'multiple' => true,
226 'label_block' => true,
227 'separator' => 'before',
228 'options' => $types_array,
229 'render_type' => 'none',
230 'description' => __('Click on the field to search and select custom post types', 'widget-options')
231 ],
232 [
233 'overwrite' => true
234 ]
235 );
236 }
237 }
238
239 if (isset($widget_options['settings']['visibility']) && isset($widget_options['settings']['visibility']['taxonomies']) && '1' == $widget_options['settings']['visibility']['taxonomies']) {
240
241 if (!empty($categories)) {
242 $cat_array = array();
243 foreach ($categories as $cat) {
244 $cat_array[$cat->cat_ID] = $cat->cat_name;
245 }
246
247 $element->add_control(
248 'widgetopts_tax_category',
249 [
250 'label' => __('Categories', 'widget-options'),
251 'type' => Elementor\Controls_Manager::SELECT2,
252 'multiple' => true,
253 'label_block' => true,
254 'separator' => 'before',
255 'options' => $cat_array,
256 'render_type' => 'none',
257 'description' => __('Click on the field to search and select categories', 'widget-options')
258 ],
259 [
260 'overwrite' => true
261 ]
262 );
263 }
264
265 if (!empty($taxonomies)) {
266 $tax_array = array();
267 foreach ($taxonomies as $taxonomy) {
268 $tax_array[$taxonomy->name] = $taxonomy->label;
269 }
270
271 $element->add_control(
272 'widgetopts_taxonomies',
273 [
274 'label' => __('Taxonomies', 'widget-options'),
275 'type' => Elementor\Controls_Manager::SELECT2,
276 'multiple' => true,
277 'label_block' => true,
278 'separator' => 'before',
279 'options' => $tax_array,
280 'render_type' => 'none',
281 'description' => __('Click on the field to search and select taxonomies', 'widget-options')
282 ],
283 [
284 'overwrite' => true
285 ]
286 );
287 }
288 }
289
290 if (isset($widget_options['settings']['visibility']) && isset($widget_options['settings']['visibility']['misc']) && '1' == $widget_options['settings']['visibility']['misc']) {
291 $element->add_control(
292 'widgetopts_misc',
293 [
294 'label' => __('Miscellaneous', 'widget-options'),
295 'type' => Elementor\Controls_Manager::SELECT2,
296 'multiple' => true,
297 'label_block' => true,
298 'separator' => 'before',
299 'options' => [
300 'home' => __('Home/Front', 'widget-options'),
301 'blog' => __('Blog', 'widget-options'),
302 'archives' => __('Archives', 'widget-options'),
303 '404' => __('404', 'widget-options'),
304 'search' => __('Search', 'widget-options')
305 ],
306 'render_type' => 'none',
307 'description' => __('Click on the field to search and select miscellaneous pages', 'widget-options')
308 ],
309 [
310 'overwrite' => true
311 ]
312 );
313 }
314 $element->end_controls_tab();
315 }
316 }
317
318 if (!function_exists('widgetopts_elementor_tab_state')) {
319 function widgetopts_elementor_tab_state($element, $section_id, $args)
320 {
321 global $widget_options;
322
323 $element->start_controls_tab(
324 'widgetopts_tab_state',
325 [
326 'label' => __('<span class="dashicons dashicons-admin-users"></span>', 'widget-options')
327 ],
328 [
329 'overwrite' => true
330 ]
331 );
332
333 $element->add_control(
334 'widgetopts_roles_state',
335 [
336 'label' => __('User Login State', 'widget-options'),
337 'type' => Elementor\Controls_Manager::SELECT,
338 'default' => 'hide',
339 'options' => [
340 '' => __('Select Visibility Option'),
341 'in' => __('Show only for Logged-in Users'),
342 'out' => __('Show only for Logged-out Users')
343 ],
344 'description' => __('Restrict widget visibility for logged-in and logged-out users.', 'widget-options')
345 ],
346 [
347 'overwrite' => true
348 ]
349 );
350
351 $element->end_controls_tab();
352 }
353 }
354
355 if (!function_exists('widgetopts_elementor_tab_settings')) {
356 function widgetopts_elementor_tab_settings($element, $section_id, $args)
357 {
358 global $widget_options;
359
360 $element->start_controls_tab(
361 'widgetopts_tab_settings',
362 [
363 'label' => __('<span class="dashicons dashicons-admin-generic"></span>', 'widget-options')
364 ],
365 [
366 'overwrite' => true
367 ]
368 );
369
370 if (is_plugin_active('sliding-widget-options/plugin.php') && 'activate' == $widget_options['sliding'] && in_array($element->get_name(), array('button', 'button_plus', 'eael-creative-button', 'cta'))) {
371 $element->add_control(
372 'widgetopts_open_sliding',
373 [
374 'label' => __('Open Pop-up or Sliding Widgets', 'widget-options'),
375 'type' => Elementor\Controls_Manager::SWITCHER,
376 'return_value' => 'on',
377 'default' => '',
378 'separator' => 'none'
379 ],
380 [
381 'overwrite' => true
382 ]
383 );
384 }
385
386 if ('activate' == $widget_options['logic']) {
387 // Hidden control to store legacy logic value (avoids self-referencing condition)
388 $element->add_control(
389 'widgetopts_logic',
390 [
391 'type' => Elementor\Controls_Manager::HIDDEN,
392 'default' => '',
393 ],
394 [
395 'overwrite' => true
396 ]
397 );
398
399 // Hidden flag: set to '1' when user clicks Clear button
400 $element->add_control(
401 'widgetopts_logic_cleared',
402 [
403 'type' => Elementor\Controls_Manager::HIDDEN,
404 'default' => '',
405 ],
406 [
407 'overwrite' => true
408 ]
409 );
410
411 // Widget Options version stamp — used to determine if legacy logic should be used
412 $element->add_control(
413 'widgetopts_wopt_version',
414 [
415 'type' => Elementor\Controls_Manager::HIDDEN,
416 'default' => '',
417 ],
418 [
419 'overwrite' => true
420 ]
421 );
422
423 // RAW_HTML warning + Clear button — shown only when legacy logic exists and no snippet assigned
424 $migration_url = admin_url('options-general.php?page=widgetopts_migration');
425 $migration_link = current_user_can(WIDGETOPTS_MIGRATION_PERMISSIONS)
426 ? '<br><a href="' . esc_url($migration_url) . '" target="_blank">' . __('Go to Migration Page', 'widget-options') . ' &rarr;</a>'
427 : '';
428 $element->add_control(
429 'widgetopts_legacy_warning',
430 [
431 'type' => Elementor\Controls_Manager::RAW_HTML,
432 'raw' => '<div style="margin-bottom:10px;">'
433 . '<p style="margin:0 0 5px;"><strong>' . __('Legacy Display Logic Code', 'widget-options') . '</strong></p>'
434 . '<p style="margin:0;padding:8px 12px;background:#fff3cd;border-left:4px solid #ffc107;color:#856404;font-size:12px;">'
435 . __('This element uses legacy inline display logic that needs migration.', 'widget-options')
436 . $migration_link
437 . '</p>'
438 . '<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>'
439 . '</div>',
440 'condition' => [
441 'widgetopts_logic!' => '',
442 'widgetopts_logic_snippet_id' => '',
443 ],
444 ],
445 [
446 'overwrite' => true
447 ]
448 );
449
450 // Get available snippets for dropdown
451 $snippet_options = array('' => __('— No Logic (Always Show) —', 'widget-options'));
452 if (class_exists('WidgetOpts_Snippets_CPT')) {
453 $snippets = WidgetOpts_Snippets_CPT::get_all_snippets();
454 foreach ($snippets as $snippet) {
455 $snippet_options[$snippet['id']] = $snippet['title'];
456 }
457 }
458
459 $snippet_description = __('Select a logic snippet to control when this widget is displayed.', 'widget-options');
460 if (current_user_can('manage_options')) {
461 $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>';
462 }
463
464 $element->add_control(
465 'widgetopts_logic_snippet_id',
466 [
467 'type' => Elementor\Controls_Manager::SELECT2,
468 'label' => __('Display Logic Snippet', 'widget-options'),
469 'label_block' => true,
470 'description' => $snippet_description,
471 'options' => $snippet_options,
472 'default' => '',
473 'condition' => [
474 'widgetopts_logic' => '',
475 ],
476 ],
477 [
478 'overwrite' => true
479 ]
480 );
481 }
482
483
484 if (isset($widget_options['acf']) && 'activate' == $widget_options['acf']) {
485 $fields = array();
486
487 if (function_exists('acf_get_field_groups')) {
488 $groups = acf_get_field_groups();
489 if (is_array($groups)) {
490 foreach ($groups as $group) {
491 $fields_group = acf_get_fields($group);
492 if (!empty($fields_group)) {
493 foreach ($fields_group as $k => $fg) {
494 $fields[$fg['key']] = $fg['label'];
495 }
496 }
497 }
498 }
499 } else {
500 $groups = apply_filters('acf/get_field_groups', array());
501 if (is_array($groups)) {
502 foreach ($groups as $group) {
503 $fields_group = apply_filters('acf/field_group/get_fields', array(), $group['id']);
504 if (!empty($fields_group)) {
505 foreach ($fields_group as $k => $fg) {
506 $fields[$fg['key']] = $fg['label'];
507 }
508 }
509 }
510 }
511 }
512
513 $element->add_control(
514 'widgetopts_acf_title',
515 [
516 'type' => Elementor\Controls_Manager::RAW_HTML,
517 'separator' => 'before',
518 'raw' => '<h3>' . __('Advanced Custom Fields', 'widget-options') . '</h3>',
519 ],
520 [
521 'overwrite' => true
522 ]
523 );
524
525 $element->add_control(
526 'widgetopts_acf_visibility',
527 [
528 'label' => __('Show/Hide', 'widget-options'),
529 'type' => Elementor\Controls_Manager::SELECT,
530 'default' => 'hide',
531 'options' => [
532 'show' => __('Show when Condition\'s Met'),
533 'hide' => __('Hide when Condition\'s Met')
534 ],
535 'separator' => 'before',
536 ],
537 [
538 'overwrite' => true
539 ]
540 );
541
542 $element->add_control(
543 'widgetopts_acf_field',
544 [
545 'label' => __('Select ACF Field', 'widget-options'),
546 'type' => Elementor\Controls_Manager::SELECT2,
547 'multiple' => false,
548 'label_block' => true,
549 'options' => $fields,
550 'render_type' => 'none',
551 'description' => __('Select ACF field.', 'widget-options')
552 ],
553 [
554 'overwrite' => true
555 ]
556 );
557
558 $element->add_control(
559 'widgetopts_acf_condition',
560 [
561 'label' => __('Condition', 'widget-options'),
562 'type' => Elementor\Controls_Manager::SELECT2,
563 'multiple' => false,
564 'label_block' => true,
565 'options' => [
566 'equal' => __('Is Equal To', 'widget-options'),
567 'not_equal' => __('Is Not Equal To', 'widget-options'),
568 'contains' => __('Contains', 'widget-options'),
569 'not_contains' => __('Does Not Contain', 'widget-options'),
570 'empty' => __('Is Empty', 'widget-options'),
571 'not_empty' => __('Is Not Empty', 'widget-options')
572 ],
573 'render_type' => 'none',
574 'description' => __('Select your condition for this widget visibility.', 'widget-options')
575 ],
576 [
577 'overwrite' => true
578 ]
579 );
580 $element->add_control(
581 'widgetopts_acf',
582 [
583 'type' => Elementor\Controls_Manager::TEXTAREA,
584 'label' => __('Conditional Value', 'widget-options'),
585 'description' => __('Add your Conditional Value here if you selected Equal to, Not Equal To or Contains on the selection above.', 'widget-options'),
586 // 'separator' => 'none',
587 ],
588 [
589 'overwrite' => true
590 ]
591 );
592 }
593
594 $element->end_controls_tab();
595 }
596 }
597
598 if (!function_exists('widgetopts_is_elementor_edit_mode')) {
599 function widgetopts_is_elementor_edit_mode()
600 {
601 // Check Elementor's edit mode using the query parameters as fallback
602 if (\Elementor\Plugin::$instance->editor->is_edit_mode()) {
603 return true;
604 }
605
606 // Fallback check based on the request parameters
607 if (isset($_GET['elementor-preview']) || isset($_GET['action']) && $_GET['action'] === 'elementor') {
608 return true;
609 }
610
611 if (isset($_POST['action']) && $_POST['action'] === 'elementor_ajax') {
612 return true;
613 }
614
615 return false;
616 }
617 }
618
619 /**
620 * Protect legacy display logic from modification/injection on Elementor save.
621 * Strategy: capture old DB value before save, restore it after save.
622 * This prevents both code injection via console AND data loss before migration.
623 *
624 * @since 5.1
625 */
626 add_action('elementor/document/before_save', function($document) {
627 $post_id = $document->get_main_id();
628 $GLOBALS['_wopts_el_pre_save_' . $post_id] = get_post_meta($post_id, '_elementor_data', true);
629 }, 5, 1);
630
631 add_action('elementor/document/after_save', function($document) {
632 $post_id = $document->get_main_id();
633 $key = '_wopts_el_pre_save_' . $post_id;
634 $old_raw = isset($GLOBALS[$key]) ? $GLOBALS[$key] : '';
635 unset($GLOBALS[$key]);
636
637 // Build map of element_id => old widgetopts_logic value
638 $old_logic = array();
639 if ($old_raw) {
640 $old_els = is_string($old_raw) ? json_decode($old_raw, true) : $old_raw;
641 if (is_array($old_els)) {
642 $walk_old = function($els) use (&$walk_old, &$old_logic) {
643 foreach ($els as $el) {
644 if (!empty($el['id']) && !empty($el['settings']['widgetopts_logic'])) {
645 $old_logic[$el['id']] = $el['settings']['widgetopts_logic'];
646 }
647 if (!empty($el['elements'])) $walk_old($el['elements']);
648 }
649 };
650 $walk_old($old_els);
651 }
652 }
653
654 // Read the just-saved data and fix it
655 $saved_raw = get_post_meta($post_id, '_elementor_data', true);
656 if (empty($saved_raw)) return;
657 $saved_els = json_decode($saved_raw, true);
658 if (!is_array($saved_els)) return;
659
660 $changed = false;
661 $protect = function(&$els) use (&$protect, &$old_logic, &$changed) {
662 foreach ($els as &$el) {
663 $id = isset($el['id']) ? $el['id'] : '';
664 $wopt_ver = isset($el['settings']['widgetopts_wopt_version']) ? $el['settings']['widgetopts_wopt_version'] : '';
665 $has_snippet = !empty($el['settings']['widgetopts_logic_snippet_id']);
666 $has_legacy = !empty($el['settings']['widgetopts_logic']);
667
668 // If wopt_version >= 4.2: legacy logic is obsolete — clear it
669 if ($wopt_ver !== '' && version_compare($wopt_ver, '4.2', '>=')) {
670 if ($has_legacy) {
671 $el['settings']['widgetopts_logic'] = '';
672 $changed = true;
673 }
674 if (!empty($el['elements'])) $protect($el['elements']);
675 continue;
676 }
677
678 // Auto-clear legacy logic if snippet_id is already set (migration done)
679 if ($has_snippet && $has_legacy) {
680 $el['settings']['widgetopts_logic'] = '';
681 $el['settings']['widgetopts_wopt_version'] = WIDGETOPTS_VERSION;
682 $changed = true;
683 if (!empty($el['elements'])) $protect($el['elements']);
684 continue;
685 }
686
687 // User intentionally cleared legacy logic via Clear button
688 $was_cleared = !empty($el['settings']['widgetopts_logic_cleared']);
689 if ($was_cleared) {
690 $el['settings']['widgetopts_logic'] = '';
691 unset($el['settings']['widgetopts_logic_cleared']);
692 $el['settings']['widgetopts_wopt_version'] = WIDGETOPTS_VERSION;
693 $changed = true;
694 if (!empty($el['elements'])) $protect($el['elements']);
695 continue;
696 }
697
698 $old_val = ($id && isset($old_logic[$id])) ? $old_logic[$id] : '';
699 $new_val = isset($el['settings']['widgetopts_logic']) ? $el['settings']['widgetopts_logic'] : '';
700 if ($new_val !== $old_val) {
701 if ($old_val !== '') {
702 $el['settings']['widgetopts_logic'] = $old_val;
703 } else {
704 unset($el['settings']['widgetopts_logic']);
705 }
706 $changed = true;
707 }
708
709 if (!empty($el['elements'])) $protect($el['elements']);
710 }
711 };
712 $protect($saved_els);
713
714 if ($changed) {
715 update_post_meta($post_id, '_elementor_data', wp_slash(json_encode($saved_els)));
716 }
717 }, 10, 1);
718
719 /**
720 * Add script to refresh snippets in Elementor editor
721 *
722 * @since 5.1
723 */
724 add_action('elementor/editor/after_enqueue_scripts', function() {
725 ?>
726 <style>
727 /* Make legacy logic textarea readonly via CSS */
728 .elementor-control-widgetopts_logic textarea[data-setting="widgetopts_logic"] {
729 background: #f9f2f4 !important;
730 color: #c7254e !important;
731 font-family: monospace !important;
732 font-size: 12px !important;
733 cursor: not-allowed !important;
734 pointer-events: none !important;
735 }
736 </style>
737 <script>
738 (function() {
739 // Convert Elementor's native Select2 to AJAX mode for snippet dropdown
740 function initElementorSnippetSelect2() {
741 if (typeof jQuery === 'undefined') return;
742 var $select = jQuery('select[data-setting="widgetopts_logic_snippet_id"]');
743 if (!$select.length) return;
744
745 // Destroy Elementor's native Select2 if present
746 if ($select.hasClass('select2-hidden-accessible')) {
747 $select.select2('destroy');
748 }
749
750 // Init Select2 with AJAX transport for dynamic search
751 $select.select2({
752 placeholder: '— No Logic (Always Show) —',
753 allowClear: true,
754 width: '100%',
755 minimumInputLength: 0,
756 dropdownParent: $select.closest('.elementor-control-content'),
757 ajax: {
758 url: ajaxurl,
759 type: 'POST',
760 dataType: 'json',
761 delay: 250,
762 data: function(params) {
763 return {
764 action: 'widgetopts_get_snippets_ajax',
765 search: params.term || ''
766 };
767 },
768 processResults: function(response) {
769 var results = [{id: '', text: '— No Logic (Always Show) —'}];
770 if (response.success && response.data && response.data.snippets) {
771 response.data.snippets.forEach(function(snippet) {
772 results.push({id: String(snippet.id), text: snippet.title, description: snippet.description || ''});
773 });
774 }
775 return { results: results };
776 },
777 cache: true
778 }
779 });
780
781 // Add dynamic description element if not present
782 var $control = $select.closest('.elementor-control-content');
783 var $descEl = $control.find('.widgetopts-snippet-desc');
784 if (!$descEl.length) {
785 $descEl = jQuery('<p class="widgetopts-snippet-desc elementor-control-field-description" style="font-style: italic; color: #666; display:none;"></p>');
786 $control.append($descEl);
787 }
788
789 // Sync Select2 change back to Elementor model + update description
790 $select.off('select2:select.woDesc select2:unselect.woDesc');
791 $select.on('select2:select.woDesc', function(e) {
792 $select.trigger('change');
793 var desc = e.params.data.description || '';
794 if (desc) {
795 $descEl.text(desc).show();
796 } else {
797 $descEl.text('').hide();
798 }
799 });
800 $select.on('select2:unselect.woDesc', function() {
801 $select.trigger('change');
802 $descEl.text('').hide();
803 });
804 }
805
806 // Clear Legacy Logic button handler
807 function initClearLegacyLogicButton() {
808 if (typeof jQuery === 'undefined') return;
809 jQuery(document).off('click.woptsClearLegacy').on('click.woptsClearLegacy', '.widgetopts-clear-legacy-logic', function(e) {
810 e.preventDefault();
811 var btn = this;
812 var elElement = jQuery(btn).closest('.elementor-editor-element-settings-list, .elementor-editor-element-edit').closest('.elementor-element');
813 if (!elElement.length) {
814 // Fallback: find from panel
815 var panelEl = document.getElementById('elementor-panel');
816 if (panelEl && typeof elementor !== 'undefined' && elementor.selection) {
817 var selected = elementor.selection.getElements();
818 if (selected && selected[0]) {
819 var container = selected[0];
820 if (typeof $e !== 'undefined') {
821 $e.run('document/elements/settings', {
822 container: container,
823 settings: { widgetopts_logic: '', widgetopts_logic_cleared: '1' }
824 });
825 }
826 jQuery(btn).closest('.elementor-control-widgetopts_legacy_warning').slideUp();
827 return;
828 }
829 }
830 }
831 var dataId = elElement.attr('data-id');
832 if (dataId && typeof elementor !== 'undefined' && typeof $e !== 'undefined') {
833 var container = elementor.getContainer(dataId);
834 if (container) {
835 $e.run('document/elements/settings', {
836 container: container,
837 settings: { widgetopts_logic: '', widgetopts_logic_cleared: '1' }
838 });
839 }
840 }
841 jQuery(btn).closest('.elementor-control-widgetopts_legacy_warning').slideUp();
842 });
843 }
844
845 // Make legacy logic textarea readonly via MutationObserver
846 var legacyObserver = null;
847 function makeLegacyLogicReadonly() {
848 if (typeof jQuery === 'undefined') return;
849 // Apply to any currently visible
850 jQuery('textarea[data-setting="widgetopts_logic"]').each(function() {
851 if (!this.hasAttribute('readonly')) {
852 this.setAttribute('readonly', 'readonly');
853 }
854 });
855 // Watch for new textareas appearing in the panel
856 if (legacyObserver) legacyObserver.disconnect();
857 var panelEl = document.getElementById('elementor-panel');
858 if (!panelEl) return;
859 legacyObserver = new MutationObserver(function(mutations) {
860 var tas = panelEl.querySelectorAll('textarea[data-setting="widgetopts_logic"]:not([readonly])');
861 tas.forEach(function(ta) {
862 ta.setAttribute('readonly', 'readonly');
863 });
864 });
865 legacyObserver.observe(panelEl, { childList: true, subtree: true });
866 }
867
868 // Wait for elementor to load and register hooks
869 function initElementorHooks() {
870 if (typeof elementor !== 'undefined' && elementor.hooks) {
871 elementor.hooks.addAction('panel/open_editor/widget', function() {
872 setTimeout(initElementorSnippetSelect2, 500);
873 setTimeout(makeLegacyLogicReadonly, 500);
874 initClearLegacyLogicButton();
875 });
876 elementor.hooks.addAction('panel/open_editor/section', function() {
877 setTimeout(initElementorSnippetSelect2, 500);
878 setTimeout(makeLegacyLogicReadonly, 500);
879 initClearLegacyLogicButton();
880 });
881 elementor.hooks.addAction('panel/open_editor/column', function() {
882 setTimeout(initElementorSnippetSelect2, 500);
883 setTimeout(makeLegacyLogicReadonly, 500);
884 initClearLegacyLogicButton();
885 });
886 } else {
887 setTimeout(initElementorHooks, 500);
888 }
889 }
890
891 initElementorHooks();
892 })();
893 </script>
894 <?php
895 });
896