PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
2.7.0 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 All 129 releases
wdesignkit / includes / admin / hooks / class-wdkit-widget-ajax.php

class-wdkit-widget-ajax.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.7.0, at includes/admin/hooks/class-wdkit-widget-ajax.php

1,546 lines 52.1 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.1.1
7 *
8 * @package Wdesignkit
9 * @subpackage Wdesignkit/includes
10 */
11
12 /**
13 * Exit if accessed directly.
14 * */
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 use wdkit\Wdkit_Wdesignkit;
20 use wdkit\wdkit_datahooks\Wdkit_Data_Hooks;
21
22
23
24 if ( ! class_exists( 'Wdkit_Widget_Ajax' ) ) {
25
26 /**
27 * It is wdesignkit Main Class
28 *
29 * @since 1.1.1
30 */
31 class Wdkit_Widget_Ajax {
32
33 /**
34 * Member Variable
35 *
36 * @var instance
37 */
38 private static $instance;
39
40 /**
41 * Member Variable
42 *
43 * @var staring $wdkit_api
44 */
45 public $wdkit_api = WDKIT_SERVER_API_URL . 'api/wp/';
46
47 /**
48 * Member Variable
49 *
50 * @var string $widget_folder_u_r_l
51 */
52 public $widget_folder_u_r_l = '';
53
54 /**
55 * Initiator
56 */
57 public static function get_instance() {
58 if ( ! isset( self::$instance ) ) {
59 self::$instance = new self();
60 }
61
62 return self::$instance;
63 }
64
65 /**
66 * Define the core functionality of the plugin.
67 */
68 public function __construct() {
69 add_filter( 'wp_wdkit_widget_ajax', array( $this, 'wdkit_widget_ajax_call' ) );
70 }
71
72 /**
73 * Get Wdkit Api Call Ajax.
74 *
75 * @since 1.1.1
76 */
77 public function wdkit_widget_ajax_call( $type ) {
78
79 check_ajax_referer( 'wdkit_nonce', 'kit_nonce' );
80
81 if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
82 wp_send_json_error( array( 'content' => __( 'Insufficient permissions.', 'wdesignkit' ) ) );
83 }
84
85 if ( ! $type ) {
86 $this->wdkit_error_msg( 'Something went wrong.' );
87 }
88
89 switch ( $type ) {
90 case 'widget_browse_page':
91 $response = $this->wdkit_widget_browse_page();
92 break;
93 case 'wkit_public_download_widget':
94 $response = $this->wdkit_public_download_widget();
95 break;
96 case 'wkit_create_widget':
97 $response = $this->wkit_create_widget();
98 break;
99 case 'wkit_import_widget':
100 $response = $this->wkit_import_widget();
101 break;
102 case 'wkit_export_widget':
103 $response = $this->wkit_export_widget();
104 break;
105 case 'wkit_delete_widget':
106 $response = $this->wkit_delete_widget();
107 break;
108 case 'wkit_widget_preview':
109 $response = $this->wkit_widget_preview();
110 break;
111 case 'wkit_check_widget_versions':
112 $response = $this->wkit_check_widget_versions();
113 break;
114 case 'wkit_widget_builder_get':
115 $response = $this->wkit_widget_builder_get();
116 break;
117 case 'wkit_plugin_download_get':
118 $response = $this->wkit_plugin_download_get();
119 break;
120 }
121
122 wp_send_json( $response );
123 wp_die();
124 }
125
126 /**
127 *
128 * It is Use to get data for widget browse page
129 *
130 * @since 1.0.4
131 */
132 public function wdkit_widget_browse_page() {
133 $array_data = array(
134 'CurrentPage' => isset( $_POST['page'] ) ? (int) $_POST['page'] : 1,
135 'builder' => isset( $_POST['buildertype'] ) ? wp_unslash( $_POST['buildertype'] ) : '',
136 'sub_builder' => isset( $_POST['sub_builder'] ) ? wp_unslash( $_POST['sub_builder'] ) : '',
137 'category' => isset( $_POST['category'] ) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ) : '',
138 'ParPage' => isset( $_POST['perpage'] ) ? (int) $_POST['perpage'] : 12,
139 'search' => isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '',
140 'free_pro' => isset( $_POST['free_pro'] ) ? sanitize_text_field( wp_unslash( $_POST['free_pro'] ) ) : '',
141 );
142
143 $response = $this->wkit_api_call( $array_data, 'browse_widget' );
144 $success = ! empty( $response['success'] ) ? $response['success'] : false;
145
146 if ( empty( $success ) ) {
147 $response = array(
148 'success' => false,
149 'message' => esc_html__( 'Data Not Found', 'wdesignkit' ),
150 'description' => esc_html__( 'Widget List Not Found', 'wdesignkit' ),
151
152 'widgets' => array(),
153 'widgetscount' => 0,
154 'showwidgets' => 0,
155 );
156
157 wp_send_json( $response );
158 wp_die();
159 }
160
161 $response = json_decode( wp_json_encode( $response['data'] ), true );
162
163 return $response;
164 }
165
166 /**
167 *
168 * It is Use for download widget from browse page.
169 *
170 * @since 1.1.4
171 */
172 public function wdkit_public_download_widget() {
173 $data = ! empty( $_POST['widget_info'] ) ? $this->wdkit_extract_post_field( $_POST, 'widget_info', 'none' ) : '';
174 $data = json_decode( stripslashes( $data ) );
175
176 $api_type = isset( $data->api_type ) ? sanitize_text_field( $data->api_type ) : 'widget/download';
177
178 $array_data = array(
179 'id' => isset( $data->w_uniq ) ? sanitize_text_field( $data->w_uniq ) : '',
180 'u_id' => isset( $data->u_id ) ? sanitize_text_field( $data->u_id ) : '',
181 'type' => isset( $data->d_type ) ? sanitize_text_field( $data->d_type ) : '',
182 'unique_id' => get_option( 'wdkit_unique_id' ) ?? '',
183 );
184
185 $response = $this->wkit_api_call( $array_data, $api_type );
186 $success = ! empty( $response['success'] ) ? $response['success'] : false;
187
188 if ( empty( $success ) ) {
189 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
190
191 $result = (object) array(
192 'success' => false,
193 'message' => $massage,
194 'description' => esc_html__( 'Widget not Downloaded', 'wdesignkit' ),
195 );
196
197 wp_send_json( $result );
198 wp_die();
199 }
200
201 $response = json_decode( wp_json_encode( $response['data'] ), true );
202 if ( ! empty( $response ) && ! empty( $response['data'] ) ) {
203 $img_url = ! empty( $response['data']['image'] ) ? esc_url_raw( $response['data']['image'] ) : '';
204 $json = ! empty( $response['data']['json'] ) ? wp_json_encode( $response['data']['json'] ) : '';
205
206 if ( ! empty( $json ) ) {
207 include_once ABSPATH . 'wp-admin/includes/file.php';
208 \WP_Filesystem();
209 global $wp_filesystem;
210
211 $json_data = json_decode( $json );
212 $json_data = json_decode( $json_data );
213 $title = ! empty( $json_data->widget_data->widgetdata->name ) ? sanitize_file_name( $json_data->widget_data->widgetdata->name ) : '';
214 // sanitize_key() strips path separators and dots from the builder segment;
215 // the allowlist below then rejects anything outside the known builders so a
216 // crafted "type" can never escape WDKIT_BUILDER_PATH before wp_mkdir_p() runs.
217 $builder = ! empty( $json_data->widget_data->widgetdata->type ) ? sanitize_key( $json_data->widget_data->widgetdata->type ) : '';
218 $widget_id = ! empty( $json_data->widget_data->widgetdata->widget_id ) ? sanitize_file_name( $json_data->widget_data->widgetdata->widget_id ) : '';
219
220 $allowed_builders = array( 'elementor', 'gutenberg', 'gutenberg_core', 'bricks' );
221 if ( '' === $title || '' === $widget_id || ! in_array( $builder, $allowed_builders, true ) ) {
222 wp_send_json(
223 (object) array(
224 'success' => false,
225 'message' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
226 )
227 );
228 wp_die();
229 }
230
231 // Canonical helpers replace spaces BEFORE sanitize_file_name(). $title above is
232 // already sanitized, so spaces had become hyphens and the underscore pass did
233 // nothing — a multi-word title wrote "My-Widget_id.json" while the builder's
234 // save path writes "My_Widget_id.php". The loader pairs them by swapping .php
235 // for .json, so the widget never registered (ClickUp 86d41cck5).
236 $folder_name = wdesignkit_widget_folder_name( $title, $widget_id );
237 $file_name = wdesignkit_widget_file_name( $title, $widget_id );
238
239 $builder_type_path = WDKIT_BUILDER_PATH . "/{$builder}/";
240
241 if ( ! is_dir( $builder_type_path . $folder_name ) ) {
242 wp_mkdir_p( $builder_type_path . $folder_name );
243 }
244
245 // Realpath validation — ensure we're still inside WDKIT_BUILDER_PATH.
246 $real_widget = realpath( $builder_type_path . $folder_name );
247 $real_base = realpath( WDKIT_BUILDER_PATH );
248 if ( ! $real_widget || ! $real_base || strpos( $real_widget, $real_base . DIRECTORY_SEPARATOR ) !== 0 ) {
249 wp_send_json(
250 (object) array(
251 'success' => false,
252 'message' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
253 )
254 );
255 wp_die();
256 }
257
258 if ( ! empty( $img_url ) ) {
259 // SSRF guard (CWE-918): validate the resolved host before fetching.
260 $img_body = wdesignkit_safe_remote_get( $img_url );
261 if ( ! is_wp_error( $img_body ) ) {
262 // The remote extension was written verbatim here, so a ".php" image URL
263 // in a cloud response landed executable PHP in the builder directory
264 // (CWE-434, ClickUp 86d41cczd). '' means the bytes are not an image.
265 $img_ext = wdesignkit_safe_image_extension( $img_url, $img_body['body'] );
266
267 if ( '' !== $img_ext ) {
268 $wp_filesystem->put_contents( WDKIT_BUILDER_PATH . "/$builder/$folder_name/$file_name.$img_ext", $img_body['body'] );
269
270 $json_data->widget_data->widgetdata->w_image = WDKIT_SERVER_PATH . "/$builder/$folder_name/$file_name.$img_ext";
271 }
272 }
273 }
274
275 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
276 wdesignkit_invalidate_widget_registry( $builder );
277 }
278
279 $response = (object) array(
280 'message' => ! empty( $response['message'] ) ? $response['message'] : '',
281 'description' => ! empty( $response['description'] ) ? $response['description'] : '',
282 'success' => ! empty( $response['success'] ) ? $response['success'] : false,
283 'r_id' => ! empty( $response['data']['rid'] ) ? $response['data']['rid'] : 0,
284 'json' => wp_json_encode( $json_data ),
285 );
286 }
287 }
288
289 wp_send_json( $response );
290 wp_die();
291 }
292
293 /**
294 * Get widget builder data from server for plugin download.
295 *
296 * @since 1.1.4
297 */
298 public function wkit_widget_builder_get() {
299 $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
300 $id = isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : '';
301
302 $array_data = array(
303 'token' => $token,
304 'id' => $id,
305 'unique_id' => get_option( 'wdkit_unique_id' ) ?? '',
306 );
307
308 $response = $this->wkit_api_call( $array_data, 'widget/builder/get', WDKIT_SERVER_API_URL . 'api/v2/' );
309 $success = ! empty( $response['success'] ) ? $response['success'] : false;
310
311 if ( empty( $success ) ) {
312 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
313
314 $result = (object) array(
315 'success' => false,
316 'message' => $massage,
317 'description' => esc_html__( 'Widget builder data not found.', 'wdesignkit' ),
318 );
319
320 wp_send_json( $result );
321 wp_die();
322 }
323
324 return $response['data'];
325 }
326
327 /**
328 * Generate and download a widget as a standalone WordPress plugin.
329 *
330 * @since 1.1.4
331 */
332 public function wkit_plugin_download_get() {
333 $fields = array(
334 'pluginName',
335 // The popup has a Version field and the cloud reads 'version' for the plugin
336 // header — leaving it out of this allowlist silently discarded whatever the
337 // user typed and stamped every download 1.0.0.
338 'version',
339 'licenceUrl',
340 'pluginSlug',
341 'pluginPrefix',
342 'authorName',
343 'authorUrl',
344 'requiredPhpVersion',
345 'requiredWpVersion',
346 'contributors',
347 'shortDescription',
348 'tags',
349 'textDomain',
350 'faq',
351 'changelog',
352 'php_file',
353 'js_file',
354 'css_file',
355 'js_react_file',
356 'widgetdata',
357 'token',
358 'id',
359 );
360
361 $array_data = array();
362 foreach ( $fields as $field ) {
363 if ( isset( $_POST[ $field ] ) ) {
364 // Code/file payloads must not be sanitized; only unslash the magic-quoted value.
365 $array_data[ $field ] = wp_unslash( $_POST[ $field ] );
366 }
367 }
368
369 $array_data['unique_id'] = get_option( 'wdkit_unique_id' ) ?? '';
370
371 $response = $this->wkit_api_call( $array_data, 'plugin/download/get', WDKIT_SERVER_API_URL . 'api/v2/' );
372 $success = ! empty( $response['success'] ) ? $response['success'] : false;
373
374 if ( empty( $success ) ) {
375 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
376
377 $result = (object) array(
378 'success' => false,
379 'message' => $massage,
380 'description' => esc_html__( 'Plugin not downloaded.', 'wdesignkit' ),
381 );
382
383 wp_send_json( $result );
384 wp_die();
385 }
386
387 return $response['data'];
388 }
389
390 /**
391 * Create a new widget
392 *
393 * @since 1.1.4
394 */
395 public function wkit_create_widget() {
396 // Site-level opt-out for generated widget PHP (ClickUp 86d41zavc). Defaults to allowed,
397 // so this changes nothing unless an operator explicitly filters it off:
398 // add_filter( 'wdesignkit_allow_widget_php_write', '__return_false' );
399 // See wdesignkit_widget_php_write_allowed() for why this is a dedicated filter rather
400 // than a DISALLOW_FILE_MODS / DISALLOW_FILE_EDIT check.
401 if ( function_exists( 'wdesignkit_widget_php_write_allowed' ) && ! wdesignkit_widget_php_write_allowed() ) {
402 wp_send_json(
403 (object) array(
404 'success' => false,
405 'message' => esc_html__( 'File modifications are disabled on this site', 'wdesignkit' ),
406 'description' => esc_html__( 'Generated widget PHP writes have been turned off via the wdesignkit_allow_widget_php_write filter.', 'wdesignkit' ),
407 )
408 );
409 wp_die();
410 }
411
412 $image = '';
413 if ( isset( $_FILES ) && ! empty( $_FILES ) && isset( $_FILES['image'] ) && ! empty( $_FILES['image'] ) ) {
414 $image = Wdkit_Data_Hooks::get_super_global_value( $_FILES, 'image' );
415 }
416
417 $icon = '';
418 if ( isset( $_FILES ) && ! empty( $_FILES ) && isset( $_FILES['icon'] ) && ! empty( $_FILES['icon'] ) ) {
419 $icon = Wdkit_Data_Hooks::get_super_global_value( $_FILES, 'icon' );
420 }
421
422 $data = ! empty( $_POST['value'] ) ? $this->wdkit_extract_post_field( $_POST, 'value', 'cr_widget' ) : '';
423 // $data = ! empty( $data ) ? stripslashes( $data ) : '';
424 $return = ! empty( $data ) ? json_decode( $data ) : '';
425
426 $all_val = ! empty( $return ) ? $return : '';
427 if ( empty( $all_val ) ) {
428
429 $responce = array(
430 'message' => esc_html__( 'Data Not Found', 'wdesignkit' ),
431 'description' => esc_html__( 'something went wrong! please try again later.', 'wdesignkit' ),
432 'success' => false,
433 );
434
435 wp_send_json( $responce );
436 wp_die();
437 }
438
439 $file_name = ! empty( $all_val->file_name ) ? sanitize_text_field( $all_val->file_name ) : '';
440 $folder_name = ! empty( $all_val->folder_name ) ? sanitize_text_field( $all_val->folder_name ) : '';
441 $old_widget = ! empty( $all_val->old_folder ) ? sanitize_text_field( $all_val->old_folder ) : '';
442 $description = ! empty( $all_val->description ) ? sanitize_text_field( $all_val->description ) : '';
443 $json_file = ! empty( $all_val->json_file ) ? $all_val->json_file : '';
444 $function_call = ! empty( $all_val->call ) ? sanitize_text_field( $all_val->call ) : '';
445 $plugin = ! empty( $all_val->plugin ) ? $all_val->plugin : '';
446 $d_image = ! empty( $all_val->d_image ) ? $all_val->d_image : '';
447 $data = json_decode( $json_file );
448
449 $elementor_php_file = ! empty( $all_val->elementor_php_file ) ? $all_val->elementor_php_file : '';
450 $elementor_js = ! empty( $all_val->elementor_js ) ? $all_val->elementor_js : '';
451 $elementor_css = ! empty( $all_val->elementor_css ) ? $all_val->elementor_css : '';
452
453 $gutenberg_php_file = ! empty( $all_val->gutenberg_php_file ) ? $all_val->gutenberg_php_file : '';
454 $gutenberg_js = ! empty( $all_val->gutenberg_js ) ? $all_val->gutenberg_js : '';
455 $gutenberg_css = ! empty( $all_val->gutenberg_css ) ? $all_val->gutenberg_css : '';
456 $external_js_file = ! empty( $all_val->external_js_file ) ? $all_val->external_js_file : '';
457 $style_file = ! empty( $all_val->style_file ) ? $all_val->style_file : '';
458
459 $bricks_php_file = ! empty( $all_val->bricks_php_file ) ? $all_val->bricks_php_file : '';
460 $bricks_js = ! empty( $all_val->bricks_js ) ? $all_val->bricks_js : '';
461 $bricks_css = ! empty( $all_val->bricks_css ) ? $all_val->bricks_css : '';
462
463 $old_folder = ! empty( $old_widget ) ? str_replace( ' ', '-', $old_widget ) : '';
464 $widget_type = ! empty( $data->widget_data->widgetdata->type ) ? sanitize_text_field( $data->widget_data->widgetdata->type ) : '';
465
466 // For gutenberg_core plugin, use the plugin value as widget_type for folder path
467 if ( 'gutenberg_core' === $plugin ) {
468 $widget_type = 'gutenberg_core';
469 }
470
471 if ( empty( $widget_type ) ) {
472 $responce = array(
473 'message' => esc_html__( 'Builder Type not found', 'wdesignkit' ),
474 'description' => esc_html__( 'something went wrong! please try again later.', 'wdesignkit' ),
475 'success' => false,
476 );
477
478 wp_send_json( $responce );
479 wp_die();
480 }
481
482 // $file_name, $folder_name and $widget_type arrive from the request body and were only
483 // passed through sanitize_text_field(), which leaves "../../../../plugins/hello"
484 // untouched — so this handler would write executable PHP anywhere the web user can
485 // reach, outside the builder directory and its .htaccess deny rule. The sibling
486 // download handler above has had these guards since 2.6.4; this one was missed
487 // (CWE-22, ClickUp 86d41cckh).
488 $safe_path = wdesignkit_widget_path_guard( $widget_type, $folder_name, $file_name );
489 if ( false === $safe_path ) {
490 wp_send_json(
491 (object) array(
492 'success' => false,
493 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
494 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
495 )
496 );
497 wp_die();
498 }
499
500 $widget_type = $safe_path['builder'];
501 $folder_name = $safe_path['folder'];
502 $file_name = $safe_path['file'];
503 $builder_type_path = trailingslashit( $safe_path['builder_dir'] );
504 $widget_file_url = $safe_path['dir'];
505
506 if ( ! is_dir( $widget_file_url ) ) {
507 wp_mkdir_p( $widget_file_url );
508 }
509
510 // Re-check the created directory: the component guard above cannot see a symlink.
511 if ( ! wdesignkit_path_inside_builder_dir( $widget_file_url ) ) {
512 wp_send_json(
513 (object) array(
514 'success' => false,
515 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
516 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
517 )
518 );
519 wp_die();
520 }
521
522 include_once ABSPATH . 'wp-admin/includes/file.php';
523 \WP_Filesystem();
524 global $wp_filesystem;
525 $widget_folder_u_r_l = trailingslashit( $widget_file_url ) . $file_name;
526 $this->widget_folder_u_r_l = $widget_file_url;
527
528 if ( 'elementor' === $plugin ) {
529 $widget_file_list = scandir( $widget_file_url );
530 $widget_file_list = array_diff( $widget_file_list, array( '.', '..' ) );
531
532 foreach ( $widget_file_list as $sub_dir_value ) {
533 $file = new SplFileInfo( $sub_dir_value );
534 $check_ext = $file->getExtension();
535 $extiona = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
536
537 if ( 'js' === $extiona || 'css' === $extiona || 'json' === $extiona || 'php' === $extiona ) {
538 $wp_filesystem->rmdir( "$widget_file_url/$sub_dir_value", true );
539 }
540 }
541
542 if ( ! empty( $elementor_php_file ) ) {
543 $wp_filesystem->put_contents( "$widget_folder_u_r_l.php", $elementor_php_file );
544 }
545 if ( ! empty( $json_file ) ) {
546 $wp_filesystem->put_contents( "$widget_folder_u_r_l.json", $json_file );
547 }
548 if ( ! empty( $elementor_css ) ) {
549 $wp_filesystem->put_contents( "$widget_folder_u_r_l.css", $elementor_css );
550 }
551 if ( ! empty( $elementor_js ) ) {
552 $wp_filesystem->put_contents( "$widget_folder_u_r_l.js", $elementor_js );
553 }
554 } elseif ( 'bricks' === $plugin ) {
555
556 $widget_file_list = scandir( $widget_file_url );
557 $widget_file_list = array_diff( $widget_file_list, array( '.', '..' ) );
558
559 foreach ( $widget_file_list as $sub_dir_value ) {
560 $file = new SplFileInfo( $sub_dir_value );
561 $check_ext = $file->getExtension();
562 $extiona = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
563
564 if ( 'js' === $extiona || 'css' === $extiona || 'json' === $extiona || 'php' === $extiona ) {
565 $wp_filesystem->rmdir( "$widget_file_url/$sub_dir_value", true );
566 }
567 }
568
569 $wp_filesystem->put_contents( "$widget_folder_u_r_l.php", $bricks_php_file );
570 $wp_filesystem->put_contents( "$widget_folder_u_r_l.json", $json_file );
571
572 if ( ! empty( $bricks_css ) ) {
573 $wp_filesystem->put_contents( "$widget_folder_u_r_l.css", $bricks_css );
574 }
575
576 if ( ! empty( $bricks_js ) ) {
577 $wp_filesystem->put_contents( "$widget_folder_u_r_l.js", $bricks_js );
578 }
579 } elseif ( 'gutenberg' === $plugin ) {
580
581 $widget_file_list = scandir( $widget_file_url );
582 $widget_file_list = array_diff( $widget_file_list, array( '.', '..' ) );
583
584 foreach ( $widget_file_list as $sub_dir_value ) {
585 $file = new SplFileInfo( $sub_dir_value );
586 $check_ext = $file->getExtension();
587 $extiona = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
588
589 if ( 'js' === $extiona || 'css' === $extiona || 'json' === $extiona || 'php' === $extiona ) {
590 $wp_filesystem->rmdir( "$widget_file_url/$sub_dir_value", true );
591 }
592 }
593
594 if ( ! empty( $external_js_file ) ) {
595 $wp_filesystem->put_contents( "$widget_file_url/index.js", $external_js_file );
596 }
597 $wp_filesystem->put_contents( "$widget_folder_u_r_l.php", $gutenberg_php_file );
598 $wp_filesystem->put_contents( "$widget_folder_u_r_l.json", $json_file );
599 if ( ! empty( $gutenberg_css ) ) {
600 $wp_filesystem->put_contents( "$widget_folder_u_r_l.css", $gutenberg_css );
601 }
602 $wp_filesystem->put_contents( "$widget_folder_u_r_l.js", $gutenberg_js );
603 } elseif ( 'gutenberg_core' === $plugin ) {
604
605 $widget_file_list = scandir( $widget_file_url );
606 $widget_file_list = array_diff( $widget_file_list, array( '.', '..' ) );
607
608 foreach ( $widget_file_list as $sub_dir_value ) {
609 $file = new SplFileInfo( $sub_dir_value );
610 $check_ext = $file->getExtension();
611 $extiona = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
612
613 if ( 'js' === $extiona || 'css' === $extiona || 'json' === $extiona || 'php' === $extiona ) {
614 $wp_filesystem->rmdir( "$widget_file_url/$sub_dir_value", true );
615 }
616 }
617
618 if ( ! empty( $external_js_file ) ) {
619 $wp_filesystem->put_contents( "$widget_file_url/index.js", $external_js_file );
620 }
621 $wp_filesystem->put_contents( "$widget_folder_u_r_l.php", $gutenberg_php_file );
622 $wp_filesystem->put_contents( "$widget_folder_u_r_l.json", $json_file );
623 if ( ! empty( $gutenberg_css ) ) {
624 $wp_filesystem->put_contents( "$widget_folder_u_r_l.css", $gutenberg_css );
625 }
626 $wp_filesystem->put_contents( "$widget_folder_u_r_l.js", $gutenberg_js );
627 }
628
629 if ( ! empty( $image ) && ! empty( $image['tmp_name'] ) ) {
630
631 $img_type = array( 'jpg', 'png' );
632
633 foreach ( $img_type as $imgext ) {
634 $wp_filesystem->rmdir( "$widget_folder_u_r_l . $imgext", true );
635 }
636
637 $ext = $image['type'];
638 $img_ext = '';
639 if ( strpos( $ext, 'jpeg' ) ) {
640 $img_ext = 'jpg';
641 } elseif ( strpos( $ext, 'png' ) ) {
642 $img_ext = 'png';
643 }
644 if ( ! empty( $img_ext ) ) {
645 add_filter( 'upload_dir', array( $this, 'custom_upload_dir' ) );
646
647 $uploaded_file = wp_handle_upload( $image, array( 'test_form' => false ) );
648
649 rename( $uploaded_file['file'], $widget_folder_u_r_l . '.' . $img_ext );
650
651 remove_filter( 'upload_dir', array( $this, 'custom_upload_dir' ) );
652
653 }
654 } elseif ( ! empty( $old_widget ) ) {
655 $img_url = $data->widget_data->widgetdata->w_image;
656 // Copy of an existing local thumbnail, so there are no downloaded bytes to verify —
657 // the allowlist alone keeps the stored w_image extension from naming the copy
658 // something executable (ClickUp 86d41cczd). A non-image extension yields 'png',
659 // which then fails the file_exists() check below and copies nothing.
660 $img_ext = wdesignkit_safe_image_extension( $img_url );
661
662 if ( ! empty( $img_ext ) ) {
663 $old_widget_folder = str_replace( ' ', '-', $old_widget );
664 $old_widget_file = str_replace( ' ', '_', $old_widget );
665 $img_path = "$builder_type_path$old_widget_folder/$old_widget_file.$img_ext";
666 $img_path = str_replace( '\\', '/', $img_path );
667
668 if ( file_exists( $img_path ) ) {
669 $get_img = $img_path;
670 $put_img = "$widget_folder_u_r_l.$img_ext";
671
672 if ( ! empty( $get_img ) && ! empty( $put_img ) ) {
673 rename( $get_img, $put_img );
674 }
675 }
676 }
677 }
678
679 if ( ! empty( $d_image ) ) {
680 $d_image = str_replace( '\\', '', $d_image );
681 $d_img_url = $d_image;
682 // SSRF guard (CWE-918): validate the resolved host before fetching.
683 $img_body = wdesignkit_safe_remote_get( $d_img_url );
684 if ( ! is_wp_error( $img_body ) ) {
685 // The on-disk extension must never be taken verbatim from a remote URL (CWE-434) —
686 // a poisoned response pointing at a ".php" path would otherwise write executable
687 // PHP into the builder directory. Restrict to an image allowlist, falling back to
688 // png, and verify the downloaded bytes' real MIME type before writing.
689 $allowed_img_exts = array( 'jpg', 'jpeg', 'png', 'webp' );
690 $img_ext = strtolower( pathinfo( $d_img_url, PATHINFO_EXTENSION ) );
691 $img_ext = in_array( $img_ext, $allowed_img_exts, true ) ? $img_ext : 'png';
692
693 $tmp_check = wp_check_filetype_and_ext( '', "temp.$img_ext" );
694 $mime_ok = ! empty( $tmp_check['ext'] ) && in_array( $tmp_check['ext'], $allowed_img_exts, true );
695
696 $finfo = function_exists( 'finfo_open' ) ? finfo_open( FILEINFO_MIME_TYPE ) : false;
697 $real_mime = $finfo ? finfo_buffer( $finfo, $img_body['body'] ) : '';
698 if ( $finfo ) {
699 finfo_close( $finfo );
700 }
701 $allowed_mimes = array( 'image/jpeg', 'image/png', 'image/webp' );
702
703 if ( $mime_ok && ( ! $real_mime || in_array( $real_mime, $allowed_mimes, true ) ) ) {
704 $wp_filesystem->put_contents( WDKIT_BUILDER_PATH . "/$widget_type/$folder_name/$file_name.$img_ext", $img_body['body'] );
705 }
706 }
707 }
708
709 if ( ! empty( $function_call ) && 'import' !== $function_call && ! empty( $old_folder ) && strtolower( $old_folder ) !== strtolower( $folder_name ) && is_dir( $builder_type_path . $old_folder ) ) {
710 require_once ABSPATH . 'wp-admin/includes/file.php';
711 global $wp_filesystem;
712 WP_Filesystem();
713 $wp_filesystem->rmdir( $builder_type_path . $old_folder, true );
714 } elseif ( ! empty( $old_folder ) && $old_folder !== $folder_name ) {
715 $old_path = wp_normalize_path( trailingslashit( WDKIT_BUILDER_PATH ) . $widget_type . '/' . $old_folder );
716 $new_path = wp_normalize_path( trailingslashit( WDKIT_BUILDER_PATH ) . $widget_type . '/' . $folder_name );
717
718 if ( is_dir( $old_path ) && ! is_dir( $new_path ) ) {
719 rename( $old_path, $new_path );
720 }
721 }
722
723 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
724 wdesignkit_invalidate_widget_registry( $widget_type );
725 }
726
727 $responce = array(
728 'message' => esc_html__( 'Updates Saved!', 'wdesignkit' ),
729 'description' => esc_html__( 'Your changes have been saved successfully.', 'wdesignkit' ),
730 'success' => true,
731 );
732
733 return $responce;
734 }
735
736 /**
737 * It is Use for delete widget from server
738 *
739 * @since 1.1.4
740 */
741 public function wkit_import_widget() {
742 $filename = '';
743 if ( isset( $_FILES ) && ! empty( $_FILES ) && isset( $_FILES['zipName'] ) && ! empty( $_FILES['zipName'] ) ) {
744 $filename = ! empty( $_FILES['zipName']['name'] ) ? sanitize_file_name( $_FILES['zipName']['name'] ) : '';
745 }
746
747 $zip = new ZipArchive();
748
749 $zipname = '';
750 if ( ! empty( $_FILES['zipName']['tmp_name'] ) ) {
751 $zipname = $this->wdkit_file_sanitizer_bypass( $_FILES, 'zipName', 'name' );
752 }
753
754 $res = $zip->open( $zipname );
755
756 if ( true === $res ) {
757 $widget_name = $image = $json_file = '';
758
759 for ( $i = 0; $i < $zip->numFiles; $i++ ) {
760
761 $sub_dir_value = $zip->getNameIndex( $i );
762
763 $file = new SplFileInfo( $sub_dir_value );
764 $check_ext = $file->getExtension();
765 $extiona = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
766 if ( 'json' === $extiona ) {
767 $json_file = $zip->getFromName( $sub_dir_value );
768 $u_r_l = json_decode( $json_file, true );
769
770 if ( ! empty( $u_r_l['widget_data']['widgetdata']['name'] ) && ! empty( $u_r_l['widget_data']['widgetdata']['widget_id'] ) ) {
771 // Sanitize as filenames before they are used to build the import path
772 // (CWE-22). sanitize_file_name()/sanitize_key() strip path separators
773 // and dots so a crafted .wdk JSON cannot escape WDKIT_BUILDER_PATH.
774 $widget_name = sanitize_file_name( $u_r_l['widget_data']['widgetdata']['name'] );
775 $widget_id = sanitize_file_name( $u_r_l['widget_data']['widgetdata']['widget_id'] );
776 $widget_type = ! empty( $u_r_l['widget_data']['widgetdata']['type'] ) ? sanitize_key( $u_r_l['widget_data']['widgetdata']['type'] ) : '';
777 }
778 } elseif ( 'jpg' === $extiona || 'png' === $extiona || 'jpeg' === $extiona ) {
779 $img_ext = $extiona;
780 $imageData = $zip->getFromName( $sub_dir_value );
781 }
782 }
783
784 if ( ! empty( $widget_name ) && ! empty( $json_file ) ) {
785
786 $local_list = $this->wdkit_get_local_widgets();
787
788 if ( ! empty( $widget_id ) ) {
789
790 if ( count( $local_list ) > 0 ) {
791 foreach ( $local_list as $key => $value ) {
792 $old_id = $value['widgetdata']['widget_id'];
793
794 if ( $old_id == $widget_id ) {
795 $responce = (object) array(
796 'success' => false,
797 'message' => esc_html__( 'Widget Already Exist!', 'wdesignkit' ),
798 'description' => esc_html__( 'Widget Already Exist in plugin', 'wdesignkit' ),
799 );
800
801 wp_send_json( $responce );
802 wp_die();
803 }
804 }
805 }
806 } else {
807 $responce = (object) array(
808 'success' => false,
809 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
810 'description' => esc_html__( 'Widget can not imported', 'wdesignkit' ),
811 );
812
813 wp_send_json( $responce );
814 wp_die();
815 }
816
817 // .wdk zip import. Same ordering bug as the download path above: $widget_name
818 // is already sanitize_file_name()'d, so the underscore pass was a no-op and a
819 // multi-word title landed as "My-Widget_id" where the loader expects
820 // "My_Widget_id" (ClickUp 86d41cck5). The helpers include the id, so the
821 // "_{$widget_id}" that used to be appended at each use site is gone.
822 $folder_name = wdesignkit_widget_folder_name( $widget_name, $widget_id );
823 $file_name = wdesignkit_widget_file_name( $widget_name, $widget_id );
824
825 $allowed_builders = array( 'elementor', 'gutenberg', 'gutenberg_core', 'bricks' );
826 if ( ! in_array( $widget_type, $allowed_builders, true ) ) {
827 $responce = (object) array(
828 'success' => false,
829 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
830 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
831 );
832
833 wp_send_json( $responce );
834 wp_die();
835 }
836
837 if ( ! is_dir( WDKIT_BUILDER_PATH . "/{$widget_type}" ) ) {
838 wp_mkdir_p( WDKIT_BUILDER_PATH . "/{$widget_type}" );
839 }
840 $file_path = WDKIT_BUILDER_PATH . "/{$widget_type}/{$folder_name}";
841
842 // Realpath containment — ensure the resolved builder dir stays inside WDKIT_BUILDER_PATH.
843 $real_base = realpath( WDKIT_BUILDER_PATH );
844 $real_type = realpath( WDKIT_BUILDER_PATH . "/{$widget_type}" );
845 if ( ! $real_base || ! $real_type || strpos( $real_type, $real_base . DIRECTORY_SEPARATOR ) !== 0 ) {
846 $responce = (object) array(
847 'success' => false,
848 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
849 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
850 );
851
852 wp_send_json( $responce );
853 wp_die();
854 }
855
856 if ( ! empty( $imageData ) ) {
857 include_once ABSPATH . 'wp-admin/includes/file.php';
858 \WP_Filesystem();
859 global $wp_filesystem;
860 if ( ! is_dir( $file_path ) ) {
861 wp_mkdir_p( $file_path );
862 }
863
864 $wp_filesystem->put_contents( "{$file_path}/{$file_name}.$img_ext", $imageData );
865 }
866 }
867
868 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
869 wdesignkit_invalidate_widget_registry( $widget_type );
870 }
871
872 $responce = (object) array(
873 'success' => true,
874 'message' => esc_html__( 'Widget imported', 'wdesignkit' ),
875 'description' => esc_html__( 'Widget imported successfully', 'wdesignkit' ),
876 'json' => $u_r_l,
877 );
878
879 return $responce;
880
881 } else {
882 $responce = (object) array(
883 'success' => false,
884 'message' => esc_html__( 'Operation Failed!', 'wdesignkit' ),
885 'description' => esc_html__( 'Widget can not imported', 'wdesignkit' ),
886 );
887
888 wp_send_json( $responce );
889 wp_die();
890 }
891 }
892
893 /**
894 *
895 * It is Use for delete widget from server
896 *
897 * @since 1.1.4
898 */
899 public function wkit_export_widget() {
900 $data = isset( $_POST['info'] ) ? wp_unslash( $_POST['info'] ) : '';
901 $data = json_decode( stripslashes( $data ) );
902
903 // Sanitize as filenames before they are used to build the export path
904 // (CWE-22). sanitize_file_name()/sanitize_key() strip path separators
905 // and dots so a crafted widget_name/widget_type cannot escape WDKIT_BUILDER_PATH.
906 $widget_name_temp = isset( $data->widget_name ) ? sanitize_file_name( $data->widget_name ) : '';
907 $widget_type = isset( $data->widget_type ) ? sanitize_key( $data->widget_type ) : '';
908
909 $widget_name = str_replace( ' ', '_', $widget_name_temp );
910 $folder = str_replace( ' ', '-', $widget_name_temp );
911 $unique_version = $this->wdkit_generate_unique_id();
912
913 if ( empty( $widget_type ) ) {
914 $result = (object) array(
915 'success' => false,
916 'url' => '',
917 'message' => esc_html__( 'Widget Type Fail', 'wdesignkit' ),
918 'description' => esc_html__( 'Widget Type Not Exists', 'wdesignkit' ),
919 );
920
921 wp_send_json( $result );
922 wp_die();
923 }
924
925 $downlod_path = WDKIT_BUILDER_PATH . "/{$widget_type}/";
926 $new_path = "{$downlod_path}/{$folder}/{$widget_name}";
927
928 // Realpath containment — ensure the resolved widget path stays inside WDKIT_BUILDER_PATH.
929 $real_base = realpath( WDKIT_BUILDER_PATH );
930 $real_new = realpath( "$new_path.json" );
931 if ( ! $real_base || ! $real_new || strpos( $real_new, $real_base . DIRECTORY_SEPARATOR ) !== 0 ) {
932 $result = (object) array(
933 'success' => false,
934 'url' => '',
935 'message' => esc_html__( 'Widget Exported Fail', 'wdesignkit' ),
936 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
937 );
938
939 wp_send_json( $result );
940 wp_die();
941 }
942
943 $download_url = WDKIT_SERVER_PATH . "/{$widget_type}/{$widget_name}.zip";
944 $zip = new ZipArchive();
945 $tmp_file = "{$downlod_path}{$widget_name}.zip";
946
947 $json_data = wp_json_file_decode( "$new_path.json" );
948 $img_ext = $json_data->widget_data->widgetdata->img_ext;
949
950 if ( true === $zip->open( $tmp_file, ZipArchive::CREATE ) ) {
951 $widget_wb = str_replace( '-', '_', $folder );
952 $zip->addFile( "$new_path.json", "$widget_wb.json" );
953 if ( ! empty( $img_ext ) ) {
954 $zip->addFile( "$new_path.$img_ext", "$widget_wb.$img_ext" );
955 }
956 $zip->close();
957
958 $result = (object) array(
959 'success' => true,
960 'url' => $download_url,
961 'message' => esc_html__( 'Widget Exported', 'wdesignkit' ),
962 'description' => esc_html__( 'Your widget has been exported successfully.', 'wdesignkit' ),
963 );
964
965 wp_send_json( $result );
966 wp_die();
967 } else {
968 $result = (object) array(
969 'success' => false,
970 'url' => '',
971 'message' => esc_html__( 'Widget Exported Fail', 'wdesignkit' ),
972 'description' => esc_html__( 'Something went wrong! please try again later.', 'wdesignkit' ),
973 );
974
975 return $result;
976 }
977 }
978
979 /**
980 *
981 * It is Use for delete widget from server
982 *
983 * @since 1.1.4
984 */
985 public function wkit_delete_widget() {
986 $data = isset( $_POST['info'] ) ? sanitize_text_field( wp_unslash( $_POST['info'] ) ) : '';
987 $data = json_decode( stripslashes( $data ) );
988
989 $delete_type = isset( $data->delete_type ) ? sanitize_text_field( $data->delete_type ) : '';
990
991 if ( 'plugin_server' === $delete_type ) {
992 $array_data = array(
993 'token' => isset( $data->token ) ? sanitize_text_field( $data->token ) : '',
994 'type' => isset( $data->type ) ? sanitize_text_field( $data->type ) : '',
995 'w_unique' => isset( $data->w_unique ) ? sanitize_text_field( $data->w_unique ) : '',
996 'id' => isset( $data->id ) ? sanitize_text_field( $data->id ) : '',
997 );
998
999 $response = $this->wkit_api_call( $array_data, 'save_widget' );
1000 $success = ! empty( $response['success'] ) ? $response['success'] : false;
1001
1002 if ( empty( $success ) ) {
1003 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'server error', 'wdesignkit' );
1004
1005 $result = (object) array(
1006 'success' => false,
1007 'message' => esc_html__( 'Widget Not Deleted', 'wdesignkit' ),
1008 'description' => esc_html__( 'Widget Not Deleted', 'wdesignkit' ),
1009 );
1010
1011 wp_send_json( $result );
1012 wp_die();
1013 }
1014 }
1015
1016 $dir_name = isset( $data->name ) ? sanitize_text_field( $data->name ) : '';
1017 $widget_type = isset( $data->builder ) ? sanitize_text_field( $data->builder ) : '';
1018
1019 // Same missing guards as wkit_create_widget, but the sink here is a RECURSIVE rmdir:
1020 // sanitize_text_field() leaves traversal intact, so a crafted name/builder could
1021 // delete any directory the web user owns (CWE-22, ClickUp 86d41cckh). Require a
1022 // builder from the allowlist and a name that is a single path segment.
1023 $safe_path = wdesignkit_widget_path_guard( $widget_type, $dir_name );
1024 if ( false === $safe_path || '' === $safe_path['folder'] ) {
1025 wp_send_json(
1026 (object) array(
1027 'success' => false,
1028 'message' => esc_html__( 'Widget Not Deleted', 'wdesignkit' ),
1029 'description' => esc_html__( 'Invalid widget path.', 'wdesignkit' ),
1030 )
1031 );
1032 wp_die();
1033 }
1034
1035 $widget_type = $safe_path['builder'];
1036 $dir = $safe_path['dir'];
1037
1038 require_once ABSPATH . 'wp-admin/includes/file.php';
1039 global $wp_filesystem;
1040 WP_Filesystem();
1041
1042 // Remove only a directory that resolves inside the builder path — the containment
1043 // re-check also rules out a symlinked folder. A missing directory is not an error:
1044 // a 'plugin_server' delete legitimately has no local folder, and the server call
1045 // above has already succeeded, so skip the removal and report success as before.
1046 if ( is_dir( $dir ) && wdesignkit_path_inside_builder_dir( $dir ) ) {
1047 $wp_filesystem->rmdir( $dir, true );
1048 }
1049
1050 if ( function_exists( 'wdesignkit_invalidate_widget_registry' ) ) {
1051 wdesignkit_invalidate_widget_registry( $widget_type );
1052 }
1053
1054 if ( 'plugin_server' === $delete_type ) {
1055 wp_send_json( $response['data'] );
1056 wp_die();
1057 } else {
1058 $result = (object) array(
1059 'success' => true,
1060 'message' => esc_html__( 'Widget Deleted', 'wdesignkit' ),
1061 'description' => esc_html__( 'Your widget has been deleted successfully.', 'wdesignkit' ),
1062 );
1063
1064 return $result;
1065 }
1066 }
1067
1068 /**
1069 *
1070 * It is Use for Check Widegt live preview
1071 *
1072 * @since 1.1.19
1073 */
1074 public function wkit_widget_preview() {
1075
1076 $widget_data = !empty( $_POST['widget_data'] ) ? json_decode(stripslashes($_POST['widget_data']), true) : '';
1077
1078 if ( empty( $widget_data ) ) {
1079 $response = array(
1080 'success' => true,
1081 'message' => esc_html__( 'Invalid widget data', 'wdesignkit' ),
1082 'description' => esc_html__( 'Invalid widget data', 'wdesignkit' ),
1083 );
1084
1085 wp_send_json($response);
1086 wp_die();
1087 }
1088
1089 $widget_name = !empty( $widget_data['widget_name'] ) ? sanitize_text_field( $widget_data['widget_name'] ) : '';
1090 $widget_id = !empty( $widget_data['widget_id'] ) ? sanitize_title($widget_data['widget_id']) : '';
1091 $widget_type = !empty( $widget_data['widget_type'] ) ? sanitize_key($widget_data['widget_type']) : '';
1092 $page_data = !empty( $widget_data['page_data'] ) ? sanitize_key($widget_data['page_data']) : '';
1093
1094 if ( $widget_type == "gutenberg_core" ){
1095 $widget_type = "gutenberg";
1096 }
1097
1098 if ( 'elementor' === $widget_type ) {
1099 $existing_page = get_posts([
1100 'post_type' => 'page',
1101 'meta_key' => '_wkit_preview_widget_id',
1102 'meta_value' => $widget_id,
1103 'posts_per_page' => 1,
1104 ]);
1105
1106 if ( !empty( $existing_page ) ) {
1107 $page_id = $existing_page[0]->ID;
1108 } else {
1109 $page_id = wp_insert_post([
1110 'post_title' => $widget_name . '-Preview',
1111 'post_status' => 'publish',
1112 'post_type' => 'page',
1113 'post_name' => $widget_id,
1114 'post_content' => '',
1115 'meta_input' => [
1116 '_is_preview_page' => true,
1117 '_wkit_preview_widget_id' => $widget_id,
1118 '_elementor_edit_mode' => 'builder',
1119 '_wp_page_template' => 'elementor_canvas',
1120 ],
1121 ]);
1122 }
1123
1124 $this->wdkit_update_elementor_data($page_id, $page_data, $widget_id, $widget_name);
1125
1126 if (class_exists('\Elementor\Plugin')) {
1127 \Elementor\Plugin::$instance->files_manager->clear_cache($page_id);
1128 \Elementor\Plugin::$instance->files_manager->generate_css($page_id);
1129 }
1130
1131 $preview_url = admin_url('post.php?post=' . $page_id . '&action=elementor');
1132 } else if ( "gutenberg" === $widget_type ) {
1133 $existing_page = get_posts([
1134 'post_type' => 'page',
1135 'meta_key' => '_wkit_preview_widget_id',
1136 'meta_value' => $widget_id,
1137 'posts_per_page' => 1,
1138 ]);
1139
1140 if ( !empty( $existing_page ) ) {
1141 $page_id = $existing_page[0]->ID;
1142 } else {
1143 $post_name = wp_unique_post_slug($widget_id, 0, 'publish', 'page', 0);
1144
1145 $page_id = wp_insert_post([
1146 'post_title' => $widget_name . ' - Preview',
1147 'post_status' => 'publish',
1148 'post_type' => 'page',
1149 'post_name' => sanitize_title($post_name),
1150 'post_content' => '',
1151 'meta_input' => [
1152 'gutenberg_preview' => true,
1153 '_wkit_preview_widget_id' => $widget_id,
1154 '_wp_page_template' => 'default',
1155 ],
1156 ]);
1157
1158 if ( is_wp_error($page_id) || !$page_id ) {
1159 $response = array(
1160 'success' => true,
1161 'message' => esc_html__( 'Page Not Found!', 'wdesignkit' ),
1162 'description' => esc_html__( 'Page Not Found!', 'wdesignkit' ),
1163 );
1164
1165 wp_send_json($response);
1166 wp_die();
1167 }
1168
1169 update_post_meta($page_id, '_edit_lock', time() . ':1');
1170 update_post_meta($page_id, '_edit_last', get_current_user_id());
1171 }
1172
1173 if (!function_exists('serialize_blocks')) {
1174 $response = array(
1175 'success' => true,
1176 'message' => esc_html__( 'Invalid serialize_blocks', 'wdesignkit' ),
1177 'description' => esc_html__( 'Invalid serialize_blocks', 'wdesignkit' ),
1178 );
1179
1180 wp_send_json($response);
1181 wp_die();
1182 }
1183
1184 $blocks = [
1185 [
1186 'blockName' => 'wdkit/' . $page_data,
1187 'attrs' => [],
1188 'innerHTML' => '',
1189 'innerContent' => [],
1190 ]
1191 ];
1192
1193 $updated_content = serialize_blocks($blocks);
1194
1195 wp_update_post([
1196 'ID' => $page_id,
1197 'post_content' => $updated_content,
1198 ]);
1199
1200 $preview_url = admin_url('post.php?post=' . $page_id . '&action=edit');
1201 } else {
1202 $result = (object) array(
1203 'success' => true,
1204 'message' => esc_html__( 'widget Type Not Found', 'wdesignkit' ),
1205 'description' => esc_html__( 'widget Type Not Found', 'wdesignkit' ),
1206 );
1207
1208 // Bug G fix: $response was undefined here — should send $result (set 3 lines above).
1209 wp_send_json($result);
1210 wp_die();
1211 }
1212
1213 $response = array(
1214 'success' => true,
1215 'preview_url' => $preview_url
1216 );
1217
1218 wp_send_json($response);
1219 wp_die();
1220 }
1221
1222 /**
1223 * Check widget versions by r_id
1224 *
1225 * @since 2.1.6
1226 */
1227 public function wkit_check_widget_versions() {
1228 $r_ids = isset( $_POST['r_ids'] ) ? sanitize_text_field( wp_unslash( $_POST['r_ids'] ) ) : '';
1229 $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
1230
1231 if ( empty( $r_ids ) ) {
1232 $response = array(
1233 'success' => false,
1234 'message' => esc_html__( 'No widget IDs provided', 'wdesignkit' ),
1235 'description' => esc_html__( 'Please provide widget r_id values', 'wdesignkit' ),
1236 'versions' => array(),
1237 );
1238
1239 wp_send_json( $response );
1240 wp_die();
1241 }
1242
1243 $array_data = array(
1244 'id' => $r_ids,
1245 'token' => $token,
1246 );
1247
1248 $response = $this->wkit_api_call( $array_data, 'widget/version/get' );
1249 $success = ! empty( $response['success'] ) ? $response['success'] : false;
1250
1251 if ( empty( $success ) ) {
1252 $massage = ! empty( $response['massage'] ) ? $response['massage'] : esc_html__( 'Server error', 'wdesignkit' );
1253
1254 $result = array(
1255 'success' => false,
1256 'message' => $massage,
1257 'description' => esc_html__( 'Failed to check widget versions', 'wdesignkit' ),
1258 'versions' => array(),
1259 );
1260
1261 wp_send_json( $result );
1262 wp_die();
1263 }
1264
1265 $versions = json_decode( wp_json_encode( $response['data'] ), true );
1266
1267 $result = array(
1268 'success' => true,
1269 'message' => esc_html__( 'Version check completed', 'wdesignkit' ),
1270 'description' => esc_html__( 'Widget versions retrieved successfully', 'wdesignkit' ),
1271 'versions' => ! empty( $versions ) ? $versions : array(),
1272 );
1273
1274 wp_send_json($result);
1275 wp_die();
1276 }
1277 public function wdkit_update_elementor_data($page_id, $page_data, $widget_id, $widget_name) {
1278 $experiments_manager = Elementor\Plugin::$instance->experiments;
1279
1280 if( $experiments_manager->is_feature_active( 'container' ) ) {
1281 $elementor_data = [
1282 [
1283 'id' => 'container_' . wp_generate_uuid4(),
1284 'elType' => 'container',
1285 'settings' => [],
1286 'elements' => [
1287 [
1288 'id' => 'widget_' . wp_generate_uuid4(),
1289 'elType' => 'widget',
1290 'widgetType' => $page_data,
1291 'settings' => [
1292 'title' => $widget_name,
1293 'widget_id' => $widget_id,
1294 ],
1295 ],
1296 ],
1297 ],
1298 ];
1299 } else {
1300 $elementor_data = [
1301 [
1302 'id' => 'section_' . wp_generate_uuid4(),
1303 'elType' => 'section',
1304 'settings' => [],
1305 'elements' => [
1306 [
1307 'id' => 'column_' . wp_generate_uuid4(),
1308 'elType' => 'column',
1309 'settings' => [],
1310 'elements' => [
1311 [
1312 'id' => 'widget_' . wp_generate_uuid4(),
1313 'elType' => 'widget',
1314 'widgetType' => $page_data,
1315 'settings' => [
1316 'title' => $widget_name,
1317 'widget_id' => $widget_id,
1318 ],
1319 ],
1320 ],
1321 ],
1322 ],
1323 ],
1324 ];
1325 }
1326
1327 update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($elementor_data)));
1328 update_post_meta($page_id, '_elementor_edit_mode', 'builder');
1329 update_post_meta($page_id, '_wp_page_template', 'elementor_canvas');
1330
1331 }
1332 /* All below functions are helper functions for this file */
1333
1334 /**
1335 *
1336 * This Function is used for API call
1337 *
1338 * @since 1.1.4
1339 *
1340 * @param array $data give array.
1341 * @param array $name store data.
1342 */
1343 public function wkit_api_call( $data, $name, $base = '' ) {
1344 $u_r_l = ! empty( $base ) ? $base : $this->wdkit_api;
1345
1346 if ( empty( $u_r_l ) ) {
1347 return array(
1348 'massage' => esc_html__( 'API Not Found', 'wdesignkit' ),
1349 'success' => false,
1350 );
1351 }
1352
1353 $args = array(
1354 'method' => 'POST',
1355 'body' => $data,
1356 'timeout' => 100,
1357 );
1358 $response = wp_remote_post( $u_r_l . $name, $args );
1359
1360 if ( is_wp_error( $response ) ) {
1361 $error_message = $response->get_error_message();
1362
1363 /* Translators: %s is a placeholder for the error message */
1364 $error_message = sprintf( esc_html__( 'API request error: %s', 'wdesignkit' ), esc_html( $error_message ) );
1365
1366 return array(
1367 'massage' => $error_message,
1368 'success' => false,
1369 );
1370 }
1371
1372 $status_code = wp_remote_retrieve_response_code( $response );
1373 if ( 200 === $status_code ) {
1374
1375 return array(
1376 'data' => json_decode( wp_remote_retrieve_body( $response ) ),
1377 'massage' => esc_html__( 'Success', 'wdesignkit' ),
1378 'status' => $status_code,
1379 'success' => true,
1380 );
1381 }
1382
1383 $error_message = sprintf( 'Server error: %d', esc_html( $status_code ) );
1384
1385 if ( isset( $error_data->message ) ) {
1386 $error_message .= ' (' . $error_data->message . ')';
1387 }
1388
1389 return array(
1390 'massage' => $error_message,
1391 'status' => $status_code,
1392 'success' => false,
1393 );
1394 }
1395
1396 /**
1397 *
1398 * Custom_upload_dir
1399 *
1400 * @since 1.1.4
1401 *
1402 * @param array $upload store data.
1403 */
1404 public function custom_upload_dir( $upload ) {
1405 // Specify the path to your custom upload directory.
1406 if ( isset( $this->widget_folder_u_r_l ) && ! empty( $this->widget_folder_u_r_l ) ) {
1407
1408 // Set the custom directory as the upload path.
1409 $upload['path'] = $this->widget_folder_u_r_l;
1410 // Set the URL for the uploaded file.
1411 $upload['url'] = $upload['baseurl'] . $upload['subdir'];
1412 }
1413
1414 return $upload;
1415 }
1416
1417 /**
1418 * Parse args $_POST
1419 *
1420 * @since 1.1.1
1421 *
1422 * @param string $data send all post data.
1423 * @param string $type store text data.
1424 * @param string $condition store text data.
1425 */
1426
1427 public function wdkit_extract_post_field( $data, $type, $condition = 'none' ) {
1428
1429 if ( 'none' === $condition ) {
1430 return $data[ $type ];
1431 } elseif ( 'cr_widget' === $condition ) {
1432 return base64_decode($data[ $type ]);
1433 }
1434
1435 return null;
1436 }
1437
1438 /**
1439 * Parse args $_POST
1440 *
1441 * @since 1.1.4
1442 *
1443 * @param string $data send all post data.
1444 * @param string $type store text data.
1445 * @param string $condition store text data.
1446 */
1447 public function wdkit_file_sanitizer_bypass( $data, $type, $condition = 'none' ) {
1448
1449 if ( 'name' === $condition ) {
1450 return wp_normalize_path( $data[ $type ]['tmp_name'] );
1451 }
1452 }
1453
1454 /**
1455 * Get list local Widget List
1456 *
1457 * @since 1.1.4
1458 */
1459 public function wdkit_get_local_widgets() {
1460 $builder = array();
1461 $a_c_s_d_s_c = array();
1462 $j_s_o_n_array = array();
1463
1464 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'bricks', 'widget' ) ) {
1465 array_push( $builder, 'bricks' );
1466 }
1467
1468 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'elementor', 'widget' ) ) {
1469 array_push( $builder, 'elementor' );
1470 }
1471
1472 if ( Wdkit_Wdesignkit::wdkit_is_compatible( 'gutenberg', 'widget' ) ) {
1473 array_push( $builder, 'gutenberg' );
1474 }
1475
1476 foreach ( $builder as $key => $name ) {
1477 $elementor_dir = WDKIT_BUILDER_PATH . '/' . $name;
1478
1479 if ( ! empty( $elementor_dir ) && is_dir( $elementor_dir ) ) {
1480 $elementor_list = scandir( $elementor_dir );
1481 $elementor_list = array_diff( $elementor_list, array( '.', '..' ) );
1482
1483 if ( ! empty( $elementor_list ) ) {
1484 foreach ( $elementor_list as $key => $value ) {
1485 $a_c_s_d_s_c[ filemtime( "{$elementor_dir}/{$value}" ) ]['data'] = $value;
1486 $a_c_s_d_s_c[ filemtime( "{$elementor_dir}/{$value}" ) ]['builder'] = $name;
1487 }
1488 }
1489 }
1490 }
1491
1492 ksort( $a_c_s_d_s_c );
1493 $a_c_s_d_s_c = array_reverse( $a_c_s_d_s_c );
1494
1495 foreach ( $a_c_s_d_s_c as $key => $value ) {
1496 $elementor_dir = WDKIT_BUILDER_PATH . '/' . $value['builder'];
1497
1498 if ( file_exists( "{$elementor_dir}/{$value['data']}" ) && is_dir( "{$elementor_dir}/{$value['data']}" ) ) {
1499 $sub_dir = scandir( "{$elementor_dir}/{$value['data']}" );
1500 $sub = array_diff( $sub_dir, array( '.', '..' ) );
1501
1502 foreach ( $sub as $sub_dir_value ) {
1503 $file = new SplFileInfo( $sub_dir_value );
1504 $check_ext = $file->getExtension();
1505 $ext = pathinfo( $sub_dir_value, PATHINFO_EXTENSION );
1506
1507 if ( 'json' === $ext ) {
1508 $widget1 = WDKIT_BUILDER_PATH . "/{$value['builder']}/{$value['data']}/{$sub_dir_value}";
1509 $filedata = wp_json_file_decode( $widget1 );
1510 $decode_data = json_decode( wp_json_encode( $filedata ), true );
1511 array_push( $j_s_o_n_array, $decode_data['widget_data'] );
1512 }
1513 }
1514 }
1515 }
1516
1517 return $j_s_o_n_array;
1518 }
1519
1520 /**
1521 * Error JSON message
1522 *
1523 * @param array $data give array.
1524 *
1525 * @since 1.1.4
1526 * */
1527 public function wdkit_error_msg( $data = null ) {
1528 wp_send_json_error( $data );
1529 wp_die();
1530 }
1531
1532 /**
1533 * Create Uniq name
1534 *
1535 * @since 1.1.4
1536 */
1537 public function wdkit_generate_unique_id() {
1538 $now = new DateTime();
1539 $unique_i_d = $now->format( 'YmdHis' );
1540 $hashed_i_d = (int) $unique_i_d % 10000;
1541 return str_pad( $hashed_i_d, 4, '0', STR_PAD_LEFT );
1542 }
1543 }
1544
1545 Wdkit_Widget_Ajax::get_instance();
1546 }