PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
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 / Core.php

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

1,708 lines 72.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Core class do all things at the start of the plugin
5 */
6
7 namespace King_Addons;
8
9 use Elementor\Plugin;
10 use Elementor\Widgets_Manager;
11 use Elementor\Controls_Manager;
12 use King_Addons\Wishlist\Wishlist_Module;
13
14 if (!defined('ABSPATH')) {
15 exit; // Exit if accessed directly.
16 }
17
18 final class Core
19 {
20 /**
21 * Instance
22 *
23 * @var Core|null The single instance of the class.
24 */
25 private static ?Core $_instance = null;
26
27 /**
28 * Wishlist module instance.
29 *
30 * @var Wishlist_Module|null
31 */
32 private ?Wishlist_Module $wishlist_module = null;
33
34 /**
35 * Instance
36 *
37 * Ensures only one instance of the class is loaded or can be loaded.
38 *
39 * @return Core An instance of the class.
40 * @since 1.0.0
41 */
42 public static function instance(): Core
43 {
44 if (is_null(self::$_instance)) {
45 self::$_instance = new self();
46 }
47 return self::$_instance;
48 }
49
50 /**
51 * Check if an extension is enabled.
52 *
53 * Checks database options first, defaults to enabled if not set (new installations).
54 * Falls back to constant check for backward compatibility.
55 *
56 * @param string $extension_id The extension ID (e.g., 'templates-catalog', 'popup-builder').
57 * @param string $constant_name The constant name to check as fallback (e.g., 'KING_ADDONS_EXT_POPUP_BUILDER').
58 * @return bool True if extension is enabled, false otherwise.
59 * @since 1.0.0
60 */
61 private function isExtensionEnabled(string $extension_id, string $constant_name): bool
62 {
63 // Get options from database
64 $options = get_option('king_addons_options', []);
65
66 // If a constant is defined and explicitly false, treat it as a hard disable.
67 // This is useful for extensions that are in development and should not be
68 // available/visible even if the database option is enabled.
69 if ($constant_name !== '' && defined($constant_name) && constant($constant_name) === false) {
70 return false;
71 }
72
73 // Check if option exists in database
74 $option_key = 'ext_' . $extension_id;
75 if (isset($options[$option_key])) {
76 // Option exists, use its value
77 return $options[$option_key] === 'enabled';
78 }
79
80 // Option doesn't exist (new installation), default to enabled
81 // But also check constant as fallback for backward compatibility
82 if ($constant_name !== '' && defined($constant_name)) {
83 return constant($constant_name);
84 }
85
86 // Default to enabled for new installations
87 return true;
88 }
89
90 /**
91 * Constructor
92 *
93 * Perform some compatibility checks to make sure basic requirements are meet.
94 * If all compatibility checks pass, initialize the functionality.
95 *
96 * @since 1.0.0
97 */
98 public function __construct()
99 {
100 require_once(KING_ADDONS_PATH . 'includes/ModulesMap.php');
101 require_once(KING_ADDONS_PATH . 'includes/LibrariesMap.php');
102
103 if ($this->hasElementorCompatibility()) {
104
105 // Initial requirements check
106 require_once(KING_ADDONS_PATH . 'includes/helpers/Check_Requirements/Check_Requirements.php');
107
108 // Templates Catalog
109 if ($this->isExtensionEnabled('templates-catalog', 'KING_ADDONS_EXT_TEMPLATES_CATALOG')) {
110 require_once(KING_ADDONS_PATH . 'includes/TemplatesMap.php');
111 require_once(KING_ADDONS_PATH . 'includes/extensions/Templates/CollectionsMap.php');
112 require_once(KING_ADDONS_PATH . 'includes/extensions/Templates/Templates.php');
113
114 // Template Catalog Button for Elementor Editor
115 require_once(KING_ADDONS_PATH . 'includes/extensions/Template_Catalog_Button/Template_Catalog_Button.php');
116 Template_Catalog_Button::instance();
117 }
118
119 // Header & Footer Builder
120 if ($this->isExtensionEnabled('header-footer-builder', 'KING_ADDONS_EXT_HEADER_FOOTER_BUILDER')) {
121 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/Header_Footer_Builder.php');
122 Header_Footer_Builder::instance();
123 }
124
125 // Popup Builder
126 if ($this->isExtensionEnabled('popup-builder', 'KING_ADDONS_EXT_POPUP_BUILDER')) {
127 require_once(KING_ADDONS_PATH . 'includes/extensions/Popup_Builder/Popup_Builder.php');
128 Popup_Builder::instance();
129 }
130
131 // Cookie / Consent Bar
132 if ($this->isExtensionEnabled('cookie-consent', 'KING_ADDONS_EXT_COOKIE_CONSENT')) {
133 require_once(KING_ADDONS_PATH . 'includes/extensions/Cookie_Consent/Cookie_Consent.php');
134 Cookie_Consent::instance();
135 }
136
137 // WooCommerce Builder
138 if ($this->isExtensionEnabled('woo-builder', 'KING_ADDONS_EXT_WOO_BUILDER')) {
139 require_once(KING_ADDONS_PATH . 'includes/extensions/Woo_Builder/Woo_Builder.php');
140 if (class_exists('King_Addons\\Woo_Builder')) {
141 new Woo_Builder();
142 }
143 }
144
145 // Sticky Contact Bar
146 if ($this->isExtensionEnabled('sticky-contact-bar', 'KING_ADDONS_EXT_STICKY_CONTACT_BAR')) {
147 require_once(KING_ADDONS_PATH . 'includes/extensions/Sticky_Contact_Bar/Sticky_Contact_Bar.php');
148
149 $pro_loaded = false;
150 if (
151 function_exists('king_addons_freemius')
152 && king_addons_freemius()->can_use_premium_code__premium_only()
153 && defined('KING_ADDONS_PRO_PATH')
154 ) {
155 $pro_file_path = KING_ADDONS_PRO_PATH . 'includes/extensions/Sticky_Contact_Bar_Pro/Sticky_Contact_Bar_Pro.php';
156 if (file_exists($pro_file_path)) {
157 require_once $pro_file_path;
158 if (class_exists('King_Addons\\Sticky_Contact_Bar_Pro')) {
159 new Sticky_Contact_Bar_Pro();
160 $pro_loaded = true;
161 }
162 }
163 }
164
165 if (!$pro_loaded && class_exists('King_Addons\\Sticky_Contact_Bar')) {
166 new Sticky_Contact_Bar();
167 }
168 }
169
170 // Theme Builder
171 if ($this->isExtensionEnabled('theme-builder', 'KING_ADDONS_EXT_THEME_BUILDER')) {
172 require_once(KING_ADDONS_PATH . 'includes/extensions/Theme_Builder/Theme_Builder.php');
173
174 $pro_loaded = false;
175 if (
176 function_exists('king_addons_freemius')
177 && king_addons_freemius()->can_use_premium_code__premium_only()
178 && defined('KING_ADDONS_PRO_PATH')
179 ) {
180 $pro_file_path = KING_ADDONS_PRO_PATH . 'includes/extensions/Theme_Builder_Pro/Theme_Builder_Pro.php';
181 if (file_exists($pro_file_path)) {
182 require_once $pro_file_path;
183 if (class_exists('King_Addons\\Theme_Builder_Pro')) {
184 new Theme_Builder_Pro();
185 $pro_loaded = true;
186 }
187 }
188 }
189
190 if (!$pro_loaded && class_exists('King_Addons\\Theme_Builder')) {
191 new Theme_Builder();
192 }
193 }
194
195 // Custom Cursor
196 if ($this->isExtensionEnabled('custom-cursor', 'KING_ADDONS_EXT_CUSTOM_CURSOR')) {
197 require_once(KING_ADDONS_PATH . 'includes/extensions/Custom_Cursor/Custom_Cursor.php');
198 if (class_exists('King_Addons\\Custom_Cursor')) {
199 new Custom_Cursor();
200 }
201 }
202
203 // Age Gate
204 if ($this->isExtensionEnabled('age-gate', 'KING_ADDONS_EXT_AGE_GATE')) {
205 require_once(KING_ADDONS_PATH . 'includes/extensions/Age_Gate/Age_Gate.php');
206 if (defined('KING_ADDONS_PRO_PATH')) {
207 $age_gate_pro = KING_ADDONS_PRO_PATH . 'includes/extensions/Age_Gate_Pro/Age_Gate_Pro.php';
208 if (file_exists($age_gate_pro)) {
209 require_once $age_gate_pro;
210 }
211 }
212 Age_Gate::instance();
213 }
214
215 // Live Chat & Support Builder
216 if ($this->isExtensionEnabled('live-chat', 'KING_ADDONS_EXT_LIVE_CHAT')) {
217 require_once(KING_ADDONS_PATH . 'includes/extensions/Live_Chat/Live_Chat.php');
218 Live_Chat::instance();
219 }
220
221 // Docs & Knowledge Base
222 if ($this->isExtensionEnabled('docs-kb', 'KING_ADDONS_EXT_DOCS_KB')) {
223 require_once(KING_ADDONS_PATH . 'includes/extensions/Docs_KB/Docs_KB.php');
224 Docs_KB::instance();
225 }
226
227 // Pricing Table Builder
228 if ($this->isExtensionEnabled('pricing-table-builder', 'KING_ADDONS_EXT_PRICING_TABLE_BUILDER')) {
229 require_once(KING_ADDONS_PATH . 'includes/extensions/Pricing_Table_Builder/Pricing_Table_Builder.php');
230 Pricing_Table_Builder::instance();
231 }
232
233 // Custom Code Manager
234 if ($this->isExtensionEnabled('custom-code-manager', 'KING_ADDONS_EXT_CUSTOM_CODE_MANAGER')) {
235 require_once(KING_ADDONS_PATH . 'includes/extensions/Custom_Code_Manager/Custom_Code_Manager.php');
236 Custom_Code_Manager::getInstance();
237 }
238
239 // Fomo Notifications
240 if ($this->isExtensionEnabled('fomo-notifications', 'KING_ADDONS_EXT_FOMO_NOTIFICATIONS')) {
241 require_once(KING_ADDONS_PATH . 'includes/extensions/Fomo_Notifications/Fomo_Notifications.php');
242 Fomo_Notifications::instance();
243 }
244
245 // Smart Links
246 if ($this->isExtensionEnabled('smart-links', 'KING_ADDONS_EXT_SMART_LINKS')) {
247 require_once(KING_ADDONS_PATH . 'includes/extensions/Smart_Links/Smart_Links.php');
248 \King_Addons\Smart_Links\Smart_Links::instance();
249 }
250
251 // Activity Log
252 if ($this->isExtensionEnabled('activity-log', 'KING_ADDONS_EXT_ACTIVITY_LOG')) {
253 require_once(KING_ADDONS_PATH . 'includes/extensions/Activity_Log/Activity_Log.php');
254 \King_Addons\Activity_Log\Activity_Log::instance();
255 }
256
257 // Maintenance Mode
258 if ($this->isExtensionEnabled('maintenance-mode', 'KING_ADDONS_EXT_MAINTENANCE_MODE')) {
259 require_once(KING_ADDONS_PATH . 'includes/extensions/Maintenance_Mode/Maintenance_Mode.php');
260 \King_Addons\Maintenance_Mode\Maintenance_Mode::instance();
261 }
262
263 // Data Table Builder
264 if ($this->isExtensionEnabled('table-builder', 'KING_ADDONS_EXT_TABLE_BUILDER')) {
265 require_once(KING_ADDONS_PATH . 'includes/extensions/Data_Table_Builder/Data_Table_Builder.php');
266 Data_Table_Builder::instance();
267 }
268
269 // Site Preloader Animation
270 if ($this->isExtensionEnabled('site-preloader', 'KING_ADDONS_EXT_SITE_PRELOADER')) {
271 require_once(KING_ADDONS_PATH . 'includes/extensions/Site_Preloader/Site_Preloader.php');
272 Site_Preloader::instance();
273 }
274
275 // Admin
276 require_once(KING_ADDONS_PATH . 'includes/Admin.php');
277
278 // Additional - Controls
279 require_once(KING_ADDONS_PATH . 'includes/controls/Ajax_Select2/Ajax_Select2.php');
280 require_once(KING_ADDONS_PATH . 'includes/controls/Ajax_Select2/Ajax_Select2_API.php');
281 require_once(KING_ADDONS_PATH . 'includes/controls/Animations/Animations.php');
282 require_once(KING_ADDONS_PATH . 'includes/controls/Animations/Button_Animations.php');
283
284 // Additional - Widgets
285 require_once(KING_ADDONS_PATH . 'includes/widgets/Search/Search_Ajax.php');
286 require_once(KING_ADDONS_PATH . 'includes/widgets/MailChimp/MailChimp_Ajax.php');
287
288 // Additional - Grids, Magazine Grid
289 require_once(KING_ADDONS_PATH . 'includes/helpers/Grid/Filter_Posts_Ajax.php');
290 require_once(KING_ADDONS_PATH . 'includes/helpers/Grid/Filter_WooCommerce_Products_Ajax.php');
291 require_once(KING_ADDONS_PATH . 'includes/helpers/Grid/Post_Likes_Ajax.php');
292
293 // Additional - Form Builder
294 if (KING_ADDONS_WGT_FORM_BUILDER) {
295 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Create_Submission.php');
296 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Send_Email.php');
297 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Send_Webhook.php');
298 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Subscribe_Mailchimp.php');
299 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Update_Action_Meta.php');
300 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Upload_Email_File.php');
301 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/Verify_Google_Recaptcha.php');
302 require_once(KING_ADDONS_PATH . 'includes/widgets/Form_Builder/helpers/View_Submissions_Pro.php');
303 }
304
305 // ADDITIONAL CLASSES
306
307 // Alt Text Generator for Media Library
308 require_once(KING_ADDONS_PATH . 'includes/extensions/alt-text-generator/Alt_Text_Generator.php');
309 new Alt_Text_Generator();
310
311 // Wishlist module - check extension toggle
312 if ($this->isExtensionEnabled('wishlist', 'KING_ADDONS_EXT_WISHLIST')) {
313 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_DB.php';
314 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Session.php';
315 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Settings.php';
316 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Service.php';
317 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Renderer.php';
318 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Frontend.php';
319 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_WooCommerce.php';
320 require_once KING_ADDONS_PATH . 'includes/wishlist/Wishlist_Module.php';
321 $this->wishlist_module = new Wishlist_Module();
322 }
323
324 // Dynamic Posts Grid AJAX Helper - Initialize regardless of Elementor compatibility
325 // This is needed for AJAX functionality to work even when PRO version is disabled
326 require_once(KING_ADDONS_PATH . 'includes/helpers/Dynamic_Posts_Grid_Ajax.php');
327 \King_Addons\Dynamic_Posts_Grid_Ajax::get_instance();
328
329 // Screenshot Generator
330 // require_once(KING_ADDONS_PATH . 'includes/extensions/Templates/screenshot-generator.php');
331 // require_once(KING_ADDONS_PATH . 'includes/extensions/Templates/screenshot-admin-page.php');
332 // new King_Addons\KingAddons\ScreenshotAdmin();
333
334 // END: ADDITIONAL CLASSES
335
336 self::enableWidgetsByDefault();
337
338 add_action('elementor/init', [$this, 'initElementor']);
339
340 add_action('elementor/elements/categories_registered', [$this, 'addWidgetCategory']);
341 add_action('elementor/controls/controls_registered', [$this, 'registerControls']);
342
343 self::enableFeatures();
344
345 // Load and register AJAX handlers for Login Register Form widget
346 require_once(KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/Login_Register_Form_Ajax.php');
347 require_once(KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/User_Profile_Fields.php');
348 require_once(KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/Email_Handler.php');
349 require_once(KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/Social_Login_Handler.php');
350 add_action('wp_ajax_nopriv_king_addons_user_login', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_login_ajax']);
351 add_action('wp_ajax_king_addons_user_login', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_login_ajax']);
352 add_action('wp_ajax_nopriv_king_addons_user_register', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_register_ajax']);
353 add_action('wp_ajax_king_addons_user_register', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_register_ajax']);
354 add_action('wp_ajax_nopriv_king_addons_user_lostpassword', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_lostpassword_ajax']);
355 add_action('wp_ajax_king_addons_user_lostpassword', ['King_Addons\Widgets\Login_Register_Form\Login_Register_Form_Ajax', 'handle_lostpassword_ajax']);
356
357 // Initialize user profile fields
358 \King_Addons\Widgets\Login_Register_Form\User_Profile_Fields::init();
359
360 // Initialize social login handler
361 \King_Addons\Widgets\Login_Register_Form\Social_Login_Handler::init();
362
363 // Initialize Security Dashboard for admins (only if Login Register Form widget is enabled)
364 if (is_admin()) {
365 $widget_options = get_option('king_addons_options', []);
366 $login_form_enabled = !isset($widget_options['login-register-form']) || $widget_options['login-register-form'] === 'enabled';
367 if ($login_form_enabled) {
368 require_once(KING_ADDONS_PATH . 'includes/widgets/Login_Register_Form/Security_Dashboard.php');
369 \King_Addons\Widgets\Login_Register_Form\Security_Dashboard::init();
370 }
371 }
372
373 new Admin();
374
375 add_action('wp_enqueue_scripts', [$this, 'enqueueFrontendStyles']);
376 add_action('wp_enqueue_scripts', [$this, 'enqueueLightboxDynamicStyles']);
377
378 // Notice - Upgrade Suggestion
379 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
380 add_action('wp_ajax_king_addons_premium_notice_dismiss', [$this, 'king_addons_premium_notice_dismiss_callback']);
381 add_action('admin_notices', [$this, 'showNoticeUpgrade']);
382 }
383
384 // Dashboard UI settings AJAX handler
385 add_action('wp_ajax_king_addons_save_dashboard_ui', [$this, 'king_addons_save_dashboard_ui_callback']);
386
387 // Conditionally enqueue AI text-field enhancement script and styles in Elementor editor
388 $ai_options = get_option('king_addons_ai_options', []);
389 $enable_ai_text_buttons = isset($ai_options['enable_ai_buttons']) ? (bool) $ai_options['enable_ai_buttons'] : true;
390 if ($enable_ai_text_buttons) {
391 // Enqueue AI text-field enhancement script
392 add_action('elementor/editor/after_enqueue_scripts', [$this, 'enqueueAiFieldScript']);
393 // Enqueue styles for AI prompt UI
394 add_action('elementor/editor/after_enqueue_styles', [$this, 'enqueueAiFieldStyles']);
395 // Enqueue AI page translator script
396 add_action('elementor/editor/after_enqueue_scripts', [$this, 'enqueueAiTranslatorScript']);
397 }
398
399 $enable_ai_image_generation_button = isset($ai_options['enable_ai_image_generation_button']) ? (bool) $ai_options['enable_ai_image_generation_button'] : true;
400 if ($enable_ai_image_generation_button) {
401 add_action('elementor/editor/after_enqueue_scripts', [$this, 'enqueueAiImageGenerationScript']);
402 // Enqueue styles for AI Image Generation controls
403 add_action('elementor/editor/after_enqueue_styles', [$this, 'enqueueAiImageFieldStyles']);
404 }
405 }
406 }
407
408 function king_addons_premium_notice_dismiss_callback()
409 {
410 // Check user capabilities
411 if (!current_user_can('manage_options')) {
412 wp_die();
413 }
414
415 check_ajax_referer('king_addons_premium_notice_dismiss', 'nonce');
416
417 $user_id = get_current_user_id();
418 // Save the current time as the last dismissal time for the premium notice
419 update_user_meta($user_id, 'king_addons_premium_notice_dismissed_time', time());
420 wp_die(); // End AJAX request
421 }
422
423 /**
424 * AJAX callback for saving dashboard UI settings (theme, view toggle)
425 *
426 * @since 1.0.0
427 */
428 function king_addons_save_dashboard_ui_callback()
429 {
430 // Check user capabilities
431 if (!current_user_can('manage_options')) {
432 wp_send_json_error(['message' => 'Unauthorized'], 403);
433 }
434
435 check_ajax_referer('king_addons_dashboard_ui', 'nonce');
436
437 $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
438
439 $user_id = get_current_user_id();
440
441 // Validate key
442 $allowed_keys = ['dark_theme', 'theme_mode', 'show_descriptions'];
443 if (!in_array($key, $allowed_keys, true)) {
444 wp_send_json_error(['message' => 'Invalid key'], 400);
445 }
446
447 // Theme preference is per-user.
448 if ($key === 'theme_mode') {
449 $mode = isset($_POST['value']) ? sanitize_key($_POST['value']) : '';
450 $allowed_modes = ['dark', 'light', 'auto'];
451 if (!in_array($mode, $allowed_modes, true)) {
452 wp_send_json_error(['message' => 'Invalid theme mode'], 400);
453 }
454
455 update_user_meta($user_id, 'king_addons_theme_mode', $mode);
456 wp_send_json_success(['key' => $key, 'value' => $mode]);
457 }
458
459 // Backward compatibility: old boolean dark_theme maps to theme_mode.
460 if ($key === 'dark_theme') {
461 $is_dark = isset($_POST['value']) && $_POST['value'] === '1';
462 $mode = $is_dark ? 'dark' : 'light';
463 update_user_meta($user_id, 'king_addons_theme_mode', $mode);
464 wp_send_json_success(['key' => 'theme_mode', 'value' => $mode]);
465 }
466
467 // Remaining UI settings are still stored as site option (shared).
468 $value = isset($_POST['value']) && $_POST['value'] === '1';
469 $settings = get_option('king_addons_dashboard_ui', []);
470 $settings[$key] = $value;
471 update_option('king_addons_dashboard_ui', $settings);
472
473 wp_send_json_success(['key' => $key, 'value' => $value]);
474 }
475
476 function showNoticeUpgrade()
477 {
478 // Check user capabilities; show notice only to administrators as an example
479 if (!current_user_can('manage_options')) {
480 return;
481 }
482
483 $user_id = get_current_user_id();
484 $now = time();
485 // Retrieve the last time the premium notice was dismissed by the user
486 $last_dismissed = get_user_meta($user_id, 'king_addons_premium_notice_dismissed_time', true);
487
488 // If the premium notice was dismissed less than a week ago (604800 seconds), do not show it
489 if ($last_dismissed && ($now - $last_dismissed) < 604800) {
490 // if ($last_dismissed && ($now - $last_dismissed) < 60) {
491 return;
492 }
493 ?>
494 <div class="king-addons-upgrade-notice notice notice-info is-dismissible"
495 style="border-left: 4px solid #0071e3;padding: 10px 15px;">
496 <p style="font-size: 15px; margin:0; display: flex; align-items: center;">
497 <span>
498 Get <strong style="font-weight: 700;">4,000+</strong> premium templates and sections,
499 <strong style="font-weight: 700;">70+</strong> widgets,
500 <strong style="font-weight: 700;">200+</strong> advanced features,
501 and AI tools for Elementor.
502 From $<strong style="font-weight: 700;">4</strong>/mo, billed annually.
503 </span>
504 </p>
505 <p style="font-size: 14px; opacity: 0.6;">Trusted by 20,000+ users</p>
506 <p style="display: flex;">
507 <a href="https://kingaddons.com/pricing?utm_source=kng-notice-offer&amp;utm_medium=plugin&amp;utm_campaign=kng" target="_blank" class="ka-wb-btn ka-wb-btn-primary" style="
508 background: #0071e3;
509 color: #fff;
510 display: inline-flex;
511 align-items: center;
512 justify-content: center;
513 gap: 6px;
514 padding: 10px 18px;
515 font-size: 14px;
516 font-weight: 500;
517 text-decoration: none;
518 border-radius: 980px;
519 border: none;
520 cursor: pointer;
521 transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1);
522 white-space: nowrap;
523 font-family: inherit;
524 ">Upgrade to Pro<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="
525 width: 16px;
526 height: 16px;
527 "><path d="M5 12h14M12 5l7 7-7 7"></path></svg>
528 </a>
529 <a style="margin-left: 20px;display: flex;align-items: center;font-size: 14px;color: #0071e3;"
530 href="https://kingaddons.com/pricing?utm_source=kng-notice-offer&utm_medium=plugin&utm_campaign=kng"
531 class="link">Learn More</a>
532 </p>
533 </div>
534 <script>
535 (function ($) {
536 // Wait for the document to be ready
537 const kingAddonsPremiumNoticeNonce = '<?php echo esc_js(wp_create_nonce('king_addons_premium_notice_dismiss')); ?>';
538 $(document).ready(function () {
539 // Attach click handler to the dismiss button of the premium notice
540 $('.king-addons-upgrade-notice.notice.is-dismissible').on('click', '.notice-dismiss', function () {
541 $.post(ajaxurl, {
542 action: 'king_addons_premium_notice_dismiss',
543 nonce: kingAddonsPremiumNoticeNonce
544 });
545 });
546 });
547 })(jQuery);
548 </script>
549 <?php
550 }
551
552 function enqueueFrontendStyles()
553 {
554 /**
555 * It fixes the default Elementor SVG icon rendering feature (Settings -> Features -> Inline Font Icons)
556 * because sometimes Elementor still renders Font Awesome icons but doesn't load the corresponding Font Awesome styles.
557 * Therefore, we have to enqueue the styles.
558 */
559 wp_enqueue_style(
560 'font-awesome-5-all',
561 ELEMENTOR_ASSETS_URL . 'lib/font-awesome/css/all' . (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min') . '.css',
562 false,
563 KING_ADDONS_VERSION
564 );
565 }
566
567 function hasElementorCompatibility(): bool
568 {
569 // Check if Elementor installed and activated
570 if (!did_action('elementor/loaded')) {
571 add_action('admin_notices', [$this, 'showAdminNotice_ElementorRequired']);
572 return false;
573 }
574
575 // Check for required Elementor version
576 if (!version_compare(ELEMENTOR_VERSION, '3.19.0', '>=')) {
577 add_action('admin_notices', [$this, 'showAdminNotice_ElementorMinimumVersion']);
578 return false;
579 }
580
581 return true;
582 }
583
584 function showAdminNotice_ElementorRequired(): void
585 {
586 $screen = get_current_screen();
587 if (isset($screen->parent_file) && 'plugins.php' === $screen->parent_file && 'update' === $screen->id) {
588 return;
589 }
590
591 if (isset(get_plugins()['elementor/elementor.php'])) {
592 if (!current_user_can('activate_plugins') || is_plugin_active('elementor/elementor.php')) {
593 return;
594 }
595 $plugin = 'elementor/elementor.php';
596 $activation_url = wp_nonce_url('plugins.php?action=activate&amp;plugin=' . $plugin . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . $plugin);
597 $message = '<div class="error"><p>' . esc_html__('King Addons plugin is not working because you need to activate the Elementor plugin.', 'king-addons') . '</p>';
598 /** @noinspection HtmlUnknownTarget */
599 $message .= '<p>' . sprintf('<a href="%s" class="button-primary">%s</a>', $activation_url, esc_html__('Activate Elementor now', 'king-addons')) . '</p></div>';
600 } else {
601 if (!current_user_can('install_plugins')) {
602 return;
603 }
604 $install_url = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=elementor'), 'install-plugin_elementor');
605 $message = '<div class="error"><p>' . esc_html__('King Addons plugin is not working because you need to install the Elementor plugin.', 'king-addons') . '</p>';
606 /** @noinspection HtmlUnknownTarget */
607 $message .= '<p>' . sprintf('<a href="%s" class="button-primary">%s</a>', $install_url, esc_html__('Install Elementor now', 'king-addons')) . '</p></div>';
608 }
609 echo $message;
610 }
611
612 function showAdminNotice_ElementorMinimumVersion(): void
613 {
614 $message = sprintf(
615 /* translators: 1: Plugin name 2: Elementor 3: Required Elementor version */
616 esc_html__('%1$s plugin requires %2$s plugin version %3$s or greater.', 'king-addons'),
617 esc_html__('King Addons', 'king-addons'),
618 esc_html__('Elementor', 'king-addons'),
619 '3.19.0'
620 );
621 echo '<div class="notice notice-error"><p>' . esc_html($message) . '</p></div>';
622 }
623
624 public function initElementor(): void
625 {
626 add_action('elementor/widgets/register', [$this, 'registerWidgets']);
627 add_action('elementor/editor/after_enqueue_styles', [$this, 'enqueueEditorStyles']);
628 add_action('elementor/editor/after_enqueue_scripts', [$this, 'enqueueEditorScripts']);
629 add_action('elementor/preview/enqueue_styles', [$this, 'enqueueEditorPreviewStyles']);
630 }
631
632 function enqueueEditorPreviewStyles(): void
633 {
634 wp_enqueue_style(
635 KING_ADDONS_ASSETS_UNIQUE_KEY . '-elementor-preview',
636 KING_ADDONS_URL . 'includes/admin/css/elementor-preview.css',
637 [],
638 KING_ADDONS_VERSION
639 );
640 }
641
642 function addWidgetCategory(): void
643 {
644 $elements_manager = Plugin::instance()->elements_manager;
645
646 // Add our categories
647 $elements_manager->add_category(
648 'king-addons',
649 [
650 'title' => esc_html__('King Addons', 'king-addons'),
651 'icon' => 'fa fa-plug'
652 ]
653 );
654
655 $elements_manager->add_category(
656 'king-addons-woo-builder',
657 [
658 'title' => esc_html__('King Addons Woo Builder', 'king-addons'),
659 'icon' => 'fa fa-shopping-cart'
660 ]
661 );
662
663 // Move our categories to the top of the panel
664 $this->reorderWidgetCategories($elements_manager);
665 }
666
667 /**
668 * Reorder widget categories so King Addons categories appear after Layout and Basic.
669 *
670 * @param \Elementor\Elements_Manager $elements_manager
671 * @return void
672 */
673 private function reorderWidgetCategories($elements_manager): void
674 {
675 try {
676 $reflection = new \ReflectionClass($elements_manager);
677 $categories_property = $reflection->getProperty('categories');
678 $categories_property->setAccessible(true);
679
680 $categories = $categories_property->getValue($elements_manager);
681 if (!is_array($categories)) {
682 return;
683 }
684
685 // Extract our categories
686 $our_categories = [];
687 if (isset($categories['king-addons'])) {
688 $our_categories['king-addons'] = $categories['king-addons'];
689 unset($categories['king-addons']);
690 }
691 if (isset($categories['king-addons-woo-builder'])) {
692 $our_categories['king-addons-woo-builder'] = $categories['king-addons-woo-builder'];
693 unset($categories['king-addons-woo-builder']);
694 }
695
696 // Insert our categories after Layout and Basic
697 $reordered = [];
698 $insert_after = ['layout', 'basic']; // Categories after which we insert ours
699 $inserted = false;
700
701 foreach ($categories as $key => $value) {
702 $reordered[$key] = $value;
703
704 // Insert our categories after the last target category
705 if (!$inserted && in_array($key, $insert_after, true)) {
706 // Check if next category is also in our target list
707 $keys = array_keys($categories);
708 $current_index = array_search($key, $keys, true);
709 $next_key = $keys[$current_index + 1] ?? null;
710
711 // Only insert if the next category is NOT in our target list
712 if ($next_key === null || !in_array($next_key, $insert_after, true)) {
713 $reordered = array_merge($reordered, $our_categories);
714 $inserted = true;
715 }
716 }
717 }
718
719 // If target categories weren't found, append at the end
720 if (!$inserted) {
721 $reordered = array_merge($reordered, $our_categories);
722 }
723
724 // Set back the reordered array
725 $categories_property->setValue($elements_manager, $reordered);
726 } catch (\ReflectionException $e) {
727 // Silently fail if reflection doesn't work (e.g., future Elementor changes)
728 }
729 }
730
731 /**
732 * Registers Elementor widgets with a mechanism to skip (and remember) broken widgets
733 * that caused a fatal error previously, and try them again if the plugin version is updated.
734 *
735 * @param Widgets_Manager $widgets_manager
736 * @return void
737 */
738 function registerWidgets(Widgets_Manager $widgets_manager): void
739 {
740 // Used to track which widget is currently being loaded when a fatal error occurs
741 static $currentlyLoadingWidgetId = null;
742
743 $currentPluginVersion = KING_ADDONS_VERSION;
744
745 // Get plugin options to check if a widget is enabled
746 $options = get_option('king_addons_options');
747 $options = is_array($options) ? $options : [];
748
749 // Extension toggles (used to prevent loading dependent widgets when extension is disabled).
750 $wishlist_extension_enabled = !isset($options['ext_wishlist']) || $options['ext_wishlist'] === 'enabled';
751 if (defined('KING_ADDONS_EXT_WISHLIST') && KING_ADDONS_EXT_WISHLIST === false) {
752 $wishlist_extension_enabled = false;
753 }
754
755 // Ensure Woo Builder base class is available for single product widgets.
756 $abstract_single_widget = KING_ADDONS_PATH . 'includes/helpers/Woo_Builder/Abstract_Single_Widget.php';
757 if (file_exists($abstract_single_widget)) {
758 require_once $abstract_single_widget;
759 }
760
761 // Ensure Woo Builder base class is available for archive widgets.
762 $abstract_archive_widget = KING_ADDONS_PATH . 'includes/helpers/Woo_Builder/Abstract_Archive_Widget.php';
763 if (file_exists($abstract_archive_widget)) {
764 require_once $abstract_archive_widget;
765 }
766
767 // Ensure Woo Builder base class is available for cart widgets.
768 $abstract_cart_widget = KING_ADDONS_PATH . 'includes/helpers/Woo_Builder/Abstract_Cart_Widget.php';
769 if (file_exists($abstract_cart_widget)) {
770 require_once $abstract_cart_widget;
771 }
772
773 // Ensure Woo Builder base class is available for checkout widgets.
774 $abstract_checkout_widget = KING_ADDONS_PATH . 'includes/helpers/Woo_Builder/Abstract_Checkout_Widget.php';
775 if (file_exists($abstract_checkout_widget)) {
776 require_once $abstract_checkout_widget;
777 }
778
779 // Ensure Woo Builder base class is available for My Account widgets.
780 $abstract_my_account_widget = KING_ADDONS_PATH . 'includes/helpers/Woo_Builder/Abstract_My_Account_Widget.php';
781 if (file_exists($abstract_my_account_widget)) {
782 require_once $abstract_my_account_widget;
783 }
784
785 /**
786 * Retrieve the array of broken widgets from the WordPress options.
787 * The structure is expected to be something like:
788 *
789 * 'widget_id' => [
790 * 'version' => '1.2.0',
791 * 'error' => 'Some fatal error message'
792 * ],
793 * ...
794 *
795 */
796 $brokenWidgets = get_option('king_addons_broken_widgets', []);
797
798 /**
799 * STEP 1: Clear out any "broken widgets" where the stored version is
800 * less than the current plugin version. This gives them a second chance
801 * after an update, assuming the issue may have been fixed.
802 */
803 foreach ($brokenWidgets as $brokenId => $brokenData) {
804 if (
805 isset($brokenData['version'])
806 && version_compare($currentPluginVersion, $brokenData['version'], '>')
807 ) {
808 // If the plugin version is now higher, we remove the widget from the blacklist
809 unset($brokenWidgets[$brokenId]);
810 }
811 }
812
813 // Update the option after cleaning up
814 update_option('king_addons_broken_widgets', $brokenWidgets);
815
816 /**
817 * STEP 2: Use register_shutdown_function to detect any fatal errors (E_ERROR, E_PARSE, etc.)
818 * that might occur during the loading of a widget. If an error is detected, store that widget
819 * in the "broken" list with the current plugin version and the error message.
820 */
821 register_shutdown_function(function () use (&$currentlyLoadingWidgetId, $currentPluginVersion) {
822 $error = error_get_last();
823 if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
824 // If a fatal error occurred while loading a specific widget
825 if (!empty($currentlyLoadingWidgetId)) {
826 $brokenWidgetsLocal = get_option('king_addons_broken_widgets', []);
827 $brokenWidgetsLocal[$currentlyLoadingWidgetId] = [
828 'version' => $currentPluginVersion,
829 'error' => $error['message'] ?? ''
830 ];
831 update_option('king_addons_broken_widgets', $brokenWidgetsLocal);
832 }
833 }
834 });
835
836 /**
837 * STEP 3: Now we iterate through all widgets in our modules map and try to load them.
838 * If a widget is in the broken list, we skip it to avoid repeated fatal errors.
839 */
840 foreach (ModulesMap::getModulesMapArray()['widgets'] as $widget_id => $widget) {
841 // Hard-disable via constant (used to QA/rollout new widgets).
842 $widget_constant = 'KING_ADDONS_WGT_' . strtoupper(str_replace('-', '_', (string) $widget_id));
843 if (defined($widget_constant) && constant($widget_constant) === false) {
844 continue;
845 }
846
847 // Check if the widget is enabled in the options
848 if (!isset($options[$widget_id]) || $options[$widget_id] !== 'enabled') {
849 continue;
850 }
851
852 // Skip Wishlist widgets when Wishlist extension is disabled.
853 // This prevents fatals when wishlist classes aren't loaded.
854 if (!$wishlist_extension_enabled && strpos((string) $widget_id, 'wishlist-') === 0) {
855 continue;
856 }
857
858 // If this widget is listed as broken, skip it
859 if (array_key_exists($widget_id, $brokenWidgets)) {
860 // Log something here if needed:
861 // error_log("Skipping widget {$widget_id}, it previously caused a fatal error.");
862 continue;
863 }
864
865 // Track which widget we're loading
866 $currentlyLoadingWidgetId = $widget_id;
867
868 // Include the base widget class
869 $widget_class = $widget['php-class'];
870 $path_widget_class = "King_Addons\\" . $widget_class;
871 $widget_file = KING_ADDONS_PATH . 'includes/widgets/' . $widget_class . '/' . $widget_class . '.php';
872 if (!file_exists($widget_file)) {
873 // Skip missing widget files to avoid fatal errors if registry is ahead of implementation.
874 $currentlyLoadingWidgetId = null;
875 continue;
876 }
877
878 require_once $widget_file;
879
880 // Check if we can load the Pro version
881 if (
882 function_exists('king_addons_freemius')
883 && king_addons_freemius()->can_use_premium_code__premium_only()
884 && defined('KING_ADDONS_PRO_PATH')
885 ) {
886 if (!empty($widget['has-pro'])) {
887 $pro_file_path = KING_ADDONS_PRO_PATH . 'includes/widgets/' . $widget_class . '_Pro/' . $widget_class . '_Pro.php';
888
889 if (file_exists($pro_file_path)) {
890 require_once($pro_file_path);
891 $path_widget_class_pro = "King_Addons\\" . $widget_class . '_Pro';
892 $widgets_manager->register(new $path_widget_class_pro);
893 } else {
894 // If Pro file doesn't exist, register the base widget
895 $widgets_manager->register(new $path_widget_class);
896 }
897 } else {
898 // No 'has-pro', register the base widget
899 $widgets_manager->register(new $path_widget_class);
900 }
901 } else {
902 // No Freemius Pro available, register the base widget
903 $widgets_manager->register(new $path_widget_class);
904 }
905
906 // Clear the tracking variable after successful load
907 $currentlyLoadingWidgetId = null;
908 }
909 }
910
911 function enableWidgetsByDefault(): void
912 {
913 $options = get_option('king_addons_options');
914
915 foreach (ModulesMap::getModulesMapArray()['widgets'] as $widget_id => $widget) {
916
917 // Hard-disable via constant (used to QA/rollout new widgets).
918 $widget_constant = 'KING_ADDONS_WGT_' . strtoupper(str_replace('-', '_', (string) $widget_id));
919 if (defined($widget_constant) && constant($widget_constant) === false) {
920 continue;
921 }
922
923 if (!($options[$widget_id] ?? null)) {
924 $options[$widget_id] = 'enabled';
925 update_option('king_addons_options', $options);
926 }
927 }
928 }
929
930 /**
931 * Enable and bootstrap registered features.
932 *
933 * Loads free feature classes and, when available and licensed, their Pro counterparts.
934 *
935 * @return void
936 */
937 public function enableFeatures(): void
938 {
939 $options = get_option('king_addons_options');
940
941 foreach (ModulesMap::getModulesMapArray()['features'] as $feature_id => $feature) {
942 // Hard-disable via constant (used to QA/rollout new features).
943 $feature_constant = 'KING_ADDONS_FEAT_' . strtoupper(str_replace('-', '_', (string) $feature_id));
944 if (defined($feature_constant) && constant($feature_constant) === false) {
945 continue;
946 }
947
948 if (!($options[$feature_id] ?? null)) {
949 $options[$feature_id] = 'enabled';
950 update_option('king_addons_options', $options);
951 }
952
953 if ($options[$feature_id] !== 'enabled') {
954 continue;
955 }
956
957 $feature_class = $feature['php-class'];
958 $path_feature_class = "King_Addons\\" . $feature_class;
959 $feature_file = KING_ADDONS_PATH . 'includes/features/' . $feature_class . '/' . $feature_class . '.php';
960
961 if (file_exists($feature_file)) {
962 require_once $feature_file;
963 }
964
965 $pro_loaded = false;
966
967 if (
968 !empty($feature['has-pro'])
969 && function_exists('king_addons_freemius')
970 && king_addons_freemius()->can_use_premium_code__premium_only()
971 && defined('KING_ADDONS_PRO_PATH')
972 ) {
973 $pro_file_path = KING_ADDONS_PRO_PATH . 'includes/features/' . $feature_class . '_Pro/' . $feature_class . '_Pro.php';
974
975 if (file_exists($pro_file_path)) {
976 require_once $pro_file_path;
977
978 $path_feature_class_pro = "King_Addons\\" . $feature_class . '_Pro';
979 if (class_exists($path_feature_class_pro)) {
980 new $path_feature_class_pro();
981 $pro_loaded = true;
982 }
983 }
984 }
985
986 if (!$pro_loaded && class_exists($path_feature_class)) {
987 new $path_feature_class();
988 }
989 }
990 }
991
992 public function registerControls(Controls_Manager $controls_manager): void
993 {
994 $controls_manager->register(new AJAX_Select2\Ajax_Select2());
995 $controls_manager->register(new Animations\Animations());
996 $controls_manager->register(new Animations\Animations_Alternative());
997 $controls_manager->register(new Button_Animations\Button_Animations());
998 }
999
1000 function enqueueEditorStyles(): void
1001 {
1002 wp_enqueue_style(KING_ADDONS_ASSETS_UNIQUE_KEY . '-elementor-editor', KING_ADDONS_URL . 'includes/admin/css/elementor-editor.css', '', KING_ADDONS_VERSION);
1003 }
1004
1005 function enqueueEditorScripts(): void
1006 {
1007 wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-elementor-editor', KING_ADDONS_URL . 'includes/admin/js/elementor-editor.js', '', KING_ADDONS_VERSION);
1008
1009 // Localize script with PRO status
1010 wp_localize_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-elementor-editor', 'kingAddonsEditor', [
1011 'isPro' => king_addons_freemius()->can_use_premium_code__premium_only() ? true : false
1012 ]);
1013
1014 wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-data-table-export', KING_ADDONS_URL . 'includes/widgets/Data_Table/preview-handler.js', '', KING_ADDONS_VERSION);
1015
1016 if (KING_ADDONS_WGT_FORM_BUILDER) {
1017 wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-form-builder-editor-handler', KING_ADDONS_URL . 'includes/widgets/Form_Builder/editor-handler.js', '', KING_ADDONS_VERSION);
1018 }
1019 }
1020
1021 public static function renderProFeaturesSection($module, $section, $type, $widget_name, $features): void
1022 {
1023 if (king_addons_freemius()->can_use_premium_code__premium_only()) {
1024 return;
1025 }
1026
1027 $module->start_controls_section(
1028 'king_addons_pro_features_section',
1029 [
1030 'label' => KING_ADDONS_ELEMENTOR_ICON_PRO . '<span class="king-addons-pro-features-heading">' . esc_html__('Pro Features', 'king-addons') . '</span>',
1031 'tab' => $section ?: null,
1032 ]
1033 );
1034
1035 $list_html = '<ul>' . implode('', array_map(fn($feature) => "<li>$feature</li>", $features)) . '</ul>';
1036
1037 $module->add_control(
1038 'king_addons_pro_features_list',
1039 [
1040 'type' => $type,
1041 'raw' => $list_html . '<a class="king-addons-pro-features-cta-btn" href="https://kingaddons.com/pricing/?utm_source=kng-module-' . $widget_name . '-upgrade-pro&utm_medium=plugin&utm_campaign=kng" target="_blank">' . esc_html__('Upgrade Now', 'king-addons') . '</a>',
1042 'content_classes' => 'king-addons-pro-features-list',
1043 ]
1044 );
1045
1046 $module->end_controls_section();
1047 }
1048
1049 public static function renderUpgradeProNotice($module, $controls_manager, $widget_name, $option, $condition = []): void
1050 {
1051 if (king_addons_freemius()->can_use_premium_code__premium_only()) {
1052 return;
1053 }
1054
1055 $module->add_control(
1056 $option . '_pro_notice_',
1057 [
1058 'raw' => 'Upgrade to the <strong><a href="https://kingaddons.com/pricing/?utm_source=kng-module-' . $widget_name . '-settings-upgrade-pro&utm_medium=plugin&utm_campaign=kng" target="_blank">Pro version</a></strong> now<br> and unlock this feature!',
1059 'type' => $controls_manager,
1060 'content_classes' => 'king-addons-pro-notice',
1061 'condition' => [
1062 $option => $condition,
1063 ]
1064 ]
1065 );
1066 }
1067
1068 public static function getCustomTypes($query, $exclude_defaults = true): array
1069 {
1070 $custom_types = $query === 'tax'
1071 ? get_taxonomies(['show_in_nav_menus' => true], 'objects')
1072 : get_post_types(['show_in_nav_menus' => true], 'objects');
1073
1074 return array_filter(
1075 array_map(fn($type) => $type->label, $custom_types),
1076 fn($label, $key) => !$exclude_defaults || !in_array($key, ['post', 'page', 'category', 'post_tag']),
1077 ARRAY_FILTER_USE_BOTH
1078 );
1079 }
1080
1081 public static function getShareIcon($args = []): string
1082 {
1083 $args = wp_parse_args($args, [
1084 'network' => '',
1085 'url' => '',
1086 'title' => '',
1087 'text' => '',
1088 'image' => '',
1089 'show_whatsapp_title' => 'no',
1090 'show_whatsapp_excerpt' => 'no',
1091 'tooltip' => 'no',
1092 'icons' => 'no',
1093 'labels' => 'no',
1094 'custom_label' => '',
1095 ]);
1096
1097 $url = esc_url($args['url']);
1098 $title = wp_strip_all_tags($args['title']);
1099 $text = wp_strip_all_tags($args['text']);
1100 $image = esc_url($args['image']);
1101 $network = $args['network'];
1102
1103 $get_whatsapp_url = function ($a) {
1104 if ('yes' === $a['show_whatsapp_title'] && 'yes' === $a['show_whatsapp_excerpt']) {
1105 return 'https://api.whatsapp.com/send?text=*' . $a['title'] . '*%0a' . $a['text'] . '%0a' . $a['url'];
1106 } elseif ('yes' === $a['show_whatsapp_title']) {
1107 return 'https://api.whatsapp.com/send?text=*' . $a['title'] . '*%0a' . $a['url'];
1108 } elseif ('yes' === $a['show_whatsapp_excerpt']) {
1109 return 'https://api.whatsapp.com/send?text=*' . $a['text'] . '%0a' . $a['url'];
1110 }
1111 return 'https://api.whatsapp.com/send?text=' . $a['url'];
1112 };
1113
1114 $networks_map = [
1115 'facebook-f' => [
1116 'url' => "https://www.facebook.com/sharer.php?u=$url",
1117 'title' => esc_html__('Facebook', 'king-addons'),
1118 'icon' => 'fab',
1119 ],
1120 'x-twitter' => [
1121 'url' => "https://twitter.com/intent/tweet?url=$url",
1122 'title' => esc_html__('X (Twitter)', 'king-addons'),
1123 'icon' => 'fab',
1124 ],
1125 'linkedin-in' => [
1126 'url' => "https://www.linkedin.com/shareArticle?mini=true&url=$url&title=$title&summary=$text&source=$url",
1127 'title' => esc_html__('LinkedIn', 'king-addons'),
1128 'icon' => 'fab',
1129 ],
1130 'pinterest-p' => [
1131 'url' => "https://www.pinterest.com/pin/create/button/?url=$url&media=$image",
1132 'title' => esc_html__('Pinterest', 'king-addons'),
1133 'icon' => 'fab',
1134 ],
1135 'reddit' => [
1136 'url' => "https://reddit.com/submit?url=$url&title=$title",
1137 'title' => esc_html__('Reddit', 'king-addons'),
1138 'icon' => 'fab',
1139 ],
1140 'tumblr' => [
1141 'url' => "https://tumblr.com/share/link?url=$url",
1142 'title' => esc_html__('Tumblr', 'king-addons'),
1143 'icon' => 'fab',
1144 ],
1145 'digg' => [
1146 'url' => "https://digg.com/submit?url=$url",
1147 'title' => esc_html__('Digg', 'king-addons'),
1148 'icon' => 'fab',
1149 ],
1150 'xing' => [
1151 'url' => "https://www.xing.com/app/user?op=share&url=$url",
1152 'title' => esc_html__('Xing', 'king-addons'),
1153 'icon' => 'fab',
1154 ],
1155 'vk' => [
1156 'url' => "https://vk.ru/share.php?url=$url&title=$title&description=" . wp_trim_words($text, 250) . "&image=$image/",
1157 'title' => esc_html__('VK', 'king-addons'),
1158 'icon' => 'fab',
1159 ],
1160 'odnoklassniki' => [
1161 'url' => "https://connect.ok.ru/offer?url=$url",
1162 'title' => esc_html__('OK', 'king-addons'),
1163 'icon' => 'fab',
1164 ],
1165 'get-pocket' => [
1166 'url' => "https://getpocket.com/edit?url=$url",
1167 'title' => esc_html__('Pocket', 'king-addons'),
1168 'icon' => 'fab',
1169 ],
1170 'skype' => [
1171 'url' => "https://web.skype.com/share?url=$url",
1172 'title' => esc_html__('Skype', 'king-addons'),
1173 'icon' => 'fab',
1174 ],
1175 'whatsapp' => [
1176 'url' => $get_whatsapp_url($args),
1177 'title' => esc_html__('WhatsApp', 'king-addons'),
1178 'icon' => 'fab',
1179 ],
1180 'telegram' => [
1181 'url' => "https://telegram.me/share/url?url=$url&text=$text",
1182 'title' => esc_html__('Telegram', 'king-addons'),
1183 'icon' => 'fab',
1184 ],
1185 'envelope' => [
1186 'url' => "mailto:?subject=$title&body=$url",
1187 'title' => esc_html__('Email', 'king-addons'),
1188 'icon' => 'fas',
1189 ],
1190 'print' => [
1191 'url' => "javascript:window.print()",
1192 'title' => esc_html__('Print', 'king-addons'),
1193 'icon' => 'fas',
1194 ],
1195 ];
1196
1197 if (!isset($networks_map[$network])) {
1198 return '';
1199 }
1200
1201 $share_url = $networks_map[$network]['url'];
1202 $network_title = $networks_map[$network]['title'];
1203 $icon_category = $networks_map[$network]['icon'];
1204
1205 $output = '<a href="' . esc_url($share_url) . '" class="king-addons-share-icon king-addons-share-' . esc_attr($network) . '" target="_blank">';
1206
1207 if ('yes' === $args['tooltip']) {
1208 $output .= '<span class="king-addons-share-tooltip king-addons-tooltip">' . esc_html($network_title) . '</span>';
1209 }
1210
1211 if ('yes' === $args['icons']) {
1212 $output .= '<i class="' . esc_attr($icon_category) . ' fa-' . esc_attr($network) . '"></i>';
1213 }
1214
1215 if ('yes' === $args['labels']) {
1216 $label = !empty($args['custom_label']) ? $args['custom_label'] : $network_title;
1217 $output .= '<span class="king-addons-share-label">' . esc_html($label) . '</span>';
1218 }
1219
1220 $output .= '</a>';
1221
1222 return $output;
1223 }
1224
1225 public static function validateHTMLTags($setting, $default, $tags_whitelist)
1226 {
1227 $value = $setting;
1228 if (!in_array($value, $tags_whitelist)) {
1229 $value = $default;
1230 }
1231 return $value;
1232 }
1233
1234 public static function getIcon($icon, $dir)
1235 {
1236 if (empty($icon) || strpos($icon, 'fa-') === false) {
1237 return '';
1238 }
1239
1240 $dir = $dir ? "-$dir" : '';
1241 return wp_kses(
1242 '<i class="' . esc_attr($icon . $dir) . '"></i>',
1243 ['i' => ['class' => []]]
1244 );
1245 }
1246
1247 public static function getPluginName()
1248 {
1249 return 'King Addons';
1250 }
1251
1252 public static function getAnimationTimings(): array
1253 {
1254 /** @noinspection DuplicatedCode */
1255 $timings = [
1256 'ease-default' => 'Default',
1257 'linear' => 'Linear',
1258 'ease-in' => 'Ease In',
1259 'ease-out' => 'Ease Out',
1260 'pro-eio' => 'EI Out (Pro)',
1261 'pro-eiqd' => 'EI Quad (Pro)',
1262 'pro-eicb' => 'EI Cubic (Pro)',
1263 'pro-eiqrt' => 'EI Quart (Pro)',
1264 'pro-eiqnt' => 'EI Quint (Pro)',
1265 'pro-eisn' => 'EI Sine (Pro)',
1266 'pro-eiex' => 'EI Expo (Pro)',
1267 'pro-eicr' => 'EI Circ (Pro)',
1268 'pro-eibk' => 'EI Back (Pro)',
1269 'pro-eoqd' => 'EO Quad (Pro)',
1270 'pro-eocb' => 'EO Cubic (Pro)',
1271 'pro-eoqrt' => 'EO Quart (Pro)',
1272 'pro-eoqnt' => 'EO Quint (Pro)',
1273 'pro-eosn' => 'EO Sine (Pro)',
1274 'pro-eoex' => 'EO Expo (Pro)',
1275 'pro-eocr' => 'EO Circ (Pro)',
1276 'pro-eobk' => 'EO Back (Pro)',
1277 'pro-eioqd' => 'EIO Quad (Pro)',
1278 'pro-eiocb' => 'EIO Cubic (Pro)',
1279 'pro-eioqrt' => 'EIO Quart (Pro)',
1280 'pro-eioqnt' => 'EIO Quint (Pro)',
1281 'pro-eiosn' => 'EIO Sine (Pro)',
1282 'pro-eioex' => 'EIO Expo (Pro)',
1283 'pro-eiocr' => 'EIO Circ (Pro)',
1284 'pro-eiobk' => 'EIO Back (Pro)',
1285 ];
1286
1287 if (king_addons_freemius()->can_use_premium_code__premium_only()) {
1288 /** @noinspection DuplicatedCode */
1289 $timings = [
1290 'ease-default' => 'Default',
1291 'linear' => 'Linear',
1292 'ease-in' => 'Ease In',
1293 'ease-out' => 'Ease Out',
1294 'ease-in-out' => 'Ease In Out',
1295 'ease-in-quad' => 'Ease In Quad',
1296 'ease-in-cubic' => 'Ease In Cubic',
1297 'ease-in-quart' => 'Ease In Quart',
1298 'ease-in-quint' => 'Ease In Quint',
1299 'ease-in-sine' => 'Ease In Sine',
1300 'ease-in-expo' => 'Ease In Expo',
1301 'ease-in-circ' => 'Ease In Circ',
1302 'ease-in-back' => 'Ease In Back',
1303 'ease-out-quad' => 'Ease Out Quad',
1304 'ease-out-cubic' => 'Ease Out Cubic',
1305 'ease-out-quart' => 'Ease Out Quart',
1306 'ease-out-quint' => 'Ease Out Quint',
1307 'ease-out-sine' => 'Ease Out Sine',
1308 'ease-out-expo' => 'Ease Out Expo',
1309 'ease-out-circ' => 'Ease Out Circ',
1310 'ease-out-back' => 'Ease Out Back',
1311 'ease-in-out-quad' => 'Ease In Out Quad',
1312 'ease-in-out-cubic' => 'Ease In Out Cubic',
1313 'ease-in-out-quart' => 'Ease In Out Quart',
1314 'ease-in-out-quint' => 'Ease In Out Quint',
1315 'ease-in-out-sine' => 'Ease In Out Sine',
1316 'ease-in-out-expo' => 'Ease In Out Expo',
1317 'ease-in-out-circ' => 'Ease In Out Circ',
1318 'ease-in-out-back' => 'Ease In Out Back',
1319 ];
1320 }
1321
1322 return $timings;
1323 }
1324
1325 public static function getAnimationTimingsConditionsPro()
1326 {
1327 return [
1328 'pro-eibk',
1329 'pro-eicb',
1330 'pro-eicr',
1331 'pro-eiex',
1332 'pro-eio',
1333 'pro-eiobk',
1334 'pro-eiocb',
1335 'pro-eiocr',
1336 'pro-eioex',
1337 'pro-eioqd',
1338 'pro-eioqnt',
1339 'pro-eioqrt',
1340 'pro-eiosn',
1341 'pro-eiqd',
1342 'pro-eiqnt',
1343 'pro-eiqrt',
1344 'pro-eisn',
1345 'pro-eobk',
1346 'pro-eocb',
1347 'pro-eocr',
1348 'pro-eoex',
1349 'pro-eoqd',
1350 'pro-eoqnt',
1351 'pro-eoqrt',
1352 'pro-eosn',
1353 ];
1354 }
1355
1356 public static function isBlogArchive()
1357 {
1358 return (
1359 is_home()
1360 && '0' === get_option('page_on_front')
1361 && '0' === get_option('page_for_posts')
1362 ) || (
1363 intval(get_option('page_for_posts')) === get_queried_object_id()
1364 && !is_404()
1365 );
1366 }
1367
1368 public static function filterOembedResults($html)
1369 {
1370 preg_match('/src="([^"]+)"/', $html, $m);
1371 return $m[1] . '&auto_play=true';
1372 }
1373
1374 public static function getWooCommerceTaxonomies()
1375 {
1376 $filtered = array_filter(get_object_taxonomies('product'), fn($t) => get_taxonomy($t)->show_ui);
1377 return array_combine($filtered, array_map(fn($t) => get_taxonomy($t)->label, $filtered));
1378 }
1379
1380 public static function getCustomMetaKeysTaxonomies()
1381 {
1382 $data = [];
1383 $tax_types = Core::getCustomTypes('tax', false);
1384
1385 foreach ($tax_types as $taxonomy_slug => $post_type_name) {
1386 $meta_keys = [];
1387 foreach (get_terms($taxonomy_slug) as $tax) {
1388 $keys = array_keys(get_term_meta($tax->term_id));
1389 $keys = array_filter($keys, fn($key) => '_' !== $key[0]);
1390 $meta_keys = array_merge($meta_keys, $keys);
1391 }
1392 $data[$taxonomy_slug] = array_unique($meta_keys);
1393 }
1394
1395
1396 $merged = call_user_func_array('array_merge', array_values($data));
1397 $merged_meta_keys = array_values(array_unique($merged));
1398
1399 $options = array_combine($merged_meta_keys, $merged_meta_keys);
1400
1401 return [$data, $options];
1402 }
1403
1404 public static function getMailchimpLists()
1405 {
1406 $api_key = get_option('king_addons_mailchimp_api_key', '');
1407 $mailchimp_list = ['def' => esc_html__('Select List', 'king-addons')];
1408
1409 if (!$api_key) {
1410 return $mailchimp_list;
1411 }
1412
1413 $url = 'https://' . explode('-', $api_key)[1] . '.api.mailchimp.com/3.0/lists/';
1414 $response = wp_remote_get($url, [
1415 'headers' => ['Authorization' => 'Basic ' . base64_encode('user:' . $api_key)]
1416 ]);
1417
1418 $body = json_decode(wp_remote_retrieve_body($response));
1419 if (!empty($body->lists)) {
1420 foreach ($body->lists as $list) {
1421 $mailchimp_list[$list->id] = $list->name . ' (' . $list->stats->member_count . ')';
1422 }
1423 }
1424
1425 return $mailchimp_list;
1426 }
1427
1428 public static function getMailchimpGroups()
1429 {
1430 $apiKey = get_option('king_addons_mailchimp_api_key');
1431 $domain = 'https://' . substr($apiKey, strpos($apiKey, '-') + 1) . '.api.mailchimp.com/3.0/';
1432 $authArgs = ['headers' => ['Authorization' => 'Basic ' . base64_encode('user:' . $apiKey)]];
1433 $groups = ['def' => 'Select Group'];
1434 $mailchimpIDs = Core::getMailchimpLists();
1435
1436 foreach ($mailchimpIDs as $audience => $ignore) {
1437 if ($audience === 'def') {
1438 continue;
1439 }
1440
1441 $cats = wp_remote_get("{$domain}lists/$audience/interest-categories", $authArgs);
1442 $cats = json_decode($cats['body'])->categories ?? [];
1443
1444 foreach ($cats as $cat) {
1445 $interests = wp_remote_get("{$domain}lists/$audience/interest-categories/$cat->id/interests", $authArgs);
1446 $interests = json_decode($interests['body'])->interests ?? [];
1447
1448 foreach ($interests as $int) {
1449 $groups[$int->id] = $int->name;
1450 }
1451 }
1452 }
1453
1454 return $groups;
1455 }
1456
1457 public static function getShopURL($settings)
1458 {
1459 global $wp;
1460 $url = ('' === get_option('permalink_structure'))
1461 ? remove_query_arg(['page', 'paged'], add_query_arg($wp->query_string, '', home_url($wp->request)))
1462 : preg_replace('%/page/[0-9]+%', '', home_url(trailingslashit($wp->request)));
1463 $url = add_query_arg('kingaddonsfilters', '', $url);
1464 $single_params = [
1465 'min_price' => true,
1466 'max_price' => true,
1467 'orderby' => false,
1468 'psearch' => false,
1469 'filter_product_cat' => false,
1470 'filter_product_tag' => false,
1471 'filter_rating' => false,
1472 ];
1473 foreach ($single_params as $param => $needs_clean) {
1474 if (isset($_GET[$param])) {
1475 $value = wp_unslash($_GET[$param]);
1476 $value = $needs_clean ? wc_clean($value) : $value;
1477 $url = add_query_arg($param, $value, $url);
1478 }
1479 }
1480 /** @noinspection DuplicatedCode */
1481 if ($chosen_attrs = WC()->query->get_layered_nav_chosen_attributes()) {
1482 foreach ($chosen_attrs as $name => $data) {
1483 $filter_name = wc_attribute_taxonomy_slug($name);
1484 if (!empty($data['terms'])) {
1485 $url = add_query_arg('filter_' . $filter_name, implode(',', $data['terms']), $url);
1486 }
1487 if (!empty($settings)) {
1488 if ('or' === $settings['tax_query_type'] || isset($_GET['query_type_' . $filter_name])) {
1489 $url = add_query_arg('query_type_' . $filter_name, 'or', $url);
1490 }
1491 }
1492 }
1493 }
1494 return $url;
1495 }
1496
1497 public static function getClientIP()
1498 {
1499 $server_ip_keys = [
1500 'HTTP_CLIENT_IP',
1501 'HTTP_X_FORWARDED_FOR',
1502 'HTTP_X_FORWARDED',
1503 'HTTP_X_CLUSTER_CLIENT_IP',
1504 'HTTP_FORWARDED_FOR',
1505 'HTTP_FORWARDED',
1506 'REMOTE_ADDR',
1507 ];
1508
1509 foreach ($server_ip_keys as $key) {
1510 if (isset($_SERVER[$key])) {
1511 $ip = wp_kses_post_deep(wp_unslash($_SERVER[$key]));
1512 if (filter_var($ip, FILTER_VALIDATE_IP)) {
1513 return $ip;
1514 }
1515 }
1516 }
1517
1518 return '127.0.0.1';
1519 }
1520
1521 public static function getCustomMetaKeys()
1522 {
1523 // Get all custom post types (slug => name).
1524 $post_types = Core::getCustomTypes('post', false);
1525
1526 // Build $data with each post type's unique custom meta keys (excluding keys beginning with "_").
1527 $data = array_combine(
1528 array_keys($post_types),
1529 array_map(function ($slug) {
1530 $keys = [];
1531 foreach (get_posts(['post_type' => $slug, 'posts_per_page' => -1]) as $post) {
1532 // get_post_custom_keys can return null, so cast to array:
1533 foreach ((array) get_post_custom_keys($post->ID) as $meta_key) {
1534 // Exclude protected keys (those beginning with "_").
1535 if ($meta_key[0] !== '_') {
1536 $keys[] = $meta_key;
1537 }
1538 }
1539 }
1540 return array_values(array_unique($keys));
1541 }, array_keys($post_types))
1542 );
1543
1544 // Flatten all meta keys across all post types, remove duplicates, and reindex.
1545 $merged_meta_keys = array_values(array_unique(array_merge([], ...$data)));
1546
1547 // Create an associative array where key == value (for convenient dropdowns, etc.).
1548 $options = array_combine($merged_meta_keys, $merged_meta_keys);
1549
1550 // Return both the per-post-type data and the merged, deduplicated options.
1551 return [$data, $options];
1552 }
1553
1554 public function enqueueLightboxDynamicStyles()
1555 {
1556 wp_register_style('king-addons-lightbox-dynamic-style', false);
1557 wp_enqueue_style('king-addons-lightbox-dynamic-style');
1558
1559 $bg = esc_html(get_option('king_addons_lightbox_bg_color', 'rgba(0,0,0,0.6)'));
1560 $toolbar = esc_html(get_option('king_addons_lightbox_toolbar_color', 'rgba(0,0,0,0.8)'));
1561 $caption = esc_html(get_option('king_addons_lightbox_caption_color', 'rgba(0,0,0,0.8)'));
1562 $gallery = esc_html(get_option('king_addons_lightbox_gallery_color', '#444444'));
1563 $progress_bar = esc_html(get_option('king_addons_lightbox_pb_color', '#8a8a8a'));
1564 $ui_color = esc_html(get_option('king_addons_lightbox_ui_color', '#efefef'));
1565 $icon_size = floatval(get_option('king_addons_lightbox_icon_size', 20));
1566 $icon_size_big = $icon_size + 4;
1567 $ui_hover = esc_html(get_option('king_addons_lightbox_ui_hover_color', '#ffffff'));
1568 $text_color = esc_html(get_option('king_addons_lightbox_text_color', '#efefef'));
1569 $text_size = esc_html(get_option('king_addons_lightbox_text_size', 14));
1570 $arrow_size = esc_html(get_option('king_addons_lightbox_arrow_size', 35));
1571
1572 $custom_css = "#lg-counter { color: $text_color !important; font-size: {$text_size}px !important; opacity: 0.9; } .lg-backdrop { background-color: $bg !important; } .lg-dropdown:after { border-bottom-color: $toolbar !important; } .lg-icon { color: $ui_color !important; font-size: {$icon_size}px !important; background-color: transparent !important; } .lg-icon.lg-toogle-thumb { font-size: {$icon_size_big}px !important; } .lg-icon:hover, .lg-dropdown-text:hover { color: $ui_hover !important; } .lg-prev, .lg-next { font-size: {$arrow_size}px !important; } .lg-progress { background-color: $progress_bar !important; } .lg-sub-html { background-color: $caption !important; } .lg-sub-html, .lg-dropdown-text { color: $text_color !important; font-size: {$text_size}px !important; } .lg-thumb-item { border-radius: 0 !important; border: none !important; opacity: 0.5; } .lg-thumb-item.active { opacity: 1; } .lg-thumb-outer, .lg-progress-bar { background-color: $gallery !important; } .lg-thumb-outer { padding: 0 10px; } .lg-toolbar, .lg-dropdown { background-color: $toolbar !important; }";
1573
1574 wp_add_inline_style('king-addons-lightbox-dynamic-style', $custom_css);
1575 }
1576
1577 /**
1578 * Enqueues the AI button injection script in the Elementor editor panel.
1579 *
1580 * @return void
1581 */
1582 public function enqueueAiFieldScript(): void
1583 {
1584 wp_enqueue_script(
1585 'king-addons-ai-field',
1586 KING_ADDONS_URL . 'includes/admin/js/ai-textfield.js',
1587 ['jquery', 'elementor-editor'],
1588 KING_ADDONS_VERSION,
1589 true
1590 );
1591
1592 // Localize for AJAX
1593 wp_localize_script(
1594 'king-addons-ai-field',
1595 'KingAddonsAiField',
1596 [
1597 'ajax_url' => admin_url('admin-ajax.php'),
1598 'generate_nonce' => wp_create_nonce('king_addons_ai_generate_nonce'),
1599 'change_nonce' => wp_create_nonce('king_addons_ai_change_nonce'),
1600 'generate_action' => 'king_addons_ai_generate_text',
1601 'change_action' => 'king_addons_ai_change_text',
1602 'icon_url' => KING_ADDONS_URL . 'includes/admin/img/ai.svg',
1603 'rewrite_icon_url' => KING_ADDONS_URL . 'includes/admin/img/ai-refresh.svg',
1604 'settings_url' => admin_url('admin.php?page=king-addons-ai-settings'),
1605 'plugin_url' => KING_ADDONS_URL,
1606 'is_pro' => king_addons_freemius()->can_use_premium_code__premium_only() ? true : false,
1607 'premium_active' => king_addons_freemius()->can_use_premium_code__premium_only() ? true : false,
1608 'translator_enabled' => isset($ai_options['enable_ai_page_translator']) ? (bool) $ai_options['enable_ai_page_translator'] : true,
1609 ]
1610 );
1611 }
1612
1613 /**
1614 * Enqueues the AI image generation field script in the Elementor editor panel.
1615 *
1616 * @return void
1617 */
1618 public function enqueueAiImageGenerationScript(): void
1619 {
1620 // Retrieve AI options and ensure it's an array to prevent warnings.
1621 $ai_options = get_option('king_addons_ai_options', []);
1622 wp_enqueue_script(
1623 'king-addons-ai-image-field',
1624 KING_ADDONS_URL . 'includes/admin/js/ai-imagefield.js',
1625 ['jquery', 'elementor-editor'],
1626 KING_ADDONS_VERSION,
1627 true
1628 );
1629
1630 // Localize for AJAX
1631 wp_localize_script(
1632 'king-addons-ai-image-field',
1633 'KingAddonsAiImageField',
1634 [
1635 'ajax_url' => admin_url('admin-ajax.php'),
1636 'generate_nonce' => wp_create_nonce('king_addons_ai_generate_image_nonce'),
1637 'generate_action' => 'king_addons_ai_generate_image',
1638 'image_model' => sanitize_text_field($ai_options['openai_image_model'] ?? ''),
1639 'icon_url' => KING_ADDONS_URL . 'includes/admin/img/ai.svg',
1640 'rewrite_icon_url' => KING_ADDONS_URL . 'includes/admin/img/ai-refresh.svg',
1641 'settings_url' => admin_url('admin.php?page=king-addons-ai-settings'),
1642 'plugin_url' => KING_ADDONS_URL,
1643 ]
1644 );
1645 }
1646
1647 /**
1648 * Enqueues the styles for AI prompt UI in the Elementor editor panel.
1649 *
1650 * @return void
1651 */
1652 public function enqueueAiFieldStyles(): void
1653 {
1654 // Enqueue CSS for the AI prompt UI
1655 wp_enqueue_style(
1656 'king-addons-ai-field-css',
1657 KING_ADDONS_URL . 'includes/admin/css/ai-textfield.css',
1658 [],
1659 KING_ADDONS_VERSION
1660 );
1661 }
1662
1663 /**
1664 * Enqueues styles for AI Image Generation UI in the Elementor editor panel.
1665 *
1666 * @return void
1667 */
1668 public function enqueueAiImageFieldStyles(): void
1669 {
1670 wp_enqueue_style(
1671 'king-addons-ai-imagefield',
1672 KING_ADDONS_URL . 'includes/admin/css/ai-imagefield.css',
1673 [],
1674 KING_ADDONS_VERSION
1675 );
1676 }
1677
1678 /**
1679 * Enqueues the AI page translator script in the Elementor editor panel.
1680 *
1681 * @return void
1682 */
1683 public function enqueueAiTranslatorScript(): void
1684 {
1685 // Check if AI Page Translator is enabled in settings
1686 $ai_options = get_option('king_addons_ai_options', []);
1687 $translator_enabled = isset($ai_options['enable_ai_page_translator']) ? (bool) $ai_options['enable_ai_page_translator'] : true;
1688
1689 if (!$translator_enabled) {
1690 return; // Don't load script if translator is disabled
1691 }
1692
1693 wp_enqueue_script(
1694 'king-addons-ai-translator',
1695 KING_ADDONS_URL . 'includes/admin/js/ai-page-translator.js',
1696 ['jquery', 'elementor-editor'],
1697 KING_ADDONS_VERSION,
1698 true
1699 );
1700
1701 // Note: Using existing KingAddonsAiField localization
1702 // The translator script will use the same AJAX endpoints and settings
1703 // No need for separate localization as it reuses existing AI infrastructure
1704 }
1705 }
1706
1707 Core::instance();
1708