PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / trunk
WDesignKit – AI Templates, Widget Builder & MCP Workflow vtrunk
2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 All 128 releases
wdesignkit / includes / admin / class-api.php

class-api.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow trunk, at includes/admin/class-api.php

6,430 lines 207.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The file that defines the core plugin class
4 *
5 * @link https://posimyth.com/
6 * @since 1.0.0
7 *
8 * @package Wdesignkit
9 * @subpackage Wdesignkit/includes
10 */
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 use wdkit\Wdkit_Wdesignkit;
17 use wdkit\WdKit_enqueue\Wdkit_Enqueue;
18 use wdkit\wdkit_datahooks\Wdkit_Data_Hooks;
19
20 if ( ! class_exists( 'Wdkit_Api_Call' ) ) {
21
22 /**
23 * Main classs call for all api
24 *
25 * @link https://posimyth.com/
26 * @since 1.0.0
27 */
28 class Wdkit_Api_Call {
29
30 /**
31 * Member Variable
32 *
33 * @var instance
34 */
35 private static $instance;
36
37 /**
38 * Member Variable
39 *
40 * @var staring widgets_with_post_category
41 */
42 public $widgets_with_post_category = array(
43 'post_category', 'include_products',
44 );
45
46 /**
47 * Member Variable
48 *
49 * @var staring $wdkit_api
50 */
51 public $wdkit_api = WDKIT_SERVER_API_URL . 'api/wp/';
52
53 /**
54 * Member Variable
55 *
56 * @var staring $wdkit_api_v2
57 */
58 public $wdkit_api_v2 = WDKIT_SERVER_API_URL . 'api/v2/wp/';
59
60 /**
61 * Member Variable
62 *
63 * @var staring $widget_folder_u_r_l
64 */
65 public $widget_folder_u_r_l = '';
66
67 /**
68 * Member Variable
69 *
70 * @var staring $e_msg_login
71 */
72 public $e_msg_login = 'Login Error: Check your details and try again.';
73
74 /**
75 * Member Variable
76 *
77 * @var staring $e_desc_login
78 */
79 public $e_desc_login = 'Invalid Login Details';
80
81 /**
82 * Member Variable
83 *
84 * @var staring wdkit_onbording_api
85 */
86 public $wdkit_onbording_api = 'https://api.posimyth.com/wp-json/wdkit/v2/wdkit_store_user_data';
87
88 /**
89 * Member Variable
90 *
91 * @var staring wdkit_onbording_end
92 */
93 public $wdkit_onbording_end = 'wkit_onbording_end';
94
95 /**
96 * Initiator
97 */
98 public static function get_instance() {
99 if ( ! isset( self::$instance ) ) {
100 self::$instance = new self();
101 }
102
103 return self::$instance;
104 }
105
106 /**
107 * Define the core functionality of the plugin.
108 */
109 public function __construct() {
110 add_action( 'wp_ajax_get_wdesignkit', array( $this, 'wdkit_api_call' ) );
111 }
112
113 /**
114 * Error JSON message
115 *
116 * @param array $data give array.
117 * @param string $status api code number.
118 *
119 * @since 1.0.0
120 * */
121 public function wdkit_error_msg( $data = null, $status = null ) {
122 wp_send_json_error( $data );
123 wp_die();
124 }
125
126 /**
127 * Success JSON message
128 *
129 * @param array $data give array.
130 * @param string $status api code number.
131 *
132 * @since 1.0.0
133 * */
134 public function wdkit_success_msg( $data = null, $status = null ) {
135 wp_send_json_success( $data, $status );
136 wp_die();
137 }
138
139
140 /**
141 * Memory headroom left for image work, in bytes. 0 means unlimited.
142 */
143 private static function wdkit_available_image_memory() {
144 $limit = wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) );
145
146 if ( $limit <= 0 ) {
147 return 0;
148 }
149
150 return max( 0, $limit - memory_get_usage( true ) );
151 }
152
153 /**
154 * Stop WordPress decoding images that cannot fit in the memory available.
155 *
156 * Both filters are consulted by wp_create_image_subsizes() *before* it loads an image
157 * editor, so refusing here means the oversized image is never decoded:
158 *
159 * big_image_size_threshold -> falsy skips the "-scaled" copy (needs a full decode)
160 * intermediate_image_sizes_advanced -> empty makes _wp_make_subsizes() return early,
161 * ahead of its wp_get_image_editor() call
162 *
163 * The original file is still attached and usable; only the derived sizes are skipped.
164 * That trades ideal thumbnails for an import that completes, instead of a fatal that
165 * takes the whole page down and repeats on every retry.
166 *
167 * @since 2.6.2
168 */
169 private static function wdkit_guard_oversized_images() {
170 static $registered = false;
171
172 // Registering twice would stack duplicate closures on both filters.
173 if ( $registered ) {
174 return;
175 }
176
177 $registered = true;
178
179 if ( ! class_exists( 'Wdkit_Image_Guard' ) ) {
180 require_once WDKIT_INCLUDES . 'admin/class-wdkit-image-guard.php';
181 }
182
183 add_filter(
184 'big_image_size_threshold',
185 function ( $threshold, $imagesize = array(), $file = '', $attachment_id = 0 ) {
186 if ( ! empty( $imagesize[0] ) && ! empty( $imagesize[1] )
187 && ! Wdkit_Image_Guard::decode_fits( $imagesize[0], $imagesize[1], self::wdkit_available_image_memory() )
188 ) {
189 return false;
190 }
191
192 return $threshold;
193 },
194 99,
195 4
196 );
197
198 add_filter(
199 'intermediate_image_sizes_advanced',
200 function ( $sizes, $image_meta = array(), $attachment_id = 0 ) {
201 if ( ! empty( $image_meta['width'] ) && ! empty( $image_meta['height'] )
202 && ! Wdkit_Image_Guard::decode_fits( $image_meta['width'], $image_meta['height'], self::wdkit_available_image_memory() )
203 ) {
204 return array();
205 }
206
207 return $sizes;
208 },
209 99,
210 3
211 );
212 }
213
214 /**
215 * Get Wdkit Api Call Ajax.
216 */
217 public function wdkit_api_call() {
218
219 check_ajax_referer( 'wdkit_nonce', 'kit_nonce' );
220
221 if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
222 wp_send_json_error( array( 'content' => __( 'Insufficient permissions.', 'wdesignkit' ) ) );
223 }
224
225 $type = isset( $_POST['type'] ) ? strtolower( sanitize_text_field( wp_unslash( $_POST['type'] ) ) ) : false;
226 if ( ! $type ) {
227 $this->wdkit_error_msg( __( 'Something went wrong.', 'wdesignkit' ) );
228 }
229
230 switch ( $type ) {
231 case 'onboarding_handler':
232 $data = $this->wdkit_onboarding_handler();
233 break;
234 case 'wkit_login':
235 $data = apply_filters( 'wp_wdkit_login_ajax', 'wkit_login' );
236 break;
237 case 'api_login':
238 $data = apply_filters( 'wp_wdkit_login_ajax', 'api_login' );
239 break;
240 case 'social_login':
241 $data = apply_filters( 'wp_wdkit_login_ajax', 'social_login' );
242 break;
243 case 'forgot_password':
244 $data = apply_filters( 'wp_wdkit_login_ajax', 'forgot_password' );
245 break;
246 case 'wdkit_user_signup':
247 $data = apply_filters( 'wp_wdkit_login_ajax', 'wdkit_user_signup' );
248 break;
249 case 'wkit_meta_data':
250 $data = $this->wdkit_meta_data();
251 break;
252 case 'get_user_info':
253 $data = $this->wdkit_get_user_info();
254 break;
255 case 'browse_page':
256 $data = $this->wdkit_browse_page();
257 break;
258 case 'kit_template':
259 $data = $this->wdkit_template();
260 break;
261 case 'wkit_preset_template':
262 $data = apply_filters( 'wp_wdkit_preset_ajax', 'wdkit_preset_template' );
263 break;
264 case 'wdkit_preset_dwnld_template':
265 $data = apply_filters( 'wp_wdkit_preset_ajax', 'wdkit_preset_dwnld_template' );
266 break;
267 case 'template_remove':
268 $data = $this->wdkit_template_remove();
269 break;
270 case 'save_template':
271 $data = $this->wdkit_put_save_template();
272 break;
273 case 'update_save_temp_image':
274 $data = $this->wdkit_update_save_temp_image();
275 break;
276 case 'save_wp_images':
277 $data = $this->wdkit_save_wp_images();
278 break;
279 case 'get_global_val':
280 $data = $this->wdkit_get_global_val();
281 break;
282 case 'update_global_val':
283 $data = $this->wdkit_update_global_val();
284 break;
285 case 'wdkit_get_site_setting':
286 $data = $this->wdkit_get_site_setting();
287 break;
288 case 'wdkit_update_site_setting':
289 $data = $this->wdkit_update_site_setting();
290 break;
291 case 'update_preset_setting':
292 $data = $this->wdkit_update_preset();
293 break;
294 case 'find_template':
295 $data = $this->wdkit_find_existing_template();
296 break;
297 case 'update_template':
298 $data = $this->wdkit_update_template();
299 break;
300 case 'manage_favorite':
301 $data = $this->wdkit_manage_favorite();
302 break;
303 case 'check_plugins_depends':
304 $data = $this->wdkit_check_plugins_depends();
305 break;
306 case 'install_plugins_depends':
307 $data = $this->wdkit_install_plugins_depends();
308 break;
309 case 'generate_site_logo':
310 $data = $this->wkit_generate_site_logo();
311 break;
312 case 'generate_ai_content':
313 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'generate_ai_content' );
314 break;
315 case 'generate_ai_content_batch':
316 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'generate_ai_content_batch' );
317 break;
318 case 'reset_site':
319 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'reset_site' );
320 break;
321 case 'wdkit_nxt_thembuilder_reset':
322 $data = $this->wdkit_nxt_thembuilder_reset();
323 break;
324 case 'wdkit_check_user_credit':
325 $data = $this->wdkit_check_user_credit();
326 break;
327 case 'wdkit_remove_header_footer':
328 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wdkit_remove_header_footer' );
329 break;
330 case 'check_post_count':
331 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'check_post_count' );
332 break;
333 case 'wkit_check_product_count':
334 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_check_product_count' );
335 break;
336 case 'select_team_img':
337 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'select_team_img' );
338 break;
339 case 'wkit_ai_desc_keyword':
340 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_ai_desc_keyword' );
341 break;
342 case 'wkit_ai_credit_update':
343 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_ai_credit_update' );
344 break;
345 case 'wkit_generate_post_data':
346 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_generate_post_data' );
347 break;
348 case 'wkit_generate_product_data':
349 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_generate_product_data' );
350 break;
351 case 'wkit_cteate_product':
352 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_cteate_product' );
353 break;
354 case 'wkit_remove_dummy_post':
355 $data = apply_filters( 'wp_wdkit_import_temp_ajax', 'wkit_remove_dummy_post' );
356 break;
357 case 'update_latest_plugin':
358 $data = $this->wdkit_update_latest_plugin();
359 break;
360 case 'activate_container':
361 $data = $this->wdkit_activate_container();
362 break;
363 case 'import_taxonomy':
364 $data = $this->wdkit_import_taxonomy();
365 break;
366 case 'import_template':
367 $data = $this->wdkit_import_template();
368 break;
369 case 'import_multi_template':
370 $data = $this->wdkit_import_multi_template();
371 break;
372 case 'import_page_section':
373 $data = $this->import_page_section_content();
374 break;
375 case 'wkit_update_elementor_template':
376 $data = $this->wkit_update_elementor_template();
377 break;
378 case 'wdkit_update_page_content':
379 $data = $this->wdkit_update_page_content();
380 break;
381 case 'update_plugin_setting':
382 $data = $this->update_plugin_setting();
383 break;
384 case 'update_theme_setting':
385 $data = $this->update_theme_setting();
386 break;
387 case 'update_site_setting':
388 $data = $this->update_site_setting();
389 break;
390 case 'import_kit_template':
391 $data = $this->wdkit_import_kit_template();
392 break;
393 case 'enable_template_widgets':
394 $data = $this->wdkit_enable_template_widgets();
395 break;
396 case 'scan_nexter_widgets':
397 if ( ! empty( $_POST['blockNames'] ) && has_filter( 'nexter_block_list_merge' ) ) {
398
399 $posted_blocks = json_decode( stripslashes( $_POST['blockNames'] ), true );
400
401 if ( is_array( $posted_blocks ) ) {
402 $blockList = array_map( 'sanitize_text_field', $posted_blocks );
403
404 // �
405 filter call करो
406 $result = apply_filters( 'nexter_block_list_merge', $blockList );
407
408 wp_send_json( $result );
409 wp_die();
410 }
411 }
412
413 wp_send_json(
414 array(
415 'success' => false,
416 'message' => __( 'No block names received or filter not found.', 'wdesignkit' ),
417 'description' => 'Ensure blockNames are posted and the filter is attached.',
418 )
419 );
420 wp_die();
421 $data = '';
422 break;
423 case 'shared_with_me':
424 $data = $this->wdkit_shared_with_me();
425 break;
426 case 'manage_workspace':
427 $data = $this->wdkit_manage_workspace();
428 break;
429 case 'widget_browse_page':
430 $data = apply_filters( 'wp_wdkit_widget_ajax', 'widget_browse_page' );
431 break;
432 case 'wkit_create_widget':
433 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_create_widget' );
434 break;
435 case 'wkit_import_widget':
436 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_import_widget' );
437 break;
438 case 'wkit_export_widget':
439 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_export_widget' );
440 break;
441 case 'wkit_delete_widget':
442 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_delete_widget' );
443 break;
444 case 'wkit_widget_preview':
445 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_widget_preview' );
446 break;
447 case 'wkit_check_widget_versions':
448 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_check_widget_versions' );
449 break;
450 case 'wkit_plugin_download_get':
451 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_plugin_download_get' );
452 break;
453 case 'wkit_manage_widget_workspace':
454 $data = $this->wdkit_manage_widget_workspace();
455 break;
456 case 'wkit_activate_key':
457 $data = $this->wdkit_activate_key();
458 break;
459 case 'wkit_manage_widget_category':
460 $data = $this->wdkit_manage_widget_category();
461 break;
462 case 'wkit_widget_json':
463 $data = $this->wkit_widget_json();
464 break;
465 case 'wkit_download_widget':
466 $data = $this->wdkit_download_widget();
467 break;
468 case 'wkit_public_download_widget':
469 $data = apply_filters( 'wp_wdkit_widget_ajax', 'wkit_public_download_widget' );
470 break;
471 case 'wkit_add_widget':
472 $data = $this->wdkit_add_widget();
473 break;
474 case 'wkit_favourite_widget':
475 $data = $this->wdkit_favourite_widget();
476 break;
477 case 'wkit_setting_panel':
478 $data = $this->wdkit_setting_panel();
479 break;
480 case 'media_import':
481 $data = $this->wdkit_media_import();
482 break;
483 case 'active_licence':
484 $data = $this->wdkit_activate_licence();
485 break;
486 case 'delete_licence':
487 $data = $this->wdkit_delete_licence_key();
488 break;
489 case 'sync_licence':
490 $data = $this->wdkit_sync_licence_key();
491 break;
492 case 'get_wkit_version':
493 $data = $this->wdkit_prev_version();
494 break;
495 case 'rollback_wdkit':
496 $data = $this->wdkit_rollback_check();
497 break;
498 case 'wkit_logout':
499 $data = $this->wdkit_logout();
500 break;
501 case 'wkit_white_label':
502 $this->wkit_white_label();
503 break;
504 case 'wkit_reset_wl':
505 $data = $this->wkit_reset_wl();
506 break;
507 case 'wdkit_dark_mode':
508 $data = $this->wdkit_dark_mode();
509 break;
510 case 'wdkit_get_workspace_data':
511 $data = $this->wdkit_get_workspace_data();
512 break;
513 default:
514 $this->wdkit_error_msg( __( 'Unknown request type.', 'wdesignkit' ) );
515 return;
516 }
517
518 $this->wdkit_success_msg( $data );
519 // wp_die();
520 }
521
522 /**
523 *
524 * This Function is used for API call
525 *
526 * @since 1.0.0
527 *
528 * @param array $data give array.
529 * @param array $name store data.
530 * @param int $timeout optional HTTP timeout in seconds. Default 100.
531 */
532 protected function wkit_api_call( $data, $name, $timeout = 100 ) {
533 $u_r_l = $this->wdkit_api;
534
535 if ( empty( $u_r_l ) ) {
536 return array(
537 'massage' => esc_html__( 'API Not Found', 'wdesignkit' ),
538 'success' => false,
539 );
540 }
541
542 $args = array(
543 'method' => 'POST',
544 'body' => $data,
545 'timeout' => $timeout,
546 );
547 $response = wp_remote_post( $u_r_l . $name, $args );
548
549 if ( is_wp_error( $response ) ) {
550 $error_message = $response->get_error_message();
551
552 /* Translators: %s is a placeholder for the error message */
553 $error_message = sprintf( esc_html__( 'API request error: %s', 'wdesignkit' ), esc_html( $error_message ) );
554
555 return array(
556 'massage' => $error_message,
557 'success' => false,
558 );
559 }
560
561 $status_code = wp_remote_retrieve_response_code( $response );
562 if ( 200 === $status_code ) {
563
564 return array(
565 'data' => json_decode( wp_remote_retrieve_body( $response ) ),
566 'massage' => esc_html__( 'Success', 'wdesignkit' ),
567 'status' => $status_code,
568 'success' => true,
569 );
570 }
571
572 $error_message = sprintf( 'Server error: %d', esc_html( $status_code ) );
573
574 if ( isset( $error_data->message ) ) {
575 $error_message .= ' (' . $error_data->message . ')';
576 }
577
578 return array(
579 'massage' => $error_message,
580 'status' => $status_code,
581 'success' => false,
582 );
583 }
584
585 /**
586 *
587 * It is Use for handle onboarding data.
588 *
589 * @since 1.0.9
590 */
591 public function wdkit_onboarding_handler() {
592 $page_template = isset( $_POST['page_template'] ) ? json_decode( wp_unslash( $_POST['page_template'] ), true ) : '';
593 $page_builder = isset( $_POST['page_builder'] ) ? json_decode( wp_unslash( $_POST['page_builder'] ), true ) : '';
594
595 $elementor_plugin = isset( $_POST['elementor_plugin'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['elementor_plugin'] ) ) : 0;
596 $tpag_plugin = isset( $_POST['tpag_plugin'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['tpag_plugin'] ) ) : 0;
597 $bricks_theme = isset( $_POST['bricks_theme'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['bricks_theme'] ) ) : 0;
598 $site_info = isset( $_POST['info'] ) ? sanitize_text_field( wp_unslash( $_POST['info'] ) ) : false;
599
600 $server_software = ! empty( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '';
601
602 $web_server = $server_software;
603 $memory_limit = ini_get( 'memory_limit' );
604 $max_execution_time = ini_get( 'max_execution_time' );
605 $php_version = phpversion();
606 $wp_version = get_bloginfo( 'version' );
607 $email = get_option( 'admin_email' );
608 $siteurl = get_option( 'siteurl' );
609 $language = get_bloginfo( 'language' );
610
611 // Active Plugin Name.
612 $act_plugin = array();
613 $actplu = get_option( 'active_plugins' );
614 if ( ! function_exists( 'get_plugins' ) ) {
615 require_once ABSPATH . 'wp-admin/includes/plugin.php';
616 }
617
618 $plugins = get_plugins();
619 foreach ( $actplu as $p ) {
620 if ( isset( $plugins[ $p ] ) ) {
621 $act_plugin[] = $plugins[ $p ]['Name'];
622 }
623 }
624
625 $plugin = wp_json_encode( $act_plugin );
626
627 $theme = '';
628 $acthemeobj = wp_get_theme();
629 if ( $acthemeobj->get( 'Name' ) !== null && ! empty( $acthemeobj->get( 'Name' ) ) ) {
630 $theme = $acthemeobj->get( 'Name' );
631 }
632
633 $basic_requirements = array(
634 'elementor_install' => $elementor_plugin,
635 'tpag_install' => $tpag_plugin,
636 'bricks_install' => $bricks_theme,
637 );
638
639 if ( ! empty( $site_info ) ) {
640 $final = array(
641 'web_server' => $web_server,
642 'memory_limit' => $memory_limit,
643 'max_execution_time' => $max_execution_time,
644 'php_version' => $php_version,
645 'wp_version' => $wp_version,
646 'email' => $email,
647 'site_url' => $siteurl,
648 'site_language' => $language,
649 'theme' => $theme,
650 'plugins' => $act_plugin,
651 'basic_requirements' => $basic_requirements,
652 'page_template' => $page_template,
653 'page_builder' => $page_builder,
654 );
655 } else {
656 $final = array();
657 }
658
659 $response = wp_remote_post(
660 $this->wdkit_onbording_api,
661 array(
662 'method' => 'POST',
663 'body' => wp_json_encode( $final ),
664 )
665 );
666
667 $existing_value = get_option( $this->wdkit_onbording_end );
668 if ( false === $existing_value ) {
669 add_option( $this->wdkit_onbording_end, true );
670 }
671
672 if ( is_wp_error( $response ) ) {
673 $result = array(
674 'success' => false,
675 'messages' => 'Oops',
676 'description' => 'Description',
677 );
678
679 wp_send_json( $result );
680 } else {
681 $status_one = wp_remote_retrieve_response_code( $response );
682
683 if ( 200 === $status_one ) {
684 $get_data_one = wp_remote_retrieve_body( $response );
685
686 $get_res = json_decode( json_decode( $get_data_one, true ), true );
687
688 $result = array(
689 'success' => ! empty( $get_res['success'] ) ? $get_res['success'] : false,
690 'messages' => ! empty( $get_res['messages'] ) ? $get_res['messages'] : 'success',
691 'description' => ! empty( $get_res['description'] ) ? $get_res['description'] : 'Description',
692 );
693
694 wp_send_json( $result );
695 } else {
696
697 $result = array(
698 'success' => false,
699 'messages' => 'Oops',
700 'description' => 'description',
701 );
702
703 wp_send_json( $result );
704 }
705 }
706
707 wp_die();
708 }
709
710 /**
711 *
712 * It is Use for get meta data for non login user
713 *
714 * @since 1.0.0
715 */
716 protected function wdkit_meta_data() {
717 $type = isset( $_POST['meta_type'] ) ? sanitize_text_field( wp_unslash( $_POST['meta_type'] ) ) : '';
718 $data = array( 'type' => $type );
719
720 $response = $this->wkit_api_call( $data, 'meta' );
721 $success = ! empty( $response['success'] ) ? $response['success'] : false;
722 $status = ! empty( $response['status'] ) ? (int) $response['status'] : 400;
723
724 if ( empty( $success ) ) {
725 $result = array(
726 'plugin' => array(),
727 'builder' => array(),
728 'category' => array(),
729 'widgetscategory' => array(),
730 'tags' => array(),
731 'widgetbuilder' => array(),
732
733 'message' => esc_html__( 'Server Error', 'wdesignkit' ),
734 'description' => esc_html__( 'Server Error, Data Not Found', 'wdesignkit' ),
735 'success' => false,
736 );
737
738 wp_send_json( $result );
739 wp_die();
740 }
741
742 $get_data_one = ! empty( $response['data'] ) ? $response['data'] : array();
743 $statuscode = array( 'HTTP_CODE' => $status );
744
745 $final = json_decode( wp_json_encode( $get_data_one ), true );
746
747 $final['Setting'] = self::wkit_get_settings_panel();
748 $final['widget_list'] = $this->wkit_manage_widget_sequence( array() );
749
750 $final = array(
751 'data' => $final,
752 );
753
754 wp_send_json( $final );
755 wp_die();
756 }
757
758 /**
759 *
760 * It is Use for get activate license key data from tpae and nexter blocks.
761 *
762 * @since 1.1.6
763 */
764 protected function wkit_manage_license_data() {
765 $manage_licence = array();
766 $theplus_active_check = is_plugin_active( 'the-plus-addons-for-elementor-page-builder/theplus_elementor_addon.php' );
767 $nexter_active_check = is_plugin_active( 'the-plus-addons-for-block-editor/the-plus-addons-for-block-editor.php' );
768
769 $theplus_licence = get_option( 'tpaep_licence_data', array() );
770
771 // Also require the TPAE Pro plugin to be active (Pro defines THEPLUS_VERSION;
772 // the free plugin defines L_THEPLUS_VERSION). This hides the "found active
773 // key" notice when the Pro plugin is removed even though its licence option
774 // still lingers in the database.
775 if ( ! empty( $theplus_active_check ) && defined( 'THEPLUS_VERSION' ) && ! empty( $theplus_licence ) ) {
776 $manage_licence['tpae'] = $theplus_licence;
777 }
778
779 $nexter_licence = get_option( 'tpgb_activate', array() );
780
781 // Also require the Nexter Blocks Pro plugin to be active (Pro defines
782 // TPGBP_VERSION; the free plugin defines TPGB_VERSION), so the notice hides
783 // when the Pro plugin is removed but its licence option persists.
784 if ( ! empty( $nexter_active_check ) && defined( 'TPGBP_VERSION' ) && ! empty( $nexter_licence ) && ! empty( $nexter_licence['tpgb_activate_key'] ) ) {
785 $tpgb_license_status = get_option( 'tpgbp_license_status', array() );
786 $tpgb_license_status['license_key'] = $nexter_licence['tpgb_activate_key'];
787 $manage_licence['tpag'] = $tpgb_license_status;
788 }
789
790 return $manage_licence;
791 }
792
793 /**
794 *
795 * It is Use for get all info of user.
796 *
797 * @since 1.0.0
798 */
799 protected function wdkit_get_user_info() {
800 $token = isset( $_POST['token'] ) ? wp_unslash( $_POST['token'] ) : false;
801 $email = isset( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : false;
802 $builder = isset( $_POST['builder'] ) ? strtolower( sanitize_text_field( wp_unslash( $_POST['builder'] ) ) ) : '';
803
804 $site_url = isset( $_POST['site_url'] ) ? esc_url_raw( wp_unslash( $_POST['site_url'] ) ) : '';
805
806 $response = array();
807
808 if ( empty( $token ) ) {
809 $response = array(
810 'success' => false,
811 'message' => $this->e_msg_login,
812 'description' => $this->e_desc_login,
813 );
814
815 wp_send_json( $response );
816 wp_die();
817 }
818
819 // $token = $this->wdkit_login_user_token( $email );
820 $args = array(
821 'token' => $token,
822 'builder' => $builder,
823 'site_url' => $site_url,
824 );
825
826 $response = WDesignKit_Data_Query::get_data( 'get_user_info', $args );
827
828 if ( is_wp_error( $response ) ) {
829 wp_send_json( array(
830 'success' => false,
831 'message' => $response->get_error_message(),
832 'description' => $response->get_error_message(),
833 ) );
834 wp_die();
835 }
836
837 $status = ( ! empty( $response['status'] ) ) ? sanitize_text_field( $response['status'] ) : 'error';
838 $email = isset( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : false;
839
840 /**Condtion user for user logout & expire token*/
841 if ( 'Token is Expired' === $status || 'Authorization Token not found' === $status ) {
842 delete_transient( 'wdkit_auth_' . wdesignkit_cloud_session_key( $email ) );
843 // Clear stored license data when token expires so banner shows again
844 delete_option( 'wdkit_licence_data' );
845 }
846
847 if ( empty( $response ) ) {
848 $response['login_reset'] = 'yes';
849 }
850
851 // Store WDesignKit license data locally if present in response
852 if ( ! empty( $response['credits']['wdkit_licence'] ) && is_array( $response['credits']['wdkit_licence'] ) ) {
853 $wdkit_licence = $response['credits']['wdkit_licence'];
854 // Handle serialized data
855 if ( is_string( $wdkit_licence ) && is_serialized( $wdkit_licence ) ) {
856 $wdkit_licence = unserialize( $wdkit_licence, array( 'allowed_classes' => false ) );
857 }
858 if ( ! empty( $wdkit_licence ) && is_array( $wdkit_licence ) ) {
859 update_option( 'wdkit_licence_data', $wdkit_licence );
860 }
861 }
862
863 $response['Setting'] = $this->wkit_get_settings_panel();
864 $response['widget_list'] = $this->wkit_manage_widget_sequence( $response );
865 $response['manage_licence'] = $this->wkit_manage_license_data();
866
867 $response = array(
868 'data' => $response,
869 'success' => true,
870 );
871
872 wp_send_json( $response );
873 wp_die();
874 }
875
876 /**
877 *
878 * It is Use for get all widgets local and server.
879 *
880 * @since 1.0.19
881 * @param string $response userinfo store.
882 */
883 protected function wkit_manage_widget_sequence( $response = array() ) {
884
885 $credits = ! empty( $response['credits']['widget_limit']['meta_value'] ) ? $response['credits']['widget_limit']['meta_value'] : 10;
886 $server_list = ! empty( $response['widgettemplate'] ) ? $response['widgettemplate'] : array();
887 $db_builder_list = ! empty( $response['widgetbuilder'] ) ? $response['widgetbuilder'] : array();
888
889 // Whether this call actually carried the server widget list that activation state is
890 // derived from. Captured before the loops below, which unset() matched $server_list
891 // entries as they go. wdkit_meta_data() calls this method with array(), and without
892 // this flag that call rebuilt $db_widget from local widgets only — every one of which
893 // is forced 'active' further down — and then wrote the empty result over
894 // wkit_deactivate_widgets, erasing every deactivation the user had made.
895 $has_server_widgets = ! empty( $server_list );
896
897 $placeholderimg = WDKIT_URL . 'assets/images/placeholder.jpg';
898
899 $local_list = $this->wdkit_get_local_widgets();
900
901 $server_w_unique = array_column( $server_list, 'w_unique' );
902
903 $idx_builder = array();
904 foreach ( $db_builder_list as $index => $value ) {
905 $builder_name = ! empty( $value['builder_slug'] ) ? $value['builder_slug'] : '';
906 $w_id = ! empty( $value['w_id'] ) ? $value['w_id'] : '';
907
908 if ( ! empty( $builder_name ) ) {
909 $idx_builder[ $w_id ] = strtolower( str_replace( ' ', '_', trim( $builder_name ) ) );
910 }
911 }
912
913 foreach ( $server_list as $index => $value ) {
914 $get_id = $server_list[ $index ]['builder'] ? $server_list[ $index ]['builder'] : '';
915
916 $server_list[ $index ]['type'] = 'server';
917 $server_list[ $index ]['builder'] = ! empty( $idx_builder[ $get_id ] ) ? $idx_builder[ $get_id ] : '';
918 }
919
920 $count = 0;
921
922 foreach ( $local_list as $key => $value ) {
923 $widget_id = ! empty( $value['widgetdata']['widget_id'] ) ? $value['widgetdata']['widget_id'] : '';
924 $allow_push = isset( $value['widgetdata']['allow_push'] ) ? $value['widgetdata']['allow_push'] : true;
925
926 if ( in_array( $widget_id, $server_w_unique ) ) {
927
928 $index = array_search( $widget_id, $server_w_unique );
929
930 $server_list[ $index ]['title'] = $local_list[ $key ]['widgetdata']['name'];
931 $server_list[ $index ]['w_version'] = $local_list[ $key ]['widgetdata']['widget_version'];
932 $server_list[ $index ]['allow_push'] = $allow_push;
933 $server_list[ $index ]['builder'] = $local_list[ $key ]['widgetdata']['type'];
934 $server_list[ $index ]['w_unique'] = $local_list[ $key ]['widgetdata']['widget_id'];
935 $server_list[ $index ]['image'] = ! empty( $local_list[ $key ]['widgetdata']['w_image'] ) ? $local_list[ $key ]['widgetdata']['w_image'] : $placeholderimg;
936
937 $local_list[ $key ] = $server_list[ $index ];
938
939 $local_list[ $key ]['type'] = 'done';
940
941 unset( $server_list[ $index ] );
942 } else {
943 $local_list[ $key ]['widgetdata']['builder'] = $local_list[ $key ]['widgetdata']['type'];
944 $local_list[ $key ]['widgetdata']['w_unique'] = $local_list[ $key ]['widgetdata']['widget_id'];
945 $local_list[ $key ]['widgetdata']['allow_push'] = $allow_push;
946 $local_list[ $key ]['widgetdata']['image'] = ! empty( $local_list[ $key ]['widgetdata']['w_image'] ) ? $local_list[ $key ]['widgetdata']['w_image'] : $placeholderimg;
947 $local_list[ $key ]['widgetdata']['is_activated'] = 'active';
948
949 $local_list[ $key ]['widgetdata']['type'] = 'plugin';
950
951 $local_list[ $key ]['widgetdata']['title'] = $local_list[ $key ]['widgetdata']['name'];
952 unset( $local_list[ $key ]['widgetdata']['name'] );
953 unset( $local_list[ $key ]['widgetdata']['widget_id'] );
954
955 $local_list[ $key ] = $local_list[ $key ]['widgetdata'];
956 }
957 }
958
959 $final = array_merge( $local_list, $server_list );
960
961 $db_widget = array();
962
963 foreach ( $final as $key => $self ) {
964 $is_activated = ! empty( $final[ $key ]['is_activated'] ) ? $final[ $key ]['is_activated'] : 'active';
965
966 if ( 'active' === $is_activated ) {
967 ++$count;
968 }
969
970 if ( ( $count > $credits ) && ( 'unlimited' !== $credits ) ) {
971 $final[ $key ]['is_activated'] = 'deactive';
972 }
973
974 if ( ! empty( $self['is_activated'] ) && 'active' !== $self['is_activated'] ) {
975 $db_widget[] = array(
976 'w_unique' => $self['w_unique'],
977 'builder' => $self['builder'],
978 'title' => $self['title'],
979 'is_activated' => $self['is_activated'],
980 );
981 }
982 }
983
984 // Only persist activation state when the server list it is derived from was actually
985 // supplied. See $has_server_widgets above.
986 if ( $has_server_widgets ) {
987 // update_option() creates the row when it is missing, so it covers both cases.
988 // The previous add_option()/update_option() split was chosen on empty( $option ),
989 // but wdkit_db_widgetlist() creates this row as an empty array on every install —
990 // so the empty branch ran while the row already existed, and add_option() is a
991 // no-op for an existing option. Deactivating from the My Widgets screen was
992 // therefore silently discarded on effectively every site. Autoload stays 'yes',
993 // matching the original add_option() call and wdkit_db_widgetlist().
994 update_option( 'wkit_deactivate_widgets', $db_widget, 'yes' );
995
996 // The cached widget registry bakes in wkit_deactivate_widgets membership and is
997 // stored as a no-expiry transient, so it never self-heals. Without this the
998 // loaders kept registering a widget the user had just switched off (and kept
999 // hiding one they had switched back on) until the transient was flushed by hand.
1000 // The write above is not per-builder — one save can change any builder's set, and
1001 // a widget can move between builders — so clear all four.
1002 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
1003 foreach ( array( 'elementor', 'gutenberg', 'gutenberg_core', 'bricks' ) as $builder_slug ) {
1004 wdesignkit_invalidate_widget_registry( $builder_slug );
1005 }
1006 }
1007 }
1008
1009 return $final;
1010 }
1011
1012 /**
1013 * Browse Page Filter
1014 *
1015 * @since 1.0.0
1016 */
1017 protected function wdkit_browse_page() {
1018 $args = $this->wdkit_parse_args( $_POST );
1019
1020 $response = WDesignKit_Data_Query::get_data( 'browse_page', $args );
1021
1022 if ( is_wp_error( $response ) ) {
1023 wp_send_json( array(
1024 'success' => false,
1025 'message' => $response->get_error_message(),
1026 ) );
1027 wp_die();
1028 }
1029
1030 $manage_licence = array();
1031 $manage_licence['theplus_elementor_addon'] = ! empty( defined( 'THEPLUS_VERSION' ) ) ? true : false;
1032 $manage_licence['tpag'] = ! empty( defined( 'TPGBP_VERSION' ) ) ? true : false;
1033 $manage_licence['elementor-pro'] = ! empty( defined( 'ELEMENTOR_PRO_VERSION' ) ) ? true : false;
1034 $response['manage_licence'] = $manage_licence;
1035
1036 wp_send_json( $response );
1037 wp_die();
1038 }
1039
1040 /**
1041 *
1042 * It is Use for get template from kit.
1043 *
1044 * @since 1.0.0
1045 */
1046 protected function wdkit_template() {
1047 $args = $this->wdkit_parse_args( $_POST );
1048
1049 $response = WDesignKit_Data_Query::get_data( 'kit_template', $args );
1050
1051 wp_send_json( $response );
1052 wp_die();
1053 }
1054
1055 /**
1056 *
1057 * It is Use for remove or delete template.
1058 *
1059 * @since 1.0.0
1060 */
1061 protected function wdkit_template_remove() {
1062 $args = $this->wdkit_parse_args( $_POST );
1063
1064 $user_email = strtolower( sanitize_email( $args['email'] ) );
1065 $response = '';
1066
1067 // Bug D fix: response()->json() is Laravel syntax — causes PHP fatal. Use plain array.
1068 if ( empty( $user_email ) || empty( $args['template_id'] ) ) {
1069 $response = array(
1070 'message' => $this->e_msg_login,
1071 'description' => $this->e_desc_login,
1072 'success' => false,
1073 );
1074
1075 wp_send_json( $response );
1076 wp_die();
1077 }
1078
1079 $args['token'] = $this->wdkit_login_user_token( $user_email );
1080
1081 unset( $user_email );
1082 $response = WDesignKit_Data_Query::get_data( 'template_remove', $args );
1083
1084 wp_send_json( $response );
1085 wp_die();
1086 }
1087
1088 /**
1089 *
1090 * It is Use for save template from page builder.
1091 *
1092 * @since 1.0.0
1093 */
1094 protected function wdkit_put_save_template() {
1095 $email = isset( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : false;
1096 $post_id = isset( $_POST['post_id'] ) ? sanitize_text_field( wp_unslash( $_POST['post_id'] ) ) : '';
1097 $builder = isset( $_POST['builder'] ) ? sanitize_text_field( wp_unslash( $_POST['builder'] ) ) : '';
1098
1099 $response = '';
1100
1101 if ( empty( $email ) ) {
1102 $response = array(
1103 'id' => 0,
1104 'editpage' => '',
1105 'message' => $this->e_msg_login,
1106 'description' => $this->e_desc_login,
1107 'success' => false,
1108 );
1109
1110 wp_send_json( $response );
1111 wp_die();
1112 }
1113
1114 $args = $this->wdkit_parse_args( $_POST );
1115 $args['token'] = $this->wdkit_login_user_token( $email );
1116 unset( $args['email'] );
1117
1118 if( 'elementor' === $builder ){
1119 $args['data'] = base64_decode( $args['data'] );
1120 } else if ( 'gutenberg' === $builder ) {
1121 $args['data'] = base64_decode( $args['data'] );
1122 }
1123
1124 global $post;
1125
1126 $custom_fields = array();
1127 if ( ! empty( $post_id ) ) {
1128 $meta_fields = get_post_custom( $post_id );
1129
1130 foreach ( $meta_fields as $key => $value ) {
1131 if ( str_contains( $key, 'nxt-' ) ) {
1132 $custom_fields[ $key ] = $value;
1133 }
1134 }
1135
1136 if ( ! empty( $custom_fields ) ) {
1137 $data = json_decode( $args['data'], true );
1138 $data['custom_meta'] = $custom_fields;
1139 $args['data'] = wp_json_encode( $data );
1140 }
1141 }
1142
1143 $response = WDesignKit_Data_Query::get_data( 'save_template', $args );
1144
1145 /**
1146 * The cloud call can come back as a WP_Error (timeout, DNS, refused) or with an
1147 * empty / unparsable body, which json_decode()s to null. Forwarding that as-is
1148 * makes admin-ajax answer with a literal `null` that the editor then reads
1149 * `.id` off, killing the whole app. Normalise it to the failure shape used above.
1150 */
1151 if ( is_wp_error( $response ) || ! is_array( $response ) ) {
1152 $response = array(
1153 'id' => 0,
1154 'editpage' => '',
1155 'message' => esc_html__( 'Template Not Saved !', 'wdesignkit' ),
1156 'description' => is_wp_error( $response ) ? $response->get_error_message() : esc_html__( 'Could not reach the WDesignKit server. Please try again.', 'wdesignkit' ),
1157 'success' => false,
1158 );
1159 }
1160
1161 wp_send_json( $response );
1162 wp_die();
1163 }
1164
1165 /**
1166 *
1167 * It is Use for update save template image.
1168 *
1169 * @since 2.0.6
1170 */
1171 protected function wdkit_update_save_temp_image() {
1172 $temp_content = isset( $_POST['temp_content'] ) ? esc_url_raw( $_POST['temp_content'] ) : '';
1173 $content_name = isset( $_POST['content_name'] ) ? sanitize_text_field( $_POST['content_name'] ) : '';
1174 $token = isset( $_POST['token'] ) ? sanitize_text_field( $_POST['token'] ) : '';
1175 $user_type = isset( $_POST['user_type'] ) ? sanitize_text_field( $_POST['user_type'] ) : '';
1176 $type = isset( $_POST['content_type'] ) ? sanitize_text_field( $_POST['content_type'] ) : '';
1177 $id = isset( $_POST['id'] ) ? sanitize_text_field( $_POST['id'] ) : '';
1178 if ( empty( $temp_content ) || empty( $user_type ) || empty( $id ) || empty( $token ) ) {
1179 $response = array(
1180 'message' => __( 'Data not found', 'wdesignkit' ),
1181 'description' => __( 'Data not found', 'wdesignkit' ),
1182 'success' => false,
1183 );
1184 } else {
1185 $temp_content = str_replace( '\\', '', $temp_content );
1186 // SSRF guard (CWE-918): validate the resolved host before fetching a caller-supplied URL.
1187 $fetched = wdesignkit_safe_remote_get( $temp_content );
1188 $temp_content = is_wp_error( $fetched ) ? '' : wp_remote_retrieve_body( $fetched );
1189 $temp_content = base64_encode( $temp_content );
1190
1191 $args = array(
1192 'token' => $token,
1193 'template_id' => $id,
1194 'name' => $content_name,
1195 'content' => $temp_content,
1196 'type' => $type,
1197 );
1198
1199 $response = $this->wkit_api_call( $args, 'save_images' );
1200 $success = ! empty( $response['success'] ) ? $response['success'] : false;
1201
1202 if ( $success ) {
1203 $response = array(
1204 'data' => $response['data'],
1205 'success' => true,
1206 );
1207 } else {
1208 $response = array(
1209 'message' => __( 'API Error', 'wdesignkit' ),
1210 'description' => __( 'API Error', 'wdesignkit' ),
1211 'data' => $response['data'],
1212 'success' => false,
1213 );
1214 }
1215 }
1216
1217 wp_send_json( $response );
1218 wp_die();
1219 }
1220
1221 /**
1222 *
1223 * It is Use for save image to WordPress Media Library.
1224 *
1225 * @since 2.3.3
1226 */
1227 protected function wdkit_save_wp_images() {
1228
1229 // media_sideload_image() generates every registered thumbnail size, which decodes
1230 // the full source bitmap. Same guard as the page import.
1231 $this->wdkit_guard_oversized_images();
1232
1233 $image_url = isset( $_POST['image'] ) ? sanitize_text_field( $_POST['image'] ) : '';
1234
1235 if ( empty( $image_url ) ) {
1236 $response = array(
1237 'message' => __( 'No Image Provided', 'wdesignkit' ),
1238 'description' => __( 'No Image URL provided for save.', 'wdesignkit' ),
1239 'success' => false,
1240 );
1241 } else {
1242
1243 $attachment_id = media_sideload_image( $image_url, 0, null, 'id' );
1244
1245 if ( is_wp_error( $attachment_id ) ) {
1246 $response = array(
1247 'message' => __( 'Upload Failed', 'wdesignkit' ),
1248 'description' => $attachment_id->get_error_message(),
1249 'success' => false,
1250 );
1251 } else {
1252 $saved_url = wp_get_attachment_url( $attachment_id );
1253
1254 // Elementor's importer skips an image only when it finds
1255 // _elementor_source_image_hash matching sha1 of the URL it is given. The
1256 // content we hand it now carries this local URL, so stamp the hash of that
1257 // URL too - without it Elementor re-downloads a file already on disk and
1258 // leaves a "-1" duplicate behind for every image on every page that uses it.
1259 if ( $saved_url ) {
1260 update_post_meta( $attachment_id, '_elementor_source_image_hash', sha1( $saved_url ) );
1261
1262 // Same purpose for the block importer, which keys off its own meta.
1263 update_post_meta( $attachment_id, 'tpgb_source_image_key', sha1( $saved_url ) );
1264 }
1265
1266 $response = array(
1267 'message' => __( 'Image Saved', 'wdesignkit' ),
1268 'description' => __( 'Image successfully saved to Media Library.', 'wdesignkit' ),
1269 'success' => true,
1270 'url' => $saved_url,
1271 );
1272 }
1273
1274 }
1275
1276 wp_send_json( $response );
1277 wp_die();
1278 }
1279
1280 /**
1281 *
1282 * Get Elementor Global color and Typography.
1283 *
1284 * @since 1.1.16
1285 */
1286 /**
1287 * Kit settings holding The Plus Addons' own globals.
1288 *
1289 * These sit in the Elementor kit's `_elementor_page_settings` alongside Elementor's
1290 * system_colors / system_typography, but the save flow only ever collected the four
1291 * Elementor keys. Widgets reference an entry in these lists by its `_id` through a
1292 * `tp_global_preset` setting, so a template saved without them travels with the
1293 * reference but not the definition - which is why imported sections come in missing
1294 * their button styling, radii and shadows.
1295 *
1296 * @since 2.6.4
1297 *
1298 * @return array Kit setting keys.
1299 */
1300 private function wdkit_tp_global_kit_keys() {
1301 return array(
1302 'tp_global_button_style_list',
1303 'tp_global_dimensions_list',
1304 'tp_global_box_shadow_list',
1305 'tp_global_gradient_list',
1306 'tp_global_gsap_list',
1307 'tp_global_scroll_animation_list',
1308 'tp_text_global_gsap_list',
1309 'tp_image_global_gsap_list',
1310 );
1311 }
1312
1313 /**
1314 * Merge incoming Plus globals into the active kit, keyed by `_id`.
1315 *
1316 * Entries are matched on their `_id`, never on position: an existing entry is always
1317 * left as it is, and only genuinely new ones are appended. That matters because
1318 * widgets - and the entries themselves, a button style points at dimension and shadow
1319 * entries - resolve by `_id`. Renumbering or overwriting would repoint references on
1320 * the destination site's own content.
1321 *
1322 * @since 2.6.4
1323 *
1324 * @param array $incoming Lists captured with the template.
1325 * @return bool True when the kit was changed.
1326 */
1327 /**
1328 * Global colour / typography ids this site already defines.
1329 *
1330 * @since 2.6.4
1331 *
1332 * @param array $kit_meta Kit `_elementor_page_settings`.
1333 * @return array{color:array<string,bool>,typography:array<string,bool>}
1334 */
1335 private function wdkit_known_global_ids( $kit_meta ) {
1336 $known = array(
1337 'color' => array(),
1338 'typography' => array(),
1339 );
1340
1341 $sources = array(
1342 'color' => array( 'system_colors', 'custom_colors' ),
1343 'typography' => array( 'system_typography', 'custom_typography' ),
1344 );
1345
1346 foreach ( $sources as $kind => $keys ) {
1347 foreach ( $keys as $key ) {
1348 if ( empty( $kit_meta[ $key ] ) || ! is_array( $kit_meta[ $key ] ) ) {
1349 continue;
1350 }
1351
1352 foreach ( $kit_meta[ $key ] as $entry ) {
1353 if ( ! empty( $entry['_id'] ) ) {
1354 $known[ $kind ][ $entry['_id'] ] = true;
1355 }
1356 }
1357 }
1358 }
1359
1360 return $known;
1361 }
1362
1363 /**
1364 * Make one incoming Plus global's colour / font references resolvable here.
1365 *
1366 * A Plus global can point at an Elementor global: the "Primary Button" entry holds
1367 * `__globals__: { text_color: "globals/colors?id=72e09b4", … }`, which The Plus Addons
1368 * turns into `var(--e-global-color-72e09b4)`. Elementor only emits that variable for ids
1369 * present in the kit, so on a site without `72e09b4` the button renders with no colour.
1370 *
1371 * Two cases, and the difference is deliberate:
1372 *
1373 * - The site ALREADY defines that id — leave the reference alone. The button then picks
1374 * up the destination's own colour, which is the point of a global. Their palette is
1375 * never read from or written to beyond this check.
1376 * - The site does NOT define it — write the captured value straight into the entry and
1377 * drop the reference, so it renders as designed.
1378 *
1379 * Nothing is ever added to the user's global colours or fonts. An earlier version injected
1380 * the missing definitions into their palette, which made the reference resolve but grew
1381 * their Site Settings by every colour an imported template happened to use.
1382 *
1383 * @since 2.6.4
1384 *
1385 * @param array $entry One repeater entry.
1386 * @param array $refs Definitions captured with the template.
1387 * @param array $known Ids this site defines, from wdkit_known_global_ids().
1388 * @return array Entry, with unresolvable references replaced by their values.
1389 */
1390 private function wdkit_resolve_entry_globals( $entry, $refs, $known ) {
1391 if ( empty( $entry['__globals__'] ) || ! is_array( $entry['__globals__'] ) ) {
1392 return $entry;
1393 }
1394
1395 foreach ( $entry['__globals__'] as $control => $ref ) {
1396 if ( ! is_string( $ref ) || false === strpos( $ref, 'id=' ) ) {
1397 continue;
1398 }
1399
1400 if ( false !== strpos( $ref, 'globals/colors' ) ) {
1401 $kind = 'color';
1402 } elseif ( false !== strpos( $ref, 'globals/typography' ) ) {
1403 $kind = 'typography';
1404 } else {
1405 continue;
1406 }
1407
1408 $id = substr( $ref, strpos( $ref, 'id=' ) + 3 );
1409 if ( '' === $id || isset( $known[ $kind ][ $id ] ) ) {
1410 // Defined here already — their value wins.
1411 continue;
1412 }
1413
1414 $definition = null;
1415 foreach ( ( $refs[ $kind ] ?? array() ) as $candidate ) {
1416 if ( is_array( $candidate ) && ( $candidate['_id'] ?? '' ) === $id ) {
1417 $definition = $candidate;
1418 break;
1419 }
1420 }
1421
1422 if ( null === $definition ) {
1423 // Nothing captured for it, so leave the reference rather than blank the field.
1424 continue;
1425 }
1426
1427 if ( 'color' === $kind ) {
1428 if ( empty( $definition['color'] ) ) {
1429 continue;
1430 }
1431
1432 $entry[ $control ] = $definition['color'];
1433 } else {
1434 // A typography global expands into its own set of controls: the reference is
1435 // held under e.g. `typography_typography`, and each definition key replaces
1436 // that suffix — `typography_font_family`, `typography_font_weight`, and so on.
1437 foreach ( $definition as $def_key => $def_value ) {
1438 if ( '_id' === $def_key || 'title' === $def_key ) {
1439 continue;
1440 }
1441
1442 $entry[ str_replace( 'typography_typography', $def_key, $control ) ] = $def_value;
1443 }
1444 }
1445
1446 unset( $entry['__globals__'][ $control ] );
1447 }
1448
1449 return $entry;
1450 }
1451
1452 private function wdkit_merge_tp_globals( $incoming, $refs = array() ) {
1453 if ( empty( $incoming ) || ! is_array( $incoming ) ) {
1454 return false;
1455 }
1456
1457 $kit_id = get_option( 'elementor_active_kit' );
1458 if ( empty( $kit_id ) ) {
1459 return false;
1460 }
1461
1462 $kit_meta = get_post_meta( $kit_id, '_elementor_page_settings', true );
1463 if ( ! is_array( $kit_meta ) ) {
1464 $kit_meta = array();
1465 }
1466
1467 // Which global ids this site already defines. The Plus Addons turns a reference into
1468 // var(--e-global-color-<_id>), and Elementor only emits that variable for ids in the
1469 // kit — so a reference the destination does not define resolves to nothing at all.
1470 $known = $this->wdkit_known_global_ids( $kit_meta );
1471
1472 $changed = false;
1473
1474 foreach ( $this->wdkit_tp_global_kit_keys() as $key ) {
1475 if ( empty( $incoming[ $key ] ) || ! is_array( $incoming[ $key ] ) ) {
1476 continue;
1477 }
1478
1479 $existing = ( ! empty( $kit_meta[ $key ] ) && is_array( $kit_meta[ $key ] ) ) ? $kit_meta[ $key ] : array();
1480
1481 $seen = array();
1482 foreach ( $existing as $entry ) {
1483 if ( ! empty( $entry['_id'] ) ) {
1484 $seen[ $entry['_id'] ] = true;
1485 }
1486 }
1487
1488 foreach ( $incoming[ $key ] as $entry ) {
1489 if ( ! is_array( $entry ) || empty( $entry['_id'] ) || isset( $seen[ $entry['_id'] ] ) ) {
1490 continue;
1491 }
1492
1493 // Only ever rewrite the entry being added — never one already in the kit.
1494 $existing[] = $this->wdkit_resolve_entry_globals( $entry, $refs, $known );
1495 $seen[ $entry['_id'] ] = true;
1496 $changed = true;
1497 }
1498
1499 $kit_meta[ $key ] = array_values( $existing );
1500 }
1501
1502 if ( $changed ) {
1503 update_post_meta( $kit_id, '_elementor_page_settings', $kit_meta );
1504
1505 // Writing kit meta directly does not rebuild the kit stylesheet, so the
1506 // merged globals would never reach the frontend.
1507 $this->wdkit_regenerate_elementor_kit_css();
1508 }
1509
1510 return $changed;
1511 }
1512
1513 protected function wdkit_get_global_val() {
1514
1515 $builder = isset( $_POST['builder'] ) ? strtolower( sanitize_text_field( $_POST['builder'] ) ) : '';
1516
1517 if ( 'elementor' === $builder ) {
1518 $kit_id = get_option( 'elementor_active_kit' );
1519 if ( empty( $kit_id ) ) {
1520 $response = array(
1521 'message' => __( 'Elementor kit not found', 'wdesignkit' ),
1522 'description' => __( 'No active Elementor kit found', 'wdesignkit' ),
1523 'success' => false,
1524 );
1525
1526 wp_send_json( $response );
1527 wp_die();
1528 }
1529
1530 $kit_meta = get_post_meta( $kit_id, '_elementor_page_settings', true );
1531 if ( empty( $kit_meta['system_colors'] ) ) {
1532 $static_meta = array(
1533 'system_colors' => array(
1534 0 => array(
1535 '_id' => 'primary',
1536 'title' => 'Primary',
1537 'color' => '#6EC1E4',
1538 ),
1539 1 => array(
1540 '_id' => 'secondary',
1541 'title' => 'Secondary',
1542 'color' => '#54595F',
1543 ),
1544 2 => array(
1545 '_id' => 'text',
1546 'title' => 'Text',
1547 'color' => '#7A7A7A',
1548 ),
1549 3 => array(
1550 '_id' => 'accent',
1551 'title' => 'Accent',
1552 'color' => '#61CE70',
1553 ),
1554 ),
1555 'custom_colors' => array(),
1556 'system_typography' => array(
1557 0 => array(
1558 '_id' => 'primary',
1559 'title' => 'Primary',
1560 'typography_typography' => 'custom',
1561 'typography_font_family' => 'Roboto',
1562 'typography_font_weight' => '600',
1563 ),
1564 1 => array(
1565 '_id' => 'secondary',
1566 'title' => 'Secondary',
1567 'typography_typography' => 'custom',
1568 'typography_font_family' => 'Roboto Slab',
1569 'typography_font_weight' => '400',
1570 ),
1571 2 => array(
1572 '_id' => 'text',
1573 'title' => 'Text',
1574 'typography_typography' => 'custom',
1575 'typography_font_family' => 'Roboto',
1576 'typography_font_weight' => '400',
1577 ),
1578 3 => array(
1579 '_id' => 'accent',
1580 'title' => 'Accent',
1581 'typography_typography' => 'custom',
1582 'typography_font_family' => 'Roboto',
1583 'typography_font_weight' => '500',
1584 ),
1585 ),
1586 'custom_typography' => array(),
1587 'default_generic_fonts' => 'Sans-serif',
1588 'site_name' => ! empty( get_bloginfo( 'name' ) ) ? get_bloginfo( 'name' ) : '',
1589 'page_title_selector' => 'h1.entry-title',
1590 'activeItemIndex' => 1,
1591 'viewport_md' => 768,
1592 'viewport_lg' => 1025,
1593 );
1594
1595 $kit_meta = $static_meta;
1596 update_post_meta( $kit_id, '_elementor_page_settings', $kit_meta );
1597 }
1598
1599 $system_colors = ! empty( $kit_meta['system_colors'] ) ? $kit_meta['system_colors'] : array();
1600 $custom_colors = ! empty( $kit_meta['custom_colors'] ) ? $kit_meta['custom_colors'] : array();
1601 $system_typography = ! empty( $kit_meta['system_typography'] ) ? $kit_meta['system_typography'] : array();
1602 $custom_typography = ! empty( $kit_meta['custom_typography'] ) ? $kit_meta['custom_typography'] : array();
1603
1604 $color_array = array_merge( $system_colors, $custom_colors );
1605 $typo_array = array_merge( $system_typography, $custom_typography );
1606
1607 $global_data = array(
1608 'color' => $color_array,
1609 'typography' => $typo_array,
1610 );
1611
1612 $response = array(
1613 'message' => __( 'Global data Found', 'wdesignkit' ),
1614 'description' => __( 'Global Color and Typography found', 'wdesignkit' ),
1615 'data' => $global_data,
1616 'success' => true,
1617 );
1618
1619 } elseif ( 'gutenberg' === $builder ) {
1620
1621 $plus_settings = get_option( 'tpgb_global_options', false );
1622 $plus_settings = ! empty( $plus_settings ) ? json_decode( $plus_settings, true ) : json_decode( '[]' );
1623
1624 if ( empty( $plus_settings ) ) {
1625
1626 $static_meta = array(
1627 'active' => 'preset1',
1628 'darkMode' => 'none',
1629 'presets' => array(
1630 'preset1' => array(
1631 'name' => 'Preset 1',
1632 'key' => 'preset1',
1633 'colors' => array(
1634 array(
1635 'label' => 'Primary',
1636 'value' => '#8072FC',
1637 ),
1638 array(
1639 'label' => 'Secondary',
1640 'value' => '#6FC784',
1641 ),
1642 array(
1643 'label' => 'Tertiary',
1644 'value' => '#FF5A6E',
1645 ),
1646 array(
1647 'label' => 'Accent',
1648 'value' => '#F3F3F3',
1649 ),
1650 array(
1651 'label' => 'Background',
1652 'value' => '#888888',
1653 ),
1654 ),
1655 'gradient' => array(
1656 array(
1657 'label' => 'Primary',
1658 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1659 ),
1660
1661 array(
1662 'label' => 'Secondary',
1663 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1664 ),
1665
1666 array(
1667 'label' => 'Tertiary',
1668 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1669 ),
1670
1671 array(
1672 'label' => 'Accent',
1673 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1674 ),
1675
1676 array(
1677 'label' => 'Background',
1678 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1679 ),
1680 ),
1681 'spacing' => array(
1682 array(
1683 'label' => 'Large',
1684 'value' => array(
1685 'md' => 70,
1686 'unit' => 'px',
1687 ),
1688 ),
1689 array(
1690 'label' => 'Medium',
1691 'value' => array(
1692 'md' => 40,
1693 'unit' => 'px',
1694 ),
1695 ),
1696 array(
1697 'label' => 'Small',
1698 'value' => array(
1699 'md' => 20,
1700 'unit' => 'px',
1701 ),
1702
1703 ),
1704 ),
1705 'typography' => array(
1706 array(
1707 'label' => 'Display Text',
1708 'value' => array(
1709 'openTypography' => 1,
1710 'size' => array(
1711 'md' => 65,
1712 'unit' => 'px',
1713 ),
1714 'height' => array(
1715 'md' => 75,
1716 'unit' => 'px',
1717 ),
1718 'fontFamily' => array(
1719 'family' => 'Roboto',
1720 'type' => 'sans-serif',
1721 'fontWeight' => 700,
1722 ),
1723 'spacing' => array(
1724 'md' => 0,
1725 'unit' => 'px',
1726 ),
1727 ),
1728 ),
1729 array(
1730 'label' => 'Headline',
1731 'value' => array(
1732 'openTypography' => 1,
1733 'size' => array(
1734 'md' => 45,
1735 'unit' => 'px',
1736 ),
1737 'height' => array(
1738 'md' => 60,
1739 'unit' => 'px',
1740 ),
1741 'fontFamily' => array(
1742 'family' => 'Roboto',
1743 'type' => 'sans-serif',
1744 'fontWeight' => 700,
1745 ),
1746 'spacing' => array(
1747 'md' => 0,
1748 'unit' => 'px',
1749 ),
1750 ),
1751 ),
1752 array(
1753 'label' => 'Sub Headline',
1754 'value' => array(
1755 'openTypography' => 1,
1756 'size' => array(
1757 'md' => 38,
1758 'unit' => 'px',
1759 ),
1760 'height' => array(
1761 'md' => 45,
1762 'unit' => 'px',
1763 ),
1764 'fontFamily' => array(
1765 'family' => 'Roboto',
1766 'type' => 'sans-serif',
1767 'fontWeight' => 500,
1768 ),
1769 'spacing' => array(
1770 'md' => 0,
1771 'unit' => 'px',
1772 ),
1773 ),
1774 ),
1775 array(
1776 'label' => 'Title 1',
1777 'value' => array(
1778 'openTypography' => 1,
1779 'size' => array(
1780 'md' => 30,
1781 'unit' => 'px',
1782 ),
1783 'height' => array(
1784 'md' => 40,
1785 'unit' => 'px',
1786 ),
1787 'fontFamily' => array(
1788 'family' => 'Roboto',
1789 'type' => 'sans-serif',
1790 'fontWeight' => 500,
1791 ),
1792 'spacing' => array(
1793 'md' => 0,
1794 'unit' => 'px',
1795 ),
1796 ),
1797 ),
1798 array(
1799 'label' => 'Title 2',
1800 'value' => array(
1801 'openTypography' => 1,
1802 'size' => array(
1803 'md' => 25,
1804 'unit' => 'px',
1805 ),
1806 'height' => array(
1807 'md' => 30,
1808 'unit' => 'px',
1809 ),
1810 'fontFamily' => array(
1811 'family' => 'Roboto',
1812 'type' => 'sans-serif',
1813 'fontWeight' => 400,
1814 ),
1815 'spacing' => array(
1816 'md' => 0,
1817 'unit' => 'px',
1818 ),
1819 ),
1820 ),
1821 array(
1822 'label' => 'Body',
1823 'value' => array(
1824 'openTypography' => 1,
1825 'size' => array(
1826 'md' => 17,
1827 'unit' => 'px',
1828 ),
1829 'height' => array(
1830 'md' => 22,
1831 'unit' => 'px',
1832 ),
1833 'fontFamily' => array(
1834 'family' => 'Roboto',
1835 'type' => 'sans-serif',
1836 'fontWeight' => 400,
1837 ),
1838 'spacing' => array(
1839 'md' => 0,
1840 'unit' => 'px',
1841 ),
1842 ),
1843 ),
1844 array(
1845 'label' => 'Captions',
1846 'value' => array(
1847 'openTypography' => 1,
1848 'size' => array(
1849 'md' => 13,
1850 'unit' => 'px',
1851 ),
1852 'height' => array(
1853 'md' => 16,
1854 'unit' => 'px',
1855 ),
1856 'fontFamily' => array(
1857 'family' => 'Roboto',
1858 'type' => 'sans-serif',
1859 'fontWeight' => 400,
1860 ),
1861 'spacing' => array(
1862 'md' => 0,
1863 'unit' => 'px',
1864 ),
1865 ),
1866 ),
1867 ),
1868 'boxshadow' => array(
1869 array(
1870 'label' => 'Normal Shadow',
1871 'value' => array(
1872 'openShadow' => 1,
1873 'inset' => 0,
1874 'horizontal' => 2,
1875 'vertical' => 6,
1876 'blur' => 10,
1877 'spread' => 0,
1878 'color' => 'rgba(0,0,0,0.15)',
1879 ),
1880 ),
1881 array(
1882 'label' => 'Hover Shadow',
1883 'value' => array(
1884 'openShadow' => 1,
1885 'inset' => 0,
1886 'horizontal' => 2,
1887 'vertical' => 5,
1888 'blur' => 14,
1889 'spread' => 3,
1890 'color' => 'rgba(0,0,0,0.2)',
1891 ),
1892 ),
1893 ),
1894 ),
1895 'preset2' => array(
1896 'name' => 'Preset 2',
1897 'key' => 'preset2',
1898 'colors' => array(
1899 array(
1900 'label' => 'Primary',
1901 'value' => '#8072FC',
1902 ),
1903 array(
1904 'label' => 'Secondary',
1905 'value' => '#6FC784',
1906 ),
1907 array(
1908 'label' => 'Tertiary',
1909 'value' => '#FF5A6E',
1910 ),
1911 array(
1912 'label' => 'Accent',
1913 'value' => '#F3F3F3',
1914 ),
1915 array(
1916 'label' => 'Background',
1917 'value' => '#888888',
1918 ),
1919 ),
1920 'gradient' => array(
1921 array(
1922 'label' => 'Primary',
1923 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1924 ),
1925
1926 array(
1927 'label' => 'Secondary',
1928 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1929 ),
1930
1931 array(
1932 'label' => 'Tertiary',
1933 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1934 ),
1935
1936 array(
1937 'label' => 'Accent',
1938 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1939 ),
1940
1941 array(
1942 'label' => 'Background',
1943 'value' => 'linear-gradient(135deg,rgb(8,148,229) 0%,rgb(155,81,224) 100%)',
1944 ),
1945 ),
1946 'spacing' => array(
1947 array(
1948 'label' => 'Large',
1949 'value' => array(
1950 'md' => 70,
1951 'unit' => 'px',
1952 ),
1953 ),
1954 array(
1955 'label' => 'Medium',
1956 'value' => array(
1957 'md' => 40,
1958 'unit' => 'px',
1959 ),
1960 ),
1961 array(
1962 'label' => 'Small',
1963 'value' => array(
1964 'md' => 20,
1965 'unit' => 'px',
1966 ),
1967
1968 ),
1969 ),
1970 'typography' => array(
1971 array(
1972 'label' => 'Display Text',
1973 'value' => array(
1974 'openTypography' => 1,
1975 'size' => array(
1976 'md' => 65,
1977 'unit' => 'px',
1978 ),
1979 'height' => array(
1980 'md' => 75,
1981 'unit' => 'px',
1982 ),
1983 'fontFamily' => array(
1984 'family' => 'Roboto',
1985 'type' => 'sans-serif',
1986 'fontWeight' => 700,
1987 ),
1988 'spacing' => array(
1989 'md' => 0,
1990 'unit' => 'px',
1991 ),
1992 ),
1993 ),
1994 array(
1995 'label' => 'Headline',
1996 'value' => array(
1997 'openTypography' => 1,
1998 'size' => array(
1999 'md' => 45,
2000 'unit' => 'px',
2001 ),
2002 'height' => array(
2003 'md' => 60,
2004 'unit' => 'px',
2005 ),
2006 'fontFamily' => array(
2007 'family' => 'Roboto',
2008 'type' => 'sans-serif',
2009 'fontWeight' => 700,
2010 ),
2011 'spacing' => array(
2012 'md' => 0,
2013 'unit' => 'px',
2014 ),
2015 ),
2016 ),
2017 array(
2018 'label' => 'Sub Headline',
2019 'value' => array(
2020 'openTypography' => 1,
2021 'size' => array(
2022 'md' => 38,
2023 'unit' => 'px',
2024 ),
2025 'height' => array(
2026 'md' => 45,
2027 'unit' => 'px',
2028 ),
2029 'fontFamily' => array(
2030 'family' => 'Roboto',
2031 'type' => 'sans-serif',
2032 'fontWeight' => 500,
2033 ),
2034 'spacing' => array(
2035 'md' => 0,
2036 'unit' => 'px',
2037 ),
2038 ),
2039 ),
2040 array(
2041 'label' => 'Title 1',
2042 'value' => array(
2043 'openTypography' => 1,
2044 'size' => array(
2045 'md' => 30,
2046 'unit' => 'px',
2047 ),
2048 'height' => array(
2049 'md' => 40,
2050 'unit' => 'px',
2051 ),
2052 'fontFamily' => array(
2053 'family' => 'Roboto',
2054 'type' => 'sans-serif',
2055 'fontWeight' => 500,
2056 ),
2057 'spacing' => array(
2058 'md' => 0,
2059 'unit' => 'px',
2060 ),
2061 ),
2062 ),
2063 array(
2064 'label' => 'Title 2',
2065 'value' => array(
2066 'openTypography' => 1,
2067 'size' => array(
2068 'md' => 25,
2069 'unit' => 'px',
2070 ),
2071 'height' => array(
2072 'md' => 30,
2073 'unit' => 'px',
2074 ),
2075 'fontFamily' => array(
2076 'family' => 'Roboto',
2077 'type' => 'sans-serif',
2078 'fontWeight' => 400,
2079 ),
2080 'spacing' => array(
2081 'md' => 0,
2082 'unit' => 'px',
2083 ),
2084 ),
2085 ),
2086 array(
2087 'label' => 'Body',
2088 'value' => array(
2089 'openTypography' => 1,
2090 'size' => array(
2091 'md' => 17,
2092 'unit' => 'px',
2093 ),
2094 'height' => array(
2095 'md' => 22,
2096 'unit' => 'px',
2097 ),
2098 'fontFamily' => array(
2099 'family' => 'Roboto',
2100 'type' => 'sans-serif',
2101 'fontWeight' => 400,
2102 ),
2103 'spacing' => array(
2104 'md' => 0,
2105 'unit' => 'px',
2106 ),
2107 ),
2108 ),
2109 array(
2110 'label' => 'Captions',
2111 'value' => array(
2112 'openTypography' => 1,
2113 'size' => array(
2114 'md' => 13,
2115 'unit' => 'px',
2116 ),
2117 'height' => array(
2118 'md' => 16,
2119 'unit' => 'px',
2120 ),
2121 'fontFamily' => array(
2122 'family' => 'Roboto',
2123 'type' => 'sans-serif',
2124 'fontWeight' => 400,
2125 ),
2126 'spacing' => array(
2127 'md' => 0,
2128 'unit' => 'px',
2129 ),
2130 ),
2131 ),
2132 ),
2133 'boxshadow' => array(
2134 array(
2135 'label' => 'Normal Shadow',
2136 'value' => array(
2137 'openShadow' => 1,
2138 'inset' => 0,
2139 'horizontal' => 2,
2140 'vertical' => 6,
2141 'blur' => 10,
2142 'spread' => 0,
2143 'color' => 'rgba(0,0,0,0.15)',
2144 ),
2145 ),
2146 array(
2147 'label' => 'Hover Shadow',
2148 'value' => array(
2149 'openShadow' => 1,
2150 'inset' => 0,
2151 'horizontal' => 2,
2152 'vertical' => 5,
2153 'blur' => 14,
2154 'spread' => 3,
2155 'color' => 'rgba(0,0,0,0.2)',
2156 ),
2157 ),
2158 ),
2159 ),
2160 ),
2161 'globalContainer' => array(
2162 'md' => '',
2163 'unit' => 'px',
2164 ),
2165 );
2166
2167 update_option( 'tpgb_global_options', json_encode( $static_meta ) );
2168 $plus_settings = $static_meta;
2169 }
2170
2171 $active_id = ! empty( $plus_settings['active'] ) ? $plus_settings['active'] : '';
2172 $preset_array = ! empty( $plus_settings['presets'] ) ? $plus_settings['presets'] : array();
2173 $act_preset = ! empty( $plus_settings['presets'][ $active_id ] ) ? $plus_settings['presets'][ $active_id ] : array();
2174
2175 foreach ( $act_preset['colors'] as $index => &$item ) {
2176 $item['id'] = $index + 1;
2177 }
2178 unset( $item );
2179
2180 foreach ( $act_preset['typography'] as $index => &$item ) {
2181 $item['id'] = $index + 1;
2182 }
2183 unset( $item );
2184
2185 $act_preset['color'] = $act_preset['colors'];
2186 unset( $act_preset['colors'] );
2187
2188 $response = array(
2189 'message' => __( 'Global data Found', 'wdesignkit' ),
2190 'description' => __( 'Global Color and Typography found', 'wdesignkit' ),
2191 'data' => $act_preset,
2192 'success' => true,
2193 );
2194 }
2195
2196 wp_send_json( $response );
2197 wp_die();
2198 }
2199
2200 /**
2201 *
2202 * Get site settings.
2203 *
2204 * @since 2.1.3
2205 */
2206 protected function wdkit_get_site_setting() {
2207
2208 $kit_id = get_option( 'elementor_active_kit' );
2209 if ( empty( $kit_id ) ) {
2210 $response = array(
2211 'message' => __( 'Elementor kit not found', 'wdesignkit' ),
2212 'description' => __( 'No active Elementor kit found', 'wdesignkit' ),
2213 'success' => false,
2214 );
2215
2216 wp_send_json( $response );
2217 wp_die();
2218 }
2219
2220 $kit_meta = get_post_meta( $kit_id, '_elementor_page_settings', true );
2221
2222 $container_width = ! empty( $kit_meta['container_width'] ) ? $kit_meta['container_width'] : array();
2223 $globals = ! empty( $kit_meta['__globals__'] ) ? $kit_meta['__globals__'] : array();
2224 $body_background_color = ! empty( $kit_meta['body_background_color'] ) ? $kit_meta['body_background_color'] : array();
2225
2226 $site_globals = array(
2227 'body_background_color' => $body_background_color,
2228 'container_width' => $container_width,
2229 'globals' => $globals,
2230 );
2231
2232 $response = array(
2233 'message' => __( 'Global data Found', 'wdesignkit' ),
2234 'description' => __( 'Global Color and Typography found', 'wdesignkit' ),
2235 'data' => $site_globals,
2236 'success' => true,
2237 );
2238
2239 wp_send_json( $response );
2240 wp_die();
2241 }
2242
2243 /**
2244 *
2245 * update site settings.
2246 *
2247 * @since 2.1.3
2248 */
2249 protected function wdkit_update_site_setting() {
2250
2251 $builder = ! empty( $_POST['builder'] ) ? sanitize_text_field( $_POST['builder'] ) : '';
2252 $site_data = ! empty( $_POST['site_data'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['site_data'] ) ), true ) : array();
2253
2254 if ( 'elementor' == $builder ) {
2255 $kit_id = get_option( 'elementor_active_kit' );
2256 if ( ! $kit_id && did_action( 'elementor/loaded' ) && class_exists( '\Elementor\Core\Kits\Manager' ) ) {
2257 \Elementor\Core\Kits\Manager::create_default_kit();
2258 $kit_id = get_option( 'elementor_active_kit' );
2259 }
2260
2261 if ( ! $kit_id ) {
2262 $response = array(
2263 'message' => __( 'Elementor kit not found', 'wdesignkit' ),
2264 'description' => __( 'No active Elementor kit found', 'wdesignkit' ),
2265 'success' => false,
2266 );
2267
2268 wp_send_json( $response );
2269 wp_die();
2270 }
2271
2272 // A freshly created kit has no `_elementor_page_settings` meta yet,
2273 // so an empty result here is a valid starting point, not an error.
2274 $kit_meta = get_post_meta( $kit_id, '_elementor_page_settings', true );
2275 if ( ! is_array( $kit_meta ) ) {
2276 $kit_meta = array();
2277 }
2278
2279 $kit_meta['container_width'] = ! empty( $site_data['container_width'] ) ? $site_data['container_width'] : array();
2280 $kit_meta['__globals__'] = ! empty( $site_data['globals'] ) ? $site_data['globals'] : array();
2281 $kit_meta['body_background_color'] = ! empty( $site_data['body_background_color'] ) ? $site_data['body_background_color'] : array();
2282
2283 update_post_meta( $kit_id, '_elementor_page_settings', $kit_meta );
2284
2285 // Regenerate Elementor's cached CSS. Writing the kit meta directly does
2286 // not rebuild the kit stylesheet, so the imported body background colour
2287 // and container width would otherwise never render on the frontend.
2288 $this->wdkit_regenerate_elementor_kit_css();
2289
2290 $response = array(
2291 'message' => __( 'Site data Updated', 'wdesignkit' ),
2292 'description' => __( 'Site Globals Updated', 'wdesignkit' ),
2293 'success' => true,
2294 );
2295
2296 } elseif ( 'gutenberg' == $builder ) {
2297 $plus_settings = get_option( 'tpgb_global_options' );
2298
2299 $site_preset = json_decode( $plus_settings, true );
2300 $site_preset['globalContainer'] = $site_data;
2301
2302 update_option( 'tpgb_global_options', json_encode( $site_preset ) );
2303
2304 $response = array(
2305 'message' => __( 'Site data Updated', 'wdesignkit' ),
2306 'description' => __( 'Site Globals Updated', 'wdesignkit' ),
2307 'success' => true,
2308 );
2309
2310 } else {
2311 $response = array(
2312 'message' => __( 'Builder Not Found !', 'wdesignkit' ),
2313 'description' => __( 'Template Builder not Found', 'wdesignkit' ),
2314 'success' => true,
2315 );
2316 }
2317
2318 wp_send_json( $response );
2319 wp_die();
2320 }
2321
2322 /**
2323 *
2324 * Update Elementor Global color and Typography.
2325 *
2326 * @since 1.1.20
2327 */
2328 protected function wdkit_update_global_val() {
2329
2330 $builder = ! empty( $_POST['builder'] ) ? sanitize_text_field( $_POST['builder'] ) : '';
2331
2332 if ( 'elementor' == $builder ) {
2333
2334 $g_color = ! empty( $_POST['g_color'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['g_color'] ) ), true ) : array();
2335 $g_typo = ! empty( $_POST['g_typography'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['g_typography'] ) ), true ) : array();
2336
2337 // Get colors from Elementor Site Kit
2338 $kit_id = get_option( 'elementor_active_kit' );
2339 if ( ! $kit_id && did_action( 'elementor/loaded' ) && class_exists( '\Elementor\Core\Kits\Manager' ) ) {
2340 // No kit has ever been created on this site (the option is only
2341 // ever populated by Elementor's own activation hook). Create one
2342 // via Elementor's own helper so the import has somewhere to write.
2343 \Elementor\Core\Kits\Manager::create_default_kit();
2344 $kit_id = get_option( 'elementor_active_kit' );
2345 }
2346
2347 if ( ! $kit_id ) {
2348 $response = array(
2349 'message' => __( 'Elementor kit not found', 'wdesignkit' ),
2350 'description' => __( 'No active Elementor kit found', 'wdesignkit' ),
2351 'success' => false,
2352 );
2353
2354 wp_send_json( $response );
2355 wp_die();
2356 }
2357
2358 // A freshly created kit has no `_elementor_page_settings` meta yet,
2359 // so an empty result here is a valid starting point, not an error.
2360 $kit_meta = get_post_meta( $kit_id, '_elementor_page_settings', true );
2361 if ( ! is_array( $kit_meta ) ) {
2362 $kit_meta = array();
2363 }
2364
2365 $kit_meta['custom_colors'] = array_merge( $g_color, $kit_meta['custom_colors'] ?? array() );
2366 $kit_meta['custom_typography'] = array_merge( $g_typo, $kit_meta['custom_typography'] ?? array() );
2367
2368 update_post_meta( $kit_id, '_elementor_page_settings', $kit_meta );
2369
2370 // Regenerate Elementor's cached CSS. Writing the kit meta directly does
2371 // not rebuild the kit stylesheet, so the imported global colours and
2372 // fonts would otherwise never render on the frontend.
2373 $this->wdkit_regenerate_elementor_kit_css();
2374
2375 $response = array(
2376 'message' => __( 'Global data Updated', 'wdesignkit' ),
2377 'description' => __( 'Global Color and Typography Updated', 'wdesignkit' ),
2378 'success' => true,
2379 );
2380
2381 } elseif ( 'gutenberg' == $builder ) {
2382 $new_preset = ! empty( $_POST['new_preset'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['new_preset'] ) ), true ) : array();
2383 $new_preset_id = ! empty( $new_preset['key'] ) ? $new_preset['key'] : '';
2384 $plus_settings = get_option( 'tpgb_global_options' );
2385 $site_preset = json_decode( $plus_settings, true );
2386
2387 $site_preset['presets'][ $new_preset_id ] = $new_preset;
2388 $site_preset['active'] = $new_preset_id;
2389
2390 update_option( 'tpgb_global_options', json_encode( $site_preset ) );
2391
2392 $response = array(
2393 'message' => __( 'Global data Updated', 'wdesignkit' ),
2394 'description' => __( 'Global Color and Typography Updated', 'wdesignkit' ),
2395 'success' => true,
2396 );
2397
2398 } else {
2399 $response = array(
2400 'message' => __( 'Builder Not Found !', 'wdesignkit' ),
2401 'description' => __( 'Template Builder not Found', 'wdesignkit' ),
2402 'success' => true,
2403 );
2404 }
2405
2406 wp_send_json( $response );
2407 wp_die();
2408 }
2409
2410 /**
2411 * Regenerate Elementor's cached CSS files after the active kit's
2412 * `_elementor_page_settings` meta has been changed directly.
2413 *
2414 * Elementor renders global colours, global fonts and the body background
2415 * colour into a cached kit stylesheet. Updating the meta via
2416 * update_post_meta() does not rebuild that stylesheet, so imported site
2417 * settings never reach the frontend until the cache is cleared. This
2418 * mirrors the clear_cache() call already used by the page/section import.
2419 *
2420 * @since 2.3.2
2421 *
2422 * @return void
2423 */
2424 protected function wdkit_regenerate_elementor_kit_css() {
2425 if ( did_action( 'elementor/loaded' ) && class_exists( '\Elementor\Plugin' ) ) {
2426 \Elementor\Plugin::$instance->files_manager->clear_cache();
2427 }
2428 }
2429
2430 /**
2431 *
2432 * Create Gutenberg page and save for re-generate css file.
2433 *
2434 * @since 1.2.3
2435 */
2436 protected function wdkit_update_preset() {
2437
2438 $act_type = ! empty( $_POST['act_type'] ) ? sanitize_text_field( $_POST['act_type'] ) : '';
2439 $post_id = ! empty( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : '';
2440
2441 if ( 'create' == $act_type ) {
2442
2443 $page_id = wp_insert_post(
2444 array(
2445 'post_title' => 'WDesignKit Gutenberg',
2446 'post_status' => 'publish',
2447 'post_type' => 'post',
2448 'post_name' => sanitize_title( 'wdesignkit' ),
2449 'post_content' => '<!-- wp:heading --><h2 class="wp-block-heading">Add Your Heading Text Here<h2><!-- /wp:heading -->',
2450 'meta_input' => array(
2451 'gutenberg_preview' => true,
2452 '_wp_page_template' => 'default',
2453 ),
2454 )
2455 );
2456
2457 if ( is_wp_error( $page_id ) || ! $page_id ) {
2458 $response = array(
2459 'success' => true,
2460 'message' => esc_html__( 'Page Not Found!', 'wdesignkit' ),
2461 'description' => esc_html__( 'Page Not Found!', 'wdesignkit' ),
2462 );
2463
2464 wp_send_json( $response );
2465 wp_die();
2466 }
2467
2468 update_post_meta( $page_id, '_edit_lock', time() . ':1' );
2469 update_post_meta( $page_id, '_edit_last', get_current_user_id() );
2470
2471 $preview_url = admin_url( 'post.php?post=' . $page_id . '&action=edit' );
2472
2473 $response = array(
2474 'success' => true,
2475 'post_id' => $page_id,
2476 'preview_url' => $preview_url,
2477 'message' => esc_html__( 'Page Created', 'wdesignkit' ),
2478 'description' => esc_html__( 'Page Created', 'wdesignkit' ),
2479 );
2480
2481 wp_send_json( $response );
2482 wp_die();
2483
2484 }
2485
2486 if ( ! empty( $post_id ) && ( $act_type == 'remove' ) ) {
2487
2488 wp_delete_post( $post_id, true );
2489
2490 $response = array(
2491 'success' => true,
2492 'message' => esc_html__( 'post deleted', 'wdesignkit' ),
2493 'description' => esc_html__( 'post deleted', 'wdesignkit' ),
2494 );
2495
2496 wp_send_json( $response );
2497 wp_die();
2498 }
2499 }
2500
2501 /**
2502 *
2503 * It is For Find User Existing template List.
2504 *
2505 * @since 1.0.6
2506 */
2507 protected function wdkit_find_existing_template() {
2508 $array_data = array(
2509 'search' => isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '',
2510 'token' => isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '',
2511 'type' => isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '',
2512 'u_id' => isset( $_POST['u_id'] ) ? sanitize_text_field( wp_unslash( $_POST['u_id'] ) ) : '',
2513 'builder' => isset( $_POST['builder'] ) ? sanitize_text_field( wp_unslash( $_POST['builder'] ) ) : '',
2514 'parpage' => isset( $_POST['parpage'] ) ? sanitize_text_field( wp_unslash( $_POST['parpage'] ) ) : 12,
2515 );
2516
2517 $response = $this->wkit_api_call( $array_data, 'existing_template' );
2518
2519 wp_send_json( $response );
2520 wp_die();
2521 }
2522
2523 /**
2524 *
2525 * It is For Update User Existing template List.
2526 *
2527 * @since 1.0.6
2528 */
2529 protected function wdkit_update_template() {
2530 $array_data = array(
2531 'data' => isset( $_POST['data'] ) ? wp_unslash( $_POST['data'] ) : '',
2532 'post_id' => isset( $_POST['post_id'] ) ? sanitize_text_field( wp_unslash( $_POST['post_id'] ) ) : '',
2533 'token' => isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '',
2534 'type' => isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '',
2535 'id' => isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : '',
2536 'global_data' => isset( $_POST['global_data'] ) ? wp_unslash( $_POST['global_data'] ) : array(),
2537 // 'global_font_family' => isset( $_POST['global_font_family'] ) ? wp_unslash( $_POST['global_font_family'] ) : array(),
2538 // 'global_color' => isset( $_POST['global_color'] ) ? wp_unslash( $_POST['global_color'] ) : array(),
2539 );
2540
2541 if ( ! empty( $array_data['post_id'] ) ) {
2542 $custom_fields = array();
2543 $post_id = $array_data['post_id'];
2544
2545 $meta_fields = get_post_custom( $post_id );
2546
2547 foreach ( $meta_fields as $key => $value ) {
2548 if ( str_contains( $key, 'nxt-' ) ) {
2549 $custom_fields[ $key ] = $value;
2550 }
2551 }
2552
2553 if ( ! empty( $custom_fields ) ) {
2554 $data = json_decode( $array_data['data'], true );
2555 $data['custom_meta'] = $custom_fields;
2556 $array_data['data'] = wp_json_encode( $data );
2557 }
2558 }
2559
2560 $array_data['remove'] = 'yes';
2561 $response = $this->wkit_api_call( $array_data, 'existing_template' );
2562 wp_send_json( $response );
2563 wp_die();
2564 }
2565
2566 /**
2567 *
2568 * It is Use for manage favourite template.
2569 *
2570 * @since 1.0.0
2571 */
2572 protected function wdkit_manage_favorite() {
2573 $template_id = isset( $_POST['template_id'] ) ? strtolower( sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) ) : 0;
2574 $email = isset( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : false;
2575
2576 if ( empty( $email ) || empty( $template_id ) ) {
2577 $response = array(
2578 'message' => $this->e_msg_login,
2579 'description' => $this->e_desc_login,
2580 'success' => false,
2581 );
2582
2583 wp_send_json( $response );
2584 wp_die();
2585 }
2586
2587 $args = $this->wdkit_parse_args( $_POST );
2588 $args['token'] = $this->wdkit_login_user_token( $email );
2589
2590 unset( $args['email'] );
2591 $response = WDesignKit_Data_Query::get_data( 'manage_favorite', $args );
2592
2593 wp_send_json( $response );
2594 wp_die();
2595 }
2596
2597 /**
2598 *
2599 * It is Use for Check Plugin Dependency of template.
2600 *
2601 * @since 1.0.0
2602 * @version 1.0.9
2603 */
2604 protected function wdkit_check_plugins_depends() {
2605 $plugins = isset( $_POST['plugins'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['plugins'] ) ) ) : array();
2606 $update_plugin = array();
2607 $update_theme = array();
2608
2609 if ( empty( $plugins ) || ! is_array( $plugins ) ) {
2610 $this->wdkit_error_msg( array( 'plugins' => 'No Plugins' ) );
2611 }
2612
2613 $all_plugins = $this->get_plugins();
2614 foreach ( $plugins as $plugin ) {
2615 $pluginslug = ! empty( $plugin->plugin_slug ) ? sanitize_text_field( wp_unslash( $plugin->plugin_slug ) ) : '';
2616 $free_pro = ! empty( $plugin->freepro ) ? sanitize_text_field( wp_unslash( $plugin->freepro ) ) : '0';
2617 $type = ! empty( $plugin->type ) ? sanitize_text_field( wp_unslash( $plugin->type ) ) : 'plugin';
2618
2619 if ( is_null( $pluginslug ) ) {
2620 $plugin->status = 'warning';
2621 $update_plugin[] = $plugin;
2622 $update_theme[] = $plugin;
2623
2624 continue;
2625 }
2626
2627 if ( 'plugin' === $type ) {
2628 if ( ! is_plugin_active( $pluginslug ) ) {
2629 if ( ! isset( $all_plugins[ $pluginslug ] ) ) {
2630 if ( isset( $free_pro ) && '1' === $free_pro ) {
2631 $plugin->status = 'manually';
2632 } else {
2633 $plugin->status = 'unavailable';
2634 }
2635 } else {
2636 $plugin->status = 'inactive';
2637 }
2638
2639 $update_plugin[] = $plugin;
2640 } elseif ( is_plugin_active( $pluginslug ) ) {
2641 $plugin->status = 'active';
2642 $update_plugin[] = $plugin;
2643 }
2644 } elseif ( 'theme' === $type ) {
2645 $theme_array = array_keys( wp_get_themes() );
2646 $theme_slug = get_stylesheet();
2647 $parent_theme_slug = get_template();
2648
2649 if ( $theme_slug === $plugin->original_slug || $parent_theme_slug === $plugin->original_slug ) {
2650
2651 $plugin->status = 'active';
2652 } else {
2653 $theme_name = $plugin->original_slug;
2654 if ( ! in_array( $theme_name, $theme_array ) ) {
2655 if ( isset( $free_pro ) && '1' === $free_pro ) {
2656 $plugin->status = 'manually';
2657 } else {
2658 $plugin->status = 'unavailable';
2659 }
2660 } else {
2661 $plugin->status = 'inactive';
2662 }
2663 }
2664
2665 $update_theme[] = $plugin;
2666 }
2667 }
2668
2669 $response = array(
2670 'plugins' => $update_plugin,
2671 'theme' => $update_theme,
2672 'ele_container' => get_option( 'elementor_experiment-container', false ),
2673 );
2674
2675 $this->wdkit_success_msg( $response );
2676 }
2677
2678 /**
2679 *
2680 * It is Use for Install dependent plugin.
2681 *
2682 * @since 1.0.0
2683 * @version 1.0.9
2684 */
2685 protected function wdkit_install_plugins_depends() {
2686 $plugins = isset( $_POST['plugins'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['plugins'] ) ), true ) : array();
2687 $type = ! empty( $plugins['type'] ) ? $plugins['type'] : 'plugin';
2688 $p_id = ! empty( $plugins['p_id'] ) ? $plugins['p_id'] : 'plugin';
2689
2690 $responce = '';
2691 if ( 'plugin' === $type ) {
2692 $responce = Wdkit_Depends_Installer::get_instance()->wdkit_install_plugin( $plugins );
2693 } elseif ( 'theme' === $type ) {
2694 $theme_name = ! empty( $plugins['original_slug'] ) ? $plugins['original_slug'] : '';
2695 if ( ! empty( $theme_name ) ) {
2696
2697 $theme_array = array_keys( wp_get_themes() );
2698 $theme_slug = get_stylesheet();
2699
2700 if ( in_array( $theme_name, $theme_array ) ) {
2701 $activate_result = switch_theme( $theme_name );
2702
2703 if ( ! is_wp_error( $activate_result ) ) {
2704 $responce = array(
2705 'message' => esc_html__( 'Theme activated successfully', 'wdesignkit' ),
2706 'description' => esc_html__( 'Theme successfully activated', 'wdesignkit' ),
2707 'slug' => 'nexter',
2708 'p_id' => $p_id,
2709 'status' => 'active',
2710 'success' => true,
2711 );
2712 } else {
2713 $responce = array(
2714 'message' => esc_html__( 'Theme Not Activated !', 'wdesignkit' ),
2715 'description' => $activate_result->get_error_message(),
2716 'status' => 'inactive',
2717 'p_id' => $p_id,
2718 'success' => false,
2719 );
2720 }
2721 } else {
2722 $result = $this->wdkit_install_theme_depends( $theme_name );
2723
2724 $message = ! empty( $result['message'] ) ? $result['message'] : esc_html__( 'Somthing Wrong', 'wdesignkit' );
2725 $description = ! empty( $result['description'] ) ? $result['description'] : esc_html__( 'Error Somthing Wrong', 'wdesignkit' );
2726 $status = ! empty( $result['status'] ) ? $result['status'] : esc_html__( 'inactive', 'wdesignkit' );
2727 $success = ! empty( $result['success'] ) ? $result['success'] : false;
2728
2729 $responce = array(
2730 'message' => $message,
2731 'description' => $description,
2732 'p_id' => $p_id,
2733 'status' => $status,
2734 'success' => $success,
2735 );
2736 }
2737 } else {
2738 $responce = array(
2739 'message' => esc_html__( 'Theme Name not Found', 'wdesignkit' ),
2740 'description' => esc_html__( 'Can Not Found Theme Name you Enterd.', 'wdesignkit' ),
2741 'success' => false,
2742 );
2743 }
2744 }
2745
2746 wp_send_json( $responce );
2747 wp_die();
2748 }
2749
2750 protected function wdkit_install_theme_depends( $name = 'nexter' ) {
2751
2752 if ( ! current_user_can( 'install_themes' ) ) {
2753 $response = $this->tpae_set_response( false, 'Invalid nonce.', 'The security check failed. Please refresh the page and try again.' );
2754 return $response;
2755 }
2756
2757 $theme_slug = $name;
2758 $theme_api_url = 'https://api.wordpress.org/themes/info/1.0/';
2759
2760 // Parameters for the request
2761 $args = array(
2762 'body' => array(
2763 'action' => 'theme_information',
2764 'request' => serialize(
2765 (object) array(
2766 'slug' => $name,
2767 'fields' => array(
2768 'description' => false,
2769 'sections' => false,
2770 'rating' => true,
2771 'ratings' => false,
2772 'downloaded' => true,
2773 'download_link' => true,
2774 'last_updated' => true,
2775 'homepage' => true,
2776 'tags' => true,
2777 'template' => true,
2778 'active_installs' => false,
2779 'parent' => false,
2780 'versions' => false,
2781 'screenshot_url' => true,
2782 'active_installs' => false,
2783 ),
2784 )
2785 ),
2786 ),
2787 );
2788
2789 // Make the request
2790 $response = wp_remote_post( $theme_api_url, $args );
2791 // Check for errors
2792 if ( is_wp_error( $response ) ) {
2793 $error_message = $response->get_error_message();
2794
2795 $result = $this->tpae_set_response( false, 'oops', 'oops', '' );
2796 } else {
2797 // api.wordpress.org's theme_information response is a serialized stdClass
2798 // (accessed below via ->name / ->download_link). allowed_classes => false
2799 // blocks stdClass too, turning it into an __PHP_Incomplete_Class whose
2800 // properties silently don't exist — allow only stdClass, still refusing any
2801 // other (potentially dangerous) class the payload might reference.
2802 $theme_info = unserialize( $response['body'], array( 'allowed_classes' => array( 'stdClass' ) ) );
2803 $theme_name = $theme_info->name;
2804 $theme_zip_url = $theme_info->download_link;
2805
2806 // SSRF guard (CWE-918): validate the resolved host before fetching the ZIP
2807 // referenced by the external theme_info response.
2808 if ( ! wdesignkit_validate_external_url( $theme_zip_url ) ) {
2809 return array(
2810 'message' => esc_html__( 'Theme Not Activated !', 'wdesignkit' ),
2811 'description' => esc_html__( 'The theme package URL is not allowed.', 'wdesignkit' ),
2812 'status' => 'inactive',
2813 'success' => false,
2814 );
2815 }
2816
2817 if ( ! function_exists( 'WP_Filesystem' ) ) {
2818 require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
2819 }
2820
2821 require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/class-wp-upgrader.php' );
2822 require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/theme.php' );
2823
2824 WP_Filesystem();
2825
2826 $active_theme = wp_get_theme();
2827 $theme_name = $active_theme->get( 'Name' );
2828
2829 // Install via WordPress core's Theme_Upgrader instead of manually fetching and
2830 // ZipArchive::extractTo()'ing the remote package: core already performs the
2831 // standard download -> unpack -> validate-package-structure -> move-into-place
2832 // flow (including cleanup on failure) used for every trusted theme install.
2833 $upgrader = new Theme_Upgrader( new Automatic_Upgrader_Skin() );
2834 $install = $upgrader->install( $theme_zip_url );
2835
2836 if ( is_wp_error( $install ) || ! $install ) {
2837 return array(
2838 'message' => esc_html__( 'Theme Not Activated !', 'wdesignkit' ),
2839 'description' => is_wp_error( $install ) ? $install->get_error_message() : esc_html__( 'Theme could not be installed.', 'wdesignkit' ),
2840 'status' => 'inactive',
2841 'success' => false,
2842 );
2843 }
2844
2845 $activate_result = switch_theme( $name );
2846
2847 if ( ! is_wp_error( $activate_result ) ) {
2848 $response = array(
2849 'message' => esc_html__( 'Theme activated successfully', 'wdesignkit' ),
2850 'description' => esc_html__( 'Theme successfully activated', 'wdesignkit' ),
2851 'status' => 'active',
2852 'success' => true,
2853 );
2854 } else {
2855 $response = array(
2856 'message' => esc_html__( 'Theme Not Activated !', 'wdesignkit' ),
2857 'description' => $activate_result->get_error_message(),
2858 'status' => 'inactive',
2859 'success' => false,
2860 );
2861 }
2862 }
2863
2864 return $response;
2865 }
2866
2867 /**
2868 *
2869 * It is Use Update WDesignKit plugin latest version.
2870 *
2871 * @since 1.0.17
2872 */
2873 protected function wdkit_update_latest_plugin() {
2874
2875 return Wdkit_Depends_Installer::get_instance()->wdkit_update_plugin();
2876 }
2877
2878 /**
2879 *
2880 * It is Use for get plugin list.
2881 *
2882 * @since 1.0.0
2883 */
2884 private function get_plugins() {
2885 if ( ! function_exists( 'get_plugins' ) ) {
2886 require_once \ABSPATH . 'wp-admin/includes/plugin.php';
2887 }
2888
2889 return get_plugins();
2890 }
2891
2892 /**
2893 * Get Download Template Content
2894 *
2895 * @since 1.0.0
2896 */
2897 protected function wdkit_activate_container() {
2898
2899 $option_value = get_option( 'elementor_experiment-container', false );
2900
2901 if ( $option_value === false ) {
2902 add_option( 'elementor_experiment-container', 'active' );
2903 } else {
2904 update_option( 'elementor_experiment-container', 'active' );
2905 }
2906
2907 $result = array(
2908 'message' => esc_html__( 'Container Activated Successfully', 'wdesignkit' ),
2909 'description' => esc_html__( 'Elementor Container Activated Successfully.', 'wdesignkit' ),
2910 'success' => true,
2911 );
2912
2913 wp_send_json( $response );
2914 wp_die();
2915 }
2916
2917 /**
2918 * import category and tags for post
2919 *
2920 * @since 2.0.0
2921 */
2922 protected function wdkit_import_taxonomy() {
2923 $category = isset( $_POST['category'] ) ? json_decode( wp_unslash( $_POST['category'] ) ) : array();
2924 $tags = isset( $_POST['tags'] ) ? json_decode( wp_unslash( $_POST['tags'] ) ) : array();
2925
2926 $response = array(
2927 'success' => false,
2928 'categories' => array(),
2929 'tags' => array(),
2930 );
2931
2932 if ( ! empty( $category ) && count( $category ) > 0 ) {
2933 foreach ( $category as $category_name ) {
2934 $category_name = sanitize_text_field( $category_name );
2935
2936 $term_exists = term_exists( $category_name, 'category' );
2937 if ( ! $term_exists ) {
2938 $result = wp_insert_term( $category_name, 'category' );
2939 if ( ! is_wp_error( $result ) ) {
2940 $response['categories'][] = array(
2941 'name' => $category_name,
2942 'term_id' => $result['term_id'],
2943 );
2944 } else {
2945 $response['categories'][] = array(
2946 'name' => $category_name,
2947 'error' => $result->get_error_message(),
2948 );
2949 }
2950 } else {
2951 $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
2952 $response['categories'][] = array(
2953 'name' => $category_name,
2954 'term_id' => $term_id,
2955 );
2956 }
2957 }
2958
2959 $response['success'] = true;
2960 }
2961
2962 if ( ! empty( $tags ) && count( $tags ) > 0 ) {
2963 foreach ( $tags as $tags_name ) {
2964 $tags_name = sanitize_text_field( $tags_name );
2965
2966 $term_exists = term_exists( $tags_name, 'post_tag' );
2967 if ( ! $term_exists ) {
2968 $result = wp_insert_term( $tags_name, 'post_tag' );
2969 if ( ! is_wp_error( $result ) ) {
2970 $response['tags'][] = array(
2971 'name' => $tags_name,
2972 'term_id' => $result['term_id'],
2973 );
2974 } else {
2975 $response['tags'][] = array(
2976 'name' => $tags_name,
2977 'error' => $result->get_error_message(),
2978 );
2979 }
2980 } else {
2981 $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
2982 $response['tags'][] = array(
2983 'name' => $tags_name,
2984 'term_id' => $term_id,
2985 );
2986 }
2987 }
2988
2989 $response['success'] = true;
2990 }
2991
2992 wp_send_json( $response );
2993 wp_die();
2994 }
2995
2996 /**
2997 * Get Download Template Content
2998 *
2999 * @since 1.0.0
3000 */
3001 protected function wdkit_import_template() {
3002 $args = $this->wdkit_parse_args( $_POST );
3003 $api_type = isset( $_POST['api_type'] ) ? sanitize_text_field( wp_unslash( $_POST['api_type'] ) ) : 'import_template';
3004
3005 $response = '';
3006 if ( empty( $args['template_id'] ) ) {
3007 $result = array(
3008 'content' => '',
3009 'message' => esc_html__( 'Invalid import', 'wdesignkit' ),
3010 'description' => esc_html__( 'Invalid import: Check your details and try again.', 'wdesignkit' ),
3011 'success' => false,
3012 );
3013
3014 wp_send_json( $response );
3015 wp_die();
3016 }
3017
3018 $args['token'] = $this->wdkit_login_user_token( $args['email'] );
3019
3020 unset( $args['email'] );
3021 $args['unique_id'] = get_option( 'wdkit_unique_id' ) ?? '';
3022 $response = WDesignKit_Data_Query::get_data( $api_type, $args );
3023
3024 if ( is_wp_error( $response ) ) {
3025 wp_send_json( array(
3026 'success' => false,
3027 'message' => $response->get_error_message(),
3028 ) );
3029 wp_die();
3030 }
3031
3032 $custom_meta = isset( $_POST['custom_meta'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_meta'] ) ) : false;
3033
3034 /** Custom meta Field */
3035 if ( ! empty( $custom_meta ) && 'true' === $custom_meta && ! empty( $response ) && ! empty( $response['content'] ) ) {
3036
3037 $res_content = json_decode( $response['content'], true );
3038 if ( isset( $res_content['custom_meta'] ) && ! empty( $res_content['custom_meta'] ) ) {
3039 $meta_data = $res_content['custom_meta'];
3040
3041 if ( ! empty( $meta_data ) ) {
3042 foreach ( $meta_data as $meta_key => $meta_val ) {
3043 if ( ! empty( $meta_val[0] ) && is_serialized( $meta_val[0] ) ) {
3044 $meta_val[0] = unserialize( $meta_val[0], array( 'allowed_classes' => false ) );
3045 }
3046
3047 if ( get_post_meta( get_the_ID(), $meta_key, true ) === '' ) {
3048 add_post_meta( get_the_ID(), $meta_key, $meta_val[0] );
3049 } else {
3050 update_post_meta( get_the_ID(), $meta_key, $meta_val[0] );
3051 }
3052 }
3053 }
3054 }
3055 }
3056
3057 /**
3058 * Fires after a template has been imported from the cloud.
3059 *
3060 * WDesignKit's templates live in the cloud, so nothing local records that an import
3061 * happened — there is no post type, no option, nothing to count after the fact. This is the
3062 * only moment the information exists.
3063 *
3064 * @since 2.6.4
3065 *
3066 * @param string $kind 'single' or 'kit'.
3067 * @param string $builder Builder the template was imported for, e.g. 'elementor'.
3068 * @param int $count How many templates this import brought in.
3069 */
3070 // Only a completed import counts. The cloud's failure shape for this endpoint family sets
3071 // content => 'error' (see the sibling check in wdkit_import_kit_template() above) — that is
3072 // non-empty, so the previous `||` fired the counter on failed imports too. Require success
3073 // AND an absent/non-'error' content instead.
3074 if ( ! empty( $response['success'] ) && ( ! isset( $response['content'] ) || 'error' !== $response['content'] ) ) {
3075 do_action(
3076 'wdkit_template_imported',
3077 'import_kit_template' === $api_type ? 'kit' : 'single',
3078 isset( $_POST['builder'] ) ? sanitize_key( wp_unslash( $_POST['builder'] ) ) : '',
3079 1
3080 );
3081 }
3082
3083 wp_send_json( $response );
3084 wp_die();
3085 }
3086
3087 /**
3088 * This Function is Use For Media Import
3089 *
3090 * @since 1.0.0
3091 *
3092 * @param array $content store media content.
3093 * @param string $editor it is check editor.
3094 */
3095 /**
3096 * Resolve a local upload URL back to its attachment ID.
3097 *
3098 * Handles the "-scaled" copy WordPress makes for large originals and any
3099 * "-1920x1280" size suffix, both of which attachment_url_to_postid() misses because
3100 * they are not the value stored in _wp_attached_file.
3101 *
3102 * @since 2.6.2
3103 *
3104 * @param string $url Local upload URL.
3105 * @return int Attachment ID, or 0.
3106 */
3107 private static function wdkit_attachment_id_from_url( $url ) {
3108 static $cache = array();
3109
3110 if ( isset( $cache[ $url ] ) ) {
3111 return $cache[ $url ];
3112 }
3113
3114 $id = (int) attachment_url_to_postid( $url );
3115
3116 if ( ! $id ) {
3117 // Try the original file behind a -scaled or -WxH derivative.
3118 $stripped = preg_replace( '/-scaled(\.[a-z0-9]+)$/i', '$1', $url );
3119 $stripped = preg_replace( '/-\d+x\d+(\.[a-z0-9]+)$/i', '$1', (string) $stripped );
3120
3121 if ( $stripped && $stripped !== $url ) {
3122 $id = (int) attachment_url_to_postid( $stripped );
3123 }
3124 }
3125
3126 // Only remember hits. Page imports run concurrently, so an attachment created by a
3127 // sibling request may not exist yet when this is first asked — caching that miss
3128 // would keep every later control in this request pointing at nothing.
3129 if ( $id ) {
3130 $cache[ $url ] = $id;
3131 }
3132
3133 return $id;
3134 }
3135
3136 /**
3137 * Is this media reference still pointing off-site?
3138 *
3139 * Template content arrives holding the URLs of wherever the media lived before. Those
3140 * carry that site's attachment IDs, which have no meaning here - and can collide with
3141 * unrelated local posts.
3142 *
3143 * @since 2.6.2
3144 *
3145 * @param string $url URL from a media control.
3146 * @return bool True when the URL points at another site's uploads.
3147 */
3148 private static function wdkit_is_foreign_media_url( $url ) {
3149
3150 if ( ! class_exists( 'Wdkit_Image_Guard' ) ) {
3151 require_once WDKIT_INCLUDES . 'admin/class-wdkit-image-guard.php';
3152 }
3153
3154 $uploads = wp_get_upload_dir();
3155
3156 return Wdkit_Image_Guard::is_foreign_media( $url, isset( $uploads['baseurl'] ) ? $uploads['baseurl'] : '' );
3157 }
3158
3159 /**
3160 * Find - or make - the local attachment behind a source-site media URL.
3161 *
3162 * Elementor stamps every image it imports with `_elementor_source_image_hash`
3163 * (sha1 of the URL it came from), and its importer consults that before doing any
3164 * network work. Delegating here means a URL already imported at create time resolves
3165 * from the database, and one that never made it is fetched exactly once.
3166 *
3167 * Only ever called for foreign URLs. Handing it a local URL would re-download the
3168 * file and leave a duplicate, because the stored hash is of the *remote* URL and so
3169 * would never match.
3170 *
3171 * @since 2.6.2
3172 *
3173 * @param string $url Source-site media URL.
3174 * @param int $source_id The source site's attachment ID, used as Elementor's cache key.
3175 * @return array Local `id` and `url`, or an empty array when it cannot be resolved.
3176 */
3177 private static function wdkit_localise_media_url( $url, $source_id = 0 ) {
3178 static $cache = array();
3179
3180 if ( isset( $cache[ $url ] ) ) {
3181 return $cache[ $url ];
3182 }
3183
3184 if ( ! did_action( 'elementor/loaded' ) || ! class_exists( '\\Elementor\\Plugin' ) ) {
3185 return array();
3186 }
3187
3188 $images = \Elementor\Plugin::$instance->templates_manager->get_import_images_instance();
3189
3190 if ( ! $images ) {
3191 return array();
3192 }
3193
3194 // A download may happen, so keep the oversized-image guard in force.
3195 self::wdkit_guard_oversized_images();
3196
3197 $imported = $images->import(
3198 array(
3199 // Elementor only checks its hash table when an id is present.
3200 'id' => $source_id ? $source_id : 1,
3201 'url' => $url,
3202 )
3203 );
3204
3205 $local = ( ! empty( $imported['id'] ) && ! empty( $imported['url'] ) )
3206 ? array(
3207 'id' => (int) $imported['id'],
3208 'url' => $imported['url'],
3209 )
3210 : array();
3211
3212 // Remember hits only: a sibling request importing concurrently may simply not have
3213 // finished yet, and caching that miss would strand every later control on this page.
3214 if ( $local ) {
3215 $cache[ $url ] = $local;
3216 }
3217
3218 return $local;
3219 }
3220
3221 /**
3222 * Repair dangling attachment IDs across every page of a finished import.
3223 *
3224 * The create-time repair in wdkit_media_import() can only see attachments that already
3225 * exist. Pages import concurrently and share images — an icon first imported by one
3226 * page is referenced by several others — so a page that runs early legitimately cannot
3227 * resolve an image a sibling request has not created yet.
3228 *
3229 * This runs at the finalize step, once every page and attachment exists, and fixes
3230 * whatever the per-page pass had to leave behind.
3231 *
3232 * @since 2.6.2
3233 *
3234 * @param array $page_ids Imported post IDs.
3235 * @return int Number of pages actually rewritten.
3236 */
3237 private function wdkit_sweep_attachment_ids( $page_ids ) {
3238
3239 if ( empty( $page_ids ) || ! did_action( 'elementor/loaded' ) ) {
3240 return 0;
3241 }
3242
3243 $fixed = 0;
3244 $ids = array_unique( array_map( 'intval', $page_ids ) );
3245
3246 // Primes the meta cache for the whole batch in one query, so the
3247 // get_post_meta() call below hits the cache instead of issuing one query
3248 // per imported page.
3249 update_meta_cache( 'post', $ids );
3250
3251 foreach ( $ids as $post_id ) {
3252
3253 if ( ! $post_id ) {
3254 continue;
3255 }
3256
3257 $raw = get_post_meta( $post_id, '_elementor_data', true );
3258
3259 if ( empty( $raw ) ) {
3260 continue;
3261 }
3262
3263 $data = is_array( $raw ) ? $raw : json_decode( $raw, true );
3264
3265 if ( ! is_array( $data ) ) {
3266 continue;
3267 }
3268
3269 $repaired = self::wdkit_repair_attachment_ids( $data );
3270
3271 if ( wp_json_encode( $repaired ) === wp_json_encode( $data ) ) {
3272 continue;
3273 }
3274
3275 // Save through the document API so Elementor regenerates the page CSS — the
3276 // background-image rules are only emitted once the IDs resolve.
3277 $document = \Elementor\Plugin::$instance->documents->get( $post_id );
3278
3279 // Count only a save that actually happened. Document::save() returns false
3280 // without saving when the current user cannot edit the post, and reporting
3281 // those as repaired hides the fact that nothing changed.
3282 if ( $document && $document->save( array( 'elements' => $repaired ) ) ) {
3283 ++$fixed;
3284 }
3285 }
3286
3287 if ( $fixed ) {
3288 \Elementor\Plugin::$instance->files_manager->clear_cache();
3289 }
3290
3291 return $fixed;
3292 }
3293
3294 /**
3295 * Repair media controls whose attachment ID does not resolve.
3296 *
3297 * Elementor media controls store `{ url, id }`. Controls flagged `has_sizes` — the
3298 * container/section **background image** among them — do not render from `url` at all:
3299 * CSS generation resolves the image through the attachment ID, so a dangling ID
3300 * produces no `background-image` rule and the section renders with no image even
3301 * though its URL is perfectly correct.
3302 *
3303 * IDs arrive dangling whenever Elementor's own importer does not rewrite a control —
3304 * it carries the source site's ID, which means nothing locally. Now that the URL is
3305 * already a local upload before import, the ID can simply be looked up from it.
3306 *
3307 * @since 2.6.2
3308 *
3309 * @param mixed $node Elementor data, walked recursively.
3310 * @return mixed Data with resolvable attachment IDs.
3311 */
3312 private static function wdkit_repair_attachment_ids( $node ) {
3313
3314 if ( ! is_array( $node ) ) {
3315 return $node;
3316 }
3317
3318 // A media control value: has a url, and an id slot to correct.
3319 if ( isset( $node['url'] ) && is_string( $node['url'] ) && array_key_exists( 'id', $node ) ) {
3320
3321 $current = (int) $node['id'];
3322 $is_live = $current && 'attachment' === get_post_type( $current );
3323
3324 if ( self::wdkit_is_foreign_media_url( $node['url'] ) ) {
3325 // Still pointing at the source site. Ask Elementor for the local copy: its
3326 // _elementor_source_image_hash lookup returns the attachment the create-time
3327 // import already made, so this normally costs a single query and no download.
3328 $local = self::wdkit_localise_media_url( $node['url'], $current );
3329
3330 if ( ! empty( $local['id'] ) && ! empty( $local['url'] ) ) {
3331 $node['id'] = $local['id'];
3332 $node['url'] = $local['url'];
3333 }
3334 } elseif ( ! $is_live && false !== strpos( $node['url'], '/wp-content/uploads/' ) ) {
3335 $resolved = self::wdkit_attachment_id_from_url( $node['url'] );
3336
3337 if ( $resolved ) {
3338 $node['id'] = $resolved;
3339 }
3340 }
3341 }
3342
3343 foreach ( $node as $key => $value ) {
3344 if ( is_array( $value ) ) {
3345 $node[ $key ] = self::wdkit_repair_attachment_ids( $value );
3346 }
3347 }
3348
3349 return $node;
3350 }
3351
3352 public function wdkit_media_import( $content = array(), $editor = '' ) {
3353
3354 if ( empty( $content ) && empty( $editor ) ) {
3355 $args = $this->wdkit_parse_args( $_POST );
3356 $content = ! empty( $args['content'] ) ? json_decode( $args['content'], true ) : array();
3357 } else {
3358 $args = array(
3359 'content' => $content,
3360 'editor' => $editor,
3361 );
3362 $content = ! empty( $args['content'] ) ? $args['content'] : array();
3363
3364 if ( 'elementor' === $args['editor'] ) {
3365 $content = json_decode( $content, true );
3366 }
3367 }
3368
3369 if ( ! class_exists( 'Wdkit_Import_Images' ) ) {
3370 require_once WDKIT_INCLUDES . 'admin/class-wdkit-import-images.php';
3371 }
3372
3373
3374 if ( ! empty( $args['editor'] ) && 'gutenberg' === $args['editor'] && ! empty( $content ) ) {
3375 $media_import = array( $content );
3376 $media_import = self::blocks_import_media_copy_content( $media_import );
3377 $content = $media_import[0];
3378 } elseif ( ! empty( $args['editor'] ) && 'elementor' === $args['editor'] && ! empty( $content ) ) {
3379 $media_import = array( $content );
3380 $media_import = self::widgets_elements_id_change( $media_import );
3381 $media_import = self::widgets_import_media_copy_content( $media_import );
3382 $content = $media_import[0];
3383
3384 // Last: point any control Elementor left holding a foreign attachment ID at the
3385 // local attachment its URL already refers to. Without this, has_sizes controls
3386 // such as container background images resolve to nothing and render empty.
3387 $content = self::wdkit_repair_attachment_ids( $content );
3388 }
3389
3390 return $content;
3391 }
3392
3393 /**
3394 * Widgets elements data
3395 *
3396 * @since 1.0.0
3397 * @param string $media_import it is store media data.
3398 */
3399 protected static function widgets_elements_id_change( $media_import ) {
3400 if ( did_action( 'elementor/loaded' ) ) {
3401 return \Elementor\Plugin::instance()->db->iterate_data(
3402 $media_import,
3403 function ( $element ) {
3404 $element['id'] = \Elementor\Utils::generate_random_string();
3405 return $element;
3406 }
3407 );
3408 } else {
3409 return $media_import;
3410 }
3411 }
3412
3413 /**
3414 * Widgets Media import copy content.
3415 *
3416 * @since 1.0.0
3417 *
3418 * @param string $media_import it is store media data.
3419 */
3420 protected static function widgets_import_media_copy_content( $media_import ) {
3421 if ( did_action( 'elementor/loaded' ) ) {
3422
3423 return \Elementor\Plugin::instance()->db->iterate_data(
3424 $media_import,
3425 function ( $element_data ) {
3426 $elements = \Elementor\Plugin::instance()->elements_manager->create_element_instance( $element_data );
3427
3428 if ( ! $elements ) {
3429 return null;
3430 }
3431
3432 return self::widgets_element_import_start( $elements );
3433 }
3434 );
3435 } else {
3436 return $media_import;
3437 }
3438 }
3439
3440 /**
3441 * Start element copy content for media import.
3442 *
3443 * @since 1.0.0
3444 *
3445 * @param string \Elementor\Controls_Stack $element it is store elementor data.
3446 */
3447 protected static function widgets_element_import_start( \Elementor\Controls_Stack $element ) {
3448 $get_element_instance = $element->get_data();
3449 $tp_mi_on_fun = 'on_import';
3450
3451 if ( method_exists( $element, $tp_mi_on_fun ) ) {
3452 $get_element_instance = $element->{$tp_mi_on_fun}( $get_element_instance );
3453 }
3454
3455 foreach ( $element->get_controls() as $get_control ) {
3456 $control_type = \Elementor\Plugin::instance()->controls_manager->get_control( $get_control['type'] );
3457 $control_name = $get_control['name'];
3458
3459 if ( ! $control_type ) {
3460 // Skip just this control. Returning here would abandon every control after
3461 // it, so a single unregistered type - routine when a kit uses an addon that
3462 // is not fully active yet - would silently leave the rest of the element's
3463 // media pointing at the source site.
3464 continue;
3465 }
3466
3467 if ( method_exists( $control_type, $tp_mi_on_fun ) ) {
3468 $get_element_instance['settings'][ $control_name ] = $control_type->{$tp_mi_on_fun}( $element->get_settings( $control_name ), $get_control );
3469 }
3470 }
3471
3472 return $get_element_instance;
3473 }
3474
3475 /**
3476 * Blocks Recursively data
3477 *
3478 * @param string $data_import gutenber data import.
3479 */
3480 public static function blocks_import_media_copy_content( $data_import ) {
3481 if ( ! empty( $data_import ) ) {
3482 foreach ( $data_import[0] as $key => $val ) {
3483 if ( array_key_exists( 'blockName', $val ) && ( empty( $val['blockName'] ) || null === $val['blockName'] || empty( $val['blockName'] ) || ' ' === $val['blockName'] ) ) {
3484 unset( $data_import[0][ $key ] );
3485 }
3486 }
3487 }
3488
3489 return self::blocks_array_recursively_data(
3490 $data_import,
3491 function ( $block_data ) {
3492 $elements = self::blocks_data_instance( $block_data );
3493 return $elements;
3494 }
3495 );
3496 }
3497
3498 /**
3499 * Blocks Recursively data
3500 *
3501 * @param array $data store data.
3502 * @param string $callback store data.
3503 * @param string $args store data.
3504 */
3505 public static function blocks_array_recursively_data( $data, $callback, $args = array() ) {
3506 if ( ( isset( $data['name'] ) && ! empty( $data['name'] ) ) || ( isset( $data['blockName'] ) && ! empty( $data['blockName'] ) ) ) {
3507 if ( ! empty( $data['innerBlocks'] ) ) {
3508 $data['innerBlocks'] = self::blocks_array_recursively_data( $data['innerBlocks'], $callback, $args );
3509 }
3510
3511 return call_user_func( $callback, $data, $args );
3512 }
3513
3514 if ( ! empty( $data ) ) {
3515 $data = (array) $data;
3516 foreach ( $data as $block_key => $block_value ) {
3517 $block_data = self::blocks_array_recursively_data( $data[ $block_key ], $callback, $args );
3518
3519 if ( null === $block_data ) {
3520 continue;
3521 }
3522
3523 $data[ $block_key ] = $block_data;
3524 }
3525 }
3526
3527 return $data;
3528 }
3529
3530 /**
3531 * Check Blocks data media Url
3532 *
3533 * @param array $block_data store data.
3534 * @param array $args store data.
3535 * @param array $block_args store data.
3536 */
3537 public static function blocks_data_instance( array $block_data, array $args = array(), $block_args = null ) {
3538
3539 if ( ( isset( $block_data['name'] ) && isset( $block_data['clientId'] ) && isset( $block_data['attributes'] ) ) || ( isset( $block_data['blockName'] ) && isset( $block_data['attrs'] ) && ! empty( $block_data['attrs'] ) ) ) {
3540 $blocks_attr = isset( $block_data['attributes'] ) ? $block_data['attributes'] : ( isset( $block_data['attrs'] ) ? $block_data['attrs'] : array() );
3541 $blocks_attr = self::wdkit_import_block_media( $blocks_attr );
3542 if ( isset( $block_data['attributes'] ) ) {
3543 $block_data['attributes'] = $blocks_attr;
3544 } elseif ( isset( $block_data['attrs'] ) ) {
3545 $block_data['attrs'] = $blocks_attr;
3546 }
3547
3548 $block_data = self::wdkit_relink_block_markup( $block_data );
3549 }
3550
3551 return $block_data;
3552 }
3553
3554 /**
3555 * Run block markup through the media import, the way the create path does.
3556 *
3557 * Used wherever block content is written from the browser: media import, then the Nexter
3558 * block processor so each block's rendered copy matches its attributes, then serialise.
3559 *
3560 * @since 2.6.2
3561 *
3562 * @param string $content Block markup.
3563 * @return string Block markup with local media.
3564 */
3565 private function wdkit_relink_gutenberg_content( $content ) {
3566
3567 if ( ! is_string( $content ) || false === strpos( $content, '<!-- wp:' ) ) {
3568 return $content;
3569 }
3570
3571 // wdkit_media_import() loads this itself, but it is referenced before that below.
3572 if ( ! class_exists( 'Wdkit_Import_Images' ) ) {
3573 require_once WDKIT_INCLUDES . 'admin/class-wdkit-import-images.php';
3574 }
3575
3576 // Thumbnail generation decodes each image, so keep the oversized-image guard in force.
3577 self::wdkit_guard_oversized_images();
3578
3579
3580 // Block attributes are JSON inside the block delimiters, so they only survive a parse
3581 // when the string carries exactly one level of escaping. Arrive with an extra level and
3582 // parse_blocks() reads no attributes at all - serialising that back out writes every
3583 // block bare, throwing away titles, body text, icons and styling.
3584 $parsable = self::wdkit_parsable_block_content( $content );
3585
3586 if ( null === $parsable ) {
3587
3588 return $content;
3589 }
3590
3591 $blocks = parse_blocks( $parsable );
3592 $blocks = $this->wdkit_media_import( $blocks, 'gutenberg' );
3593
3594 if ( empty( $blocks ) || ! is_array( $blocks ) ) {
3595 return $content;
3596 }
3597
3598 if ( class_exists( 'WDKIT_Nexter_Block_Processor' ) ) {
3599 $processor = new WDKIT_Nexter_Block_Processor();
3600 $blocks = $processor->run( $blocks );
3601 }
3602
3603 $serialised = serialize_blocks( $blocks );
3604
3605 // Last line of defence. This function exists to repoint media, so a result carrying
3606 // fewer block attributes than it started with is a broken round trip, not a rewrite.
3607 // Leaving the media wrong is recoverable; saving gutted content is not.
3608 $before = self::wdkit_block_attr_count( $parsable );
3609 $after = self::wdkit_block_attr_count( $serialised );
3610
3611 if ( $after < $before ) {
3612
3613 return $content;
3614 }
3615
3616 // Never hand back nothing: an empty result would blank the page.
3617 return ! empty( $serialised ) ? $serialised : $content;
3618 }
3619
3620 /**
3621 * Rebuild the block stylesheet for a page whose content we just rewrote.
3622 *
3623 * The addon keeps each block's styling in a generated per-page stylesheet, and every rule
3624 * is keyed to the block id it was written for. That file is produced when the page is
3625 * saved through the editor - not by wp_update_post() from an AJAX handler - so rewriting
3626 * content here leaves the page pointing at a stylesheet built for the previous markup.
3627 * Blocks whose ids are not in that file get no rules at all and render unstyled.
3628 *
3629 * @since 2.6.2
3630 *
3631 * @param int $post_id Page whose content changed.
3632 * @return bool True when a rebuild was triggered.
3633 */
3634 private static function wdkit_rebuild_block_css( $post_id ) {
3635
3636 if ( ! $post_id ) {
3637 return false;
3638 }
3639
3640 foreach ( get_declared_classes() as $class ) {
3641 if ( ! method_exists( $class, 'make_block_css_by_post_id' ) ) {
3642 continue;
3643 }
3644
3645 try {
3646 if ( method_exists( $class, 'instance' ) ) {
3647 $instance = $class::instance();
3648 } elseif ( method_exists( $class, 'get_instance' ) ) {
3649 $instance = $class::get_instance();
3650 } else {
3651 $instance = new $class();
3652 }
3653
3654 $instance->make_block_css_by_post_id( $post_id );
3655
3656
3657 return true;
3658 } catch ( \Throwable $e ) {
3659 // Styling is best-effort: a failure here must not fail the import.
3660
3661 return false;
3662 }
3663 }
3664
3665 return false;
3666 }
3667
3668 /**
3669 * How many block attributes does this markup actually yield when parsed?
3670 *
3671 * Used as a before/after measure: block attributes are the part of block markup a round
3672 * trip can silently drop, so counting them is how we tell a rewrite from a mangling.
3673 *
3674 * @since 2.6.2
3675 *
3676 * @param string $content Block markup.
3677 * @return int Total attributes across every block.
3678 */
3679 private static function wdkit_block_attr_count( $content ) {
3680 $total = 0;
3681
3682 $walk = function ( $blocks ) use ( &$walk, &$total ) {
3683 foreach ( $blocks as $block ) {
3684 if ( ! empty( $block['attrs'] ) && is_array( $block['attrs'] ) ) {
3685 $total += count( $block['attrs'] );
3686 }
3687
3688 if ( ! empty( $block['innerBlocks'] ) ) {
3689 $walk( $block['innerBlocks'] );
3690 }
3691 }
3692 };
3693
3694 $walk( parse_blocks( (string) $content ) );
3695
3696 return $total;
3697 }
3698
3699 /**
3700 * Return this content in a form whose block attributes actually parse.
3701 *
3702 * Content written straight to post_content never had to parse, so an extra level of
3703 * escaping on the way in did no harm. Parsing it - which repointing media requires - makes
3704 * that escaping fatal: `{\"Title\":\"…\"}` is not JSON, so every attribute is discarded.
3705 *
3706 * Rather than assume a slash depth, this measures: if stripping one level yields more
3707 * attributes, the content was over-escaped and the stripped form is the real one.
3708 *
3709 * @since 2.6.2
3710 *
3711 * @param string $content Block markup as received.
3712 * @return string|null Markup safe to parse, or null when no form of it parses.
3713 */
3714 private static function wdkit_parsable_block_content( $content ) {
3715
3716 $as_is = self::wdkit_block_attr_count( $content );
3717
3718 // Nothing claims to carry attributes, so there is nothing to lose either.
3719 if ( false === strpos( $content, '{' ) ) {
3720 return $content;
3721 }
3722
3723 $stripped = wp_unslash( $content );
3724 $stripped_attrs = self::wdkit_block_attr_count( $stripped );
3725
3726 if ( $stripped_attrs > $as_is ) {
3727 return $stripped;
3728 }
3729
3730 if ( $as_is > 0 ) {
3731 return $content;
3732 }
3733
3734 // Neither form parses into attributes even though the markup contains JSON: better to
3735 // leave the content exactly as it arrived than to rewrite it into something bare.
3736 return null;
3737 }
3738
3739 /**
3740 * Point a block's saved markup at the media that was just localised.
3741 *
3742 * A block stores a rendered copy of itself in `innerHTML` / `innerContent`, and for many
3743 * blocks that copy is what the front end actually outputs. Importing the attributes alone
3744 * therefore fixes the editor while leaving the page still loading from the site the
3745 * template came from - and those hosts answer 403, so the image renders broken.
3746 *
3747 * @since 2.6.2
3748 *
3749 * @param array $block_data One parsed block.
3750 * @return array The block with its markup repointed.
3751 */
3752 private static function wdkit_relink_block_markup( $block_data ) {
3753
3754 $map = Wdkit_Import_Images::get_url_map();
3755
3756 if ( empty( $map ) ) {
3757 return $block_data;
3758 }
3759
3760 $from = array_keys( $map );
3761 $to = array_values( $map );
3762
3763 if ( ! empty( $block_data['innerHTML'] ) && is_string( $block_data['innerHTML'] ) ) {
3764 $block_data['innerHTML'] = str_replace( $from, $to, $block_data['innerHTML'] );
3765 }
3766
3767 if ( ! empty( $block_data['innerContent'] ) && is_array( $block_data['innerContent'] ) ) {
3768 foreach ( $block_data['innerContent'] as $index => $chunk ) {
3769 if ( is_string( $chunk ) ) {
3770 $block_data['innerContent'][ $index ] = str_replace( $from, $to, $chunk );
3771 }
3772 }
3773 }
3774
3775 return $block_data;
3776 }
3777
3778 /**
3779 * Import every media reference held in a block's attributes.
3780 *
3781 * Block attributes nest arbitrarily - a repeater of cards each with an image, responsive
3782 * variants, nested inner settings - so this recurses rather than reaching a fixed number
3783 * of levels down. The previous version was unrolled exactly four levels deep and also
3784 * skipped any subtree carrying an `md` key, which meant anything below that simply kept
3785 * the source site's URL and attachment ID and rendered as an empty placeholder.
3786 *
3787 * A node counts as media when it has a non-empty string `url` and either an id slot or a
3788 * URL that names an image file. That pairing is what distinguishes a media control from
3789 * a link, which also carries a `url`.
3790 *
3791 * @since 2.6.2
3792 *
3793 * @param mixed $node Block attributes, walked recursively.
3794 * @return mixed Attributes with local media.
3795 */
3796 private static function wdkit_import_block_media( $node ) {
3797
3798 if ( ! is_array( $node ) ) {
3799 return $node;
3800 }
3801
3802 $url = isset( $node['url'] ) && is_string( $node['url'] ) ? $node['url'] : '';
3803
3804 if ( '' !== $url
3805 && ( array_key_exists( 'id', $node ) || array_key_exists( 'Id', $node )
3806 || preg_match( '/\.(?:jpe?g|png|gif|svg|webp|avif|bmp)$/i', (string) wp_parse_url( $url, PHP_URL_PATH ) ) )
3807 ) {
3808 $imported = Wdkit_Import_Images::wdkit_Import_media( $node );
3809
3810 // Only accept a real result. The importer returns the node untouched when it
3811 // cannot localise the file, and anything falsy here would wipe out the URL and
3812 // leave the block with no image at all.
3813 if ( ! empty( $imported['url'] ) ) {
3814 $node = array_merge( $node, $imported );
3815 }
3816 }
3817
3818 // Keep walking even after importing this node. A media value carries its own `sizes`
3819 // map of per-size URLs, and returning here left every one of those pointing at the
3820 // site the template came from - which is what the widgets actually render from.
3821 foreach ( $node as $key => $value ) {
3822 if ( is_array( $value ) ) {
3823 $node[ $key ] = self::wdkit_import_block_media( $value );
3824 }
3825 }
3826
3827 return $node;
3828 }
3829
3830 /**
3831 * Kit Template Import Pages/Sections
3832 *
3833 * @since 1.0.0
3834 * */
3835 protected function wdkit_import_kit_template() {
3836
3837 if ( ! current_user_can( 'manage_options' ) ) {
3838 return false;
3839 }
3840
3841 $builder = isset( $_POST['builder'] ) ? sanitize_text_field( wp_unslash( $_POST['builder'] ) ) : '';
3842
3843 $page_section = ! empty( $_POST['page_section'] ) ? sanitize_text_field( wp_unslash( $_POST['page_section'] ) ) : '';
3844
3845 if ( isset( $page_section ) ) {
3846 $args['page_section'] = ! empty( $page_section ) ? sanitize_text_field( wp_unslash( $page_section ) ) : '';
3847 }
3848
3849 $template_ids = ! empty( $_POST['template_ids'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['template_ids'] ) ), true ) : array();
3850 $email = ! empty( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : '';
3851 $editor = isset( $_POST['editor'] ) ? sanitize_text_field( wp_unslash( $_POST['editor'] ) ) : '';
3852 $website_kit = isset( $_POST['website_kit'] ) ? sanitize_text_field( wp_unslash( $_POST['website_kit'] ) ) : '';
3853 $api_type = isset( $_POST['api_type'] ) ? sanitize_text_field( wp_unslash( $_POST['api_type'] ) ) : 'import_template';
3854 $ai_compitible = isset( $_POST['ai_compitible'] ) ? sanitize_text_field( wp_unslash( $_POST['ai_compitible'] ) ) : false;
3855
3856 if ( empty( $template_ids ) ) {
3857 $output = array(
3858 'message' => esc_html__( 'Invalid import', 'wdesignkit' ),
3859 'description' => esc_html__( 'Invalid import: Check your details and try again.', 'wdesignkit' ),
3860 'success' => false,
3861 );
3862
3863 wp_send_json( $output );
3864 wp_die();
3865 }
3866
3867 /**Not Usefull*/
3868 if ( isset( $_POST['select'] ) ) {
3869 $args['post_type'] = ! empty( $_POST['select'] ) ? sanitize_text_field( wp_unslash( $_POST['select'] ) ) : '';
3870 }
3871
3872 $args['custom_meta'] = isset( $_POST['custom_meta'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_meta'] ) ) : false;
3873 $args['editor'] = $editor;
3874
3875 $token = $this->wdkit_login_user_token( $email );
3876
3877 $temp_args = array(
3878 'token' => $token,
3879 'template_id' => $template_ids['id'],
3880 'editor' => $editor,
3881 'website_kit' => $website_kit,
3882 'unique_id' => get_option( 'wdkit_unique_id' ) ?? '',
3883 );
3884
3885 $response = WDesignKit_Data_Query::get_data( $api_type, $temp_args );
3886 $output = array();
3887
3888 if ( is_wp_error( $response ) ) {
3889
3890 wp_send_json( $response );
3891 wp_die();
3892 }
3893
3894 if ( isset( $response['content'] ) && 'error' === $response['content'] ) {
3895 wp_send_json( $response );
3896 wp_die();
3897 }
3898
3899 $result = array(
3900 'response' => $response,
3901 'args' => $args,
3902 'id' => $template_ids['id'],
3903 'temp_data' => $template_ids,
3904 );
3905
3906 $output['message'] = $response['message'];
3907 $output['description'] = $response['description'];
3908 $output['data'] = $result;
3909 $output['success'] = $response['success'];
3910
3911 // Counts the IMPORT ACTION, not what it brought in. A kit import always counts as 1 kit,
3912 // no matter how many blocks/pages that kit contains — confirmed live: a single gutenberg
3913 // kit import recorded total=680, kinds.kit=680, because $template_ids for that call was a
3914 // 680-element array of the kit's own blocks and count( $template_ids ) counted every one of
3915 // them. A page-kit's *size* is not tracking's concern; "was a kit imported" is.
3916 //
3917 // The 'single' branch keeps a defensive fallback for the one shape this endpoint's own
3918 // $template_ids reliably takes when it is not a kit — a single {id, name, slug, thumb...}
3919 // object — where count() would likewise count JSON keys instead of "1 template imported".
3920 if ( ! empty( $output['success'] ) ) {
3921 $is_kit = ( '' !== $website_kit );
3922 $import_count = $is_kit ? 1 : ( isset( $template_ids['id'] ) ? 1 : ( is_array( $template_ids ) ? count( $template_ids ) : 1 ) );
3923 do_action(
3924 'wdkit_template_imported',
3925 $is_kit ? 'kit' : 'single',
3926 sanitize_key( $builder ),
3927 $import_count
3928 );
3929 }
3930
3931 wp_send_json( $output );
3932 wp_die();
3933 }
3934
3935 public function wdkit_enable_template_widgets() {
3936 $widget_list = ! empty( $_POST['widget_list'] ) ? json_decode( wp_unslash( $_POST['widget_list'] ), true ) : array();
3937 $extensions_list = ! empty( $_POST['extensions_list'] ) ? json_decode( wp_unslash( $_POST['extensions_list'] ), true ) : array();
3938
3939 if ( empty( $widget_list ) && empty( $extensions_list ) ) {
3940 $res = array(
3941 'massage' => __( 'Widget array not found', 'wdesignkit' ),
3942 'description' => __( 'Widget array not found', 'wdesignkit' ),
3943 'success' => false,
3944 );
3945 wp_send_json( $res );
3946 wp_die();
3947 }
3948
3949 if ( ! has_filter( 'tpae_enable_selected_widgets' ) ) {
3950 $res = array(
3951 'massage' => __( 'Relevant Plugin not Activated', 'wdesignkit' ),
3952 'description' => __( 'Relevant Plugin not Installed / Activated', 'wdesignkit' ),
3953 'success' => false,
3954 );
3955 wp_send_json( $res );
3956 wp_die();
3957 }
3958
3959 $w_list = array(
3960 'widgets' => $widget_list,
3961 'extensions' => $extensions_list,
3962 );
3963
3964 $result = apply_filters( 'tpae_enable_selected_widgets', $w_list );
3965
3966 if ( ! empty( $result['success'] ) ) {
3967 $res = array(
3968 'massage' => __( 'Enabled widgets successfully', 'wdesignkit' ),
3969 'description' => __( 'Used widgets have been enabled successfully', 'wdesignkit' ),
3970 'success' => true,
3971 );
3972 } else {
3973
3974 $message = isset( $result['message'] ) ? $result['message'] : __( 'Failed to enable widgets', 'wdesignkit' );
3975 $description = isset( $result['description'] ) ? $result['description'] : __( 'Failed to enable widgets', 'wdesignkit' );
3976
3977 $res = array(
3978 'massage' => $message,
3979 'description' => $description,
3980 'success' => false,
3981 );
3982 }
3983
3984 wp_send_json( $res );
3985 wp_die();
3986 }
3987
3988 /**
3989 * Import single template and section from plugin only
3990 * */
3991 protected function wdkit_import_multi_template() {
3992 $args = $this->wdkit_parse_args( $_POST );
3993 $api_type = isset( $_POST['api_type'] ) ? sanitize_text_field( wp_unslash( $_POST['api_type'] ) ) : 'import_template';
3994
3995 if ( ! current_user_can( 'manage_options' ) ) {
3996 return false;
3997 }
3998
3999 if ( empty( $_POST['template_ids'] ) ) {
4000 $output = array(
4001 'data' => array(),
4002 'message' => esc_html__( 'Invalid import', 'wdesignkit' ),
4003 'description' => esc_html__( 'Invalid import: Check your details and try again.', 'wdesignkit' ),
4004 'success' => false,
4005 );
4006
4007 wp_send_json( $output );
4008 wp_die();
4009 }
4010
4011 if ( isset( $_POST['template_ids'] ) ) {
4012 $args['template_ids'] = ! empty( $_POST['template_ids'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['template_ids'] ) ), true ) : array();
4013 }
4014
4015 if ( isset( $_POST['page_section'] ) ) {
4016 $args['page_section'] = ! empty( $_POST['page_section'] ) ? sanitize_text_field( wp_unslash( $_POST['page_section'] ) ) : '';
4017 }
4018
4019 if ( isset( $_POST['select'] ) ) {
4020 $args['post_type'] = ! empty( $_POST['select'] ) ? sanitize_text_field( wp_unslash( $_POST['select'] ) ) : '';
4021 }
4022
4023 $args['custom_meta'] = isset( $_POST['custom_meta'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_meta'] ) ) : false;
4024
4025 if ( ! empty( $args['template_ids'] ) && ! empty( $args['page_section'] ) ) {
4026 $output = array();
4027 if ( ! empty( $args['template_ids']['id'] ) ) {
4028 $token = $this->wdkit_login_user_token( $args['email'] );
4029
4030 $temp_args = array(
4031 'token' => $token,
4032 'template_id' => $args['template_ids']['id'],
4033 'editor' => $args['editor'],
4034 'unique_id' => get_option( 'wdkit_unique_id' ) ?? '',
4035 );
4036
4037 $response = WDesignKit_Data_Query::get_data( $api_type, $temp_args );
4038
4039 if ( is_wp_error( $response ) ) {
4040 wp_send_json( array(
4041 'success' => false,
4042 'message' => $response->get_error_message(),
4043 ) );
4044 wp_die();
4045 }
4046
4047 if ( isset( $response['content'] ) && 'error' === $response['content'] ) {
4048 wp_send_json( $response );
4049 wp_die();
4050 }
4051
4052 $result = array(
4053 'response' => $response,
4054 'args' => $args,
4055 'id' => $args['template_ids'],
4056 'value' => $args['template_ids'],
4057 );
4058
4059 $output['message'] = $response['message'];
4060 $output['description'] = $response['description'];
4061 $output['data'] = $result;
4062 $output['success'] = true;
4063
4064 }
4065
4066 wp_send_json( $output );
4067 wp_die();
4068 }
4069 }
4070
4071 /**
4072 * It is Use for remove selected category from content.
4073 *
4074 * @since 2.0.5
4075 */
4076 public function wdkit_content_remover( &$data ) {
4077
4078 if ( is_array( $data ) ) {
4079
4080 foreach ( $data as $key => &$value ) {
4081 if ( $key === 'post_category' ) {
4082 $data[ $key ] = array();
4083 } else if ($key === 'include_products'){
4084 $data[ $key ] = "";
4085 } else {
4086 $this->wdkit_content_remover( $value );
4087 }
4088 }
4089 } elseif ( is_object( $data ) ) {
4090
4091 foreach ( $data as $key => &$value ) {
4092 if ( $key === 'post_category' ) {
4093 $data->$key = array();
4094 } else if ($key === 'include_products'){
4095 $data->$key = "";
4096 } else {
4097 $this->wdkit_content_remover( $value );
4098 }
4099 }
4100 }
4101
4102 return $data;
4103 }
4104
4105 protected function wkit_update_elementor_template(){
4106
4107 if ( isset( $_POST['data'] ) ) {
4108 $content = ! empty( $_POST['data'] ) ? json_decode( wp_unslash( $_POST['data'] ), true ) : '';
4109 }
4110
4111 if ( isset( $_POST['template_id'] ) ) {
4112 $template_id = ! empty( $_POST['template_id'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['template_id'] ), true ) ) : '';
4113 }
4114
4115 $document = \Elementor\Plugin::$instance->documents->get($template_id);
4116
4117 // This saves content posted straight from the browser, which carries local image
4118 // URLs but still the source template's attachment IDs. Without repairing them the
4119 // save undoes what wdkit_media_import() fixed on create, and has_sizes controls —
4120 // container background images especially — resolve to nothing and render empty.
4121 $content = self::wdkit_repair_attachment_ids( $content );
4122
4123 $document->save([
4124 'elements' => $content
4125 ]);
4126 }
4127
4128 /**
4129 * Update the content of an already-created page.
4130 *
4131 * Used by the async ("Site Ready first") import path: pages are created up front with
4132 * their un-rewritten template content, then this writes the AI-rewritten content into
4133 * each page in the background. Elementor saves via the document API (same as
4134 * wkit_update_elementor_template); Gutenberg writes post_content directly.
4135 *
4136 * @since 2.6.2
4137 */
4138 protected function wdkit_update_page_content() {
4139 $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
4140 $builder = isset( $_POST['builder'] ) ? sanitize_text_field( wp_unslash( $_POST['builder'] ) ) : '';
4141
4142 if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
4143 return array(
4144 'success' => false,
4145 'message' => esc_html__( 'Invalid page or insufficient permission', 'wdesignkit' ),
4146 );
4147 }
4148
4149 if ( 'gutenberg' === $builder ) {
4150 // Do NOT run kses here: Gutenberg block delimiters are HTML comments
4151 // (<!-- wp:... -->) which kses strips. Mirror the create path, which stores
4152 // the block markup slashed and unfiltered (endpoint is manage_options-gated
4153 // and the content is plugin-generated).
4154 $content = isset( $_POST['content'] ) ? wp_unslash( $_POST['content'] ) : '';
4155
4156 // This content comes straight from the browser and still carries the template
4157 // site's media URLs and attachment IDs, so it has to go through the same pipeline
4158 // the create path uses. Without this the save simply undid the import: the files
4159 // were fetched, then overwritten by a copy still pointing at the source site.
4160 //
4161 // Re-running is cheap. Every URL already handled resolves from the source-hash
4162 // lookup, and media that is already local resolves straight from its URL, so no
4163 // image is fetched or stored twice.
4164 $content = $this->wdkit_relink_gutenberg_content( $content );
4165
4166 $result = wp_update_post(
4167 array(
4168 'ID' => $post_id,
4169 'post_content' => wp_slash( $content ),
4170 ),
4171 true
4172 );
4173
4174 if ( is_wp_error( $result ) ) {
4175 return array(
4176 'success' => false,
4177 'message' => $result->get_error_message(),
4178 );
4179 }
4180
4181 self::wdkit_rebuild_block_css( $post_id );
4182 } else {
4183 $elements = isset( $_POST['content'] ) ? json_decode( wp_unslash( $_POST['content'] ), true ) : array();
4184
4185 if ( ! class_exists( '\\Elementor\\Plugin' ) ) {
4186 return array(
4187 'success' => false,
4188 'message' => esc_html__( 'Elementor not available', 'wdesignkit' ),
4189 );
4190 }
4191
4192 $document = \Elementor\Plugin::$instance->documents->get( $post_id );
4193 if ( ! $document ) {
4194 return array(
4195 'success' => false,
4196 'message' => esc_html__( 'Elementor document not found', 'wdesignkit' ),
4197 );
4198 }
4199
4200 // Same as wkit_update_elementor_template(): browser-posted content keeps the
4201 // source template's attachment IDs, so repair them or this save undoes the
4202 // create-time fix and background images stop rendering.
4203 $elements = self::wdkit_repair_attachment_ids( $elements );
4204
4205 $document->save( array( 'elements' => $elements ) );
4206 }
4207
4208 return array(
4209 'success' => true,
4210 'message' => esc_html__( 'Page content updated', 'wdesignkit' ),
4211 );
4212 }
4213
4214 /**
4215 * Import single template and section from plugin only
4216 *
4217 * @param array $args store data.
4218 * @param array $template_id store data.
4219 * @param array $data store data.
4220 * @param array $temp_data store data.
4221 * */
4222 protected function import_page_section_content() {
4223
4224 // Elementor sideloads every image referenced by the page from inside this request.
4225 // A single oversized source image decodes to more than the whole memory limit, so
4226 // guard before any of that starts.
4227 $this->wdkit_guard_oversized_images();
4228
4229 // Sideloading images for image-heavy pages (wdkit_media_import → Imagick
4230 // thumbnail generation per image) can exceed the default 30s execution
4231 // limit and fatal the request mid-import. Give this single page import
4232 // more headroom; harmless no-op where set_time_limit() is disabled.
4233 if ( function_exists( 'set_time_limit' ) ) {
4234 @set_time_limit( 120 );
4235 }
4236
4237 if ( isset( $_POST['args'] ) ) {
4238 $args = ! empty( $_POST['args'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['args'] ) ), true ) : array();
4239 }
4240
4241 if ( isset( $_POST['temp_data'] ) ) {
4242 $temp_data = ! empty( $_POST['temp_data'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['temp_data'] ) ), true ) : array();
4243 }
4244
4245 if ( isset( $_POST['category_list'] ) ) {
4246 $category_list = ! empty( $_POST['category_list'] ) ? json_decode( wp_unslash( $_POST['category_list'] ), true ) : '';
4247 }
4248
4249 if ( isset( $_POST['tag_list'] ) ) {
4250 $tag_list = ! empty( $_POST['tag_list'] ) ? json_decode( wp_unslash( $_POST['tag_list'] ), true ) : '';
4251 }
4252
4253 if ( isset( $_POST['thumb_image'] ) ) {
4254 $thumb_image = ! empty( $_POST['thumb_image'] ) ? esc_url_raw( $_POST['thumb_image'] ) : '';
4255 }
4256
4257 if ( isset( $_POST['template_id'] ) ) {
4258 $template_id = ! empty( $_POST['template_id'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['template_id'] ), true ) ) : '';
4259 }
4260
4261 $temp_type = isset( $_POST['temp_type'] ) ? sanitize_text_field( wp_unslash( $_POST['temp_type'] ) ) : 'normal';
4262
4263 if ( isset( $_POST['data'] ) ) {
4264 $data = ! empty( $_POST['data'] ) ? json_decode( wp_unslash( $_POST['data'] ) ) : '';
4265 }
4266
4267 $enqueue_instance = new Wdkit_Enqueue();
4268 $get_post_type = $enqueue_instance->wdkit_get_post_type_list();
4269
4270 $post_type = ! empty( $temp_data['type'] ) ? sanitize_text_field( wp_unslash( $temp_data['type'] ) ) : 'page';
4271
4272 if ( 'section' === $post_type ) {
4273 $post_type = $temp_data['wp_post_type'];
4274 } else {
4275 $post_type = $temp_data['wp_post_type'];
4276 }
4277
4278 if ( ! array_key_exists( $post_type, $get_post_type ) ) {
4279 $post_type = 'page';
4280 }
4281
4282 if ( ! empty( $data ) && ! empty( $template_id ) && ! empty( $post_type ) && current_user_can( 'manage_options' ) ) {
4283 $post_content = $data;
4284 // Restore The Plus Addons' globals before the page is built, so the widgets'
4285 // tp_global_preset references resolve as soon as it renders. Done here rather
4286 // than in the save-template UI's confirmation dialog so that every import path
4287 // - the library, the abilities, the theme builder - gets it.
4288 if ( isset( $post_content->tp_globals ) && ! empty( $post_content->tp_globals ) ) {
4289 $this->wdkit_merge_tp_globals(
4290 json_decode( wp_json_encode( $post_content->tp_globals ), true ),
4291 isset( $post_content->tp_global_refs )
4292 ? json_decode( wp_json_encode( $post_content->tp_global_refs ), true )
4293 : array()
4294 );
4295 }
4296
4297 $post_title = isset( $post_content->title ) ? sanitize_text_field( $post_content->title ) : '';
4298 $post_slug = isset( $post_content->slug ) ? sanitize_text_field( $post_content->slug ) : '';
4299 $file_type = isset( $post_content->file_type ) ? sanitize_text_field( $post_content->file_type ) : '';
4300 $content = isset( $post_content->content ) ? wp_slash( $post_content->content ) : '';
4301 $temp_con = '';
4302
4303 if ( 'gutenberg' === $args['editor'] || ( 'wdkit' === $args['editor'] && ! empty( $file_type ) && 'wp_block' === $file_type ) ) {
4304 if ( empty( $content ) ) {
4305 wp_send_json(
4306 array(
4307 'template_id' => $template_id,
4308 'message' => __( 'Content is Empty.', 'wdesignkit' ),
4309 )
4310 );
4311 wp_die();
4312 } elseif ( ! empty( $content ) && ! empty( $file_type ) && 'wp_block' === $file_type ) {
4313
4314 $editor = ( 'wdkit' === $args['editor'] ) ? 'gutenberg' : $args['editor'];
4315 $blocks = parse_blocks( stripslashes( $content ) );
4316
4317 $blocks = $this->wdkit_media_import( $blocks, $editor );
4318
4319 $processor = new WDKIT_Nexter_Block_Processor();
4320 $blocks = $processor->run( $blocks );
4321 $content = serialize_blocks( $blocks );
4322
4323 $content = $this->replace_unicode_glitch( serialize_blocks( $blocks ) );
4324
4325 $inserted_post = wp_insert_post(
4326 array(
4327 'post_status' => 'publish',
4328 'post_type' => $post_type,
4329 'post_title' => $post_title,
4330 'post_name' => $post_slug,
4331 'post_content' => $content,
4332 )
4333 );
4334
4335 if ( is_wp_error( $inserted_post ) ) {
4336 wp_send_json(
4337 array(
4338 'import_failed' => $inserted_post->get_error_message(),
4339 'code' => $inserted_post->get_error_code(),
4340 )
4341 );
4342 wp_die();
4343 }
4344
4345 if ( ! empty( $thumb_image ) && wdesignkit_validate_external_url( $thumb_image ) ) {
4346 // $featured_image_url = esc_url_raw( $thumb_image );
4347 $tmp = download_url( $thumb_image );
4348 if ( is_wp_error( $tmp ) ) {
4349 error_log( 'Image download failed: ' . esc_html( $tmp->get_error_message() ) );
4350 } else {
4351 $file_array = array(
4352 'name' => wp_basename( $thumb_image ),
4353 'tmp_name' => $tmp,
4354 );
4355
4356 $image_id = media_handle_sideload( $file_array, $inserted_post );
4357
4358 if ( is_wp_error( $image_id ) ) {
4359 error_log( 'Image sideload failed: ' . esc_html( $image_id->get_error_message() ) );
4360 } else {
4361 set_post_thumbnail( $inserted_post, $image_id );
4362 }
4363
4364 @unlink( $tmp );
4365 }
4366 }
4367
4368 if ( ! empty( $category_list ) && is_array( $category_list ) ) {
4369 $category_ids = array_map( 'intval', $category_list );
4370 wp_set_post_terms( $inserted_post, $category_ids, 'category' );
4371 }
4372
4373 if ( ! empty( $tag_list ) && is_array( $tag_list ) ) {
4374 $tag_ids = array_map( 'intval', $tag_list );
4375 wp_set_post_terms( $inserted_post, $tag_ids, 'post_tag' );
4376 }
4377
4378 if ( ! empty( $args['custom_meta'] ) && 'true' == $args['custom_meta'] ) {
4379 $custom_meta = isset( $post_content->custom_meta ) ? json_decode( wp_json_encode( $post_content->custom_meta ), true ) : '';
4380 if ( ! empty( $custom_meta ) ) {
4381 foreach ( $custom_meta as $meta_key => $meta_val ) {
4382 if ( isset( $meta_val[0] ) && ! empty( $meta_val[0] ) && is_serialized( $meta_val[0] ) ) {
4383 $meta_val[0] = unserialize( $meta_val[0], array( 'allowed_classes' => false ) );
4384 }
4385
4386 if ( '' === get_post_meta( $inserted_post, $meta_key, true ) && isset( $meta_val[0] ) ) {
4387 add_post_meta( $inserted_post, $meta_key, $meta_val[0] );
4388 }
4389 }
4390 }
4391 }
4392
4393 $temp_detail = array(
4394 'title' => get_the_title( $inserted_post ),
4395 'edit_link' => get_edit_post_link( $inserted_post, 'internal' ),
4396 'view' => get_permalink( $inserted_post ),
4397 'id' => $inserted_post,
4398 );
4399
4400 if ( ! empty( $template_id->id ) ) {
4401 $temp_id = $template_id->id;
4402 } elseif ( ! empty( $template_id ) ) {
4403 $temp_id = $template_id;
4404 } else {
4405 $temp_id = '';
4406 }
4407
4408 wp_update_post([
4409 'ID' => $inserted_post,
4410 ]);
4411
4412 if (class_exists('Tpgb_Library') && method_exists('Tpgb_Library', 'remove_backend_dir_files')) {
4413 Tpgb_Library()->remove_backend_dir_files();
4414 }
4415
4416 clean_post_cache( $inserted_post );
4417
4418 // This whole method imports exactly one section per call — unlike
4419 // wdkit_import_template()/wdkit_import_kit_template(), it never fired this hook
4420 // at all, so single-section imports (Header/Footer/CTA/etc., a primary import
4421 // path per the Template Type sidebar) were invisible to tracking entirely.
4422 do_action( 'wdkit_template_imported', 'single', sanitize_key( $editor ), 1 );
4423
4424 wp_send_json(
4425 array(
4426 $temp_id => $temp_detail,
4427 'description' => 'Yay! Your Section has been Successfully Imported.',
4428 'message' => __( 'Successfully Imported.', 'wdesignkit' ),
4429 'inserted_id' => $inserted_post,
4430 'success' => true,
4431 )
4432 );
4433 wp_die();
4434 }
4435 } elseif ( 'elementor' === $args['editor'] || ( 'wdkit' === $args['editor'] && ! empty( $file_type ) && 'elementor' === $file_type ) ) {
4436 if ( did_action( 'elementor/loaded' ) ) {
4437 if ( empty( $content ) ) {
4438 wp_send_json(
4439 array(
4440 'template_id' => $template_id,
4441 'message' => __( 'Content is Empty.', 'wdesignkit' ),
4442 )
4443 );
4444 wp_die();
4445 } elseif ( ! empty( $content ) && ! empty( $file_type ) && 'elementor' === $file_type ) {
4446
4447 $content = $this->wdkit_content_remover( $content );
4448
4449 $post_attributes = array(
4450 'post_title' => $post_title,
4451 'post_type' => $post_type,
4452 'post_status' => 'publish',
4453 'post_name' => $post_slug,
4454 );
4455
4456 if ( 'elementor_library' === $post_type ) {
4457 $el_type = ( isset( $post_content->el_type ) && ! empty( $post_content->el_type ) ) ? sanitize_text_field( $post_content->el_type ) : 'page';
4458 $new_document = \Elementor\Plugin::$instance->documents->create(
4459 $el_type,
4460 $post_attributes
4461 );
4462 } else {
4463 $new_document = \Elementor\Plugin::$instance->documents->create(
4464 $post_attributes['post_type'],
4465 $post_attributes
4466 );
4467 }
4468
4469 if ( is_wp_error( $new_document ) ) {
4470 wp_send_json(
4471 array(
4472 'import_failed' => $new_document->get_error_message(),
4473 'code' => $new_document->get_error_code(),
4474 )
4475 );
4476 wp_die();
4477 }
4478
4479 $inserted_id = $new_document->get_main_id();
4480
4481 if ( ! empty( $thumb_image ) && wdesignkit_validate_external_url( $thumb_image ) ) {
4482 // $featured_image_url = esc_url_raw( $thumb_image );
4483 $tmp = download_url( $thumb_image );
4484 if ( is_wp_error( $tmp ) ) {
4485 error_log( 'Image download failed: ' . esc_html( $tmp->get_error_message() ) );
4486 } else {
4487 $file_array = array(
4488 'name' => wp_basename( $thumb_image ),
4489 'tmp_name' => $tmp,
4490 );
4491
4492 $image_id = media_handle_sideload( $file_array, $inserted_id );
4493
4494 if ( is_wp_error( $image_id ) ) {
4495 error_log( 'Image sideload failed: ' . esc_html( $image_id->get_error_message() ) );
4496 } else {
4497 set_post_thumbnail( $inserted_id, $image_id );
4498 }
4499
4500 @unlink( $tmp );
4501 }
4502 }
4503
4504 if ( ! empty( $category_list ) && is_array( $category_list ) ) {
4505 $category_ids = array_map( 'intval', $category_list );
4506 wp_set_post_terms( $inserted_id, $category_ids, 'category' );
4507 }
4508
4509 if ( ! empty( $tag_list ) && is_array( $tag_list ) ) {
4510 $tag_ids = array_map( 'intval', $tag_list );
4511 wp_set_post_terms( $inserted_id, $tag_ids, 'post_tag' );
4512 }
4513
4514 $settings = ( isset( $post_content->settings ) && ! empty( $post_content->settings ) ) ? json_decode( wp_json_encode( $post_content->settings ), true ) : array();
4515
4516 $content = wp_json_encode( $content );
4517 $content = $this->wdkit_media_import( $content, $file_type );
4518
4519 $new_document->save(
4520 array(
4521 'elements' => $content,
4522 'settings' => ! empty( $settings ) ? $settings : array(),
4523 )
4524 );
4525
4526 $inserted_id = $new_document->get_main_id();
4527
4528 if( $temp_type == 'navigation' ){
4529 $temp_con = $content;
4530 }
4531
4532 if ( ! empty( $args['custom_meta'] ) && 'true' == $args['custom_meta'] ) {
4533 $custom_meta = isset( $post_content->custom_meta ) ? json_decode( wp_json_encode( $post_content->custom_meta ), true ) : '';
4534 if ( ! empty( $custom_meta ) ) {
4535 foreach ( $custom_meta as $meta_key => $meta_val ) {
4536 if ( ! empty( $meta_val[0] ) && is_serialized( $meta_val[0] ) ) {
4537 $meta_val[0] = unserialize( $meta_val[0], array( 'allowed_classes' => false ) );
4538 }
4539 if ( '' === get_post_meta( $inserted_id, $meta_key, true ) ) {
4540 add_post_meta( $inserted_id, $meta_key, $meta_val[0] );
4541 }
4542 }
4543 }
4544 }
4545
4546 $temp_detail = array(
4547 'title' => get_the_title( $inserted_id ),
4548 'edit_link' => get_edit_post_link( $inserted_id, 'internal' ),
4549 'view' => get_permalink( $inserted_id ),
4550 'id' => $inserted_id,
4551 );
4552
4553 if ( ! empty( $template_id->id ) ) {
4554 $temp_id = $template_id->id;
4555 } elseif ( ! empty( $template_id ) ) {
4556 $temp_id = $template_id;
4557 } else {
4558 $temp_id = '';
4559 }
4560
4561 \Elementor\Plugin::$instance->files_manager->clear_cache();
4562
4563 // See the matching note in the Gutenberg branch above — this method never
4564 // fired the tracking hook for either editor.
4565 do_action( 'wdkit_template_imported', 'single', 'elementor', 1 );
4566
4567 wp_send_json(
4568 array(
4569 $temp_id => $temp_detail,
4570 'content' => $temp_con,
4571 'description' => 'Yay! Your Section has been Successfully Imported.',
4572 'message' => __( 'Successfully Imported.', 'wdesignkit' ),
4573 'inserted_id' => $inserted_id,
4574 'success' => true,
4575 )
4576 );
4577 wp_die();
4578 }
4579 } else {
4580 wp_send_json(
4581 array(
4582 'template_id' => $template_id,
4583 'message' => esc_html__( 'Relevant Page Builder not installed or activated', 'wdesignkit' ),
4584 )
4585 );
4586 wp_die();
4587 }
4588 }
4589 }
4590
4591 wp_send_json(
4592 array(
4593 'success' => false,
4594 'message' => esc_html__( 'Something went wrong', 'wdesignkit' ),
4595 )
4596 );
4597 wp_die();
4598 }
4599
4600 /**
4601 * Replace unicode glitch
4602 *
4603 * @since 2.0.0
4604 */
4605 private function replace_unicode_glitch( $content ) {
4606
4607 // Fix escaped unicode like \u003c → <
4608 $content = preg_replace_callback(
4609 '/\\\\u([0-9a-fA-F]{4})/',
4610 function ( $match ) {
4611 return html_entity_decode(
4612 mb_convert_encoding(
4613 pack('H*', $match[1]),
4614 'UTF-8',
4615 'UCS-2BE'
4616 ),
4617 ENT_QUOTES,
4618 'UTF-8'
4619 );
4620 },
4621 $content
4622 );
4623
4624 return $content;
4625 }
4626
4627
4628 /**
4629 * change plugins setting for import kit
4630 *
4631 * @since 2.0.0
4632 */
4633 protected function update_plugin_setting() {
4634 $temp_id = isset( $_POST['plugin_type'] ) ? sanitize_text_field( $_POST['plugin_type'] ) : '';
4635
4636 if ( $temp_id == 'elementor' ) {
4637 $unfiltered_files = get_option( 'elementor_unfiltered_files_upload', false );
4638 $load_fa4 = get_option( 'elementor_load_fa4_shim', false );
4639 $Inline_font_icons = get_option( 'elementor_experiment-e_font_icon_svg', false );
4640 $container = get_option( 'elementor_experiment-container', false );
4641
4642 if ( isset( $unfiltered_files ) ) {
4643 update_option( 'elementor_unfiltered_files_upload', 1 );
4644 } else {
4645 add_option( 'elementor_unfiltered_files_upload', 1 );
4646 }
4647
4648 if ( isset( $load_fa4 ) ) {
4649 update_option( 'elementor_load_fa4_shim', 'yes' );
4650 } else {
4651 add_option( 'elementor_load_fa4_shim', 'yes' );
4652 }
4653
4654 if ( isset( $container ) ) {
4655 update_option( 'elementor_experiment-container', 'active' );
4656 } else {
4657 add_option( 'elementor_experiment-container', 'active' );
4658 }
4659
4660 if ( isset( $Inline_font_icons ) ) {
4661 update_option( 'elementor_experiment-e_font_icon_svg', 'inactive' );
4662 } else {
4663 add_option( 'elementor_experiment-e_font_icon_svg', 'inactive' );
4664 }
4665
4666 $response = array(
4667 'message' => esc_html__( 'Plugin Setting updated', 'wdesignkit' ),
4668 'description' => esc_html__( 'Plugin Setting updated', 'wdesignkit' ),
4669 'success' => true,
4670 );
4671 } else {
4672 $response = array(
4673 'message' => esc_html__( 'Plugin not found', 'wdesignkit' ),
4674 'description' => esc_html__( 'Plugin not found', 'wdesignkit' ),
4675 'success' => false,
4676 );
4677 }
4678
4679 wp_send_json( $response );
4680 wp_die();
4681 }
4682
4683 /**
4684 * generate different color logo
4685 *
4686 * @since 2.0.0
4687 */
4688 protected function wkit_generate_site_logo() {
4689
4690 if ( empty( $_POST['image_url'] ) ) {
4691 wp_send_json_error( 'Image URL not provided.' );
4692 }
4693
4694 if ( isset( $_POST['colors'] ) ) {
4695 $img_colors = ! empty( $_POST['colors'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['colors'] ) ), true ) : array();
4696 }
4697
4698 if ( empty( $img_colors ) ) {
4699 wp_send_json_error( 'Image color not provided.' );
4700 }
4701
4702 $image_url = esc_url_raw( $_POST['image_url'] );
4703
4704 if ( ! wdesignkit_validate_external_url( $image_url ) ) {
4705 wp_send_json_error( 'Image could not be downloaded.' );
4706 }
4707
4708 $tmp_file = download_url( $image_url );
4709 if ( is_wp_error( $tmp_file ) ) {
4710 wp_send_json_error( 'Image could not be downloaded.' );
4711 }
4712
4713 if ( mime_content_type( $tmp_file ) !== 'image/png' ) {
4714 unlink( $tmp_file );
4715 wp_send_json_error( 'Not a PNG file.' );
4716 }
4717
4718 $src = imagecreatefrompng( $tmp_file );
4719 imagesavealpha( $src, true );
4720
4721 $width = imagesx( $src );
4722 $height = imagesy( $src );
4723
4724 $hasTransparency = false;
4725 for ( $x = 0; $x < $width; $x++ ) {
4726 for ( $y = 0; $y < $height; $y++ ) {
4727 $rgba = imagecolorat( $src, $x, $y );
4728 $alpha = ( $rgba & 0x7F000000 ) >> 24;
4729
4730 if ( $alpha > 0 ) {
4731 $hasTransparency = true;
4732 break 2;
4733 }
4734 }
4735 }
4736
4737 if ( ! $hasTransparency ) {
4738 unlink( $tmp_file );
4739 wp_send_json_error( 'PNG has no transparent pixels.' );
4740 }
4741
4742 $upload_dir = wp_upload_dir();
4743 $result_urls = array();
4744
4745 $colour_index = 0;
4746 foreach ( $img_colors as $name => $rgb ) {
4747 // $name is a key from the posted colours payload and went straight into the output
4748 // filename, so traversal sequences in it steered imagepng() outside the upload
4749 // directory (CWE-22, ClickUp 86d41ced6). sanitize_file_name() flattens it to one
4750 // path segment; a key made only of dots/separators sanitizes to empty, so fall back
4751 // to a positional index rather than writing to a bare "colored--<time>.png".
4752 ++$colour_index;
4753 $safe_name = sanitize_file_name( (string) $name );
4754 if ( '' === $safe_name ) {
4755 $safe_name = 'colour-' . $colour_index;
4756 }
4757
4758 $new = imagecreatetruecolor( $width, $height );
4759 imagesavealpha( $new, true );
4760 imagealphablending( $new, false );
4761
4762 $transparent = imagecolorallocatealpha( $new, 0, 0, 0, 127 );
4763 imagefill( $new, 0, 0, $transparent );
4764
4765 for ( $x = 0; $x < $width; $x++ ) {
4766 for ( $y = 0; $y < $height; $y++ ) {
4767 $rgba = imagecolorat( $src, $x, $y );
4768 $alpha = ( $rgba & 0x7F000000 ) >> 24;
4769
4770 // Skip fully transparent pixels
4771 if ( $alpha === 127 ) {
4772 continue;
4773 }
4774
4775 // Replace pixel color directly
4776 $new_r = $rgb[0];
4777 $new_g = $rgb[1];
4778 $new_b = $rgb[2];
4779
4780 $color = imagecolorallocatealpha( $new, $new_r, $new_g, $new_b, $alpha );
4781 imagesetpixel( $new, $x, $y, $color );
4782 }
4783 }
4784
4785 $filename = 'colored-' . $safe_name . '-' . time() . '.png';
4786 $filepath = $upload_dir['path'] . '/' . $filename;
4787
4788 imagepng( $new, $filepath );
4789 imagedestroy( $new );
4790
4791 $attachment = array(
4792 'post_mime_type' => 'image/png',
4793 'post_title' => sanitize_file_name( $filename ),
4794 'post_content' => '',
4795 'post_status' => 'inherit',
4796 );
4797
4798 $attach_id = wp_insert_attachment( $attachment, $filepath );
4799 require_once ABSPATH . 'wp-admin/includes/image.php';
4800 $attach_data = wp_generate_attachment_metadata( $attach_id, $filepath );
4801 wp_update_attachment_metadata( $attach_id, $attach_data );
4802
4803 $result_urls[ $name ] = wp_get_attachment_url( $attach_id );
4804 }
4805
4806 imagedestroy( $src );
4807 unlink( $tmp_file );
4808
4809 wp_send_json_success( $result_urls );
4810 }
4811
4812 /**
4813 * change theme setting for import kit
4814 *
4815 * @since 2.0.0
4816 */
4817 protected function update_theme_setting() {
4818 $theme_db = get_option( 'nxt-theme-options', false );
4819
4820 $container_type = 'container-fluid';
4821 $fluid_spacing = array(
4822 'md' => array(
4823 'left' => '0',
4824 'right' => '0',
4825 ),
4826 'sm' => array(
4827 'left' => '',
4828 'right' => '',
4829 ),
4830 'xs' => array(
4831 'left' => '',
4832 'right' => '',
4833 ),
4834 'md-unit' => 'px',
4835 'sm-unit' => 'px',
4836 'xs-unit' => 'px',
4837 );
4838
4839 if ( isset( $theme_db ) ) {
4840 $nexter_setting = $theme_db;
4841 $nexter_setting['site-header-container'] = $container_type;
4842 $nexter_setting['site-footer-container'] = $container_type;
4843 $nexter_setting['site-layout-container'] = $container_type;
4844 $nexter_setting['site-page-container'] = $container_type;
4845
4846 $nexter_setting['header-fluid-spacing'] = $fluid_spacing;
4847 $nexter_setting['footer-fluid-spacing'] = $fluid_spacing;
4848 $nexter_setting['site-fluid-spacing'] = $fluid_spacing;
4849 $nexter_setting['page-fluid-spacing'] = $fluid_spacing;
4850
4851 update_option( 'nxt-theme-options', $nexter_setting );
4852 } else {
4853 $nexter_setting = array(
4854 'site-header-container' => 'container-fluid',
4855 'header-fluid-spacing' => array(
4856 'md' => array(
4857 'left' => '0',
4858 'right' => '0',
4859 ),
4860 'sm' => array(
4861 'left' => '',
4862 'right' => '',
4863 ),
4864 'xs' => array(
4865 'left' => '',
4866 'right' => '',
4867 ),
4868 'md-unit' => 'px',
4869 'sm-unit' => 'px',
4870 'xs-unit' => 'px',
4871 ),
4872 'site-footer-container' => 'container-fluid',
4873 'footer-fluid-spacing' => array(
4874 'md' => array(
4875 'left' => '0',
4876 'right' => '0',
4877 ),
4878 'sm' => array(
4879 'left' => '',
4880 'right' => '',
4881 ),
4882 'xs' => array(
4883 'left' => '',
4884 'right' => '',
4885 ),
4886 'md-unit' => 'px',
4887 'sm-unit' => 'px',
4888 'xs-unit' => 'px',
4889 ),
4890 'site-layout-container' => 'container-fluid',
4891 'site-fluid-spacing' => array(
4892 'md' => array(
4893 'left' => '0',
4894 'right' => '0',
4895 ),
4896 'sm' => array(
4897 'left' => '',
4898 'right' => '',
4899 ),
4900 'xs' => array(
4901 'left' => '',
4902 'right' => '',
4903 ),
4904 'md-unit' => 'px',
4905 'sm-unit' => 'px',
4906 'xs-unit' => 'px',
4907 ),
4908 'site-page-container' => 'container-fluid',
4909 'page-fluid-spacing' => array(
4910 'md' => array(
4911 'left' => '0',
4912 'right' => '0',
4913 ),
4914 'sm' => array(
4915 'left' => '',
4916 'right' => '',
4917 ),
4918 'xs' => array(
4919 'left' => '',
4920 'right' => '',
4921 ),
4922 'md-unit' => 'px',
4923 'sm-unit' => 'px',
4924 'xs-unit' => 'px',
4925 ),
4926 'site-page-container' => '',
4927 'site-posts-container' => '',
4928 'site-archive-container' => '',
4929 );
4930
4931 add_option( 'nxt-theme-options', $nexter_setting );
4932 }
4933
4934 $response = array(
4935 'message' => esc_html__( 'Theme Setting updated', 'wdesignkit' ),
4936 'description' => esc_html__( 'Theme Setting updated', 'wdesignkit' ),
4937 'success' => true,
4938 );
4939
4940 wp_send_json( $response );
4941 wp_die();
4942 }
4943
4944 /**
4945 * change site setting for import kit
4946 *
4947 * @since 2.0.0
4948 */
4949 protected function update_site_setting() {
4950 $temp_id = isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : '';
4951 $shop_id = isset( $_POST['shop_id'] ) ? sanitize_text_field( wp_unslash( $_POST['shop_id'] ) ) : '';
4952 $temp_type = isset( $_POST['temp_type'] ) ? sanitize_text_field( wp_unslash( $_POST['temp_type'] ) ) : 'page';
4953 $site_name = isset( $_POST['site_name'] ) ? sanitize_text_field( wp_unslash( $_POST['site_name'] ) ) : '';
4954 $site_tagline = isset( $_POST['site_tagline'] ) ? sanitize_text_field( wp_unslash( $_POST['site_tagline'] ) ) : '';
4955
4956 $this->wdkit_nxt_thembuilder_update();
4957
4958 if ( ! empty( $shop_id ) ) {
4959 update_option( 'woocommerce_shop_page_id', $shop_id );
4960 }
4961
4962 if ( $temp_id ) {
4963 update_option( 'show_on_front', $temp_type );
4964 update_option( 'page_on_front', $temp_id );
4965
4966 if ( ! empty( $site_name ) ) {
4967 update_option( 'blogname', $site_name );
4968 }
4969
4970 if ( ! empty( $site_tagline ) ) {
4971 update_option( 'blogdescription', $site_tagline );
4972 }
4973
4974 $response = array(
4975 'message' => esc_html__( 'Site link updated', 'wdesignkit' ),
4976 'description' => esc_html__( 'Site link updated', 'wdesignkit' ),
4977 'site_link' => get_site_url(),
4978 'success' => true,
4979 );
4980 } else {
4981 $response = array(
4982 'message' => esc_html__( 'Site not found', 'wdesignkit' ),
4983 'description' => esc_html__( 'Site not found', 'wdesignkit' ),
4984 'success' => false,
4985 );
4986 }
4987
4988 wp_send_json( $response );
4989 wp_die();
4990 }
4991
4992 /**
4993 * update theme builder
4994 *
4995 * @since 2.0.4
4996 */
4997 public function wdkit_nxt_thembuilder_update() {
4998
4999 $page_information = isset( $_POST['page_information'] ) ? sanitize_text_field( wp_unslash( $_POST['page_information'] ) ) : '';
5000 $page_information = json_decode( $page_information, true );
5001
5002 if ( ! empty( $page_information ) && is_array( $page_information ) ) {
5003
5004 // Every page and attachment now exists, so resolve any image ID the per-page
5005 // pass could not (siblings import concurrently and share icons).
5006 $this->wdkit_sweep_attachment_ids( wp_list_pluck( $page_information, 'inserted_id' ) );
5007
5008 // Step 1: banavo mapping [ old_id => new_id ]
5009 $id_mapping = array();
5010 foreach ( $page_information as $page_info ) {
5011 if ( ! empty( $page_info['old_page_id'] ) ) {
5012 $id_mapping[ $page_info['old_page_id'] ] = $page_info['inserted_id'];
5013 }
5014 }
5015
5016 // Step 2: loop karo and update exclude
5017 foreach ( $page_information as $page_info ) {
5018
5019 $post_id = $page_info['inserted_id'] ?? '';
5020 $old_post_id = $page_info['old_page_id'] ?? '';
5021 $post_type = $page_info['post_type'] ?? '';
5022
5023 if ( empty( $old_post_id ) ) {
5024 continue; // only update where old id exists
5025 }
5026
5027 if ( $post_type != 'nxt_builder' ) {
5028 continue;
5029 }
5030
5031 $include_specific = get_post_meta( $post_id, 'nxt-hooks-layout-specific', true );
5032 if ( ! empty( $include_specific ) && is_array( $include_specific ) ) {
5033
5034 foreach ( $include_specific as $key => $val ) {
5035
5036 // check karo ke koi old_id ka post match kare che ke nahi
5037 foreach ( $id_mapping as $old_id => $new_id ) {
5038 $search = 'post-' . $old_id;
5039 $replace = 'post-' . $new_id;
5040
5041 if ( $val === $search ) {
5042 $include_specific[ $key ] = $replace;
5043 }
5044 }
5045 }
5046
5047 // save back updated array
5048 update_post_meta( $post_id, 'nxt-hooks-layout-specific', $include_specific );
5049 }
5050
5051 // Get exclude meta
5052 $exclude_specific = get_post_meta( $post_id, 'nxt-hooks-layout-exclude-specific', true );
5053 if ( ! empty( $exclude_specific ) && is_array( $exclude_specific ) ) {
5054
5055 foreach ( $exclude_specific as $key => $val ) {
5056
5057 // check karo ke koi old_id ka post match kare che ke nahi
5058 foreach ( $id_mapping as $old_id => $new_id ) {
5059 $search = 'post-' . $old_id;
5060 $replace = 'post-' . $new_id;
5061
5062 if ( $val === $search ) {
5063 $exclude_specific[ $key ] = $replace;
5064 }
5065 }
5066 }
5067
5068 // save back updated array
5069 update_post_meta( $post_id, 'nxt-hooks-layout-exclude-specific', $exclude_specific );
5070 }
5071 }
5072 }
5073 }
5074
5075 /**
5076 *
5077 * select team image for import kit
5078 *
5079 * @since 2.2.2
5080 */
5081 public function wdkit_check_user_credit() {
5082 $array_data = array(
5083 'token' => isset( $_POST['token'] ) ? sanitize_text_field( $_POST['token'] ) : '',
5084 );
5085
5086 $response = $this->wkit_api_call( $array_data, 'ai/credits/get' );
5087 $success = ! empty( $response['success'] ) ? $response['success'] : false;
5088
5089 if ( empty( $success ) ) {
5090 $response = array(
5091 'success' => false,
5092 'message' => esc_html__( 'Data Not Found', 'wdesignkit' ),
5093 'description' => esc_html__( 'Data not found', 'wdesignkit' ),
5094 );
5095
5096 wp_send_json( $response );
5097 wp_die();
5098 }
5099
5100 $response = json_decode( wp_json_encode( $response['data'] ), true );
5101
5102 $this->wdkit_cache_cloud_usage( $response );
5103
5104 wp_send_json( $response );
5105 wp_die();
5106 }
5107
5108 /**
5109 * Caches the storage / credit figures this response carried.
5110 *
5111 * This handler is the ONLY place those numbers ever exist on the site: the cloud endpoint
5112 * authenticates with a user token that only a logged-in dashboard request carries, so the
5113 * analytics heartbeat — which runs on cron with no user at all — can never fetch them itself.
5114 * Caching them here is what lets Posimyth_Tracker_WDK report them, and it reports the cache's
5115 * age alongside so a stale reading is recognisable as one.
5116 *
5117 * Field names are probed rather than assumed: the cloud has renamed these before, and the
5118 * licence ability already carries six spellings of its own key field for the same reason. An
5119 * unrecognised shape simply caches nothing rather than storing a wrong number.
5120 *
5121 * Only the figures are kept. No token, no account id, no email — the analytics consent copy
5122 * promises non-sensitive data only, and this is read by the payload builder.
5123 *
5124 * @since 2.6.4
5125 *
5126 * @param mixed $data Decoded `data` object from the credits endpoint.
5127 * @return void
5128 */
5129 private function wdkit_cache_cloud_usage( $data ) {
5130 if ( ! is_array( $data ) ) {
5131 return;
5132 }
5133
5134 $pick = static function ( $source, array $fields ) {
5135 foreach ( $fields as $field ) {
5136 if ( isset( $source[ $field ] ) && is_numeric( $source[ $field ] ) ) {
5137 return (float) $source[ $field ];
5138 }
5139 }
5140 return null;
5141 };
5142
5143 $usage = array(
5144 'storage_used' => $pick( $data, array( 'used_storage', 'storage_used', 'used_space' ) ),
5145 'storage_total' => $pick( $data, array( 'total_storage', 'storage_total', 'storage', 'total_space' ) ),
5146 'credit_used' => $pick( $data, array( 'used_credit', 'credit_used', 'used_credits' ) ),
5147 'credit_total' => $pick( $data, array( 'total_credit', 'credit_total', 'credits', 'real_credit' ) ),
5148 );
5149
5150 $usage = array_filter(
5151 $usage,
5152 static function ( $value ) {
5153 return null !== $value;
5154 }
5155 );
5156
5157 if ( empty( $usage ) ) {
5158 return;
5159 }
5160
5161 $usage['cached_at'] = gmdate( 'Y-m-d H:i:s' );
5162
5163 // Not autoloaded: read once a week by the heartbeat, never on a front-end request.
5164 update_option( 'wdkit_cloud_usage', $usage, false );
5165 }
5166
5167 public function wdkit_nxt_thembuilder_reset() {
5168 $post_id = isset( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : '';
5169 $sections_layout = get_post_meta( $post_id, 'nxt-hooks-layout-sections', true );
5170
5171 if ( ( ! empty( $sections_layout ) && ( $sections_layout == 'header' || $sections_layout == 'footer' || $sections_layout == 'breadcrumb' || $sections_layout == 'hooks' ) ) ) {
5172 if ( get_post_meta( $post_id, 'nxt-add-display-rule' ) ) {
5173 delete_post_meta( $post_id, 'nxt-add-display-rule' );
5174 }
5175
5176 if ( get_post_meta( $post_id, 'nxt-hooks-layout-specific' ) ) {
5177 update_post_meta( $post_id, 'nxt-hooks-layout-specific', '' );
5178 }
5179
5180 if ( get_post_meta( $post_id, 'nxt-exclude-display-rule' ) ) {
5181 update_post_meta( $post_id, 'nxt-exclude-display-rule', '' );
5182 }
5183
5184 if ( get_post_meta( $post_id, 'nxt-hooks-layout-exclude-specific' ) ) {
5185 update_post_meta( $post_id, 'nxt-hooks-layout-exclude-specific', '' );
5186 }
5187 }
5188 }
5189
5190
5191 /**
5192 * Share with Me Template and widgets
5193 *
5194 * @since 1.0.0
5195 */
5196 protected function wdkit_shared_with_me() {
5197 $data = isset( $_POST['api_info'] ) ? json_decode( stripslashes( sanitize_text_field( wp_unslash( $_POST['api_info'] ) ) ) ) : '';
5198
5199 $array_data = array(
5200 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
5201 'type' => isset( $data->type ) ? sanitize_text_field( wp_unslash( $data->type ) ) : '',
5202 'ParPage' => isset( $data->par_page ) ? (int) $data->par_page : 12,
5203 'CurrentPage' => isset( $data->current_page ) ? (int) $data->current_page : 1,
5204 'builder' => isset( $data->builder ) ? sanitize_text_field( wp_unslash( $data->builder ) ) : '',
5205 );
5206
5207 $response = $this->wkit_api_call( $array_data, 'shared_with_me' );
5208 $success = ! empty( $response['success'] ) ? $response['success'] : false;
5209
5210 wp_send_json( $response );
5211 wp_die();
5212 }
5213
5214 /**
5215 * Add new WorkSpace
5216 *
5217 * @since 1.0.0
5218 */
5219 protected function wdkit_manage_workspace() {
5220 $args = $this->wdkit_parse_args( $_POST );
5221
5222 $user_email = ! empty( $args['email'] ) ? strtolower( sanitize_email( $args['email'] ) ) : '';
5223 $current_wid = ! empty( $_POST['current_wid'] ) ? strtolower( sanitize_text_field( $_POST['current_wid'] ) ) : '';
5224
5225 $args['current_wid'] = $current_wid;
5226
5227 if ( empty( $user_email ) ) {
5228 $response = array(
5229 'message' => $this->e_msg_login,
5230 'description' => $this->e_desc_login,
5231 'success' => false,
5232 );
5233
5234 wp_send_json( $response );
5235 wp_die();
5236 }
5237
5238 $args['token'] = $this->wdkit_login_user_token( $user_email );
5239 unset( $user_email );
5240
5241 $response = WDesignKit_Data_Query::get_data( 'manage_workspace', $args );
5242
5243 return $response;
5244 }
5245
5246 /**
5247 *
5248 * It is Use for manage workspace
5249 *
5250 * @since 1.0.0
5251 */
5252 protected function wdkit_manage_widget_workspace() {
5253 $workspace_info = isset( $_POST['workspace_info'] ) ? sanitize_text_field( wp_unslash( $_POST['workspace_info'] ) ) : array();
5254 $data = isset( $workspace_info ) ? json_decode( stripslashes( $workspace_info ) ) : array();
5255
5256 $array_data = array(
5257 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
5258 'wstype' => isset( $data->type ) ? sanitize_text_field( $data->type ) : '',
5259 'widget_id' => isset( $data->widget_id ) ? (int) $data->widget_id : '',
5260 'wid' => isset( $data->wid ) ? (int) $data->wid : '',
5261 'current_wid' => isset( $data->current_wid ) ? (int) $data->current_wid : '',
5262 );
5263
5264 $response = $this->wkit_api_call( $array_data, 'manage_workspace' );
5265
5266 wp_send_json( $response['data'] );
5267 wp_die();
5268 }
5269
5270 /**
5271 *
5272 * It is Use for manage api key page
5273 *
5274 * @since 1.0.0
5275 */
5276 protected function wdkit_activate_key() {
5277 $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
5278 $response = '';
5279
5280 // Bug C fix: variable was $user_email but only $email is set above — always triggered empty() guard.
5281 if ( empty( $email ) ) {
5282 $response = array(
5283 'message' => $this->e_msg_login,
5284 'description' => $this->e_desc_login,
5285 'success' => false,
5286 );
5287
5288 wp_send_json( $response );
5289 wp_die();
5290 }
5291
5292 $token = $this->wdkit_login_user_token( $email );
5293 $apikey = isset( $_POST['apikey'] ) ? sanitize_key( wp_unslash( $_POST['apikey'] ) ) : '';
5294 $product = isset( $_POST['product'] ) ? sanitize_text_field( wp_unslash( $_POST['product'] ) ) : '';
5295 $product_action = isset( $_POST['product_action'] ) ? sanitize_text_field( wp_unslash( $_POST['product_action'] ) ) : 'activate';
5296 if ( ! empty( $token ) && ! empty( $product ) && ! empty( $product_action ) ) {
5297 $args = array(
5298 'token' => $token,
5299 'product' => $product,
5300 'product_action' => $product_action,
5301 );
5302
5303 if ( 'activate' === $product_action ) {
5304 $args['apikey'] = $apikey;
5305 $args['site_url'] = home_url();
5306 }
5307
5308 $response = Wdkit_Data_Hooks::get_data( 'wkit_activate_key', $args );
5309 }
5310
5311 wp_send_json( $response );
5312 wp_die();
5313 }
5314
5315 /**
5316 *
5317 * Get list local Widget List
5318 *
5319 * @since 1.0.0
5320 */
5321 protected function wdkit_get_local_widgets() {
5322 $builder = array();
5323 $a_c_s_d_s_c = array();
5324 $j_s_o_n_array = array();
5325
5326 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'bricks', 'widget' ) ) {
5327 array_push( $builder, 'bricks' );
5328 }
5329
5330 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'elementor', 'widget' ) ) {
5331 array_push( $builder, 'elementor' );
5332 }
5333
5334 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'gutenberg', 'widget' ) ) {
5335 array_push( $builder, 'gutenberg' );
5336 }
5337
5338 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'gutenberg_core', 'widget' ) ) {
5339 array_push( $builder, 'gutenberg_core' );
5340 }
5341
5342 foreach ( $builder as $key => $name ) {
5343 $elementor_dir = WDKIT_BUILDER_PATH . '/' . $name;
5344
5345 if ( ! empty( $elementor_dir ) && is_dir( $elementor_dir ) ) {
5346 $elementor_list = scandir( $elementor_dir );
5347 $elementor_list = array_diff( $elementor_list, array( '.', '..' ) );
5348
5349 if ( ! empty( $elementor_list ) ) {
5350 foreach ( $elementor_list as $key => $value ) {
5351 $a_c_s_d_s_c[ filemtime( "{$elementor_dir}/{$value}" ) . $key ]['data'] = $value;
5352 $a_c_s_d_s_c[ filemtime( "{$elementor_dir}/{$value}" ) . $key ]['builder'] = $name;
5353 }
5354 }
5355 }
5356 }
5357
5358 ksort( $a_c_s_d_s_c );
5359 $a_c_s_d_s_c = array_reverse( $a_c_s_d_s_c );
5360
5361 foreach ( $a_c_s_d_s_c as $key => $value ) {
5362 $elementor_dir = WDKIT_BUILDER_PATH . '/' . $value['builder'];
5363
5364 if ( file_exists( "{$elementor_dir}/{$value['data']}" ) && is_dir( "{$elementor_dir}/{$value['data']}" ) ) {
5365 $sub_dir = scandir( "{$elementor_dir}/{$value['data']}" );
5366 $sub = array_diff( $sub_dir, array( '.', '..' ) );
5367
5368 foreach ( $sub as $sub_dir_value ) {
5369 $file = new SplFileInfo( $sub_dir_value );
5370 $check_ext = $file->getExtension();
5371 $ext = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
5372
5373 if ( 'json' === $ext ) {
5374 $widget1 = WDKIT_BUILDER_PATH . "/{$value['builder']}/{$value['data']}/{$sub_dir_value}";
5375 $filedata = wp_json_file_decode( $widget1 );
5376 $decode_data = json_decode( wp_json_encode( $filedata ), true );
5377 array_push( $j_s_o_n_array, $decode_data['widget_data'] );
5378 }
5379 }
5380 }
5381 }
5382
5383 return $j_s_o_n_array;
5384 }
5385
5386 /**
5387 *
5388 * It is Use for manage widget category.
5389 *
5390 * @since 1.0.0
5391 */
5392 protected function wdkit_manage_widget_category() {
5393 $data = isset( $_POST['info'] ) ? sanitize_text_field( wp_unslash( $_POST['info'] ) ) : '';
5394 $data = json_decode( stripslashes( $data ) );
5395
5396 $type = isset( $data->manage_type ) ? sanitize_text_field( wp_unslash( $data->manage_type ) ) : '';
5397 $wkit_builder_option = get_option( 'wkit_builder' );
5398
5399 if ( empty( $wkit_builder_option ) ) {
5400 add_option( 'wkit_builder', array( 'WDesignKit' ), '', 'yes' );
5401 }
5402
5403 if ( 'get' === $type ) {
5404 if ( ! in_array( 'WDesignKit', $wkit_builder_option ) ) {
5405 update_option( 'wkit_builder', array( 'WDesignKit' ) );
5406 }
5407 } elseif ( 'update' === $type ) {
5408 $list = isset( $data->category_list ) ? $data->category_list : array();
5409 $list = array_unique( $list );
5410 $list = array_values( $list );
5411
5412 if ( ! empty( $list ) ) {
5413 update_option( 'wkit_builder', $list );
5414 }
5415 }
5416
5417 wp_send_json( get_option( 'wkit_builder' ) );
5418 }
5419
5420 /**
5421 * Get Workspace data
5422 *
5423 * @since 2.2.5
5424 */
5425 public function wdkit_get_workspace_data() {
5426
5427 $wid = isset( $_POST['wid'] ) ? sanitize_text_field( $_POST['wid'] ) : '';
5428 $token = isset( $_POST['token'] ) ? sanitize_text_field( $_POST['token'] ) : '';
5429
5430 if ( empty( $wid ) ) {
5431 return array(
5432 'success' => false,
5433 'message' => esc_html__( 'Workspace ID Not Found', 'wdesignkit' ),
5434 'description' => esc_html__( 'Workspace ID is required', 'wdesignkit' ),
5435 );
5436 }
5437
5438 if ( empty( $token ) ) {
5439 return array(
5440 'success' => false,
5441 'message' => esc_html__( 'Token Not Found', 'wdesignkit' ),
5442 'description' => esc_html__( 'Token is required', 'wdesignkit' ),
5443 );
5444 }
5445
5446 $args = array(
5447 'token' => $token,
5448 'wid' => $wid,
5449 );
5450
5451 $this->wdkit_api = $this->wdkit_api_v2;
5452
5453 $url = "workspace/{$wid}/get";
5454
5455 $response = $this->wkit_api_call( $args, $url );
5456
5457 wp_send_json( $response['data'] );
5458 wp_die();
5459 }
5460
5461 /**
5462 *
5463 * Custom_upload_dir
5464 *
5465 * @since 1.0.0
5466 *
5467 * @param array $upload store data.
5468 */
5469 public function custom_upload_dir( $upload ) {
5470 // Specify the path to your custom upload directory.
5471 if ( isset( $this->widget_folder_u_r_l ) && ! empty( $this->widget_folder_u_r_l ) ) {
5472
5473 // Set the custom directory as the upload path.
5474 $upload['path'] = $this->widget_folder_u_r_l;
5475 // Set the URL for the uploaded file.
5476 $upload['url'] = $upload['baseurl'] . $upload['subdir'];
5477 }
5478
5479 return $upload;
5480 }
5481
5482 /**
5483 *
5484 * It is Use for delete widget from server
5485 *
5486 * @since 1.0.0
5487 */
5488 protected function wkit_widget_json() {
5489 $widget_type = ! empty( $_POST['widget_type'] ) ? wp_unslash( $_POST['widget_type'] ) : '';
5490 $folder_name = ! empty( $_POST['folder_name'] ) ? wp_unslash( $_POST['folder_name'] ) : '';
5491 $file_name = ! empty( $_POST['file_name'] ) ? ( wp_unslash( $_POST['file_name'] ) ) : '';
5492
5493 if ( empty( $widget_type ) || empty( $folder_name ) || empty( $file_name ) ) {
5494 return array(
5495 'success' => false,
5496 'message' => esc_html__( 'Widget JSON not found', 'wdesignkit' ),
5497 'description' => esc_html__( 'widget JSON file not found.', 'wdesignkit' ),
5498 );
5499 }
5500
5501 // Read-side twin of the write and delete traversals fixed in 86d41cckh / 86d41ccz2: all
5502 // three segments arrive from $_POST with only wp_unslash() applied — which strips
5503 // nothing path-relevant — so "../" in any of them walked out of the builder directory
5504 // and this handler returned the decoded contents of any .json file the web server user
5505 // could read (CWE-22, ClickUp 86d41zaun).
5506 $safe_path = wdesignkit_widget_path_guard( $widget_type, $folder_name, $file_name );
5507
5508 if ( false === $safe_path || '' === $safe_path['folder'] || '' === $safe_path['file'] ) {
5509 return array(
5510 'success' => false,
5511 'message' => esc_html__( 'Widget JSON not found', 'wdesignkit' ),
5512 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
5513 );
5514 }
5515
5516 $json_path = $safe_path['base'];
5517
5518 // Re-check the resolved file: the component guard above cannot see a symlink. Returns
5519 // false for a path that does not exist, which is the same answer we want anyway.
5520 if ( ! wdesignkit_path_inside_builder_dir( "$json_path.json" ) ) {
5521 return array(
5522 'success' => false,
5523 'message' => esc_html__( 'Widget JSON not found', 'wdesignkit' ),
5524 'description' => esc_html__( 'widget JSON file not found.', 'wdesignkit' ),
5525 );
5526 }
5527
5528 $json_data = wp_json_file_decode( "$json_path.json" );
5529 if ( ! empty( $json_data ) ) {
5530 $result = (object) array(
5531 'success' => true,
5532 'data' => $json_data,
5533 'message' => esc_html__( 'Widget get Successfully', 'wdesignkit' ),
5534 'description' => esc_html__( 'Widget JSON get Successfully', 'wdesignkit' ),
5535 );
5536 } else {
5537 $result = (object) array(
5538 'success' => false,
5539 'message' => esc_html__( 'Widget not get', 'wdesignkit' ),
5540 'description' => esc_html__( 'Widget JSON not get', 'wdesignkit' ),
5541 );
5542 }
5543
5544 wp_send_json( $result );
5545 wp_die();
5546 }
5547
5548 /**
5549 *
5550 * It is Use for download widget from widget listing.
5551 *
5552 * @since 1.0.0
5553 */
5554 protected function wdkit_download_widget() {
5555 $data = ! empty( $_POST['widget_info'] ) ? $this->wdkit_extract_post_field( $_POST, 'widget_info', 'none' ) : '';
5556 $data = json_decode( stripslashes( $data ) );
5557
5558 $array_data = array(
5559 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
5560 'type' => isset( $data->type ) ? sanitize_text_field( $data->type ) : '',
5561 'w_unique' => isset( $data->w_uniq ) ? sanitize_text_field( $data->w_uniq ) : '',
5562 // Bug F fix: u_id (widget owner's user ID) was missing — cloud cannot locate the widget without it.
5563 'u_id' => isset( $data->u_id ) ? sanitize_text_field( $data->u_id ) : '',
5564 );
5565
5566 $response = $this->wkit_api_call( $array_data, 'save_widget' );
5567 $success = ! empty( $response['success'] ) ? $response['success'] : false;
5568
5569 if ( empty( $success ) ) {
5570 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
5571 $result = (object) array(
5572 'success' => false,
5573 'message' => $massage,
5574 'description' => esc_html__( ' Widget not Downloaded', 'wdesignkit' ),
5575 );
5576
5577 wp_send_json( $result );
5578 wp_die();
5579 }
5580
5581 $response = json_decode( wp_json_encode( $response['data'] ), true );
5582
5583 if ( empty( $response ) || empty( $response['data'] ) ) {
5584 $message = ! empty( $response['message'] ) ? $response['message'] : 'No Response Found';
5585 $description = ! empty( $response['description'] ) ? $response['description'] : 'Widget not Downloaded';
5586
5587 $result = (object) array(
5588 'success' => false,
5589 'message' => esc_html( $message ),
5590 'description' => esc_html( $description ),
5591 );
5592
5593 wp_send_json( $result );
5594 wp_die();
5595 }
5596
5597 $img_url = ! empty( $response['data']['image'] ) ? $response['data']['image'] : '';
5598 $json_data = ! empty( $response['data']['json'] ) ? json_decode( $response['data']['json'], true ) : '';
5599
5600 // Bug E fix (part 1): $responce was a typo of $response — sent undefined variable (null) to frontend.
5601 if ( empty( $response['success'] ) ) {
5602 wp_send_json( $response );
5603 wp_die();
5604 }
5605
5606 if ( empty( $img_url ) && empty( $json_data ) ) {
5607 $responce = (object) array(
5608 'success' => false,
5609 'message' => esc_html__( 'No Response Found', 'wdesignkit' ),
5610 'description' => esc_html__( 'Widget not Downloaded', 'wdesignkit' ),
5611 );
5612
5613 wp_send_json( $responce );
5614 wp_die();
5615 }
5616
5617 include_once ABSPATH . 'wp-admin/includes/file.php';
5618 \WP_Filesystem();
5619 global $wp_filesystem;
5620
5621 if ( ! is_array( $json_data ) ) {
5622 $json_data = json_decode( $json_data, true );
5623 }
5624
5625 // Sanitize as filenames before use in the widget path (CWE-22): sanitize_file_name()
5626 // on name/id and sanitize_key() + allowlist on the builder strip path separators and
5627 // dots so a crafted cloud response cannot escape WDKIT_BUILDER_PATH.
5628 $title = ! empty( $json_data['widget_data']['widgetdata']['name'] ) ? sanitize_file_name( $json_data['widget_data']['widgetdata']['name'] ) : '';
5629 $builder = ! empty( $json_data['widget_data']['widgetdata']['type'] ) ? sanitize_key( $json_data['widget_data']['widgetdata']['type'] ) : '';
5630 $w_uniq = ! empty( $json_data['widget_data']['widgetdata']['widget_id'] ) ? sanitize_file_name( $json_data['widget_data']['widgetdata']['widget_id'] ) : '';
5631
5632 $allowed_builders = array( 'elementor', 'gutenberg', 'gutenberg_core', 'bricks' );
5633 if ( '' === $title || '' === $w_uniq || ! in_array( $builder, $allowed_builders, true ) ) {
5634 $responce = (object) array(
5635 'success' => false,
5636 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
5637 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
5638 );
5639
5640 wp_send_json( $responce );
5641 wp_die();
5642 }
5643
5644 // Canonical helpers replace spaces BEFORE sanitize_file_name(). $title above is
5645 // already sanitized, which collapsed spaces to hyphens and left the underscore pass
5646 // with nothing to do — a multi-word title wrote "My-Widget_id.json" next to the
5647 // "My_Widget_id.php" the builder's save path writes. The loader pairs the two by
5648 // swapping .php for .json, so the widget was silently dropped (ClickUp 86d41cck5).
5649 $folder_name = wdesignkit_widget_folder_name( $title, $w_uniq );
5650 $file_name = wdesignkit_widget_file_name( $title, $w_uniq );
5651 $builder_type_path = WDKIT_BUILDER_PATH . "/{$builder}/";
5652
5653 if ( ! is_dir( $builder_type_path ) ) {
5654 wp_mkdir_p( $builder_type_path );
5655 }
5656
5657 if ( ! is_dir( $builder_type_path . $folder_name ) ) {
5658 wp_mkdir_p( $builder_type_path . $folder_name );
5659 }
5660
5661 if ( ! empty( $img_url ) ) {
5662 // SSRF guard (CWE-918): validate the resolved host before fetching.
5663 $img_body = wdesignkit_safe_remote_get( $img_url );
5664 if ( ! is_wp_error( $img_body ) ) {
5665 // The remote extension was written verbatim here, so a cloud response naming a
5666 // ".php" image put executable PHP in the builder directory (CWE-434,
5667 // ClickUp 86d41cczd). An empty return means the bytes are not an image.
5668 $img_ext = wdesignkit_safe_image_extension( $img_url, $img_body['body'] );
5669
5670 if ( '' !== $img_ext ) {
5671 $wp_filesystem->put_contents( WDKIT_BUILDER_PATH . "/$builder/$folder_name/$file_name.$img_ext", $img_body['body'] );
5672 $json_data['widget_data']['widgetdata']['w_image'] = WDKIT_SERVER_PATH . "/$builder/$folder_name/$file_name.$img_ext";
5673 }
5674 }
5675 }
5676
5677 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
5678 wdesignkit_invalidate_widget_registry( $builder );
5679 }
5680
5681 // Bug E fix (part 2): success was hardcoded false on the successful download path — always reported failure.
5682 $result = (object) array(
5683 'success' => true,
5684 'message' => ! empty( $response['message'] ) ? $response['message'] : esc_html__( 'no message', 'wdesignkit' ),
5685 'description' => '',
5686 'json' => wp_json_encode( $json_data ),
5687 );
5688
5689 wp_send_json( $result );
5690 wp_die();
5691 }
5692
5693 /**
5694 *
5695 * It is Use for sync widget to server
5696 *
5697 * @since 1.0.0
5698 */
5699 protected function wdkit_add_widget() {
5700 $data = ! empty( $_POST['widget_info'] ) ? $this->wdkit_extract_post_field( $_POST, 'widget_info', 'none' ) : '';
5701 $data = base64_decode( $data );
5702 $data = json_decode( $data );
5703
5704 $title = isset( $data->title ) ? sanitize_text_field( $data->title ) : '';
5705 $builder = isset( $data->builder ) ? sanitize_text_field( $data->builder ) : '';
5706 $w_uniq = isset( $data->w_uniq ) ? sanitize_text_field( $data->w_uniq ) : '';
5707 $w_image = isset( $data->w_image ) ? esc_url_raw( $data->w_image ) : '';
5708
5709 if ( ! empty( $w_image ) ) {
5710 $w_image = str_replace( '\\', '', $w_image );
5711 // SSRF guard (CWE-918): validate the resolved host before fetching.
5712 $fetched = wdesignkit_safe_remote_get( $w_image );
5713 $w_image = is_wp_error( $fetched ) ? '' : wp_remote_retrieve_body( $fetched );
5714 }
5715
5716 $array_data = array(
5717 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
5718 'type' => isset( $data->type ) ? sanitize_text_field( $data->type ) : '',
5719 'title' => isset( $data->title ) ? sanitize_text_field( $data->title ) : '',
5720 'content' => isset( $data->content ) ? sanitize_text_field( $data->content ) : '',
5721 'builder' => isset( $data->builder ) ? sanitize_text_field( $data->builder ) : '',
5722 'w_data' => isset( $data->w_data ) ? $data->w_data : '',
5723 'w_unique' => isset( $data->w_uniq ) ? sanitize_text_field( $data->w_uniq ) : '',
5724 'w_image' => $w_image,
5725 'w_imgext' => isset( $data->w_imgext ) ? sanitize_text_field( $data->w_imgext ) : '',
5726 'w_version' => isset( $data->w_version ) ? $data->w_version : '',
5727 'w_updates' => ! empty( $data->w_updates ) ? serialize( $data->w_updates ) : serialize( array() ),
5728 'r_id' => isset( $data->r_id ) ? $data->r_id : 0,
5729 'unique_id' => get_option( 'wdkit_unique_id' ) ?? '',
5730 );
5731
5732 $response = $this->wkit_api_call( $array_data, 'save_widget' );
5733 $success = ! empty( $response['success'] ) ? $response['success'] : false;
5734
5735 if ( empty( $success ) ) {
5736 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
5737
5738 $result = (object) array(
5739 'success' => false,
5740 'message' => $massage,
5741 'description' => esc_html__( 'Widget Not Added', 'wdesignkit' ),
5742 );
5743
5744 wp_send_json( $result );
5745 wp_die();
5746 }
5747
5748 $res = ! empty( $response['data'] ) ? $response['data'] : array();
5749
5750 $response = json_decode( wp_json_encode( $res ), true );
5751 $img_url = ! empty( $response['data']['imgurl'] ) ? $response['data']['imgurl'] : '';
5752
5753 if ( ! empty( $img_url ) && 'error' !== $res ) {
5754
5755 // SSRF guard (CWE-918): validate the resolved host before fetching.
5756 $img_body = wdesignkit_safe_remote_get( $img_url );
5757 if ( ! is_wp_error( $img_body ) ) {
5758 // Verified against the payload rather than trusted from the URL (CWE-434,
5759 // ClickUp 86d41cczd); '' means the bytes are not an image we accept.
5760 $img_ext = wdesignkit_safe_image_extension( $img_url, $img_body['body'] );
5761 include_once ABSPATH . 'wp-admin/includes/file.php';
5762 \WP_Filesystem();
5763 global $wp_filesystem;
5764 // Canonical helpers, so the JSON read and the image write here address the same
5765 // base name every other writer uses (ClickUp 86d41cck5). They also apply
5766 // sanitize_file_name(), which $title and $w_uniq had not been through.
5767 $folder_name = wdesignkit_widget_folder_name( $title, $w_uniq );
5768 $file_name = wdesignkit_widget_file_name( $title, $w_uniq );
5769
5770 // $builder reaches here with only sanitize_text_field() applied and no
5771 // allowlist, so it was a live traversal segment in this path (CWE-22,
5772 // ClickUp 86d41cckh). Unlike the download handler earlier in this file, this
5773 // one had neither the builder allowlist nor a containment check.
5774 $safe_path = wdesignkit_widget_path_guard( $builder, $folder_name, $file_name );
5775 if ( false === $safe_path || ! wdesignkit_path_inside_builder_dir( $safe_path['dir'] ) ) {
5776 wp_send_json(
5777 (object) array(
5778 'success' => false,
5779 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
5780 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
5781 )
5782 );
5783 wp_die();
5784 }
5785
5786 $builder = $safe_path['builder'];
5787 $file_path = $safe_path['base'];
5788
5789 $u_r_l = wp_json_file_decode( "$file_path.json" );
5790
5791 if ( '' !== $img_ext ) {
5792 $u_r_l->widget_data->widgetdata->w_image = WDKIT_SERVER_PATH . "/$builder/$folder_name/$file_name.$img_ext";
5793 $wp_filesystem->put_contents( "$file_path.$img_ext", $img_body['body'] );
5794 }
5795
5796 $wp_filesystem->put_contents( "$file_path.json", wp_json_encode( $u_r_l ) );
5797 }
5798 }
5799
5800 wp_send_json( $response );
5801 wp_die();
5802 }
5803
5804 /**
5805 *
5806 * It is Use for manage favourite widget
5807 *
5808 * @since 1.0.0
5809 */
5810 protected function wdkit_favourite_widget() {
5811 $data = isset( $_POST['widget_info'] ) ? json_decode( stripslashes( sanitize_text_field( wp_unslash( $_POST['widget_info'] ) ) ) ) : '';
5812 $array_data = array(
5813 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
5814 'type' => isset( $data->type ) ? sanitize_text_field( $data->type ) : '',
5815 'w_unique' => isset( $data->w_uniq ) ? sanitize_text_field( $data->w_uniq ) : '',
5816 );
5817
5818 $response = $this->wkit_api_call( $array_data, 'save_widget' );
5819 $success = ! empty( $response['success'] ) ? $response['success'] : false;
5820
5821 if ( empty( $success ) ) {
5822 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
5823
5824 $result = (object) array(
5825 'success' => false,
5826 'message' => $massage,
5827 'description' => '',
5828 );
5829
5830 wp_send_json( $result );
5831 wp_die();
5832 }
5833
5834 wp_send_json( $response );
5835 wp_die();
5836 }
5837
5838 /**
5839 * It is Used Setting Panel Defalut Data Get.
5840 *
5841 * @since 1.0.0
5842 */
5843 protected function wdkit_setting_panel() {
5844 $event = ! empty( $_POST['event'] ) ? sanitize_text_field( wp_unslash( $_POST['event'] ) ) : 'get';
5845
5846 if ( 'get' === $event ) {
5847 return self::wkit_get_settings_panel();
5848 } elseif ( 'set' === $event ) {
5849 $data = ! empty( $_POST['data'] ) ? stripslashes( sanitize_text_field( wp_unslash( $_POST['data'] ) ) ) : array();
5850
5851 $data = json_decode( $data, true );
5852
5853 update_option( 'wkit_settings_panel', $data );
5854 return self::wkit_get_settings_panel();
5855 } else {
5856 return false;
5857 }
5858 }
5859
5860 /**
5861 * Get Setting Panal Data
5862 *
5863 * @since 1.0.0
5864 */
5865 protected static function wkit_get_settings_panel() {
5866 $new_version = '';
5867 $current_version = WDKIT_VERSION;
5868 $response = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/wdesignkit.json' );
5869
5870 if ( is_wp_error( $response ) ) {
5871 return false;
5872 }
5873
5874 $body = wp_remote_retrieve_body( $response );
5875 $data = json_decode( $body );
5876
5877 if ( isset( $data->version ) ) {
5878 $new_version = $data->version;
5879 }
5880
5881 $version_check = array();
5882
5883 if ( $new_version && version_compare( $current_version, $new_version, '<' ) ) {
5884 $version_check['success'] = true;
5885 $version_check['version'] = $new_version;
5886 } else {
5887 $version_check['success'] = false;
5888 $version_check['version'] = $new_version;
5889 }
5890
5891 $get_setting = get_option( 'wkit_settings_panel', false );
5892
5893 $setting_data = array(
5894 'builder' => isset( $get_setting['builder'] ) ? $get_setting['builder'] : true,
5895 'template' => isset( $get_setting['template'] ) ? $get_setting['template'] : true,
5896 'gutenberg_builder' => isset( $get_setting['gutenberg_builder'] ) ? $get_setting['gutenberg_builder'] : true,
5897 'gutenberg_core_builder' => isset( $get_setting['gutenberg_core_builder'] ) ? $get_setting['gutenberg_core_builder'] : false,
5898 'elementor_builder' => isset( $get_setting['elementor_builder'] ) ? $get_setting['elementor_builder'] : true,
5899 'bricks_builder' => isset( $get_setting['bricks_builder'] ) ? $get_setting['bricks_builder'] : true,
5900 'gutenberg_template' => isset( $get_setting['gutenberg_template'] ) ? $get_setting['gutenberg_template'] : true,
5901 'elementor_template' => isset( $get_setting['elementor_template'] ) ? $get_setting['elementor_template'] : true,
5902 'code_snippet' => isset( $get_setting['code_snippet'] ) ? $get_setting['code_snippet'] : true,
5903 'cross_copy_paste' => isset( $get_setting['cross_copy_paste'] ) ? $get_setting['cross_copy_paste'] : false,
5904 'cross_copy_paste_elementor' => isset( $get_setting['cross_copy_paste_elementor'] ) ? $get_setting['cross_copy_paste_elementor'] : false,
5905 'cross_copy_paste_gutenberg' => isset( $get_setting['cross_copy_paste_gutenberg'] ) ? $get_setting['cross_copy_paste_gutenberg'] : false,
5906 'cross_copy_paste_bricks' => isset( $get_setting['cross_copy_paste_bricks'] ) ? $get_setting['cross_copy_paste_bricks'] : false,
5907 'plugin_version' => $version_check,
5908 );
5909
5910 if ( isset( $get_setting['remove_db'] ) ) {
5911 $setting_data['remove_db'] = $get_setting['remove_db'];
5912 }
5913
5914 if ( isset( $get_setting['debugger_mode'] ) ) {
5915 $setting_data['debugger_mode'] = $get_setting['debugger_mode'];
5916 }
5917
5918 return $setting_data;
5919 }
5920
5921 /**
5922 * Updated White Label Data.
5923 *
5924 * @since 1.1.8
5925 */
5926 protected function wkit_white_label() {
5927
5928 $get_wl_data = ! empty( $_POST['WhiteLabelData'] ) ? wp_unslash( $_POST['WhiteLabelData'] ) : array();
5929
5930 if ( ! empty( $get_wl_data ) ) {
5931 $white_label_data = json_decode( $get_wl_data, true );
5932 $plugin_name = $white_label_data['plugin_name'];
5933 } else {
5934 $result = array(
5935 'success' => false,
5936 'message' => esc_html__( 'Data Not Found', 'wdesignkit' ),
5937 );
5938
5939 wp_send_json( $result );
5940 wp_die();
5941 }
5942
5943 if ( ! empty( $plugin_name ) ) {
5944 $get_white_label = get_option( 'wkit_white_label', false );
5945 if ( ! empty( $get_white_label ) ) {
5946 update_option( 'wkit_white_label', $white_label_data );
5947 } else {
5948 add_option( 'wkit_white_label', $white_label_data );
5949 }
5950 } else {
5951 $result = array(
5952 'success' => false,
5953 'message' => esc_html__( 'Plugin Name Not Found', 'wdesignkit' ),
5954 );
5955
5956 wp_send_json( $result );
5957 wp_die();
5958 }
5959
5960 $get_updated_data = get_option( 'wkit_white_label', false );
5961 $response = array(
5962 'message' => __( 'Data Added successfully', 'wdesignkit' ),
5963 'success' => true,
5964 'data' => $get_updated_data,
5965 );
5966
5967 wp_send_json( $response );
5968 }
5969
5970 /**
5971 * Reset White Label Data.
5972 *
5973 * @since 1.1.8
5974 */
5975 public function wkit_reset_wl() {
5976 $wl_data = get_option( 'wkit_white_label' );
5977
5978 if ( ! empty( $wl_data ) ) {
5979 delete_option( 'wkit_white_label' );
5980
5981 $result = array(
5982 'success' => true,
5983 'message' => esc_html__( 'Reset White Label Successfully', 'wdesignkit' ),
5984 );
5985
5986 wp_send_json( $result );
5987 wp_die();
5988 }
5989 }
5990
5991 /**
5992 *
5993 * Use for Add new licence key.
5994 *
5995 * @since 1.0.0
5996 */
5997 protected function wdkit_activate_licence() {
5998 $args = array(
5999 'token' => ! empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '',
6000 'licencekey' => ! empty( $_POST['licencekey'] ) ? sanitize_text_field( wp_unslash( $_POST['licencekey'] ) ) : '',
6001 'licencename' => ! empty( $_POST['licencename'] ) ? sanitize_text_field( wp_unslash( $_POST['licencename'] ) ) : '',
6002 'uichemyid' => ! empty( $_POST['uichemyid'] ) ? sanitize_text_field( wp_unslash( $_POST['uichemyid'] ) ) : '',
6003 );
6004
6005 $response = $this->wkit_api_call( $args, 'wkit_activate_key' );
6006
6007 if ( ! empty( $response['data'] ) ) {
6008 $response = json_decode( wp_json_encode( $response['data'] ), true );
6009
6010 if ( ! empty( $response['data']['tpae_licence'] ) && is_serialized( $response['data']['tpae_licence'] ) ) {
6011 $response['data']['tpae_licence'] = unserialize( $response['data']['tpae_licence'], array( 'allowed_classes' => false ) );
6012 }
6013
6014 if ( ! empty( $response['data']['tpag_licence'] ) && is_serialized( $response['data']['tpag_licence'] ) ) {
6015 $response['data']['tpag_licence'] = unserialize( $response['data']['tpag_licence'], array( 'allowed_classes' => false ) );
6016 }
6017
6018 if ( ! empty( $response['data']['uichemy_licence'] ) && is_serialized( $response['data']['uichemy_licence'] ) ) {
6019 $response['data']['uichemy_licence'] = unserialize( $response['data']['uichemy_licence'], array( 'allowed_classes' => false ) );
6020 }
6021
6022 if ( ! empty( $response['data']['wdkit_licence'] ) && is_serialized( $response['data']['wdkit_licence'] ) ) {
6023 $response['data']['wdkit_licence'] = unserialize( $response['data']['wdkit_licence'], array( 'allowed_classes' => false ) );
6024
6025 // Store WDesignKit license status locally for quick access
6026 if ( ! empty( $response['data']['wdkit_licence'] ) && is_array( $response['data']['wdkit_licence'] ) ) {
6027 update_option( 'wdkit_licence_data', $response['data']['wdkit_licence'] );
6028 }
6029 }
6030
6031 if ( ! empty( $response['data']['wdkit_licence_extra'] ) && is_serialized( $response['data']['wdkit_licence_extra'] ) ) {
6032 $response['data']['wdkit_licence_extra'] = unserialize( $response['data']['wdkit_licence_extra'], array( 'allowed_classes' => false ) );
6033 }
6034 }
6035
6036 wp_send_json( $response );
6037 wp_die();
6038 }
6039
6040 /**
6041 *
6042 * Use for Delete licence key.
6043 *
6044 * @since 1.0.0
6045 */
6046 protected function wdkit_delete_licence_key() {
6047 $token = ! empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
6048 $licencename = ! empty( $_POST['licencename'] ) ? sanitize_text_field( wp_unslash( $_POST['licencename'] ) ) : '';
6049 $apikey = ! empty( $_POST['apikey'] ) ? sanitize_text_field( wp_unslash( $_POST['apikey'] ) ) : '';
6050
6051 $args = array(
6052 'token' => $token,
6053 'licencename' => $licencename,
6054 'apikey' => $apikey,
6055 );
6056
6057 $response = $this->wkit_api_call( $args, 'licence_delete' );
6058
6059 // Remove local WDesignKit license data if deleting WDesignKit license
6060 if ( 'wdkit' === $licencename ) {
6061 delete_option( 'wdkit_licence_data' );
6062 }
6063
6064 wp_send_json( $response['data'] );
6065 wp_die();
6066 }
6067
6068 /**
6069 *
6070 * Use for Sync licence key.
6071 *
6072 * @since 1.0.0
6073 */
6074 protected function wdkit_sync_licence_key() {
6075 $token = ! empty( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
6076 $licencename = ! empty( $_POST['licencename'] ) ? sanitize_text_field( wp_unslash( $_POST['licencename'] ) ) : '';
6077 // Needed to identify which extra-credit key to sync (wdkit_extra / wdkit_ai_extra
6078 // are arrays matched by the api key's last digits on the server).
6079 $apikey = ! empty( $_POST['apikey'] ) ? sanitize_text_field( wp_unslash( $_POST['apikey'] ) ) : '';
6080
6081 $args = array(
6082 'token' => $token,
6083 'licencename' => $licencename,
6084 'apikey' => $apikey,
6085 );
6086
6087 $response = $this->wkit_api_call( $args, 'licence_sync' );
6088
6089 wp_send_json( $response['data'] );
6090 wp_die();
6091 }
6092
6093 /**
6094 * Rollback to Previous Versions
6095 *
6096 * @since 1.1.0
6097 */
6098 protected function wdkit_prev_version() {
6099
6100 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
6101
6102 $plugin_info = plugins_api(
6103 'plugin_information',
6104 array(
6105 'slug' => 'wdesignkit',
6106 )
6107 );
6108
6109 if ( empty( $plugin_info->versions ) || ! is_array( $plugin_info->versions ) ) {
6110 return array();
6111 }
6112
6113 krsort( $plugin_info->versions );
6114
6115 $versions_list = array();
6116 $index = 0;
6117
6118 foreach ( $plugin_info->versions as $version => $download_link ) {
6119
6120 $lowercase_version = strtolower( $version );
6121
6122 $is_valid_version = ! preg_match( '/(beta|rc|trunk|dev)/i', $lowercase_version );
6123
6124 $is_valid_version = apply_filters( 'wdkit_check_rollback_version', $is_valid_version, $lowercase_version );
6125
6126 if ( ! $is_valid_version || version_compare( $version, WDKIT_VERSION, '>=' ) ) {
6127 continue;
6128 }
6129
6130 $versions_list[] = $version;
6131 ++$index;
6132 }
6133
6134 // set_transient( 'wdkit_rollback_version_' . WDKIT_VERSION, $versions_list, WEEK_IN_SECONDS );
6135
6136 return $versions_list;
6137 }
6138
6139 /**
6140 * Rollback to Previous Versions
6141 *
6142 * @since 1.1.0
6143 */
6144 protected function wdkit_rollback_check() {
6145
6146 $current_ver = isset( $_POST['version'] ) ? sanitize_text_field( wp_unslash( $_POST['version'] ) ) : '';
6147 $rv = $this->wdkit_prev_version();
6148
6149 if ( empty( $current_ver ) || ! in_array( $current_ver, $rv ) ) {
6150 return array(
6151 'message' => esc_html__( 'Invalid Nonce or version not found', 'wdesignkit' ),
6152 'status' => 'error',
6153 'success' => false,
6154 );
6155 }
6156
6157 $plugin_slug = basename( WDKIT_PBNAME, '.php' );
6158
6159 $this_version = $current_ver;
6160 $this_pluginname = WDKIT_PBNAME;
6161 $this_plugin_u_r_l = sprintf( 'https://downloads.wordpress.org/plugin/%s.%s.zip', $plugin_slug, $this_version );
6162
6163 $plugin_info = new \stdClass();
6164 $plugin_info->new_version = $this_version;
6165 $plugin_info->slug = $plugin_slug;
6166 $plugin_info->package = $this_plugin_u_r_l;
6167 $plugin_info->url = 'https://wdesignkit.com/';
6168
6169 $update_plugins_data = get_site_transient( 'update_plugins' );
6170
6171 if ( ! is_object( $update_plugins_data ) ) {
6172 $update_plugins_data = new \stdClass();
6173 }
6174
6175 $update_plugins_data->response[ $this_pluginname ] = $plugin_info;
6176
6177 set_site_transient( 'update_plugins', $update_plugins_data );
6178
6179 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
6180
6181 $logo_url = WDKIT_URL . 'assets/images/jpg/Wdesignkit-logo.png';
6182
6183 $args = array(
6184 'url' => 'update.php?action=upgrade-plugin&plugin=' . rawurlencode( $this_pluginname ),
6185 'plugin' => $this_pluginname,
6186 'nonce' => 'upgrade-plugin_' . $this_pluginname,
6187 'title' => '<img src="' . esc_url( $logo_url ) . '" alt="wdesignkit-logo"><div class="theplus-rb-subtitle">' . esc_html__( 'Rollback to Previous Version', 'wdesignkit' ) . '</div>',
6188 );
6189
6190 $upgrader_plugin = new \Plugin_Upgrader( new \Plugin_Upgrader_Skin( $args ) );
6191 $upgrader_plugin->upgrade( $this_pluginname );
6192
6193 activate_plugin( $this_pluginname );
6194
6195 return array(
6196 'message' => esc_html__( 'Rollback Successful, Plugin Re-activated', 'wdesignkit' ),
6197 'status' => 'Success',
6198 'success' => true,
6199 );
6200 }
6201
6202 /**
6203 *
6204 * It is Use for logout.
6205 *
6206 * @since 1.0.0
6207 */
6208 protected function wdkit_logout() {
6209 $email = isset( $_POST['email'] ) ? strtolower( sanitize_email( wp_unslash( $_POST['email'] ) ) ) : false;
6210 $logout_type = isset( $_POST['logout_type'] ) ? strtolower( sanitize_text_field( wp_unslash( $_POST['logout_type'] ) ) ) : '';
6211
6212 $response = '';
6213
6214 if ( ! empty( $email ) ) {
6215 $token = $this->wdkit_login_user_token( $email );
6216 $args = array( 'token' => $token );
6217
6218 if ( 'session' !== $logout_type ) {
6219 delete_transient( 'wdkit_auth_' . wdesignkit_cloud_session_key( $email ) );
6220 // Clear stored license data on logout so banner shows again
6221 delete_option( 'wdkit_licence_data' );
6222 $response = WDesignKit_Data_Query::get_data( 'logout', $args );
6223 }
6224 }
6225
6226 wp_send_json( $response );
6227 wp_die();
6228 }
6229
6230 /**
6231 *
6232 * It is Use for get token of login user.
6233 *
6234 * @since 1.0.0
6235 *
6236 * @param string $email check user email.
6237 */
6238 protected function wdkit_login_user_token( $email = '' ) {
6239
6240 if ( ! empty( $email ) ) {
6241 $user_key = wdesignkit_cloud_session_key( $email );
6242 $get_login = get_transient( 'wdkit_auth_' . $user_key );
6243
6244 if ( ! empty( $get_login ) && ! empty( $get_login['token'] ) ) {
6245 return $get_login['token'];
6246 }
6247 }
6248
6249 return false;
6250 }
6251
6252 /**
6253 * Parse args $_POST
6254 *
6255 * @since 1.0.0
6256 *
6257 * @param string $data send all post data.
6258 * @param string $type store text data.
6259 * @param string $condition store text data.
6260 */
6261 protected function wdkit_extract_post_field( $data, $type, $condition = 'none' ) {
6262
6263 if ( 'none' === $condition ) {
6264 return $data[ $type ];
6265 } elseif ( 'cr_widget' === $condition ) {
6266 return $data[ $type ];
6267 }
6268
6269 return null;
6270 }
6271
6272
6273 /**
6274 * Parse args $_POST
6275 *
6276 * @since 1.0.0
6277 *
6278 * @param string $data send all post data.
6279 */
6280 protected function wdkit_parse_args( $data = array() ) {
6281 if ( empty( $data ) ) {
6282 return array();
6283 }
6284
6285 $args = array();
6286 if ( isset( $data['email'] ) ) {
6287 $args['email'] = isset( $data['email'] ) ? sanitize_email( $data['email'] ) : '';
6288 }
6289
6290 if ( isset( $data['data'] ) ) {
6291 $args['data'] = isset( $data['data'] ) ? wp_unslash( $data['data'] ) : array();
6292 }
6293
6294 if ( isset( $data['plugins'] ) ) {
6295 $args['plugins'] = isset( $data['plugins'] ) ? wp_unslash( $data['plugins'] ) : array();
6296 }
6297
6298 if ( isset( $data['template_id'] ) ) {
6299 $args['template_id'] = isset( $data['template_id'] ) ? intval( strtolower( sanitize_text_field( $data['template_id'] ) ) ) : '';
6300 }
6301
6302 if ( isset( $data['builder'] ) ) {
6303 $args['builder'] = isset( $data['builder'] ) ? wp_unslash( $data['builder'] ) : '';
6304 }
6305
6306 if ( isset( $data['editor'] ) ) {
6307 $args['editor'] = isset( $data['editor'] ) ? sanitize_text_field( $data['editor'] ) : '';
6308 }
6309
6310 if ( isset( $data['type_upload'] ) ) {
6311 $args['type_upload'] = isset( $data['type_upload'] ) ? sanitize_text_field( $data['type_upload'] ) : '';
6312 }
6313
6314 if ( isset( $data['title'] ) ) {
6315 $args['title'] = isset( $data['title'] ) ? wp_strip_all_tags( $data['title'] ) : '';
6316 }
6317
6318 if ( isset( $data['template_type'] ) ) {
6319 $args['template_type'] = isset( $data['template_type'] ) ? sanitize_text_field( $data['template_type'] ) : '';
6320 }
6321
6322 if ( isset( $data['wstype'] ) ) {
6323 $args['wstype'] = isset( $data['wstype'] ) ? wp_strip_all_tags( $data['wstype'] ) : '';
6324 }
6325
6326 if ( isset( $data['wid'] ) ) {
6327 $args['wid'] = isset( $data['wid'] ) ? intval( strtolower( sanitize_text_field( $data['wid'] ) ) ) : '';
6328 }
6329
6330 if ( isset( $data['perpage'] ) ) {
6331 $args['perpage'] = isset( $data['perpage'] ) ? intval( strtolower( sanitize_text_field( $data['perpage'] ) ) ) : 12;
6332 }
6333
6334 if ( isset( $data['page'] ) ) {
6335 $args['page'] = isset( $data['page'] ) ? intval( strtolower( sanitize_text_field( $data['page'] ) ) ) : 1;
6336 }
6337
6338 if ( isset( $data['buildertype'] ) ) {
6339 $args['buildertype'] = isset( $data['buildertype'] ) ? sanitize_text_field( $data['buildertype'] ) : '';
6340 }
6341
6342 if ( isset( $data['search'] ) ) {
6343 $args['search'] = isset( $data['search'] ) ? sanitize_text_field( $data['search'] ) : '';
6344 }
6345
6346 if ( isset( $data['plugin'] ) ) {
6347 $args['plugin'] = isset( $data['plugin'] ) ? wp_unslash( $data['plugin'] ) : array();
6348 }
6349
6350 if ( isset( $data['plugin_exclude'] ) ) {
6351 $args['plugin_exclude'] = isset( $data['plugin_exclude'] ) ? wp_unslash( $data['plugin_exclude'] ) : array();
6352 }
6353
6354 if ( isset( $data['ai_compatibility'] ) ) {
6355 $args['ai_compatibility'] = isset( $data['ai_compatibility'] ) ? wp_unslash( $data['ai_compatibility'] ) : array();
6356 }
6357
6358 // if ( isset( $data['global_color'] ) ) {
6359 // $args['global_color'] = isset( $data['global_color'] ) ? wp_unslash( $data['global_color'] ) : array();
6360 // }
6361
6362 // if ( isset( $data['global_font_family'] ) ) {
6363 // $args['global_font_family'] = isset( $data['global_font_family'] ) ? wp_unslash( $data['global_font_family'] ) : array();
6364 // }
6365
6366 if ( isset( $data['global_data'] ) ) {
6367 $args['global_data'] = isset( $data['global_data'] ) ? wp_unslash( $data['global_data'] ) : array();
6368 }
6369
6370 if ( isset( $data['tag'] ) ) {
6371 $args['tag'] = isset( $data['tag'] ) ? wp_unslash( $data['tag'] ) : array();
6372 }
6373
6374 if ( isset( $data['category'] ) ) {
6375 $args['category'] = isset( $data['category'] ) ? wp_unslash( $data['category'] ) : array();
6376 }
6377
6378 if ( isset( $data['free_pro'] ) ) {
6379 $args['free_pro'] = isset( $data['free_pro'] ) ? sanitize_text_field( wp_unslash( $data['free_pro'] ) ) : '';
6380 }
6381
6382 if ( isset( $data['wp_post_type'] ) ) {
6383 $args['wp_post_type'] = isset( $data['wp_post_type'] ) ? sanitize_text_field( wp_unslash( $data['wp_post_type'] ) ) : '';
6384 }
6385
6386 if ( isset( $data['favorite'] ) ) {
6387 $args['favorite'] = isset( $data['favorite'] ) ? sanitize_text_field( wp_unslash( $data['favorite'] ) ) : '';
6388 }
6389
6390 if ( isset( $data['content'] ) ) {
6391 $args['content'] = isset( $data['content'] ) ? wp_unslash( $data['content'] ) : '';
6392 }
6393
6394 if ( isset( $data['page_type'] ) ) {
6395 $args['page_type'] = isset( $data['page_type'] ) ? wp_unslash( $data['page_type'] ) : array();
6396 }
6397
6398 return $args;
6399 }
6400
6401 /**
6402 * Dark Mode
6403 *
6404 * @since 2.0.0
6405 *
6406 * @param string store darkmode value in database.
6407 */
6408 protected function wdkit_dark_mode() {
6409 $dark_mode = ! empty( $_POST['dark_mode'] ) ? sanitize_text_field( $_POST['dark_mode'] ) : 'light';
6410
6411 if ( get_option( 'wdkit_dark_mode' ) ) {
6412 update_option( 'wdkit_dark_mode', $dark_mode );
6413 } else {
6414 add_option( 'wdkit_dark_mode', $dark_mode );
6415 }
6416
6417 $response = array(
6418 'message' => esc_html__( 'Dark Mode Updated', 'wdesignkit' ),
6419 'status' => 'Success',
6420 'success' => true,
6421 );
6422
6423 wp_send_json( $response );
6424 wp_die();
6425 }
6426 }
6427
6428 Wdkit_Api_Call::get_instance();
6429 }
6430