PluginProbe
Elementor Website Builder – more than just a page builder / 3.11.5
Elementor Website Builder – more than just a page builder v3.11.5
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / template-library / manager.php

manager.php in Elementor Website Builder – more than just a page builder 3.11.5, at includes/template-library/manager.php

714 lines 17.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\TemplateLibrary;
3
4 use Elementor\Api;
5 use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
6 use Elementor\Core\Settings\Manager as SettingsManager;
7 use Elementor\Includes\TemplateLibrary\Data\Controller;
8 use Elementor\TemplateLibrary\Classes\Import_Images;
9 use Elementor\Plugin;
10 use Elementor\User;
11 use Elementor\Utils;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Elementor template library manager.
19 *
20 * Elementor template library manager handler class is responsible for
21 * initializing the template library.
22 *
23 * @since 1.0.0
24 */
25 class Manager {
26
27 /**
28 * Registered template sources.
29 *
30 * Holds a list of all the supported sources with their instances.
31 *
32 * @access protected
33 *
34 * @var Source_Base[]
35 */
36 protected $_registered_sources = []; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
37
38 /**
39 * Imported template images.
40 *
41 * Holds an instance of `Import_Images` class.
42 *
43 * @access private
44 *
45 * @var Import_Images
46 */
47 private $_import_images = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
48
49 /**
50 * Template library manager constructor.
51 *
52 * Initializing the template library manager by registering default template
53 * sources and initializing ajax calls.
54 *
55 * @since 1.0.0
56 * @access public
57 */
58 public function __construct() {
59 Plugin::$instance->data_manager_v2->register_controller( new Controller() );
60
61 $this->register_default_sources();
62
63 $this->add_actions();
64 }
65
66 /**
67 * @since 2.3.0
68 * @access public
69 */
70 public function add_actions() {
71 add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
72 add_action( 'wp_ajax_elementor_library_direct_actions', [ $this, 'handle_direct_actions' ] );
73 }
74
75 /**
76 * Get `Import_Images` instance.
77 *
78 * Retrieve the instance of the `Import_Images` class.
79 *
80 * @since 1.0.0
81 * @access public
82 *
83 * @return Import_Images Imported images instance.
84 */
85 public function get_import_images_instance() {
86 if ( null === $this->_import_images ) {
87 $this->_import_images = new Import_Images();
88 }
89
90 return $this->_import_images;
91 }
92
93 /**
94 * Register template source.
95 *
96 * Used to register new template sources displayed in the template library.
97 *
98 * @since 1.0.0
99 * @access public
100 *
101 * @param string $source_class The name of source class.
102 * @param array $args Optional. Class arguments. Default is an
103 * empty array.
104 *
105 * @return \WP_Error|true True if the source was registered, `WP_Error`
106 * otherwise.
107 */
108 public function register_source( $source_class, $args = [] ) {
109 if ( ! class_exists( $source_class ) ) {
110 return new \WP_Error( 'source_class_name_not_exists' );
111 }
112
113 $source_instance = new $source_class( $args );
114
115 if ( ! $source_instance instanceof Source_Base ) {
116 return new \WP_Error( 'wrong_instance_source' );
117 }
118
119 $source_id = $source_instance->get_id();
120
121 if ( isset( $this->_registered_sources[ $source_id ] ) ) {
122 return new \WP_Error( 'source_exists' );
123 }
124
125 $this->_registered_sources[ $source_id ] = $source_instance;
126
127 return true;
128 }
129
130 /**
131 * Unregister template source.
132 *
133 * Remove an existing template sources from the list of registered template
134 * sources.
135 *
136 * @deprecated 2.7.0
137 *
138 * @since 1.0.0
139 * @access public
140 *
141 * @param string $id The source ID.
142 *
143 * @return bool Whether the source was unregistered.
144 */
145 public function unregister_source( $id ) {
146 return true;
147 }
148
149 /**
150 * Get registered template sources.
151 *
152 * Retrieve registered template sources.
153 *
154 * @since 1.0.0
155 * @access public
156 *
157 * @return Source_Base[] Registered template sources.
158 */
159 public function get_registered_sources() {
160 return $this->_registered_sources;
161 }
162
163 /**
164 * Get template source.
165 *
166 * Retrieve single template sources for a given template ID.
167 *
168 * @since 1.0.0
169 * @access public
170 *
171 * @param string $id The source ID.
172 *
173 * @return false|Source_Base Template sources if one exist, False otherwise.
174 */
175 public function get_source( $id ) {
176 $sources = $this->get_registered_sources();
177
178 if ( ! isset( $sources[ $id ] ) ) {
179 return false;
180 }
181
182 return $sources[ $id ];
183 }
184
185 /**
186 * Get templates.
187 *
188 * Retrieve all the templates from all the registered sources.
189 *
190 * @param array $filter_sources
191 * @param bool $force_update
192 * @return array
193 */
194 public function get_templates( array $filter_sources = [], bool $force_update = false ): array {
195 $templates = [];
196
197 foreach ( $this->get_registered_sources() as $source ) {
198 if ( ! empty( $filter_sources ) && ! in_array( $source->get_id(), $filter_sources, true ) ) {
199 continue;
200 }
201
202 $templates = array_merge( $templates, $source->get_items( [ 'force_update' => $force_update ] ) );
203 }
204
205 return $templates;
206 }
207
208 /**
209 * Get library data.
210 *
211 * Retrieve the library data.
212 *
213 * @since 1.9.0
214 * @access public
215 *
216 * @param array $args Library arguments.
217 *
218 * @return array Library data.
219 */
220 public function get_library_data( array $args ) {
221 $library_data = Api::get_library_data( ! empty( $args['sync'] ) );
222
223 if ( empty( $library_data ) ) {
224 return $library_data;
225 }
226
227 // Ensure all document are registered.
228 Plugin::$instance->documents->get_document_types();
229
230 $filter_sources = ! empty( $args['filter_sources'] ) ? $args['filter_sources'] : [];
231 $force_update = ! empty( $args['sync'] );
232
233 return [
234 'templates' => $this->get_templates( $filter_sources, $force_update ),
235 'config' => $library_data['types_data'],
236 ];
237 }
238
239 /**
240 * Save template.
241 *
242 * Save new or update existing template on the database.
243 *
244 * @since 1.0.0
245 * @access public
246 *
247 * @param array $args Template arguments.
248 *
249 * @return \WP_Error|int The ID of the saved/updated template.
250 */
251 public function save_template( array $args ) {
252 $validate_args = $this->ensure_args( [ 'post_id', 'source', 'content', 'type' ], $args );
253
254 if ( is_wp_error( $validate_args ) ) {
255 return $validate_args;
256 }
257
258 $source = $this->get_source( $args['source'] );
259
260 if ( ! $source ) {
261 return new \WP_Error( 'template_error', 'Template source not found.' );
262 }
263
264 $args['content'] = json_decode( $args['content'], true );
265
266 $page = SettingsManager::get_settings_managers( 'page' )->get_model( $args['post_id'] );
267
268 $args['page_settings'] = $page->get_data( 'settings' );
269
270 $template_id = $source->save_item( $args );
271
272 if ( is_wp_error( $template_id ) ) {
273 return $template_id;
274 }
275
276 return $source->get_item( $template_id );
277 }
278
279 /**
280 * Update template.
281 *
282 * Update template on the database.
283 *
284 * @since 1.0.0
285 * @access public
286 *
287 * @param array $template_data New template data.
288 *
289 * @return \WP_Error|Source_Base Template sources instance if the templates
290 * was updated, `WP_Error` otherwise.
291 */
292 public function update_template( array $template_data ) {
293 $validate_args = $this->ensure_args( [ 'source', 'content', 'type' ], $template_data );
294
295 if ( is_wp_error( $validate_args ) ) {
296 return $validate_args;
297 }
298
299 $source = $this->get_source( $template_data['source'] );
300
301 if ( ! $source ) {
302 return new \WP_Error( 'template_error', 'Template source not found.' );
303 }
304
305 $template_data['content'] = json_decode( $template_data['content'], true );
306
307 $update = $source->update_item( $template_data );
308
309 if ( is_wp_error( $update ) ) {
310 return $update;
311 }
312
313 return $source->get_item( $template_data['id'] );
314 }
315
316 /**
317 * Update templates.
318 *
319 * Update template on the database.
320 *
321 * @since 1.0.0
322 * @access public
323 *
324 * @param array $args Template arguments.
325 *
326 * @return \WP_Error|true True if templates updated, `WP_Error` otherwise.
327 */
328 public function update_templates( array $args ) {
329 foreach ( $args['templates'] as $template_data ) {
330 $result = $this->update_template( $template_data );
331
332 if ( is_wp_error( $result ) ) {
333 return $result;
334 }
335 }
336
337 return true;
338 }
339
340 /**
341 * Get template data.
342 *
343 * Retrieve the template data.
344 *
345 * @since 1.5.0
346 * @access public
347 *
348 * @param array $args Template arguments.
349 *
350 * @return \WP_Error|bool|array ??
351 */
352 public function get_template_data( array $args ) {
353 $validate_args = $this->ensure_args( [ 'source', 'template_id' ], $args );
354
355 if ( is_wp_error( $validate_args ) ) {
356 return $validate_args;
357 }
358
359 if ( isset( $args['edit_mode'] ) ) {
360 Plugin::$instance->editor->set_edit_mode( $args['edit_mode'] );
361 }
362
363 $source = $this->get_source( $args['source'] );
364
365 if ( ! $source ) {
366 return new \WP_Error( 'template_error', 'Template source not found.' );
367 }
368
369 do_action( 'elementor/template-library/before_get_source_data', $args, $source );
370
371 $data = $source->get_data( $args );
372
373 do_action( 'elementor/template-library/after_get_source_data', $args, $source );
374
375 return $data;
376 }
377
378 /**
379 * Delete template.
380 *
381 * Delete template from the database.
382 *
383 * @since 1.0.0
384 * @access public
385 *
386 * @param array $args Template arguments.
387 *
388 * @return \WP_Post|\WP_Error|false|null Post data on success, false or null
389 * or 'WP_Error' on failure.
390 */
391 public function delete_template( array $args ) {
392 $validate_args = $this->ensure_args( [ 'source', 'template_id' ], $args );
393
394 if ( is_wp_error( $validate_args ) ) {
395 return $validate_args;
396 }
397
398 $source = $this->get_source( $args['source'] );
399
400 if ( ! $source ) {
401 return new \WP_Error( 'template_error', 'Template source not found.' );
402 }
403
404 return $source->delete_template( $args['template_id'] );
405 }
406
407 /**
408 * Export template.
409 *
410 * Export template to a file.
411 *
412 * @since 1.0.0
413 * @access public
414 *
415 * @param array $args Template arguments.
416 *
417 * @return mixed Whether the export succeeded or failed.
418 */
419 public function export_template( array $args ) {
420 $validate_args = $this->ensure_args( [ 'source', 'template_id' ], $args );
421
422 if ( is_wp_error( $validate_args ) ) {
423 return $validate_args;
424 }
425
426 $source = $this->get_source( $args['source'] );
427
428 if ( ! $source ) {
429 return new \WP_Error( 'template_error', 'Template source not found' );
430 }
431
432 return $source->export_template( $args['template_id'] );
433 }
434
435 /**
436 * @since 2.3.0
437 * @access public
438 */
439 public function direct_import_template() {
440 /** @var Source_Local $source */
441 $source = $this->get_source( 'local' );
442 $file = Utils::get_super_global_value( $_FILES, 'file' );
443 return $source->import_template( $file['name'], $file['tmp_name'] );
444 }
445
446 /**
447 * Import template.
448 *
449 * Import template from a file.
450 *
451 * @since 1.0.0
452 * @access public
453 *
454 * @param array $data
455 *
456 * @return mixed Whether the export succeeded or failed.
457 */
458 public function import_template( array $data ) {
459 // If the template is a JSON file, allow uploading it.
460 add_filter( 'elementor/files/allow-file-type/json', [ $this, 'enable_json_template_upload' ] );
461 add_filter( 'elementor/files/allow_unfiltered_upload', [ $this, 'enable_json_template_upload' ] );
462
463 // Imported templates can be either JSON files, or Zip files containing multiple JSON files
464 $upload_result = Plugin::$instance->uploads_manager->handle_elementor_upload( $data, [ 'zip', 'json' ] );
465
466 remove_filter( 'elementor/files/allow-file-type/json', [ $this, 'enable_json_template_upload' ] );
467 remove_filter( 'elementor/files/allow_unfiltered_upload', [ $this, 'enable_json_template_upload' ] );
468
469 if ( is_wp_error( $upload_result ) ) {
470 Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $upload_result['tmp_name'] ) );
471
472 return $upload_result;
473 }
474
475 /** @var Source_Local $source_local */
476 $source_local = $this->get_source( 'local' );
477
478 $import_result = $source_local->import_template( $upload_result['name'], $upload_result['tmp_name'] );
479
480 // Remove the temporary directory generated for the stream-uploaded file.
481 Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $upload_result['tmp_name'] ) );
482
483 return $import_result;
484 }
485
486 /**
487 * Enable JSON Template Upload
488 *
489 * Runs on the 'elementor/files/allow-file-type/json' Uploads Manager filter.
490 *
491 * @since 3.5.0
492 * @access public
493 *
494 * return bool
495 */
496 public function enable_json_template_upload() {
497 return true;
498 }
499
500 /**
501 * Mark template as favorite.
502 *
503 * Add the template to the user favorite templates.
504 *
505 * @since 1.9.0
506 * @access public
507 *
508 * @param array $args Template arguments.
509 *
510 * @return mixed Whether the template marked as favorite.
511 */
512 public function mark_template_as_favorite( $args ) {
513 $validate_args = $this->ensure_args( [ 'source', 'template_id', 'favorite' ], $args );
514
515 if ( is_wp_error( $validate_args ) ) {
516 return $validate_args;
517 }
518
519 $source = $this->get_source( $args['source'] );
520
521 return $source->mark_as_favorite( $args['template_id'], filter_var( $args['favorite'], FILTER_VALIDATE_BOOLEAN ) );
522 }
523
524 public function import_from_json( array $args ) {
525 $validate_args = $this->ensure_args( [ 'editor_post_id', 'elements' ], $args );
526
527 if ( is_wp_error( $validate_args ) ) {
528 return $validate_args;
529 }
530
531 $elements = json_decode( $args['elements'], true );
532
533 $document = Plugin::$instance->documents->get( $args['editor_post_id'] );
534 if ( ! $document ) {
535 return new \WP_Error( 'template_error', 'Document not found.' );
536 }
537
538 $import_data = $document->get_import_data( [ 'content' => $elements ] );
539
540 return $import_data['content'];
541 }
542
543 /**
544 * Register default template sources.
545 *
546 * Register the 'local' and 'remote' template sources that Elementor use by
547 * default.
548 *
549 * @since 1.0.0
550 * @access private
551 */
552 private function register_default_sources() {
553 $sources = [
554 'local',
555 'remote',
556 ];
557
558 foreach ( $sources as $source_filename ) {
559 $class_name = ucwords( $source_filename );
560 $class_name = str_replace( '-', '_', $class_name );
561
562 $this->register_source( __NAMESPACE__ . '\Source_' . $class_name );
563 }
564 }
565
566 /**
567 * Handle ajax request.
568 *
569 * Fire authenticated ajax actions for any given ajax request.
570 *
571 * @since 1.0.0
572 * @access private
573 *
574 * @param string $ajax_request Ajax request.
575 *
576 * @param array $data
577 *
578 * @return mixed
579 * @throws \Exception
580 */
581 private function handle_ajax_request( $ajax_request, array $data ) {
582 if ( ! User::is_current_user_can_edit_post_type( Source_Local::CPT ) ) {
583 throw new \Exception( 'Access denied.' );
584 }
585
586 if ( ! empty( $data['editor_post_id'] ) ) {
587 $editor_post_id = absint( $data['editor_post_id'] );
588
589 if ( ! get_post( $editor_post_id ) ) {
590 throw new \Exception( 'Post not found.' );
591 }
592
593 Plugin::$instance->db->switch_to_post( $editor_post_id );
594 }
595
596 $result = call_user_func( [ $this, $ajax_request ], $data );
597
598 if ( is_wp_error( $result ) ) {
599 throw new \Exception( $result->get_error_message() );
600 }
601
602 return $result;
603 }
604
605 /**
606 * Init ajax calls.
607 *
608 * Initialize template library ajax calls for allowed ajax requests.
609 *
610 * @since 2.3.0
611 * @access public
612 *
613 * @param Ajax $ajax
614 */
615 public function register_ajax_actions( Ajax $ajax ) {
616 $library_ajax_requests = [
617 'get_library_data',
618 'get_template_data',
619 'save_template',
620 'update_templates',
621 'delete_template',
622 'import_template',
623 'mark_template_as_favorite',
624 'import_from_json',
625 ];
626
627 foreach ( $library_ajax_requests as $ajax_request ) {
628 $ajax->register_ajax_action( $ajax_request, function( $data ) use ( $ajax_request ) {
629 return $this->handle_ajax_request( $ajax_request, $data );
630 } );
631 }
632 }
633
634 /**
635 * @since 2.3.0
636 * @access public
637 */
638 public function handle_direct_actions() {
639 if ( ! User::is_current_user_can_edit_post_type( Source_Local::CPT ) ) {
640 return;
641 }
642
643 /** @var Ajax $ajax */
644 $ajax = Plugin::$instance->common->get_component( 'ajax' );
645
646 if ( ! $ajax->verify_request_nonce() ) {
647 $this->handle_direct_action_error( 'Access Denied' );
648 }
649
650 $action = Utils::get_super_global_value( $_REQUEST, 'library_action' ); // phpcs:ignore -- Nonce already verified.
651
652 $result = $this->$action( $_REQUEST ); // phpcs:ignore -- Nonce already verified.
653
654 if ( is_wp_error( $result ) ) {
655 /** @var \WP_Error $result */
656 $this->handle_direct_action_error( $result->get_error_message() . '.' );
657 }
658
659 $callback = "on_{$action}_success";
660
661 if ( method_exists( $this, $callback ) ) {
662 $this->$callback( $result );
663 }
664
665 die;
666 }
667
668 /**
669 * On successful template import.
670 *
671 * Redirect the user to the template library after template import was
672 * successful finished.
673 *
674 * @since 2.3.0
675 * @access private
676 */
677 private function on_direct_import_template_success() {
678 wp_safe_redirect( admin_url( Source_Local::ADMIN_MENU_SLUG ) );
679 }
680
681 /**
682 * @since 2.3.0
683 * @access private
684 */
685 private function handle_direct_action_error( $message ) {
686 _default_wp_die_handler( $message, 'Elementor Library' );
687 }
688
689 /**
690 * Ensure arguments exist.
691 *
692 * Checks whether the required arguments exist in the specified arguments.
693 *
694 * @since 1.0.0
695 * @access private
696 *
697 * @param array $required_args Required arguments to check whether they
698 * exist.
699 * @param array $specified_args The list of all the specified arguments to
700 * check against.
701 *
702 * @return \WP_Error|true True on success, 'WP_Error' otherwise.
703 */
704 private function ensure_args( array $required_args, array $specified_args ) {
705 $not_specified_args = array_diff( $required_args, array_keys( $specified_args ) );
706
707 if ( $not_specified_args ) {
708 return new \WP_Error( 'arguments_not_specified', sprintf( 'The required argument(s) "%s" not specified.', implode( ', ', $not_specified_args ) ) );
709 }
710
711 return true;
712 }
713 }
714