PluginProbe ʕ •ᴥ•ʔ
ShopPress – Shop Builder for Elementor and WooCommerce / trunk
ShopPress – Shop Builder for Elementor and WooCommerce vtrunk
shop-press / Elementor / template-library / TemplatesLib.php
shop-press / Elementor / template-library Last commit date
assets 2 years ago TemplateSourceContent.php 4 months ago TemplatesLib.php 2 weeks ago index.php 2 years ago
TemplatesLib.php
1083 lines
1 <?php
2 /**
3 * Template Library.
4 *
5 * @package ShopPress
6 */
7
8 namespace ShopPress\Elementor\TemplateLibrary;
9
10 defined( 'ABSPATH' ) || exit;
11
12 use Elementor\User;
13 use Elementor\Plugin;
14 use Elementor\TemplateLibrary\Source_Local;
15 use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
16 use Elementor\Modules\KitElementsDefaults\Utils\Settings_Sanitizer;
17 use Elementor\Core\Utils\Collection;
18
19 /**
20 * Template Library.
21 *
22 * @since 1.2.0
23 */
24 class TemplatesLib
25 {
26
27 /**
28 * library option key.
29 */
30 const LIBRARY_OPTION_KEY = 'shoppress_templates_library';
31
32 /**
33 * Presets option key.
34 */
35 const PRESETS_OPTION_KEY = 'shoppress_presets_library';
36
37 /**
38 * Presets option key.
39 */
40 const PRESETS_ELEMENTS_OPTION_KEY = 'shoppress_preset_elements';
41
42 /**
43 * API templates URL.
44 *
45 * Holds the URL of the templates API.
46 *
47 * @access public
48 * @static
49 *
50 * @var string API URL.
51 */
52 public static $api_url = 'https://katademos.com/api/shoppress-template-manager/v1/';
53
54 /**
55 * templates Path.
56 *
57 * Holds the URL of the templates path.
58 *
59 * @access public
60 * @static
61 *
62 * @var string path.
63 */
64 public static $path;
65
66 /**
67 * templates url.
68 *
69 * Holds the URL of the templates url.
70 *
71 * @access public
72 * @static
73 *
74 * @var string url.
75 */
76 public static $url;
77
78 /**
79 * Init.
80 *
81 * Initializes the hooks.
82 *
83 * @since 1.2.0
84 * @access public
85 *
86 * @return void
87 */
88 public static function init()
89 {
90 self::$path = plugin_dir_path(__FILE__);
91 self::$url = plugin_dir_url(__FILE__);
92
93 include_once self::$path . 'TemplateSourceContent.php';
94
95 add_action('elementor/init', array(__CLASS__, 'register_source'));
96 add_action('elementor/editor/after_enqueue_scripts', array(__CLASS__, 'enqueue_editor_scripts'));
97 add_action('elementor/ajax/register_actions', array(__CLASS__, 'register_ajax_actions'));
98 add_action('elementor/editor/footer', array(__CLASS__, 'render_template'));
99 add_action('elementor/editor/after_enqueue_styles', array(__CLASS__, 'editor_enqueue_styles'), 0);
100 add_action('elementor/preview/enqueue_styles', array(__CLASS__, 'preview_enqueue_styles'));
101 add_action('wp_ajax_shoppress_get_preset_element', array(__CLASS__, 'shoppress_get_preset_element'));
102 add_action('wp_ajax_nopriv_shoppress_get_preset_element', array(__CLASS__, 'shoppress_get_preset_element'));
103 }
104
105 /**
106 * Register source.
107 *
108 * Registers the library source.
109 *
110 * @since 1.2.0
111 * @access public
112 *
113 * @return void
114 */
115 public static function register_source()
116 {
117 Plugin::$instance->templates_manager->register_source('\ShopPress\Elementor\TemplateLibrary\TemplateSourceContent');
118 }
119
120 /**
121 * Enqueue Editor Style.
122 *
123 * Enqueues required scripts in Elementor edit mode.
124 *
125 * @since 1.2.0
126 * @access public
127 *
128 * @return void
129 */
130 public static function editor_enqueue_styles()
131 {
132 wp_enqueue_style('shoppress-elementor-template-manager-editor', self::$url . 'assets/css/template-manager-editor.css', array(), SHOPPRESS_VERSION);
133 wp_enqueue_style('shoppress-elementor-template-manager-editor-dark', self::$url . 'assets/css/template-manager-editor-dark.css', array(), SHOPPRESS_VERSION);
134 }
135
136 /**
137 * Enqueue Editor Style.
138 *
139 * Enqueues required scripts in Elementor edit mode.
140 *
141 * @since 1.2.0
142 * @access public
143 *
144 * @return void
145 */
146 public static function preview_enqueue_styles()
147 {
148 wp_enqueue_style('shoppress-elementor-template-manager-preview', self::$url . 'assets/css/template-manager-preview.css', array(), SHOPPRESS_VERSION);
149 }
150
151 /**
152 * Enqueue Editor Scripts.
153 *
154 * Enqueues required scripts in Elementor edit mode.
155 *
156 * @since 1.2.0
157 * @access public
158 *
159 * @return void
160 */
161 public static function enqueue_editor_scripts()
162 {
163
164 $post_types = array(
165 'shoppress_pages',
166 'shoppress_loop',
167 'shoppress_myaccount',
168 );
169
170 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Editor context, post ID for asset loading, sanitized with absint().
171 $post_type = get_post_type( isset( $_GET['post'] ) ? absint( wp_unslash( $_GET['post'] ) ) : 0 );
172
173 if (! in_array($post_type, $post_types)) {
174 return;
175 }
176
177 wp_enqueue_script('sp-nicescroll-script');
178 wp_enqueue_script(
179 'shoppress-templates-lib',
180 self::$url . 'assets/js/templates-lib.js',
181 array(
182 'jquery',
183 'sp-nicescroll-script',
184 'backbone-marionette',
185 'backbone-radio',
186 'elementor-common-modules',
187 'elementor-dialog',
188 ),
189 SHOPPRESS_VERSION,
190 true
191 );
192
193 $post_type = get_post_type();
194 $post_id = get_the_ID();
195 $template_groups = array();
196 $post_types = array(
197 'shoppress_pages',
198 );
199 if (in_array($post_type, $post_types)) {
200
201 $types = (array) get_post_meta($post_id, 'custom_type', true);
202
203 if (in_array('archive', $types) || in_array('shop', $types)) {
204
205 $template_groups['templates/shop'] = array(
206 'title' => __('Shop', 'shop-press'),
207 'filter' => array(
208 'source' => 'shop',
209 'type' => 'shop',
210 'subtype' => 'shop',
211 ),
212 );
213 } elseif (in_array('single', $types)) {
214
215 $template_groups['templates/single_product'] = array(
216 'title' => __('Product Single', 'shop-press'),
217 'filter' => array(
218 'source' => 'single_product',
219 'type' => 'single_product',
220 'subtype' => 'single_product',
221 ),
222 );
223 } elseif (in_array('cart', $types)) {
224 $template_groups['templates/cart'] = array(
225 'title' => __('Cart', 'shop-press'),
226 'filter' => array(
227 'source' => 'cart',
228 'type' => 'cart',
229 'subtype' => 'cart',
230 ),
231 );
232 } elseif (in_array('empty_cart', $types)) {
233 $template_groups['templates/empty_cart'] = array(
234 'title' => __('Empty Cart', 'shop-press'),
235 'filter' => array(
236 'source' => 'empty_cart',
237 'type' => 'empty_cart',
238 'subtype' => 'empty_cart',
239 ),
240 );
241 } elseif (in_array('checkout', $types)) {
242
243 $template_groups['templates/checkout'] = array(
244 'title' => __('Checkout', 'shop-press'),
245 'filter' => array(
246 'source' => 'checkout',
247 'type' => 'checkout',
248 'subtype' => 'checkout',
249 ),
250 );
251 } elseif (in_array('checkout', $types)) {
252
253 $template_groups['templates/checkout'] = array(
254 'title' => __('Checkout', 'shop-press'),
255 'filter' => array(
256 'source' => 'checkout',
257 'type' => 'checkout',
258 'subtype' => 'checkout',
259 ),
260 );
261 } elseif (in_array('thank_you', $types)) {
262
263 $template_groups['templates/thank_you'] = array(
264 'title' => __('Thank You', 'shop-press'),
265 'filter' => array(
266 'source' => 'thank_you',
267 'type' => 'thank_you',
268 'subtype' => 'thank_you',
269 ),
270 );
271 } elseif (in_array('compare', $types)) {
272
273 $template_groups['templates/compare'] = array(
274 'title' => __('Compare', 'shop-press'),
275 'filter' => array(
276 'source' => 'compare',
277 'type' => 'compare',
278 'subtype' => 'compare',
279 ),
280 );
281 } elseif (in_array('quick_view', $types)) {
282
283 $template_groups['templates/quick_view'] = array(
284 'title' => __('Quick View', 'shop-press'),
285 'filter' => array(
286 'source' => 'quick_view',
287 'type' => 'quick_view',
288 'subtype' => 'quick_view',
289 ),
290 );
291 } elseif (in_array('wishlist', $types)) {
292
293 $template_groups['templates/wishlist'] = array(
294 'title' => __('Wishlist', 'shop-press'),
295 'filter' => array(
296 'source' => 'wishlist',
297 'type' => 'wishlist',
298 'subtype' => 'wishlist',
299 ),
300 );
301 } elseif (in_array('wishlist', $types)) {
302
303 $template_groups['templates/wishlist'] = array(
304 'title' => __('Wishlist', 'shop-press'),
305 'filter' => array(
306 'source' => 'wishlist',
307 'type' => 'wishlist',
308 'subtype' => 'wishlist',
309 ),
310 );
311 }
312 }
313
314 if ('shoppress_loop' === $post_type) {
315
316 $template_groups['templates/product_loop'] = array(
317 'title' => __('Product Loop', 'shop-press'),
318 'filter' => array(
319 'source' => 'product_loop',
320 'type' => 'product_loop',
321 'subtype' => 'product_loop',
322 ),
323 );
324 }
325
326 if ('shoppress_myaccount' === $post_type) {
327
328 $template_groups['templates/my_account'] = array(
329 'title' => __('My Account', 'shop-press'),
330 'filter' => array(
331 'source' => 'my_account',
332 'type' => 'my_account',
333 'subtype' => 'my_account',
334 ),
335 );
336 }
337
338 $elementor_data = get_post_meta($post_id, '_elementor_data', true);
339 $open_templates_lib = (empty($elementor_data) || '[]' === $elementor_data) ? 'yes' : 'no';
340 $is_kata = false;
341 $theme = wp_get_theme();
342 $parent_theme = $theme->parent();
343 if (false !== strpos($theme->get('TextDomain'), 'kata') || ($parent_theme && false !== strpos($parent_theme->get('TextDomain'), 'kata'))) {
344 $is_kata = true;
345 }
346
347 wp_localize_script(
348 'shoppress-templates-lib',
349 'shoppress_templates_lib',
350 array(
351 'template_groups' => $template_groups,
352 'open_templates_lib' => $open_templates_lib,
353 'is_kata' => $is_kata,
354 )
355 );
356 }
357
358 /**
359 * Init ajax calls.
360 *
361 * Initialize template library ajax calls for allowed ajax requests.
362 *
363 * @since 1.2.0
364 * @access public
365 *
366 * @param Ajax $ajax Elementor's Ajax object.
367 * @return void
368 */
369 public static function register_ajax_actions(Ajax $ajax)
370 {
371 $library_ajax_requests = array(
372 'shoppress_get_library_data',
373 'shoppress_get_template_data',
374 );
375
376 foreach ($library_ajax_requests as $ajax_request) {
377 $ajax->register_ajax_action(
378 $ajax_request,
379 function ($data) use ($ajax_request) {
380 return self::handle_ajax_request($ajax_request, $data);
381 }
382 );
383 }
384 }
385
386 /**
387 * Handle ajax request.
388 *
389 * Fire authenticated ajax actions for any given ajax request.
390 *
391 * @since 1.2.0
392 * @access private
393 *
394 * @param string $ajax_request Ajax request.
395 * @param array $data Elementor data.
396 *
397 * @return mixed
398 * @throws \Exception Throws error message.
399 */
400 private static function handle_ajax_request($ajax_request, array $data)
401 {
402 if (! User::is_current_user_can_edit_post_type(Source_Local::CPT)) {
403 throw new \Exception('Access Denied');
404 }
405
406 if (! empty($data['editor_post_id'])) {
407 $editor_post_id = absint($data['editor_post_id']);
408
409 if (! get_post($editor_post_id)) {
410 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception message is not directly output.
411 throw new \Exception(__('Post not found.', 'shop-press'));
412 }
413
414 Plugin::$instance->db->switch_to_post($editor_post_id);
415 }
416
417 $result = call_user_func(array(__CLASS__, $ajax_request), $data);
418
419 if (is_wp_error($result)) {
420 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception message is not directly output.
421 throw new \Exception($result->get_error_message());
422 }
423
424 return $result;
425 }
426
427 /**
428 * Get library data.
429 *
430 * Get data for template library.
431 *
432 * @since 1.2.0
433 * @access public
434 *
435 * @param array $args Arguments.
436 *
437 * @return array Collection of templates data.
438 */
439 public static function shoppress_get_library_data(array $args)
440 {
441 $library_data = self::get_library_data(! empty($args['sync']));
442
443 // Ensure all document are registered.
444 Plugin::$instance->documents->get_document_types();
445
446 return array(
447 'templates' => self::get_templates(),
448 'config' => $library_data,
449 );
450 }
451
452 /**
453 * Get templates.
454 *
455 * Retrieve all the templates from all the registered sources.
456 *
457 * @since 1.2.0
458 * @access public
459 *
460 * @return array Templates array.
461 */
462 public static function get_templates()
463 {
464 $source = Plugin::$instance->templates_manager->get_source('product_loop');
465
466 return $source->get_items();
467 }
468
469 /**
470 * Get templates data.
471 *
472 * This function the templates data.
473 *
474 * @since 1.2.0
475 * @access private
476 * @static
477 *
478 * @param bool $force_update Optional. Whether to force the data retrieval or
479 * not. Default is false.
480 *
481 * @return array|false Templates data, or false.
482 */
483 private static function get_templates_data($force_update = false)
484 {
485 $cache_key = 'shoppress_templates_data_' . SHOPPRESS_VERSION;
486
487 $templates_data = get_transient($cache_key);
488
489 if ($force_update || false === $templates_data) {
490 $timeout = ($force_update) ? 25 : 8;
491
492 $response = wp_remote_get(
493 self::$api_url . 'templates.json',
494 array(
495 'timeout' => $timeout,
496 )
497 );
498
499 if (is_wp_error($response) || 200 !== (int) wp_remote_retrieve_response_code($response)) {
500 set_transient($cache_key, array(), 2 * HOUR_IN_SECONDS);
501
502 return false;
503 }
504
505 $templates_data = json_decode(wp_remote_retrieve_body($response), true);
506
507 if (empty($templates_data) || ! is_array($templates_data)) {
508 set_transient($cache_key, array(), 2 * HOUR_IN_SECONDS);
509
510 return false;
511 }
512
513 if (isset($templates_data)) {
514 update_option(self::LIBRARY_OPTION_KEY, $templates_data, 'no');
515 }
516
517 set_transient($cache_key, $templates_data, 12 * HOUR_IN_SECONDS);
518 }
519
520 return $templates_data;
521 }
522
523 /**
524 * Get Presets data.
525 *
526 * This function the Presets data.
527 *
528 * @since 1.2.0
529 * @access public
530 * @static
531 *
532 * @param bool $force_update Optional. Whether to force the data retrieval or
533 * not. Default is false.
534 *
535 * @return array|false Templates data, or false.
536 */
537 public static function get_presets($force_update = false)
538 {
539 $cache_key = 'shoppress_presets_data_' . SHOPPRESS_VERSION;
540
541 $presets_data = get_transient($cache_key);
542
543 if ($force_update || false === $presets_data) {
544 $timeout = ($force_update) ? 25 : 8;
545
546 $response = wp_remote_get(
547 self::$api_url . 'presets/presets.json',
548 array(
549 'timeout' => $timeout,
550 )
551 );
552
553 if (is_wp_error($response) || 200 !== (int) wp_remote_retrieve_response_code($response)) {
554 set_transient($cache_key, array(), 2 * HOUR_IN_SECONDS);
555
556 return false;
557 }
558
559 $presets_data = json_decode(wp_remote_retrieve_body($response), true);
560
561 if (empty($presets_data) || ! is_array($presets_data)) {
562 set_transient($cache_key, array(), 2 * HOUR_IN_SECONDS);
563
564 return false;
565 }
566
567 if (isset($presets_data)) {
568 update_option(self::PRESETS_OPTION_KEY, $presets_data, 'no');
569 }
570
571 set_transient($cache_key, $presets_data, 12 * HOUR_IN_SECONDS);
572 }
573
574 return $presets_data;
575 }
576
577 /**
578 * Get Preset element data.
579 *
580 * This function the Preset element data.
581 *
582 * @since 1.2.0
583 * @access public
584 * @static
585 *
586 * @return array|false Templates data, or false.
587 */
588 public static function shoppress_get_preset_element()
589 {
590
591 if (! check_ajax_referer('shoppress_get_preset_element', 'nonce')) {
592 wp_send_json('Invalid nonce!', 400);
593 wp_die();
594 }
595
596 $element = isset( $_POST['element'] ) ? sanitize_text_field( wp_unslash( $_POST['element'] ) ) : '';
597 $reset = isset( $_POST['reset'] ) ? sanitize_text_field( wp_unslash( $_POST['reset'] ) ) : '';
598
599 $presets = get_option(self::PRESETS_ELEMENTS_OPTION_KEY);
600
601 if (! isset($presets[$element]) || $reset === 'true') {
602
603 $response = wp_remote_get(
604 self::$api_url . 'presets/elements/' . $element . '.json',
605 array('timeout' => 30)
606 );
607
608 if (is_wp_error($response) || 200 !== (int) wp_remote_retrieve_response_code($response)) {
609 wp_send_json('Unexpected Error, Code: ', wp_remote_retrieve_response_code($response));
610 wp_die();
611 }
612
613 $presets_data = json_decode(wp_remote_retrieve_body($response), true);
614
615 if (isset($presets_data)) {
616 $presets[$element] = $presets_data;
617 update_option(self::PRESETS_ELEMENTS_OPTION_KEY, $presets, 'no');
618 }
619
620 $presets = get_option(self::PRESETS_ELEMENTS_OPTION_KEY);
621 wp_send_json($presets, 200);
622 } else {
623 $presets = get_option(self::PRESETS_ELEMENTS_OPTION_KEY);
624 wp_send_json($presets, 200);
625 }
626
627 wp_die();
628 }
629
630 /**
631 * Get templates data.
632 *
633 * Retrieve the templates data from a remote server.
634 *
635 * @since 1.2.0
636 * @access public
637 * @static
638 *
639 * @param bool $force_update Optional. Whether to force the data update or
640 * not. Default is false.
641 *
642 * @return array The templates data.
643 */
644 public static function get_library_data($force_update = false)
645 {
646 self::get_templates_data($force_update);
647
648 $library_data = get_option(self::LIBRARY_OPTION_KEY);
649
650 if (empty($library_data)) {
651 return array();
652 }
653
654 return $library_data;
655 }
656
657 /**
658 * Test the upload of an image from a given URL.
659 *
660 * @since 1.3.8
661 *
662 * @param string $image_url The URL of the image to test.
663 * @return bool Returns true if the image is valid and can be uploaded, false otherwise.
664 */
665 public static function test_image_for_upload($image_url)
666 {
667
668 // Check for if user can download images
669 if (
670 (strpos('katademos.com', $image_url) !== false || strpos('webnus.net', $image_url) !== false) &&
671 ! current_user_can('upload_files')
672 ) {
673 return false;
674 }
675
676 // Check the URL of the image
677 if (! filter_var($image_url, FILTER_VALIDATE_URL)) {
678 return false;
679 }
680
681 // Check the file type of the image
682 $file_type = wp_check_filetype($image_url);
683 if (false === $file_type['type'] && false === $file_type['ext']) {
684 return false;
685 }
686
687 // Check the file extension of the image
688 $file_extension = pathinfo($image_url, PATHINFO_EXTENSION);
689 if (! in_array($file_extension, array('jpg', 'jpeg', 'png', 'gif'))) {
690 return false;
691 }
692
693 // Check the upload directory
694 $upload_dir = wp_upload_dir();
695 if (! is_dir($upload_dir['path'])) {
696 return false;
697 }
698
699 return true;
700 }
701
702 /**
703 * Downloads an image from a given URL and uploads it to the WordPress media library.
704 *
705 * @since 1.2.0
706 *
707 * @param string $image_url The URL of the image to download and upload.
708 *
709 * @return int|false|string The attachment ID of the uploaded image, false if there was an error saving the image, or a string describing the error.
710 */
711 public static function download_and_upload_image($image_url)
712 {
713 // Make sure WordPress core is loaded
714 if (! defined('ABSPATH')) {
715 require_once '../../../../wp-load.php'; // Adjust the path to wp-load.php if necessary
716 }
717
718 // Test the upload of an image from a given URL.
719 static::test_image_for_upload($image_url);
720
721 // Get the file extension from the image URL
722 $file_extension = pathinfo($image_url, PATHINFO_EXTENSION);
723
724 // Generate a unique file name for the downloaded image
725 $unique_file_name = wp_unique_filename(wp_upload_dir()['path'], 'downloaded-image-' . time()) . '.' . $file_extension;
726
727 // Download the image
728 $downloaded_image = download_url($image_url);
729
730 if (! is_wp_error($downloaded_image)) {
731 // Save the downloaded image to the uploads directory
732 $upload_dir = wp_upload_dir();
733 $target_path = $upload_dir['path'] . '/' . $unique_file_name;
734
735 if (copy(strval($downloaded_image), $target_path)) {
736
737 wp_delete_file($downloaded_image);
738
739 // Insert the downloaded image as a media attachment
740 $attachment = array(
741 'post_mime_type' => 'image/' . $file_extension,
742 'post_title' => sanitize_file_name(pathinfo($unique_file_name, PATHINFO_FILENAME)),
743 'post_content' => '',
744 'post_status' => 'inherit',
745 );
746
747 $attachment_id = wp_insert_attachment($attachment, $target_path);
748
749 if (! is_wp_error($attachment_id)) {
750 require_once ABSPATH . 'wp-admin/includes/image.php';
751 $attachment_data = wp_generate_attachment_metadata($attachment_id, $target_path);
752 wp_update_attachment_metadata($attachment_id, $attachment_data);
753
754 return array(
755 'url' => wp_get_attachment_url($attachment_id),
756 'id' => $attachment_id,
757 );
758 } else {
759 return false;
760 }
761 } else {
762 return false;
763 }
764 } else {
765 return false;
766 }
767 }
768
769 /**
770 * Updates the images in the input array.
771 *
772 * @since 1.2.0
773 *
774 * @param array $inputArray The input array containing the content and elements.
775 *
776 * @return array The updated input array with the images updated.
777 */
778 public static function update_images($data)
779 {
780
781 foreach ($data['content'] as $section_key => $section) {
782 foreach ($section['elements'] as $elements_key => $elements) {
783 foreach ($elements as $element_key => $element) {
784 // if it's element/widget
785 if ($element_key === 'elements') {
786 foreach ($element as $widgets_key => $widgets) {
787 foreach ($widgets as $widget_id => $widget) {
788 if (is_array($widget)) {
789 foreach ($widget as $widget_setting_key => $widget_setting) {
790 if (is_array($widget_setting) && ! empty($widget_setting)) {
791
792 // update images for repeater
793 if (isset($widget_setting[0])) {
794 foreach ($widget_setting as $repeater_key => $repeater_value) {
795 if (is_array($repeater_value)) {
796 foreach ($repeater_value as $repeater_item_key => $repeater_item_value) {
797 if (is_array($repeater_item_value) && isset($repeater_item_value['url']) && isset($repeater_item_value['id'])) {
798 $new_image = static::download_and_upload_image(static::update_template_url($repeater_item_value['url']));
799 $data['content'][$section_key]['elements'][$elements_key][$element_key][$widgets_key][$widget_id][$widget_setting_key][$repeater_key][$repeater_item_key]['url'] = $new_image['url'];
800 $data['content'][$section_key]['elements'][$elements_key][$element_key][$widgets_key][$widget_id][$widget_setting_key][$repeater_key][$repeater_item_key]['id'] = $new_image['id'];
801 }
802 }
803 }
804 }
805 }
806
807 // update images
808 if (is_array($widget_setting) && isset($widget_setting['url']) && isset($widget_setting['id'])) {
809 $new_image = static::download_and_upload_image(static::update_template_url($widget_setting['url']));
810 $data['content'][$section_key]['elements'][$elements_key][$element_key][$widgets_key][$widget_id][$widget_setting_key]['url'] = $new_image['url'];
811 $data['content'][$section_key]['elements'][$elements_key][$element_key][$widgets_key][$widget_id][$widget_setting_key]['id'] = $new_image['id'];
812 }
813 }
814 }
815 }
816 }
817 }
818 }
819 }
820 }
821 }
822
823 return $data;
824 }
825
826 /**
827 * Sanitize Elements
828 *
829 * @param array $template_data
830 * @param array|null $types
831 *
832 * @since 1.2.0
833 *
834 * @return array
835 */
836 public static function sanitize_elements($template_data, $types = null)
837 {
838
839 if (! is_array($template_data)) {
840
841 return $template_data;
842 }
843
844 if (is_null($types)) {
845
846 $element_types = array_keys(Plugin::$instance->elements_manager->get_element_types());
847 $widget_types = array_keys(Plugin::$instance->widgets_manager->get_widget_types());
848
849 $types = array_merge($element_types, $widget_types);
850 }
851
852 foreach ($template_data as $k => $data) {
853
854 $elements = $data['elements'] ?? array();
855 if (! empty($elements)) {
856
857 $template_data[$k]['elements'] = array_values(
858 static::sanitize_elements($elements, $types)
859 );
860 } else {
861
862 $widget_type = $data['widgetType'] ?? false;
863 if ($widget_type && ! in_array($widget_type, $types)) {
864 unset($template_data[$k]);
865 }
866 }
867 }
868
869 return $template_data;
870 }
871
872 /**
873 * Get template content.
874 *
875 * Retrieve the templates content received from a remote server.
876 *
877 * @since 1.2.0
878 * @access public
879 * @static
880 *
881 * @param int $template_id The template ID.
882 *
883 * @return object|\WP_Error The template content.
884 */
885 public static function shoppress_get_template_data($template_id)
886 {
887 $url = self::$api_url . 'template/template-' . $template_id['template_id'] . '.json';
888
889 $response = wp_remote_post($url, array('timeout' => 25));
890
891 if (is_wp_error($response)) {
892 // @codingStandardsIgnoreStart WordPress.XSS.EscapeOutput.
893 wp_die($response, [
894 'back_link' => true,
895 ]);
896 // @codingStandardsIgnoreEnd WordPress.XSS.EscapeOutput.
897 }
898
899 $body = wp_remote_retrieve_body($response);
900 $response_code = (int) wp_remote_retrieve_response_code($response);
901
902 if (! $response_code) {
903 return new \WP_Error(500, 'No Response');
904 }
905
906 // Server sent a success message without content.
907 if ('null' === $body) {
908 $body = true;
909 }
910
911 $as_array = true;
912 $body = json_decode($body, $as_array);
913
914 if (false === $body) {
915 return new \WP_Error(422, 'Wrong Server Response');
916 }
917
918 if (200 !== $response_code) {
919 // In case $as_array = true.
920 $body = (object) $body;
921
922 $message = isset($body->message) ? $body->message : wp_remote_retrieve_response_message($response);
923 $code = isset($body->code) ? $body->code : $response_code;
924
925 return new \WP_Error($code, $message);
926 }
927
928 $body['content'] = static::sanitize_elements($body['content']);
929
930 // update images
931 $body = static::update_images($body);
932
933 return $body;
934 }
935
936 /**
937 * Render template.
938 *
939 * Library modal template.
940 *
941 * @since 1.2.0
942 * @access public
943 * @static
944 *
945 * @return void
946 */
947 public static function render_template()
948 {
949 ?>
950 <script type="text/template" id="tmpl-elementor-template-library-header-actions-pp">
951 <div id="elementor-template-library-header-sync" class="elementor-templates-modal__header__item">
952 <i class="eicon-sync" aria-hidden="true" title="<?php esc_attr_e('Sync Templates', 'shop-press'); ?>"></i>
953 <span class="elementor-screen-only"><?php echo esc_html__('Sync Templates', 'shop-press'); ?></span>
954 </div>
955 </script>
956 <script type="text/template" id="tmpl-elementor-templates-modal__header__logo_pp">
957 <span class="elementor-templates-modal__header__logo__title">{{{ title }}}</span>
958 </script>
959 <script type="text/template" id="shoppress-tmpl-elementor-template-library-header-preview-pp">
960 <div id="elementor-template-library-header-preview-insert-wrapper" class="elementor-templates-modal__header__item">
961 {{{ shoppress_templates_lib.templates.layout.getTemplateActionButton( obj ) }}}
962 </div>
963 </script>
964 <script type="text/template" id="tmpl-elementor-template-library-templates-shoppress">
965 <#
966 var activeSource = shoppress_templates_lib.templates.getFilter('source');
967 #>
968 <div id="elementor-template-library-toolbar">
969 <# if ( activeSource ) {
970 var activeType = shoppress_templates_lib.templates.getFilter('type');
971 #>
972 <div id="elementor-template-library-filter-toolbar-remote" class="elementor-template-library-filter-toolbar">
973 <# if ( 'new_page' === activeType ) { #>
974 <div id="elementor-template-library-order">
975 <input type="radio" id="elementor-template-library-order-new" class="elementor-template-library-order-input" name="elementor-template-library-order" value="date">
976 <label for="-new" class="elementor-template-library-order-label"><?php echo esc_html__('New', 'shop-press'); ?></label>
977 <input type="radio" id="elementor-template-library-order-trend" class="elementor-template-library-order-input" name="elementor-template-library-order" value="trendIndex">
978 <label for="elementor-template-library-order-trend" class="elementor-template-library-order-label"><?php echo esc_html__('Trend', 'shop-press'); ?></label>
979 <input type="radio" id="elementor-template-library-order-popular" class="elementor-template-library-order-input" name="elementor-template-library-order" value="popularityIndex">
980 <label for="elementor-template-library-order-popular" class="elementor-template-library-order-label"><?php echo esc_html__('Popular', 'shop-press'); ?></label>
981 </div>
982 <# } else {
983 var config = shoppress_templates_lib.templates.getConfig( activeType );
984 if ( config.categories ) { #>
985 <div id="elementor-template-library-filter">
986 <select id="elementor-template-library-filter-subtype" class="elementor-template-library-filter-select" data-elementor-filter="subtype">
987 <option></option>
988 <# config.categories.forEach( function( category ) {
989 var selected = category === shoppress_templates_lib.templates.getFilter( 'subtype' ) ? ' selected' : '';
990 #>
991 <option value="{{ category }}"{{{ selected }}}>{{{ category }}}</option>
992 <# } ); #>
993 </select>
994 </div>
995 <# }
996 } #>
997 <div id="elementor-template-library-my-favorites">
998 <# var checked = shoppress_templates_lib.templates.getFilter( 'favorite' ) ? ' checked' : ''; #>
999 <input id="elementor-template-library-filter-my-favorites" type="checkbox"{{{ checked }}}>
1000 <label id="elementor-template-library-filter-my-favorites-label" for="elementor-template-library-filter-my-favorites">
1001 <i class="eicon" aria-hidden="true"></i>
1002 <?php echo esc_html__('My Favorites', 'shop-press'); ?>
1003 </label>
1004 </div>
1005 </div>
1006 <# } #>
1007 <div id="elementor-template-library-filter-text-wrapper">
1008 <label for="elementor-template-library-filter-text" class="elementor-screen-only"><?php echo esc_html__('Search Templates:', 'shop-press'); ?></label>
1009 <input id="elementor-template-library-filter-text" placeholder="<?php echo esc_attr__('Search', 'shop-press'); ?>">
1010 <i class="eicon-search"></i>
1011 </div>
1012 </div>
1013 <#
1014 var is_kata = shoppress_templates_lib.is_kata ? true : false;
1015 if ( ! is_kata ) {
1016 #>
1017 <div id="shoppress-elementor-template-library-templates-kata-popup-notice">
1018 <div id="shoppress-elementor-template-library-templates-kata-popup-notice-wrapper">
1019 <i id="shoppress-elementor-template-library-templates-kata-popup-notice-close" class="eicon-close" aria-hidden="true"></i>
1020 <div id="shoppress-elementor-template-library-templates-kata-popup-notice-content">
1021 <h3><?php esc_html_e('Please Note!', 'shop-press'); ?></h3>
1022 <p><?php esc_html_e('This template you\'re seeing in the image has been crafted using the Kata theme.', 'shop-press'); ?></p>
1023 <p><?php esc_html_e('As you may be aware, every theme possesses its own distinctive style, so it\'s possible that you\'ll encounter a divergent style upon import.', 'shop-press'); ?></p>
1024 </div>
1025 <div id="shoppress-elementor-template-library-templates-kata-popup-notice-actions">
1026 <a href="#" data-action="import"><?php esc_html_e('Import Now', 'shop-press'); ?></a>
1027 <a href="#" data-action="cancel"><?php esc_html_e('Cancel', 'shop-press'); ?></a>
1028 <a href="https://wordpress.org/themes/kata/" data-action="get-kata" target="_blank"><?php esc_html_e('Get Kata Theme', 'shop-press'); ?></a>
1029 </div>
1030 </div>
1031 </div>
1032 <# } #>
1033
1034 <div id="elementor-template-library-templates-container"></div>
1035
1036 <# if ( 'content' === activeSource ) { #>
1037 <div id="elementor-template-library-footer-banner">
1038 <img class="elementor-nerd-box-icon" src="<?php echo esc_url(ELEMENTOR_ASSETS_URL . 'images/information.svg'); ?>" />
1039 <div class="elementor-excerpt"><?php echo esc_html__('Stay tuned! More awesome templates coming real soon.', 'shop-press'); ?></div>
1040 </div>
1041 <# } #>
1042 </script>
1043 <script type="text/template" id="shoppress-tmpl-elementor-template-library-template-pp">
1044 <div class="elementor-template-library-template-body">
1045 <# if ( 'page' === type ) { #>
1046 <div class="elementor-template-library-template-screenshot" style="background-image: url({{ thumbnail }});"></div>
1047 <# } else { #>
1048 <img src="{{ thumbnail }}">
1049 <# } #>
1050 <div class="elementor-template-library-template-preview">
1051 <i class="eicon-zoom-in-bold" aria-hidden="true"></i>
1052 </div>
1053 </div>
1054 <div class="elementor-template-library-template-footer">
1055 {{{ shoppress_templates_lib.templates.layout.getTemplateActionButton( obj ) }}}
1056 <div class="elementor-template-library-template-name">{{{ title }}} - {{{ type }}}</div>
1057 <div class="elementor-template-library-favorite">
1058 <input id="elementor-template-library-template-{{ template_id }}-favorite-input" class="elementor-template-library-template-favorite-input" type="checkbox"{{ favorite ? " checked" : "" }}>
1059 <label for="elementor-template-library-template-{{ template_id }}-favorite-input" class="elementor-template-library-template-favorite-label">
1060 <i class="eicon-heart-o" aria-hidden="true"></i>
1061 <span class="elementor-screen-only"><?php echo esc_html__('Favorite', 'shop-press'); ?></span>
1062 </label>
1063 </div>
1064 </div>
1065 </script>
1066 <script type="text/template" id="tmpl-elementor-template-library-get-pro-button-pp">
1067 <a class="elementor-template-library-template-action elementor-button elementor-go-pro" href="https://powerpackelements.com/pricing/?utm_source=panel-library&utm_campaign=gopro&utm_medium=wp-dash" target="_blank">
1068 <i class="eicon-external-link-square" aria-hidden="true"></i>
1069 <span class="elementor-button-title"><?php echo esc_html(__('Go Pro', 'shop-press')); ?></span>
1070 </a>
1071 </script>
1072 <script type="text/template" id="tmpl-elementor-pro-template-library-activate-license-button-pp">
1073 <a class="elementor-template-library-template-action elementor-button elementor-go-pro" href="#" target="_blank">
1074 <i class="eicon-external-link-square"></i>
1075 <span class="elementor-button-title"><?php esc_html_e('Activate License', 'shop-press'); ?></span>
1076 </a>
1077 </script>
1078 <?php
1079 }
1080 }
1081
1082 TemplatesLib::init();
1083