PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.81
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.81
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / features / Protected_Content / Protected_Content.php

Protected_Content.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.81, at includes/features/Protected_Content/Protected_Content.php

1,545 lines 52.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Protected Content feature.
4 *
5 * Adds protection controls and runtime logic for Elementor elements.
6 * Supports role-based access, password protection, conditions, and content locker.
7 *
8 * @package King_Addons
9 */
10
11 namespace King_Addons;
12
13 use Elementor\Controls_Manager;
14 use Elementor\Element_Base;
15 use Elementor\Plugin;
16
17 if (!defined('ABSPATH')) {
18 exit;
19 }
20
21 /**
22 * Protected Content class.
23 */
24 class Protected_Content
25 {
26 /**
27 * Templates cache.
28 *
29 * @var array<int,string>|null
30 */
31 private static ?array $templates_cache = null;
32
33 /**
34 * Roles cache.
35 *
36 * @var array<string,string>|null
37 */
38 private static ?array $roles_cache = null;
39
40 /**
41 * Constructor.
42 */
43 public function __construct()
44 {
45 // Styles and scripts.
46 add_action('elementor/preview/enqueue_styles', [$this, 'enqueue_styles'], 1);
47 add_action('elementor/frontend/after_enqueue_styles', [$this, 'enqueue_styles'], 1);
48 add_action('elementor/preview/enqueue_scripts', [$this, 'enqueue_preview_script'], 1);
49
50 // Controls for widgets / columns / sections / containers.
51 add_action('elementor/element/common/_section_style/after_section_end', [$this, 'register_controls'], 10, 2);
52 add_action('elementor/element/section/section_advanced/after_section_end', [$this, 'register_controls'], 10, 2);
53 add_action('elementor/element/container/section_layout/after_section_end', [$this, 'register_controls'], 10, 2);
54 add_action('elementor/element/column/section_advanced/after_section_end', [$this, 'register_controls'], 10, 2);
55
56 // Runtime check.
57 add_action('elementor/frontend/widget/before_render', [$this, 'before_render_element']);
58 add_action('elementor/frontend/section/before_render', [$this, 'before_render_element']);
59 add_action('elementor/frontend/container/before_render', [$this, 'before_render_element']);
60 add_action('elementor/frontend/column/before_render', [$this, 'before_render_element']);
61
62 // Locker helper.
63 add_action('king_addons_locker_unlock', [$this, 'set_locker_cookie']);
64
65 // Password AJAX handler.
66 add_action('wp_ajax_king_addons_verify_password', [$this, 'ajax_verify_password']);
67 add_action('wp_ajax_nopriv_king_addons_verify_password', [$this, 'ajax_verify_password']);
68
69 // Enqueue password form script.
70 add_action('wp_enqueue_scripts', [$this, 'enqueue_password_form_script']);
71 }
72
73 /**
74 * Enqueue styles.
75 *
76 * @return void
77 */
78 public function enqueue_styles(): void
79 {
80 wp_enqueue_style(
81 KING_ADDONS_ASSETS_UNIQUE_KEY . '-protected-content-style',
82 KING_ADDONS_URL . 'includes/features/Protected_Content/style.css',
83 [],
84 KING_ADDONS_VERSION
85 );
86 }
87
88 /**
89 * Enqueue preview script in editor.
90 *
91 * @return void
92 */
93 public function enqueue_preview_script(): void
94 {
95 wp_enqueue_script(
96 KING_ADDONS_ASSETS_UNIQUE_KEY . '-protected-content-preview-handler',
97 KING_ADDONS_URL . 'includes/features/Protected_Content/preview-handler.js',
98 ['jquery'],
99 KING_ADDONS_VERSION,
100 true
101 );
102 }
103
104 /**
105 * Enqueue password form script on frontend.
106 *
107 * @return void
108 */
109 public function enqueue_password_form_script(): void
110 {
111 if ($this->is_editor_or_preview()) {
112 return;
113 }
114
115 wp_enqueue_script(
116 KING_ADDONS_ASSETS_UNIQUE_KEY . '-protected-content-password',
117 KING_ADDONS_URL . 'includes/features/Protected_Content/password-form.js',
118 ['jquery'],
119 KING_ADDONS_VERSION,
120 true
121 );
122
123 wp_localize_script(
124 KING_ADDONS_ASSETS_UNIQUE_KEY . '-protected-content-password',
125 'kingAddonsProtectedContent',
126 [
127 'ajaxUrl' => admin_url('admin-ajax.php'),
128 'nonce' => wp_create_nonce('king_addons_protected_content'),
129 'strings' => [
130 'enterPassword' => esc_html__('Please enter a password', 'king-addons'),
131 'incorrectPassword' => esc_html__('Incorrect password', 'king-addons'),
132 'errorOccurred' => esc_html__('An error occurred. Please try again.', 'king-addons'),
133 ],
134 ]
135 );
136 }
137
138 /**
139 * Register protection controls on supported elements.
140 *
141 * @param Element_Base $element Element.
142 * @param array<mixed> $args Args.
143 *
144 * @return void
145 */
146 public function register_controls(Element_Base $element, $args): void
147 {
148 $element->start_controls_section(
149 'king_addons_protected_content_section',
150 [
151 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Protection', 'king-addons'),
152 'tab' => Controls_Manager::TAB_ADVANCED,
153 ]
154 );
155
156 // =============================================
157 // MAIN SETTINGS
158 // =============================================
159
160 $element->add_control(
161 'protected_content_enable',
162 [
163 'label' => esc_html__('Enable Protection', 'king-addons'),
164 'type' => Controls_Manager::SWITCHER,
165 'label_on' => esc_html__('Yes', 'king-addons'),
166 'label_off' => esc_html__('No', 'king-addons'),
167 'return_value' => 'yes',
168 'default' => '',
169 ]
170 );
171
172 $element->add_control(
173 'protected_content_mode',
174 [
175 'label' => esc_html__('Protection Type', 'king-addons'),
176 'type' => Controls_Manager::SELECT,
177 'options' => [
178 'role' => esc_html__('By Role / Login', 'king-addons'),
179 'password' => $this->get_pro_label(__('By Password', 'king-addons')),
180 'conditions' => esc_html__('By Conditions', 'king-addons'),
181 'locker' => $this->get_pro_label(__('Content Locker', 'king-addons')),
182 ],
183 'default' => 'role',
184 'condition' => [
185 'protected_content_enable' => 'yes',
186 ],
187 ]
188 );
189
190 // =============================================
191 // FALLBACK SETTINGS
192 // =============================================
193
194 $element->add_control(
195 'protected_content_fallback_heading',
196 [
197 'label' => esc_html__('Fallback Options', 'king-addons'),
198 'type' => Controls_Manager::HEADING,
199 'separator' => 'before',
200 'condition' => [
201 'protected_content_enable' => 'yes',
202 ],
203 ]
204 );
205
206 $element->add_control(
207 'protected_content_fallback_type',
208 [
209 'label' => esc_html__('If access denied', 'king-addons'),
210 'type' => Controls_Manager::SELECT,
211 'options' => [
212 'none' => esc_html__('Hide element', 'king-addons'),
213 'message' => esc_html__('Show message', 'king-addons'),
214 'template' => $this->get_pro_label(__('Show template', 'king-addons')),
215 'form' => $this->get_pro_label(__('Show form', 'king-addons')),
216 ],
217 'default' => 'none',
218 'condition' => [
219 'protected_content_enable' => 'yes',
220 ],
221 ]
222 );
223
224 $element->add_control(
225 'protected_content_fallback_msg',
226 [
227 'label' => esc_html__('Message', 'king-addons'),
228 'type' => Controls_Manager::TEXTAREA,
229 'rows' => 3,
230 'default' => esc_html__('This content is protected.', 'king-addons'),
231 'condition' => [
232 'protected_content_enable' => 'yes',
233 'protected_content_fallback_type' => 'message',
234 ],
235 ]
236 );
237
238 $element->add_control(
239 'protected_content_fallback_template',
240 [
241 'label' => esc_html__('Template', 'king-addons'),
242 'type' => Controls_Manager::SELECT2,
243 'options' => $this->get_elementor_templates(),
244 'label_block' => true,
245 'condition' => [
246 'protected_content_enable' => 'yes',
247 'protected_content_fallback_type' => 'template',
248 ],
249 ]
250 );
251
252 $element->add_control(
253 'protected_content_fallback_form',
254 [
255 'label' => esc_html__('Form Shortcode', 'king-addons'),
256 'type' => Controls_Manager::TEXT,
257 'placeholder' => '[contact-form-7 id="123"]',
258 'description' => esc_html__('Enter a form shortcode (CF7, WPForms, etc.)', 'king-addons'),
259 'condition' => [
260 'protected_content_enable' => 'yes',
261 'protected_content_fallback_type' => 'form',
262 ],
263 ]
264 );
265
266 $element->add_control(
267 'protected_content_invert',
268 [
269 'label' => esc_html__('Invert Logic', 'king-addons'),
270 'description' => esc_html__('Show content only when condition is NOT met', 'king-addons'),
271 'type' => Controls_Manager::SWITCHER,
272 'return_value' => 'yes',
273 'default' => '',
274 'condition' => [
275 'protected_content_enable' => 'yes',
276 ],
277 ]
278 );
279
280 // =============================================
281 // ROLE MODE (Free)
282 // =============================================
283
284 $element->add_control(
285 'protected_content_role_heading',
286 [
287 'label' => esc_html__('Role / Login Settings', 'king-addons'),
288 'type' => Controls_Manager::HEADING,
289 'separator' => 'before',
290 'condition' => [
291 'protected_content_enable' => 'yes',
292 'protected_content_mode' => 'role',
293 ],
294 ]
295 );
296
297 $element->add_control(
298 'protected_content_require_login',
299 [
300 'label' => esc_html__('Require Logged In User', 'king-addons'),
301 'type' => Controls_Manager::SWITCHER,
302 'default' => 'yes',
303 'condition' => [
304 'protected_content_enable' => 'yes',
305 'protected_content_mode' => 'role',
306 ],
307 ]
308 );
309
310 $element->add_control(
311 'protected_content_roles',
312 [
313 'label' => esc_html__('Allowed Roles', 'king-addons'),
314 'description' => esc_html__('Leave empty to allow all logged-in users', 'king-addons'),
315 'type' => Controls_Manager::SELECT2,
316 'multiple' => true,
317 'options' => $this->get_wp_roles_for_control(),
318 'condition' => [
319 'protected_content_enable' => 'yes',
320 'protected_content_mode' => 'role',
321 ],
322 ]
323 );
324
325 // =============================================
326 // PASSWORD MODE (Pro)
327 // =============================================
328
329 $element->add_control(
330 'protected_content_password_heading',
331 [
332 'label' => $this->get_pro_label(__('Password Settings', 'king-addons')),
333 'type' => Controls_Manager::HEADING,
334 'separator' => 'before',
335 'condition' => [
336 'protected_content_enable' => 'yes',
337 'protected_content_mode' => 'password',
338 ],
339 ]
340 );
341
342 $element->add_control(
343 'protected_content_password_type',
344 [
345 'label' => esc_html__('Password Scope', 'king-addons'),
346 'type' => Controls_Manager::SELECT,
347 'options' => [
348 'element' => esc_html__('Per Element', 'king-addons'),
349 'global' => esc_html__('Global Key', 'king-addons'),
350 ],
351 'default' => 'element',
352 'condition' => [
353 'protected_content_enable' => 'yes',
354 'protected_content_mode' => 'password',
355 ],
356 ]
357 );
358
359 $element->add_control(
360 'protected_content_password',
361 [
362 'label' => esc_html__('Password', 'king-addons'),
363 'type' => Controls_Manager::TEXT,
364 'input_type' => 'password',
365 'condition' => [
366 'protected_content_enable' => 'yes',
367 'protected_content_mode' => 'password',
368 'protected_content_password_type' => 'element',
369 ],
370 ]
371 );
372
373 $element->add_control(
374 'protected_content_password_key',
375 [
376 'label' => esc_html__('Global Key', 'king-addons'),
377 'description' => esc_html__('Shared key for multiple elements', 'king-addons'),
378 'type' => Controls_Manager::TEXT,
379 'condition' => [
380 'protected_content_enable' => 'yes',
381 'protected_content_mode' => 'password',
382 'protected_content_password_type' => 'global',
383 ],
384 ]
385 );
386
387 $element->add_control(
388 'protected_content_password_global_password',
389 [
390 'label' => esc_html__('Global Password', 'king-addons'),
391 'type' => Controls_Manager::TEXT,
392 'input_type' => 'password',
393 'condition' => [
394 'protected_content_enable' => 'yes',
395 'protected_content_mode' => 'password',
396 'protected_content_password_type' => 'global',
397 ],
398 ]
399 );
400
401 $element->add_control(
402 'protected_content_password_cookie_days',
403 [
404 'label' => esc_html__('Remember for (days)', 'king-addons'),
405 'type' => Controls_Manager::NUMBER,
406 'default' => 7,
407 'min' => 1,
408 'max' => 365,
409 'condition' => [
410 'protected_content_enable' => 'yes',
411 'protected_content_mode' => 'password',
412 ],
413 ]
414 );
415
416 // =============================================
417 // CONDITIONS MODE
418 // =============================================
419
420 $element->add_control(
421 'protected_content_conditions_heading',
422 [
423 'label' => esc_html__('Conditions Settings', 'king-addons'),
424 'type' => Controls_Manager::HEADING,
425 'separator' => 'before',
426 'condition' => [
427 'protected_content_enable' => 'yes',
428 'protected_content_mode' => 'conditions',
429 ],
430 ]
431 );
432
433 // Login Status (Free)
434 $element->add_control(
435 'protected_content_cond_login',
436 [
437 'label' => esc_html__('Login Status', 'king-addons'),
438 'type' => Controls_Manager::SELECT,
439 'options' => [
440 'any' => esc_html__('Any', 'king-addons'),
441 'logged_in' => esc_html__('Logged In', 'king-addons'),
442 'logged_out' => esc_html__('Logged Out', 'king-addons'),
443 ],
444 'default' => 'any',
445 'condition' => [
446 'protected_content_enable' => 'yes',
447 'protected_content_mode' => 'conditions',
448 ],
449 ]
450 );
451
452 // Device (Free)
453 $element->add_control(
454 'protected_content_cond_device',
455 [
456 'label' => esc_html__('Allowed Devices', 'king-addons'),
457 'description' => esc_html__('Leave empty to allow all devices', 'king-addons'),
458 'type' => Controls_Manager::SELECT2,
459 'multiple' => true,
460 'options' => [
461 'desktop' => esc_html__('Desktop', 'king-addons'),
462 'tablet' => esc_html__('Tablet', 'king-addons'),
463 'mobile' => esc_html__('Mobile', 'king-addons'),
464 ],
465 'condition' => [
466 'protected_content_enable' => 'yes',
467 'protected_content_mode' => 'conditions',
468 ],
469 ]
470 );
471
472 // Browser (Pro)
473 $element->add_control(
474 'protected_content_cond_browser',
475 [
476 'label' => $this->get_pro_label(__('Browser', 'king-addons')),
477 'description' => esc_html__('Leave empty to allow all browsers', 'king-addons'),
478 'type' => Controls_Manager::SELECT2,
479 'multiple' => true,
480 'options' => [
481 'chrome' => esc_html__('Chrome', 'king-addons'),
482 'firefox' => esc_html__('Firefox', 'king-addons'),
483 'safari' => esc_html__('Safari', 'king-addons'),
484 'edge' => esc_html__('Edge', 'king-addons'),
485 'opera' => esc_html__('Opera', 'king-addons'),
486 'ie' => esc_html__('Internet Explorer', 'king-addons'),
487 ],
488 'condition' => [
489 'protected_content_enable' => 'yes',
490 'protected_content_mode' => 'conditions',
491 ],
492 ]
493 );
494
495 // Date Range (Pro)
496 $element->add_control(
497 'protected_content_date_heading',
498 [
499 'label' => $this->get_pro_label(__('Date Range', 'king-addons')),
500 'type' => Controls_Manager::HEADING,
501 'separator' => 'before',
502 'condition' => [
503 'protected_content_enable' => 'yes',
504 'protected_content_mode' => 'conditions',
505 ],
506 ]
507 );
508
509 $element->add_control(
510 'protected_content_cond_date_from',
511 [
512 'label' => esc_html__('From Date', 'king-addons'),
513 'type' => Controls_Manager::DATE_TIME,
514 'condition' => [
515 'protected_content_enable' => 'yes',
516 'protected_content_mode' => 'conditions',
517 ],
518 ]
519 );
520
521 $element->add_control(
522 'protected_content_cond_date_to',
523 [
524 'label' => esc_html__('To Date', 'king-addons'),
525 'type' => Controls_Manager::DATE_TIME,
526 'condition' => [
527 'protected_content_enable' => 'yes',
528 'protected_content_mode' => 'conditions',
529 ],
530 ]
531 );
532
533 // URL Parameters (Pro)
534 $element->add_control(
535 'protected_content_url_heading',
536 [
537 'label' => $this->get_pro_label(__('URL Parameters', 'king-addons')),
538 'type' => Controls_Manager::HEADING,
539 'separator' => 'before',
540 'condition' => [
541 'protected_content_enable' => 'yes',
542 'protected_content_mode' => 'conditions',
543 ],
544 ]
545 );
546
547 $element->add_control(
548 'protected_content_cond_url_param',
549 [
550 'label' => esc_html__('URL Parameter Name', 'king-addons'),
551 'type' => Controls_Manager::TEXT,
552 'placeholder' => 'ref',
553 'description' => esc_html__('e.g., "ref" for ?ref=value', 'king-addons'),
554 'condition' => [
555 'protected_content_enable' => 'yes',
556 'protected_content_mode' => 'conditions',
557 ],
558 ]
559 );
560
561 $element->add_control(
562 'protected_content_cond_url_value',
563 [
564 'label' => esc_html__('URL Parameter Value', 'king-addons'),
565 'type' => Controls_Manager::TEXT,
566 'placeholder' => 'campaign',
567 'description' => esc_html__('Leave empty to check only parameter existence', 'king-addons'),
568 'condition' => [
569 'protected_content_enable' => 'yes',
570 'protected_content_mode' => 'conditions',
571 ],
572 ]
573 );
574
575 // WooCommerce Conditions (Pro)
576 $element->add_control(
577 'protected_content_woo_heading',
578 [
579 'label' => $this->get_pro_label(__('WooCommerce Conditions', 'king-addons')),
580 'type' => Controls_Manager::HEADING,
581 'separator' => 'before',
582 'condition' => [
583 'protected_content_enable' => 'yes',
584 'protected_content_mode' => 'conditions',
585 ],
586 ]
587 );
588
589 $element->add_control(
590 'protected_content_cond_woo_in_cart_product_ids',
591 [
592 'label' => esc_html__('Products in Cart', 'king-addons'),
593 'type' => Controls_Manager::TEXT,
594 'placeholder' => '123, 456, 789',
595 'description' => esc_html__('Comma-separated product IDs. Show if ANY is in cart.', 'king-addons'),
596 'condition' => [
597 'protected_content_enable' => 'yes',
598 'protected_content_mode' => 'conditions',
599 ],
600 ]
601 );
602
603 $element->add_control(
604 'protected_content_cond_woo_min_cart_total',
605 [
606 'label' => esc_html__('Minimum Cart Total', 'king-addons'),
607 'type' => Controls_Manager::NUMBER,
608 'placeholder' => '50.00',
609 'description' => esc_html__('Show content if cart total is at least this amount', 'king-addons'),
610 'condition' => [
611 'protected_content_enable' => 'yes',
612 'protected_content_mode' => 'conditions',
613 ],
614 ]
615 );
616
617 $element->add_control(
618 'protected_content_cond_woo_customer_bought_ids',
619 [
620 'label' => esc_html__('Customer Bought Products', 'king-addons'),
621 'type' => Controls_Manager::TEXT,
622 'placeholder' => '123, 456',
623 'description' => esc_html__('Comma-separated product IDs. Show if user purchased ALL.', 'king-addons'),
624 'condition' => [
625 'protected_content_enable' => 'yes',
626 'protected_content_mode' => 'conditions',
627 ],
628 ]
629 );
630
631 // =============================================
632 // LOCKER MODE (Pro)
633 // =============================================
634
635 $element->add_control(
636 'protected_content_locker_heading',
637 [
638 'label' => $this->get_pro_label(__('Content Locker Settings', 'king-addons')),
639 'type' => Controls_Manager::HEADING,
640 'separator' => 'before',
641 'condition' => [
642 'protected_content_enable' => 'yes',
643 'protected_content_mode' => 'locker',
644 ],
645 ]
646 );
647
648 $element->add_control(
649 'protected_content_locker_method',
650 [
651 'label' => esc_html__('Unlock Method', 'king-addons'),
652 'type' => Controls_Manager::SELECT,
653 'options' => [
654 'form_submit' => esc_html__('After Form Submit', 'king-addons'),
655 'manual_token' => esc_html__('By Token', 'king-addons'),
656 ],
657 'default' => 'form_submit',
658 'condition' => [
659 'protected_content_enable' => 'yes',
660 'protected_content_mode' => 'locker',
661 ],
662 ]
663 );
664
665 $element->add_control(
666 'protected_content_locker_token',
667 [
668 'label' => esc_html__('Locker Token', 'king-addons'),
669 'description' => esc_html__('Unique identifier for this locked content', 'king-addons'),
670 'type' => Controls_Manager::TEXT,
671 'placeholder' => 'newsletter-unlock',
672 'condition' => [
673 'protected_content_enable' => 'yes',
674 'protected_content_mode' => 'locker',
675 ],
676 ]
677 );
678
679 $element->add_control(
680 'protected_content_locker_form',
681 [
682 'label' => esc_html__('Form Shortcode', 'king-addons'),
683 'description' => esc_html__('Form to display for unlocking content', 'king-addons'),
684 'type' => Controls_Manager::TEXT,
685 'placeholder' => '[contact-form-7 id="123"]',
686 'condition' => [
687 'protected_content_enable' => 'yes',
688 'protected_content_mode' => 'locker',
689 'protected_content_locker_method' => 'form_submit',
690 ],
691 ]
692 );
693
694 $element->add_control(
695 'protected_content_locker_cookie_days',
696 [
697 'label' => esc_html__('Remember for (days)', 'king-addons'),
698 'type' => Controls_Manager::NUMBER,
699 'default' => 7,
700 'min' => 1,
701 'max' => 365,
702 'condition' => [
703 'protected_content_enable' => 'yes',
704 'protected_content_mode' => 'locker',
705 ],
706 ]
707 );
708
709 $element->end_controls_section();
710 }
711
712 /**
713 * Before render hook.
714 *
715 * @param Element_Base $element Element.
716 *
717 * @return void
718 */
719 public function before_render_element(Element_Base $element): void
720 {
721 $settings = $element->get_settings_for_display();
722
723 if (empty($settings['protected_content_enable']) || 'yes' !== $settings['protected_content_enable']) {
724 return;
725 }
726
727 // In editor or preview: show content, mark protected.
728 if ($this->is_editor_or_preview()) {
729 $element->add_render_attribute('_wrapper', 'data-king-protected', 'yes');
730 $element->add_render_attribute('_wrapper', 'class', 'king-addons-protected-preview');
731
732 // Add protection type info for editor badge.
733 $protection_info = $this->get_protection_info($settings);
734 $element->add_render_attribute('_wrapper', 'data-king-protected-type', esc_attr($protection_info));
735 return;
736 }
737
738 $has_access = $this->evaluate_access($settings, $element);
739
740 if (!empty($settings['protected_content_invert']) && 'yes' === $settings['protected_content_invert']) {
741 $has_access = !$has_access;
742 }
743
744 if ($has_access) {
745 return;
746 }
747
748 // Special handling for password mode - show password form.
749 $mode = $settings['protected_content_mode'] ?? 'role';
750 if ('password' === $mode && $this->can_use_pro()) {
751 $this->render_password_form($settings, $element);
752 $this->suppress_element_render($element);
753 return;
754 }
755
756 $fallback = $settings['protected_content_fallback_type'] ?? 'none';
757
758 switch ($fallback) {
759 case 'message':
760 $this->render_fallback_message($settings);
761 $this->suppress_element_render($element);
762 return;
763
764 case 'template':
765 if ($this->can_use_pro() && !empty($settings['protected_content_fallback_template'])) {
766 $this->render_fallback_template((int) $settings['protected_content_fallback_template']);
767 $this->suppress_element_render($element);
768 return;
769 }
770 $this->render_fallback_message($settings);
771 $this->suppress_element_render($element);
772 return;
773
774 case 'form':
775 if ($this->can_use_pro() && !empty($settings['protected_content_fallback_form'])) {
776 echo '<div class="king-addons-protected-fallback king-addons-protected-form-fallback">';
777 echo do_shortcode($settings['protected_content_fallback_form']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
778 echo '</div>';
779 $this->suppress_element_render($element);
780 return;
781 }
782 $this->render_fallback_message($settings);
783 $this->suppress_element_render($element);
784 return;
785
786 case 'none':
787 default:
788 $this->suppress_element_render($element);
789 return;
790 }
791 }
792
793 /**
794 * Evaluate access.
795 *
796 * @param array<string,mixed> $settings Settings.
797 * @param Element_Base $element Element.
798 *
799 * @return bool
800 */
801 private function evaluate_access(array $settings, Element_Base $element): bool
802 {
803 $mode = $settings['protected_content_mode'] ?? 'role';
804
805 switch ($mode) {
806 case 'role':
807 return $this->check_role_access($settings);
808
809 case 'password':
810 return $this->check_password_access($settings, $element);
811
812 case 'conditions':
813 return $this->check_conditions_access($settings);
814
815 case 'locker':
816 return $this->check_locker_access($settings);
817
818 default:
819 return true;
820 }
821 }
822
823 /**
824 * Role-based access.
825 *
826 * @param array<string,mixed> $settings Settings.
827 *
828 * @return bool
829 */
830 private function check_role_access(array $settings): bool
831 {
832 $require_login = !empty($settings['protected_content_require_login']) && 'yes' === $settings['protected_content_require_login'];
833
834 if (!is_user_logged_in()) {
835 return !$require_login;
836 }
837
838 // User is logged in.
839 if (empty($settings['protected_content_roles']) || !is_array($settings['protected_content_roles'])) {
840 // No specific roles required - all logged in users have access.
841 return true;
842 }
843
844 $user = wp_get_current_user();
845 foreach ($user->roles as $role) {
846 if (in_array($role, $settings['protected_content_roles'], true)) {
847 return true;
848 }
849 }
850
851 return false;
852 }
853
854 /**
855 * Password access.
856 *
857 * @param array<string,mixed> $settings Settings.
858 * @param Element_Base $element Element.
859 *
860 * @return bool
861 */
862 private function check_password_access(array $settings, Element_Base $element): bool
863 {
864 if (!$this->can_use_pro()) {
865 return true;
866 }
867
868 $scope = $settings['protected_content_password_type'] ?? 'element';
869
870 if ('element' === $scope) {
871 $cookie_name = 'king_protect_' . $element->get_id();
872 return !empty($_COOKIE[$cookie_name]) && '1' === $_COOKIE[$cookie_name];
873 }
874
875 // Global scope.
876 $key = $settings['protected_content_password_key'] ?? '';
877 if (empty($key)) {
878 return false;
879 }
880
881 $cookie_name = 'king_protect_global_' . sanitize_key($key);
882 return !empty($_COOKIE[$cookie_name]) && '1' === $_COOKIE[$cookie_name];
883 }
884
885 /**
886 * Conditions access.
887 *
888 * @param array<string,mixed> $settings Settings.
889 *
890 * @return bool
891 */
892 private function check_conditions_access(array $settings): bool
893 {
894 // Login condition (Free).
895 $login_cond = $settings['protected_content_cond_login'] ?? 'any';
896 if ('logged_in' === $login_cond && !is_user_logged_in()) {
897 return false;
898 }
899 if ('logged_out' === $login_cond && is_user_logged_in()) {
900 return false;
901 }
902
903 // Device condition (Free).
904 if (!empty($settings['protected_content_cond_device']) && is_array($settings['protected_content_cond_device'])) {
905 $device = $this->detect_device();
906 if (!in_array($device, $settings['protected_content_cond_device'], true)) {
907 return false;
908 }
909 }
910
911 // Pro conditions.
912 if ($this->can_use_pro()) {
913 // Browser condition.
914 if (!empty($settings['protected_content_cond_browser']) && is_array($settings['protected_content_cond_browser'])) {
915 $browser = $this->detect_browser();
916 if (!in_array($browser, $settings['protected_content_cond_browser'], true)) {
917 return false;
918 }
919 }
920
921 // Date range.
922 $now = wp_date('U'); // Use WordPress timezone.
923 if (!empty($settings['protected_content_cond_date_from'])) {
924 $from = strtotime((string) $settings['protected_content_cond_date_from']);
925 if ($from && $now < $from) {
926 return false;
927 }
928 }
929 if (!empty($settings['protected_content_cond_date_to'])) {
930 $to = strtotime((string) $settings['protected_content_cond_date_to']);
931 if ($to && $now > $to) {
932 return false;
933 }
934 }
935
936 // URL parameter.
937 if (!empty($settings['protected_content_cond_url_param'])) {
938 $param = sanitize_text_field($settings['protected_content_cond_url_param']);
939 $value = $settings['protected_content_cond_url_value'] ?? '';
940
941 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
942 if (!isset($_GET[$param])) {
943 return false;
944 }
945
946 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
947 if ('' !== $value && sanitize_text_field($_GET[$param]) !== $value) {
948 return false;
949 }
950 }
951
952 // WooCommerce conditions.
953 if (class_exists('WooCommerce')) {
954 // Products in cart.
955 if (!empty($settings['protected_content_cond_woo_in_cart_product_ids'])) {
956 $ids = $this->sanitize_ids($settings['protected_content_cond_woo_in_cart_product_ids']);
957 if (!empty($ids) && !$this->woo_cart_has_products($ids)) {
958 return false;
959 }
960 }
961
962 // Minimum cart total.
963 if (!empty($settings['protected_content_cond_woo_min_cart_total'])) {
964 $min_total = (float) $settings['protected_content_cond_woo_min_cart_total'];
965 $total = (float) (WC()->cart ? WC()->cart->get_total('edit') : 0);
966 if ($total < $min_total) {
967 return false;
968 }
969 }
970
971 // Customer bought products.
972 if (!empty($settings['protected_content_cond_woo_customer_bought_ids']) && is_user_logged_in()) {
973 $ids = $this->sanitize_ids($settings['protected_content_cond_woo_customer_bought_ids']);
974 if (!empty($ids)) {
975 $user = wp_get_current_user();
976 foreach ($ids as $pid) {
977 if (!wc_customer_bought_product($user->user_email, $user->ID, $pid)) {
978 return false;
979 }
980 }
981 }
982 }
983 }
984 }
985
986 return true;
987 }
988
989 /**
990 * Locker access.
991 *
992 * @param array<string,mixed> $settings Settings.
993 *
994 * @return bool
995 */
996 private function check_locker_access(array $settings): bool
997 {
998 if (!$this->can_use_pro()) {
999 return true;
1000 }
1001
1002 $token = $settings['protected_content_locker_token'] ?? '';
1003 if (empty($token)) {
1004 return false;
1005 }
1006
1007 $cookie_name = 'king_locker_' . sanitize_key($token);
1008 return !empty($_COOKIE[$cookie_name]) && '1' === $_COOKIE[$cookie_name];
1009 }
1010
1011 /**
1012 * Set locker cookie after unlock event.
1013 *
1014 * @param string $token Token.
1015 * @param int $days Days to remember (default 7).
1016 *
1017 * @return void
1018 */
1019 public function set_locker_cookie(string $token, int $days = 7): void
1020 {
1021 $cookie_name = 'king_locker_' . sanitize_key($token);
1022 $expiry = time() + ($days * DAY_IN_SECONDS);
1023 setcookie($cookie_name, '1', $expiry, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
1024 }
1025
1026 /**
1027 * AJAX handler for password verification.
1028 *
1029 * @return void
1030 */
1031 public function ajax_verify_password(): void
1032 {
1033 check_ajax_referer('king_addons_protected_content', 'nonce');
1034
1035 if (!$this->can_use_pro()) {
1036 wp_send_json_error(['message' => esc_html__('Pro feature', 'king-addons')]);
1037 }
1038
1039 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1040 $password = isset($_POST['password']) ? sanitize_text_field(wp_unslash($_POST['password'])) : '';
1041 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1042 $element_id = isset($_POST['element_id']) ? sanitize_text_field(wp_unslash($_POST['element_id'])) : '';
1043 $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
1044 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1045 $scope = isset($_POST['scope']) ? sanitize_text_field(wp_unslash($_POST['scope'])) : 'element';
1046 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1047 $global_key = isset($_POST['global_key']) ? sanitize_text_field(wp_unslash($_POST['global_key'])) : '';
1048 $days = isset($_POST['days']) ? absint($_POST['days']) : 7;
1049
1050 $days = max(1, min(365, $days));
1051
1052 if (empty($password) || empty($post_id) || empty($element_id)) {
1053 wp_send_json_error(['message' => esc_html__('Invalid request', 'king-addons')]);
1054 }
1055
1056 if (!in_array($scope, ['element', 'global'], true)) {
1057 wp_send_json_error(['message' => esc_html__('Invalid request', 'king-addons')]);
1058 }
1059
1060 // Get element settings from post meta.
1061 $elementor_data = get_post_meta($post_id, '_elementor_data', true);
1062 if (empty($elementor_data)) {
1063 wp_send_json_error(['message' => esc_html__('Element not found', 'king-addons')]);
1064 }
1065
1066 if (is_array($elementor_data)) {
1067 $data = $elementor_data;
1068 } else {
1069 $data = json_decode((string) $elementor_data, true);
1070 }
1071 if (!is_array($data)) {
1072 wp_send_json_error(['message' => esc_html__('Invalid data', 'king-addons')]);
1073 }
1074
1075 // Find element settings.
1076 $settings = $this->find_element_settings($data, $element_id);
1077 if (null === $settings) {
1078 wp_send_json_error(['message' => esc_html__('Element not found', 'king-addons')]);
1079 }
1080
1081 // Validate password mode configuration.
1082 $mode = $settings['protected_content_mode'] ?? '';
1083 $enabled = $settings['protected_content_enable'] ?? '';
1084 if ('yes' !== $enabled || 'password' !== $mode) {
1085 wp_send_json_error(['message' => esc_html__('Configuration error', 'king-addons')]);
1086 }
1087
1088 $stored_scope = $settings['protected_content_password_type'] ?? 'element';
1089 if (!in_array($stored_scope, ['element', 'global'], true)) {
1090 $stored_scope = 'element';
1091 }
1092
1093 if ($stored_scope !== $scope) {
1094 wp_send_json_error(['message' => esc_html__('Configuration error', 'king-addons')]);
1095 }
1096
1097 if ('global' === $scope) {
1098 $stored_key = isset($settings['protected_content_password_key']) ? sanitize_text_field((string) $settings['protected_content_password_key']) : '';
1099 if (empty($stored_key) || empty($global_key) || sanitize_key($stored_key) !== sanitize_key($global_key)) {
1100 wp_send_json_error(['message' => esc_html__('Configuration error', 'king-addons')]);
1101 }
1102 $stored_password = $settings['protected_content_password_global_password'] ?? null;
1103 } else {
1104 $stored_password = $settings['protected_content_password'] ?? null;
1105 }
1106
1107 if (empty($stored_password) || !is_string($stored_password)) {
1108 wp_send_json_error(['message' => esc_html__('Configuration error', 'king-addons')]);
1109 }
1110
1111 if (!hash_equals((string) $stored_password, (string) $password)) {
1112 wp_send_json_error(['message' => esc_html__('Incorrect password', 'king-addons')]);
1113 }
1114
1115 // Set cookie.
1116 $expiry = time() + ($days * DAY_IN_SECONDS);
1117 if ('global' === $scope && !empty($global_key)) {
1118 $cookie_name = 'king_protect_global_' . sanitize_key($global_key);
1119 } else {
1120 $cookie_name = 'king_protect_' . $element_id;
1121 }
1122
1123 setcookie($cookie_name, '1', $expiry, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
1124
1125 wp_send_json_success(['message' => esc_html__('Access granted', 'king-addons')]);
1126 }
1127
1128 /**
1129 * Find element settings in Elementor data.
1130 *
1131 * @param array<mixed> $data Elementor data.
1132 * @param string $element_id Element ID.
1133 *
1134 * @return array<string,mixed>|null
1135 */
1136 private function find_element_settings(array $data, string $element_id): ?array
1137 {
1138 foreach ($data as $element) {
1139 if (!is_array($element)) {
1140 continue;
1141 }
1142
1143 $id = $element['id'] ?? '';
1144 $settings = $element['settings'] ?? [];
1145
1146 if ($id === $element_id) {
1147 return is_array($settings) ? $settings : [];
1148 }
1149
1150 // Recursively search in children.
1151 if (!empty($element['elements']) && is_array($element['elements'])) {
1152 $found = $this->find_element_settings($element['elements'], $element_id);
1153 if (null !== $found) {
1154 return $found;
1155 }
1156 }
1157 }
1158
1159 return null;
1160 }
1161
1162 /**
1163 * Render password form.
1164 *
1165 * @param array<string,mixed> $settings Settings.
1166 * @param Element_Base $element Element.
1167 *
1168 * @return void
1169 */
1170 private function render_password_form(array $settings, Element_Base $element): void
1171 {
1172 $scope = $settings['protected_content_password_type'] ?? 'element';
1173 $global_key = $settings['protected_content_password_key'] ?? '';
1174 $days = (int) ($settings['protected_content_password_cookie_days'] ?? 7);
1175 $message = $settings['protected_content_fallback_msg'] ?? esc_html__('This content is password protected.', 'king-addons');
1176
1177 $document_post_id = $this->get_current_elementor_document_id();
1178 if ($document_post_id <= 0) {
1179 $document_post_id = (int) get_queried_object_id();
1180 }
1181
1182 $days = max(1, min(365, $days));
1183
1184 ?>
1185 <div class="king-addons-protected-fallback king-addons-protected-password-form">
1186 <div class="king-addons-protected-password-message"><?php echo esc_html($message); ?></div>
1187 <form class="king-addons-password-form"
1188 data-element-id="<?php echo esc_attr($element->get_id()); ?>"
1189 data-post-id="<?php echo esc_attr($document_post_id); ?>"
1190 data-scope="<?php echo esc_attr($scope); ?>"
1191 data-global-key="<?php echo esc_attr($global_key); ?>"
1192 data-days="<?php echo esc_attr($days); ?>">
1193 <div class="king-addons-password-input-wrap">
1194 <input type="password"
1195 class="king-addons-password-input"
1196 placeholder="<?php esc_attr_e('Enter password', 'king-addons'); ?>"
1197 required />
1198 <button type="submit" class="king-addons-password-submit">
1199 <span class="king-addons-password-submit-text"><?php esc_html_e('Unlock', 'king-addons'); ?></span>
1200 <span class="king-addons-password-submit-loading" style="display:none;"></span>
1201 </button>
1202 </div>
1203 <div class="king-addons-password-error" style="display:none;"></div>
1204 </form>
1205 </div>
1206 <?php
1207 }
1208
1209 /**
1210 * Render fallback message.
1211 *
1212 * @param array<string,mixed> $settings Settings.
1213 *
1214 * @return void
1215 */
1216 private function render_fallback_message(array $settings): void
1217 {
1218 $msg = $settings['protected_content_fallback_msg'] ?? esc_html__('This content is protected.', 'king-addons');
1219 echo '<div class="king-addons-protected-fallback">' . esc_html($msg) . '</div>';
1220 }
1221
1222 /**
1223 * Render Elementor template.
1224 *
1225 * @param int $template_id Template ID.
1226 *
1227 * @return void
1228 */
1229 private function render_fallback_template(int $template_id): void
1230 {
1231 $frontend = Plugin::$instance->frontend;
1232 echo '<div class="king-addons-protected-fallback king-addons-protected-template-fallback">';
1233 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1234 echo $frontend->get_builder_content_for_display($template_id);
1235 echo '</div>';
1236 }
1237
1238 /**
1239 * Suppress element rendering.
1240 *
1241 * @param Element_Base $element Element.
1242 *
1243 * @return void
1244 */
1245 private function suppress_element_render(Element_Base $element): void
1246 {
1247 // Try modern Elementor method first.
1248 if (method_exists($element, 'set_should_render')) {
1249 $element->set_should_render(false);
1250 return;
1251 }
1252
1253 // Fallback: hide with CSS.
1254 $element->add_render_attribute('_wrapper', 'class', 'king-addons-protected-hidden');
1255 }
1256
1257 /**
1258 * Check if in editor or preview mode.
1259 *
1260 * @return bool
1261 */
1262 private function is_editor_or_preview(): bool
1263 {
1264 if (!class_exists('\Elementor\Plugin')) {
1265 return false;
1266 }
1267
1268 $editor = Plugin::$instance->editor;
1269 $preview = Plugin::$instance->preview;
1270
1271 if ($editor && $editor->is_edit_mode()) {
1272 return true;
1273 }
1274
1275 if ($preview && $preview->is_preview_mode()) {
1276 return true;
1277 }
1278
1279 return false;
1280 }
1281
1282 /**
1283 * Get current Elementor document post ID (important for Theme Builder templates).
1284 *
1285 * @return int
1286 */
1287 private function get_current_elementor_document_id(): int
1288 {
1289 if (!class_exists('\Elementor\Plugin') || !Plugin::$instance || !isset(Plugin::$instance->documents)) {
1290 return 0;
1291 }
1292
1293 $document = Plugin::$instance->documents->get_current();
1294 if ($document && method_exists($document, 'get_main_id')) {
1295 return (int) $document->get_main_id();
1296 }
1297
1298 return 0;
1299 }
1300
1301 /**
1302 * Get protection info for editor badge.
1303 *
1304 * @param array<string,mixed> $settings Settings.
1305 *
1306 * @return string
1307 */
1308 private function get_protection_info(array $settings): string
1309 {
1310 $mode = $settings['protected_content_mode'] ?? 'role';
1311 $info = [];
1312
1313 switch ($mode) {
1314 case 'role':
1315 $info[] = 'Role/Login';
1316 if (!empty($settings['protected_content_roles'])) {
1317 $info[] = implode(', ', (array) $settings['protected_content_roles']);
1318 }
1319 break;
1320
1321 case 'password':
1322 $scope = $settings['protected_content_password_type'] ?? 'element';
1323 $info[] = 'Password (' . $scope . ')';
1324 break;
1325
1326 case 'conditions':
1327 $info[] = 'Conditions';
1328 if (!empty($settings['protected_content_cond_login']) && 'any' !== $settings['protected_content_cond_login']) {
1329 $info[] = $settings['protected_content_cond_login'];
1330 }
1331 if (!empty($settings['protected_content_cond_device'])) {
1332 $info[] = 'Device: ' . implode(', ', (array) $settings['protected_content_cond_device']);
1333 }
1334 break;
1335
1336 case 'locker':
1337 $info[] = 'Content Locker';
1338 if (!empty($settings['protected_content_locker_token'])) {
1339 $info[] = 'Token: ' . $settings['protected_content_locker_token'];
1340 }
1341 break;
1342 }
1343
1344 return implode(' | ', $info);
1345 }
1346
1347 /**
1348 * Detect device type.
1349 *
1350 * @return string desktop|tablet|mobile
1351 */
1352 private function detect_device(): string
1353 {
1354 if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1355 return 'desktop';
1356 }
1357
1358 $ua = strtolower($_SERVER['HTTP_USER_AGENT']);
1359
1360 // Tablet detection (before mobile since tablets often have "mobile" in UA).
1361 $tablets = ['ipad', 'tablet', 'kindle', 'silk', 'playbook'];
1362 foreach ($tablets as $tablet) {
1363 if (strpos($ua, $tablet) !== false) {
1364 return 'tablet';
1365 }
1366 }
1367
1368 // Android tablet (no "mobile" in UA).
1369 if (strpos($ua, 'android') !== false && strpos($ua, 'mobile') === false) {
1370 return 'tablet';
1371 }
1372
1373 // Mobile detection.
1374 if (wp_is_mobile()) {
1375 return 'mobile';
1376 }
1377
1378 return 'desktop';
1379 }
1380
1381 /**
1382 * Detect browser.
1383 *
1384 * @return string
1385 */
1386 private function detect_browser(): string
1387 {
1388 if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1389 return 'unknown';
1390 }
1391
1392 $ua = $_SERVER['HTTP_USER_AGENT'];
1393
1394 // Order matters: Edge contains "Chrome", Chrome contains "Safari".
1395 if (preg_match('/Edg/i', $ua)) {
1396 return 'edge';
1397 }
1398 if (preg_match('/OPR|Opera/i', $ua)) {
1399 return 'opera';
1400 }
1401 if (preg_match('/Chrome/i', $ua)) {
1402 return 'chrome';
1403 }
1404 if (preg_match('/Safari/i', $ua)) {
1405 return 'safari';
1406 }
1407 if (preg_match('/Firefox/i', $ua)) {
1408 return 'firefox';
1409 }
1410 if (preg_match('/MSIE|Trident/i', $ua)) {
1411 return 'ie';
1412 }
1413
1414 return 'unknown';
1415 }
1416
1417 /**
1418 * Get WP roles as options with caching.
1419 *
1420 * @return array<string,string>
1421 */
1422 private function get_wp_roles_for_control(): array
1423 {
1424 if (null !== self::$roles_cache) {
1425 return self::$roles_cache;
1426 }
1427
1428 global $wp_roles;
1429
1430 if (!isset($wp_roles)) {
1431 $wp_roles = wp_roles();
1432 }
1433
1434 self::$roles_cache = [];
1435 foreach ($wp_roles->roles as $key => $role) {
1436 self::$roles_cache[$key] = $role['name'];
1437 }
1438
1439 return self::$roles_cache;
1440 }
1441
1442 /**
1443 * Get Elementor templates with caching.
1444 *
1445 * @return array<int,string>
1446 */
1447 private function get_elementor_templates(): array
1448 {
1449 if (null !== self::$templates_cache) {
1450 return self::$templates_cache;
1451 }
1452
1453 self::$templates_cache = [];
1454
1455 $templates = get_posts([
1456 'post_type' => 'elementor_library',
1457 'post_status' => 'publish',
1458 'posts_per_page' => -1,
1459 'orderby' => 'title',
1460 'order' => 'ASC',
1461 ]);
1462
1463 foreach ($templates as $template) {
1464 self::$templates_cache[$template->ID] = $template->post_title;
1465 }
1466
1467 return self::$templates_cache;
1468 }
1469
1470 /**
1471 * Check Woo cart for products.
1472 *
1473 * @param array<int> $ids Product IDs.
1474 *
1475 * @return bool
1476 */
1477 private function woo_cart_has_products(array $ids): bool
1478 {
1479 if (!function_exists('WC') || !WC()->cart) {
1480 return false;
1481 }
1482
1483 foreach (WC()->cart->get_cart() as $cart_item) {
1484 $pid = (int) ($cart_item['product_id'] ?? 0);
1485 $vid = (int) ($cart_item['variation_id'] ?? 0);
1486
1487 if (in_array($pid, $ids, true) || ($vid > 0 && in_array($vid, $ids, true))) {
1488 return true;
1489 }
1490 }
1491
1492 return false;
1493 }
1494
1495 /**
1496 * Sanitize comma-separated IDs.
1497 *
1498 * @param string $raw Raw string.
1499 *
1500 * @return array<int>
1501 */
1502 private function sanitize_ids(string $raw): array
1503 {
1504 $ids = array_filter(array_map('absint', array_map('trim', explode(',', $raw))));
1505 return array_values($ids);
1506 }
1507
1508 /**
1509 * Is pro available.
1510 *
1511 * @return bool
1512 */
1513 private function can_use_pro(): bool
1514 {
1515 if (!function_exists('king_addons_freemius')) {
1516 return false;
1517 }
1518
1519 return king_addons_freemius()->can_use_premium_code__premium_only();
1520 }
1521
1522 /**
1523 * Get label with Pro icon.
1524 *
1525 * @param string $label Label text.
1526 *
1527 * @return string
1528 */
1529 private function get_pro_label(string $label): string
1530 {
1531 if ($this->can_use_pro()) {
1532 return $label;
1533 }
1534
1535 return sprintf('%s <i class="eicon-pro-icon"></i>', $label);
1536 }
1537 }
1538
1539
1540
1541
1542
1543
1544
1545