PluginProbe
Elementor Website Builder – more than just a page builder / 3.3.0
Elementor Website Builder – more than just a page builder v3.3.0
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 / sources / local.php

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

1,649 lines 43.9 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\Core\Base\Document;
5 use Elementor\Core\Editor\Editor;
6 use Elementor\Core\Files\File_Types\Zip;
7 use Elementor\DB;
8 use Elementor\Core\Settings\Manager as SettingsManager;
9 use Elementor\Core\Settings\Page\Model;
10 use Elementor\Modules\Library\Documents\Library_Document;
11 use Elementor\Plugin;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Elementor template library local source.
19 *
20 * Elementor template library local source handler class is responsible for
21 * handling local Elementor templates saved by the user locally on his site.
22 *
23 * @since 1.0.0
24 */
25 class Source_Local extends Source_Base {
26
27 /**
28 * Elementor template-library post-type slug.
29 */
30 const CPT = 'elementor_library';
31
32 /**
33 * Elementor template-library taxonomy slug.
34 */
35 const TAXONOMY_TYPE_SLUG = 'elementor_library_type';
36
37 /**
38 * Elementor template-library category slug.
39 */
40 const TAXONOMY_CATEGORY_SLUG = 'elementor_library_category';
41
42 /**
43 * Elementor template-library meta key.
44 * @deprecated 2.3.0 Use \Elementor\Core\Base\Document::TYPE_META_KEY instead
45 */
46 const TYPE_META_KEY = '_elementor_template_type';
47
48 /**
49 * Elementor template-library temporary files folder.
50 */
51 const TEMP_FILES_DIR = 'elementor/tmp';
52
53 /**
54 * Elementor template-library bulk export action name.
55 */
56 const BULK_EXPORT_ACTION = 'elementor_export_multiple_templates';
57
58 const ADMIN_MENU_SLUG = 'edit.php?post_type=elementor_library';
59
60 const ADMIN_SCREEN_ID = 'edit-elementor_library';
61
62 /**
63 * Template types.
64 *
65 * Holds the list of supported template types that can be displayed.
66 *
67 * @access private
68 * @static
69 *
70 * @var array
71 */
72 private static $template_types = [];
73
74 /**
75 * Post type object.
76 *
77 * Holds the post type object of the current post.
78 *
79 * @access private
80 *
81 * @var \WP_Post_Type
82 */
83 private $post_type_object;
84
85 /**
86 * @since 2.3.0
87 * @access public
88 * @static
89 * @return array
90 */
91 public static function get_template_types() {
92 return self::$template_types;
93 }
94
95 /**
96 * Get local template type.
97 *
98 * Retrieve the template type from the post meta.
99 *
100 * @since 1.0.0
101 * @access public
102 * @static
103 *
104 * @param int $template_id The template ID.
105 *
106 * @return mixed The value of meta data field.
107 */
108 public static function get_template_type( $template_id ) {
109 return get_post_meta( $template_id, Document::TYPE_META_KEY, true );
110 }
111
112 /**
113 * Is base templates screen.
114 *
115 * Whether the current screen base is edit and the post type is template.
116 *
117 * @since 1.0.0
118 * @access public
119 * @static
120 *
121 * @return bool True on base templates screen, False otherwise.
122 */
123 public static function is_base_templates_screen() {
124 global $current_screen;
125
126 if ( ! $current_screen ) {
127 return false;
128 }
129
130 return 'edit' === $current_screen->base && self::CPT === $current_screen->post_type;
131 }
132
133 /**
134 * Add template type.
135 *
136 * Register new template type to the list of supported local template types.
137 *
138 * @since 1.0.3
139 * @access public
140 * @static
141 *
142 * @param string $type Template type.
143 */
144 public static function add_template_type( $type ) {
145 self::$template_types[ $type ] = $type;
146 }
147
148 /**
149 * Remove template type.
150 *
151 * Remove existing template type from the list of supported local template
152 * types.
153 *
154 * @since 1.8.0
155 * @access public
156 * @static
157 *
158 * @param string $type Template type.
159 */
160 public static function remove_template_type( $type ) {
161 if ( isset( self::$template_types[ $type ] ) ) {
162 unset( self::$template_types[ $type ] );
163 }
164 }
165
166 public static function get_admin_url( $relative = false ) {
167 $base_url = self::ADMIN_MENU_SLUG;
168 if ( ! $relative ) {
169 $base_url = admin_url( $base_url );
170 }
171
172 return add_query_arg( 'tabs_group', 'library', $base_url );
173 }
174
175 /**
176 * Get local template ID.
177 *
178 * Retrieve the local template ID.
179 *
180 * @since 1.0.0
181 * @access public
182 *
183 * @return string The local template ID.
184 */
185 public function get_id() {
186 return 'local';
187 }
188
189 /**
190 * Get local template title.
191 *
192 * Retrieve the local template title.
193 *
194 * @since 1.0.0
195 * @access public
196 *
197 * @return string The local template title.
198 */
199 public function get_title() {
200 return __( 'Local', 'elementor' );
201 }
202
203 /**
204 * Register local template data.
205 *
206 * Used to register custom template data like a post type, a taxonomy or any
207 * other data.
208 *
209 * The local template class registers a new `elementor_library` post type
210 * and an `elementor_library_type` taxonomy. They are used to store data for
211 * local templates saved by the user on his site.
212 *
213 * @since 1.0.0
214 * @access public
215 */
216 public function register_data() {
217 $labels = [
218 'name' => _x( 'My Templates', 'Template Library', 'elementor' ),
219 'singular_name' => _x( 'Template', 'Template Library', 'elementor' ),
220 'add_new' => _x( 'Add New', 'Template Library', 'elementor' ),
221 'add_new_item' => _x( 'Add New Template', 'Template Library', 'elementor' ),
222 'edit_item' => _x( 'Edit Template', 'Template Library', 'elementor' ),
223 'new_item' => _x( 'New Template', 'Template Library', 'elementor' ),
224 'all_items' => _x( 'All Templates', 'Template Library', 'elementor' ),
225 'view_item' => _x( 'View Template', 'Template Library', 'elementor' ),
226 'search_items' => _x( 'Search Template', 'Template Library', 'elementor' ),
227 'not_found' => _x( 'No Templates found', 'Template Library', 'elementor' ),
228 'not_found_in_trash' => _x( 'No Templates found in Trash', 'Template Library', 'elementor' ),
229 'parent_item_colon' => '',
230 'menu_name' => _x( 'Templates', 'Template Library', 'elementor' ),
231 ];
232
233 $args = [
234 'labels' => $labels,
235 'public' => true,
236 'rewrite' => false,
237 'menu_icon' => 'dashicons-admin-page',
238 'show_ui' => true,
239 'show_in_menu' => true,
240 'show_in_nav_menus' => false,
241 'exclude_from_search' => true,
242 'capability_type' => 'post',
243 'hierarchical' => false,
244 'supports' => [ 'title', 'thumbnail', 'author', 'elementor' ],
245 ];
246
247 /**
248 * Register template library post type args.
249 *
250 * Filters the post type arguments when registering elementor template library post type.
251 *
252 * @since 1.0.0
253 *
254 * @param array $args Arguments for registering a post type.
255 */
256 $args = apply_filters( 'elementor/template_library/sources/local/register_post_type_args', $args );
257
258 $this->post_type_object = register_post_type( self::CPT, $args );
259
260 $args = [
261 'hierarchical' => false,
262 'show_ui' => false,
263 'show_in_nav_menus' => false,
264 'show_admin_column' => true,
265 'query_var' => is_admin(),
266 'rewrite' => false,
267 'public' => false,
268 'label' => _x( 'Type', 'Template Library', 'elementor' ),
269 ];
270
271 /**
272 * Register template library taxonomy args.
273 *
274 * Filters the taxonomy arguments when registering elementor template library taxonomy.
275 *
276 * @since 1.0.0
277 *
278 * @param array $args Arguments for registering a taxonomy.
279 */
280 $args = apply_filters( 'elementor/template_library/sources/local/register_taxonomy_args', $args );
281
282 $cpts_to_associate = apply_filters( 'elementor/template_library/sources/local/register_taxonomy_cpts', [ self::CPT ] );
283
284 register_taxonomy( self::TAXONOMY_TYPE_SLUG, $cpts_to_associate, $args );
285
286 /**
287 * Categories
288 */
289 $args = [
290 'hierarchical' => true,
291 'show_ui' => true,
292 'show_in_nav_menus' => false,
293 'show_admin_column' => true,
294 'query_var' => is_admin(),
295 'rewrite' => false,
296 'public' => false,
297 'labels' => [
298 'name' => _x( 'Categories', 'Template Library', 'elementor' ),
299 'singular_name' => _x( 'Category', 'Template Library', 'elementor' ),
300 'all_items' => _x( 'All Categories', 'Template Library', 'elementor' ),
301 ],
302 ];
303
304 /**
305 * Register template library category args.
306 *
307 * Filters the category arguments when registering elementor template library category.
308 *
309 * @since 2.4.0
310 *
311 * @param array $args Arguments for registering a category.
312 */
313 $args = apply_filters( 'elementor/template_library/sources/local/register_category_args', $args );
314
315 register_taxonomy( self::TAXONOMY_CATEGORY_SLUG, self::CPT, $args );
316 }
317
318 /**
319 * Remove Add New item from admin menu.
320 *
321 * Fired by `admin_menu` action.
322 *
323 * @since 2.4.0
324 * @access public
325 */
326 public function admin_menu_reorder() {
327 global $submenu;
328
329 if ( ! isset( $submenu[ self::ADMIN_MENU_SLUG ] ) ) {
330 return;
331 }
332 $library_submenu = &$submenu[ self::ADMIN_MENU_SLUG ];
333
334 // Remove 'All Templates' menu.
335 unset( $library_submenu[5] );
336
337 // If current use can 'Add New' - move the menu to end, and add the '#add_new' anchor.
338 if ( isset( $library_submenu[10][2] ) ) {
339 $library_submenu[700] = $library_submenu[10];
340 unset( $library_submenu[10] );
341 $library_submenu[700][2] = admin_url( self::ADMIN_MENU_SLUG . '#add_new' );
342 }
343
344 // Move the 'Categories' menu to end.
345 if ( isset( $library_submenu[15] ) ) {
346 $library_submenu[800] = $library_submenu[15];
347 unset( $library_submenu[15] );
348 }
349
350 if ( $this->is_current_screen() ) {
351 $library_title = $this->get_library_title();
352
353 foreach ( $library_submenu as &$item ) {
354 if ( $library_title === $item[0] ) {
355 if ( ! isset( $item[4] ) ) {
356 $item[4] = '';
357 }
358 $item[4] .= ' current';
359 }
360 }
361 }
362 }
363
364 public function admin_menu() {
365 add_submenu_page( self::ADMIN_MENU_SLUG, '', __( 'Saved Templates', 'elementor' ), Editor::EDITING_CAPABILITY, self::get_admin_url( true ) );
366 }
367
368 public function admin_title( $admin_title, $title ) {
369 $library_title = $this->get_library_title();
370
371 if ( $library_title ) {
372 $admin_title = str_replace( $title, $library_title, $admin_title );
373 }
374
375 return $admin_title;
376 }
377
378 public function replace_admin_heading() {
379 $library_title = $this->get_library_title();
380
381 if ( $library_title ) {
382 global $post_type_object;
383
384 $post_type_object->labels->name = $library_title;
385 }
386 }
387
388 /**
389 * Get local templates.
390 *
391 * Retrieve local templates saved by the user on his site.
392 *
393 * @since 1.0.0
394 * @access public
395 *
396 * @param array $args Optional. Filter templates based on a set of
397 * arguments. Default is an empty array.
398 *
399 * @return array Local templates.
400 */
401 public function get_items( $args = [] ) {
402 $template_types = array_values( self::$template_types );
403
404 if ( ! empty( $args['type'] ) ) {
405 $template_types = $args['type'];
406 unset( $args['type'] );
407 }
408
409 $defaults = [
410 'post_type' => self::CPT,
411 'post_status' => 'publish',
412 'posts_per_page' => -1,
413 'orderby' => 'title',
414 'order' => 'ASC',
415 'meta_query' => [
416 [
417 'key' => Document::TYPE_META_KEY,
418 'value' => $template_types,
419 ],
420 ],
421 ];
422
423 $query_args = wp_parse_args( $args, $defaults );
424
425 $templates_query = new \WP_Query( $query_args );
426
427 $templates = [];
428
429 if ( $templates_query->have_posts() ) {
430 foreach ( $templates_query->get_posts() as $post ) {
431 $templates[] = $this->get_item( $post->ID );
432 }
433 }
434
435 return $templates;
436 }
437
438 /**
439 * Save local template.
440 *
441 * Save new or update existing template on the database.
442 *
443 * @since 1.0.0
444 * @access public
445 *
446 * @param array $template_data Local template data.
447 *
448 * @return \WP_Error|int The ID of the saved/updated template, `WP_Error` otherwise.
449 */
450 public function save_item( $template_data ) {
451 if ( ! current_user_can( $this->post_type_object->cap->edit_posts ) ) {
452 return new \WP_Error( 'save_error', __( 'Access denied.', 'elementor' ) );
453 }
454
455 $defaults = [
456 'title' => __( '(no title)', 'elementor' ),
457 'page_settings' => [],
458 'status' => current_user_can( 'publish_posts' ) ? 'publish' : 'pending',
459 ];
460
461 $template_data = wp_parse_args( $template_data, $defaults );
462
463 $document = Plugin::$instance->documents->create(
464 $template_data['type'],
465 [
466 'post_title' => $template_data['title'],
467 'post_status' => $template_data['status'],
468 'post_type' => self::CPT,
469 ]
470 );
471
472 if ( is_wp_error( $document ) ) {
473 /**
474 * @var \WP_Error $document
475 */
476 return $document;
477 }
478
479 if ( ! empty( $template_data['content'] ) ) {
480 $template_data['content'] = $this->replace_elements_ids( $template_data['content'] );
481 }
482
483 $document->save( [
484 'elements' => $template_data['content'],
485 'settings' => $template_data['page_settings'],
486 ] );
487
488 $template_id = $document->get_main_id();
489
490 /**
491 * After template library save.
492 *
493 * Fires after Elementor template library was saved.
494 *
495 * @since 1.0.1
496 *
497 * @param int $template_id The ID of the template.
498 * @param array $template_data The template data.
499 */
500 do_action( 'elementor/template-library/after_save_template', $template_id, $template_data );
501
502 /**
503 * After template library update.
504 *
505 * Fires after Elementor template library was updated.
506 *
507 * @since 1.0.1
508 *
509 * @param int $template_id The ID of the template.
510 * @param array $template_data The template data.
511 */
512 do_action( 'elementor/template-library/after_update_template', $template_id, $template_data );
513
514 return $template_id;
515 }
516
517 /**
518 * Update local template.
519 *
520 * Update template on the database.
521 *
522 * @since 1.0.0
523 * @access public
524 *
525 * @param array $new_data New template data.
526 *
527 * @return \WP_Error|true True if template updated, `WP_Error` otherwise.
528 */
529 public function update_item( $new_data ) {
530 if ( ! current_user_can( $this->post_type_object->cap->edit_post, $new_data['id'] ) ) {
531 return new \WP_Error( 'save_error', __( 'Access denied.', 'elementor' ) );
532 }
533
534 $document = Plugin::$instance->documents->get( $new_data['id'] );
535
536 if ( ! $document ) {
537 return new \WP_Error( 'save_error', __( 'Template not exist.', 'elementor' ) );
538 }
539
540 $document->save( [
541 'elements' => $new_data['content'],
542 ] );
543
544 /**
545 * After template library update.
546 *
547 * Fires after Elementor template library was updated.
548 *
549 * @since 1.0.0
550 *
551 * @param int $new_data_id The ID of the new template.
552 * @param array $new_data The new template data.
553 */
554 do_action( 'elementor/template-library/after_update_template', $new_data['id'], $new_data );
555
556 return true;
557 }
558
559 /**
560 * Get local template.
561 *
562 * Retrieve a single local template saved by the user on his site.
563 *
564 * @since 1.0.0
565 * @access public
566 *
567 * @param int $template_id The template ID.
568 *
569 * @return array Local template.
570 */
571 public function get_item( $template_id ) {
572 $post = get_post( $template_id );
573
574 $user = get_user_by( 'id', $post->post_author );
575
576 $page = SettingsManager::get_settings_managers( 'page' )->get_model( $template_id );
577
578 $page_settings = $page->get_data( 'settings' );
579
580 $date = strtotime( $post->post_date );
581
582 $data = [
583 'template_id' => $post->ID,
584 'source' => $this->get_id(),
585 'type' => self::get_template_type( $post->ID ),
586 'title' => $post->post_title,
587 'thumbnail' => get_the_post_thumbnail_url( $post ),
588 'date' => $date,
589 'human_date' => date_i18n( get_option( 'date_format' ), $date ),
590 'human_modified_date' => date_i18n( get_option( 'date_format' ), strtotime( $post->post_modified ) ),
591 'author' => $user->display_name,
592 'status' => $post->post_status,
593 'hasPageSettings' => ! empty( $page_settings ),
594 'tags' => [],
595 'export_link' => $this->get_export_link( $template_id ),
596 'url' => get_permalink( $post->ID ),
597 ];
598
599 /**
600 * Get template library template.
601 *
602 * Filters the template data when retrieving a single template from the
603 * template library.
604 *
605 * @since 1.0.0
606 *
607 * @param array $data Template data.
608 */
609 $data = apply_filters( 'elementor/template-library/get_template', $data );
610
611 return $data;
612 }
613
614 /**
615 * Get template data.
616 *
617 * Retrieve the data of a single local template saved by the user on his site.
618 *
619 * @since 1.5.0
620 * @access public
621 *
622 * @param array $args Custom template arguments.
623 *
624 * @return array Local template data.
625 */
626 public function get_data( array $args ) {
627 $template_id = $args['template_id'];
628
629 $document = Plugin::$instance->documents->get( $template_id );
630 $content = [];
631
632 if ( $document ) {
633 // TODO: Validate the data (in JS too!).
634 if ( ! empty( $args['display'] ) ) {
635 $content = $document->get_elements_raw_data( null, true );
636 } else {
637 $content = $document->get_elements_data();
638 }
639
640 if ( ! empty( $content ) ) {
641 $content = $this->replace_elements_ids( $content );
642 }
643 }
644
645 $data = [
646 'content' => $content,
647 ];
648
649 if ( ! empty( $args['with_page_settings'] ) ) {
650 $page = SettingsManager::get_settings_managers( 'page' )->get_model( $args['template_id'] );
651
652 $data['page_settings'] = $page->get_data( 'settings' );
653 }
654
655 return $data;
656 }
657
658 /**
659 * Delete local template.
660 *
661 * Delete template from the database.
662 *
663 * @since 1.0.0
664 * @access public
665 *
666 * @param int $template_id The template ID.
667 *
668 * @return \WP_Post|\WP_Error|false|null Post data on success, false or null
669 * or 'WP_Error' on failure.
670 */
671 public function delete_template( $template_id ) {
672 if ( ! current_user_can( $this->post_type_object->cap->delete_post, $template_id ) ) {
673 return new \WP_Error( 'template_error', __( 'Access denied.', 'elementor' ) );
674 }
675
676 return wp_delete_post( $template_id, true );
677 }
678
679 /**
680 * Export local template.
681 *
682 * Export template to a file.
683 *
684 * @since 1.0.0
685 * @access public
686 *
687 * @param int $template_id The template ID.
688 *
689 * @return \WP_Error WordPress error if template export failed.
690 */
691 public function export_template( $template_id ) {
692 $file_data = $this->prepare_template_export( $template_id );
693
694 if ( is_wp_error( $file_data ) ) {
695 return $file_data;
696 }
697
698 $this->send_file_headers( $file_data['name'], strlen( $file_data['content'] ) );
699
700 // Clear buffering just in case.
701 @ob_end_clean();
702
703 flush();
704
705 // Output file contents.
706 echo $file_data['content'];
707
708 die;
709 }
710
711 /**
712 * Export multiple local templates.
713 *
714 * Export multiple template to a ZIP file.
715 *
716 * @since 1.6.0
717 * @access public
718 *
719 * @param array $template_ids An array of template IDs.
720 *
721 * @return \WP_Error WordPress error if export failed.
722 */
723 public function export_multiple_templates( array $template_ids ) {
724 $files = [];
725
726 $wp_upload_dir = wp_upload_dir();
727
728 $temp_path = $wp_upload_dir['basedir'] . '/' . self::TEMP_FILES_DIR;
729
730 // Create temp path if it doesn't exist
731 wp_mkdir_p( $temp_path );
732
733 // Create all json files
734 foreach ( $template_ids as $template_id ) {
735 $file_data = $this->prepare_template_export( $template_id );
736
737 if ( is_wp_error( $file_data ) ) {
738 continue;
739 }
740
741 $complete_path = $temp_path . '/' . $file_data['name'];
742
743 $put_contents = file_put_contents( $complete_path, $file_data['content'] );
744
745 if ( ! $put_contents ) {
746 return new \WP_Error( '404', sprintf( 'Cannot create file "%s".', $file_data['name'] ) );
747 }
748
749 $files[] = [
750 'path' => $complete_path,
751 'name' => $file_data['name'],
752 ];
753 }
754
755 if ( ! $files ) {
756 return new \WP_Error( 'empty_files', 'There is no files to export (probably all the requested templates are empty).' );
757 }
758
759 // Create temporary .zip file
760 $zip_archive_filename = 'elementor-templates-' . gmdate( 'Y-m-d' ) . '.zip';
761
762 $zip_archive = new \ZipArchive();
763
764 $zip_complete_path = $temp_path . '/' . $zip_archive_filename;
765
766 $zip_archive->open( $zip_complete_path, \ZipArchive::CREATE );
767
768 foreach ( $files as $file ) {
769 $zip_archive->addFile( $file['path'], $file['name'] );
770 }
771
772 $zip_archive->close();
773
774 foreach ( $files as $file ) {
775 unlink( $file['path'] );
776 }
777
778 $this->send_file_headers( $zip_archive_filename, filesize( $zip_complete_path ) );
779
780 @ob_end_flush();
781
782 @readfile( $zip_complete_path );
783
784 unlink( $zip_complete_path );
785
786 die;
787 }
788
789 /**
790 * Import local template.
791 *
792 * Import template from a file.
793 *
794 * @since 1.0.0
795 * @access public
796 *
797 * @param string $name - The file name
798 * @param string $path - The file path
799 * @return \WP_Error|array An array of items on success, 'WP_Error' on failure.
800 */
801 public function import_template( $name, $path ) {
802 if ( empty( $path ) ) {
803 return new \WP_Error( 'file_error', 'Please upload a file to import' );
804 }
805
806 $items = [];
807
808 // If the import file is a Zip file with potentially multiple JSON files
809 if ( 'zip' === pathinfo( $name, PATHINFO_EXTENSION ) ) {
810 $extracted_files = Plugin::$instance->uploads_manager->extract_and_validate_zip( $path );
811
812 if ( is_wp_error( $extracted_files ) ) {
813 // Remove the temporary zip file, since it's now not necessary.
814 Plugin::$instance->uploads_manager->remove_file_or_dir( $path );
815 // Delete the temporary extraction directory, since it's now not necessary.
816 Plugin::$instance->uploads_manager->remove_file_or_dir( $extracted_files['extraction_directory'] );
817
818 return $extracted_files;
819 }
820
821 foreach ( $extracted_files['files'] as $file_path ) {
822 $import_result = $this->import_single_template( $file_path );
823
824 if ( is_wp_error( $import_result ) ) {
825 Plugin::$instance->uploads_manager->remove_file_or_dir( $import_result );
826
827 return $import_result;
828 }
829
830 $items[] = $import_result;
831 }
832
833 // Delete the temporary extraction directory, since it's now not necessary.
834 Plugin::$instance->uploads_manager->remove_file_or_dir( $extracted_files['extraction_directory'] );
835 } else {
836 // If the import file is a single JSON file
837 $import_result = $this->import_single_template( $path );
838
839 if ( is_wp_error( $import_result ) ) {
840 Plugin::$instance->uploads_manager->remove_file_or_dir( $import_result );
841
842 return $import_result;
843 }
844
845 $items[] = $import_result;
846 }
847
848 return $items;
849 }
850
851 /**
852 * Post row actions.
853 *
854 * Add an export link to the template library action links table list.
855 *
856 * Fired by `post_row_actions` filter.
857 *
858 * @since 1.0.0
859 * @access public
860 *
861 * @param array $actions An array of row action links.
862 * @param \WP_Post $post The post object.
863 *
864 * @return array An updated array of row action links.
865 */
866 public function post_row_actions( $actions, \WP_Post $post ) {
867 if ( self::is_base_templates_screen() ) {
868 if ( $this->is_template_supports_export( $post->ID ) ) {
869 $actions['export-template'] = sprintf( '<a href="%1$s">%2$s</a>', $this->get_export_link( $post->ID ), __( 'Export Template', 'elementor' ) );
870 }
871 }
872
873 return $actions;
874 }
875
876 /**
877 * Admin import template form.
878 *
879 * The import form displayed in "My Library" screen in WordPress dashboard.
880 *
881 * The form allows the user to import template in json/zip format to the site.
882 *
883 * Fired by `admin_footer` action.
884 *
885 * @since 1.0.0
886 * @access public
887 */
888 public function admin_import_template_form() {
889 if ( ! self::is_base_templates_screen() ) {
890 return;
891 }
892
893 /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */
894 $ajax = Plugin::$instance->common->get_component( 'ajax' );
895 ?>
896 <div id="elementor-hidden-area">
897 <a id="elementor-import-template-trigger" class="page-title-action"><?php echo __( 'Import Templates', 'elementor' ); ?></a>
898 <div id="elementor-import-template-area">
899 <div id="elementor-import-template-title"><?php echo __( 'Choose an Elementor template JSON file or a .zip archive of Elementor templates, and add them to the list of templates available in your library.', 'elementor' ); ?></div>
900 <form id="elementor-import-template-form" method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>" enctype="multipart/form-data">
901 <input type="hidden" name="action" value="elementor_library_direct_actions">
902 <input type="hidden" name="library_action" value="direct_import_template">
903 <input type="hidden" name="_nonce" value="<?php echo $ajax->create_nonce(); ?>">
904 <fieldset id="elementor-import-template-form-inputs">
905 <input type="file" name="file" accept=".json,application/json,.zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed" required>
906 <input type="submit" class="button" value="<?php echo esc_attr__( 'Import Now', 'elementor' ); ?>">
907 </fieldset>
908 </form>
909 </div>
910 </div>
911 <?php
912 }
913
914 /**
915 * Block template frontend
916 *
917 * Don't display the single view of the template library post type in the
918 * frontend, for users that don't have the proper permissions.
919 *
920 * Fired by `template_redirect` action.
921 *
922 * @since 1.0.0
923 * @access public
924 */
925 public function block_template_frontend() {
926 if ( is_singular( self::CPT ) && ! current_user_can( Editor::EDITING_CAPABILITY ) ) {
927 wp_safe_redirect( site_url(), 301 );
928 die;
929 }
930 }
931
932 /**
933 * Is template library supports export.
934 *
935 * whether the template library supports export.
936 *
937 * Template saved by the user locally on his site, support export by default
938 * but this can be changed using a filter.
939 *
940 * @since 1.0.0
941 * @access public
942 *
943 * @param int $template_id The template ID.
944 *
945 * @return bool Whether the template library supports export.
946 */
947 public function is_template_supports_export( $template_id ) {
948 $export_support = true;
949
950 /**
951 * Is template library supports export.
952 *
953 * Filters whether the template library supports export.
954 *
955 * @since 1.0.0
956 *
957 * @param bool $export_support Whether the template library supports export.
958 * Default is true.
959 * @param int $template_id Post ID.
960 */
961 $export_support = apply_filters( 'elementor/template_library/is_template_supports_export', $export_support, $template_id );
962
963 return $export_support;
964 }
965
966 /**
967 * Remove Elementor post state.
968 *
969 * Remove the 'elementor' post state from the display states of the post.
970 *
971 * Used to remove the 'elementor' post state from the template library items.
972 *
973 * Fired by `display_post_states` filter.
974 *
975 * @since 1.8.0
976 * @access public
977 *
978 * @param array $post_states An array of post display states.
979 * @param \WP_Post $post The current post object.
980 *
981 * @return array Updated array of post display states.
982 */
983 public function remove_elementor_post_state_from_library( $post_states, $post ) {
984 if ( self::CPT === $post->post_type && isset( $post_states['elementor'] ) ) {
985 unset( $post_states['elementor'] );
986 }
987 return $post_states;
988 }
989
990 /**
991 * Get template export link.
992 *
993 * Retrieve the link used to export a single template based on the template
994 * ID.
995 *
996 * @since 2.0.0
997 * @access private
998 *
999 * @param int $template_id The template ID.
1000 *
1001 * @return string Template export URL.
1002 */
1003 private function get_export_link( $template_id ) {
1004 // TODO: BC since 2.3.0 - Use `$ajax->create_nonce()`
1005 /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */
1006 // $ajax = Plugin::$instance->common->get_component( 'ajax' );
1007
1008 return add_query_arg(
1009 [
1010 'action' => 'elementor_library_direct_actions',
1011 'library_action' => 'export_template',
1012 'source' => $this->get_id(),
1013 '_nonce' => wp_create_nonce( 'elementor_ajax' ),
1014 'template_id' => $template_id,
1015 ],
1016 admin_url( 'admin-ajax.php' )
1017 );
1018 }
1019
1020 /**
1021 * On template save.
1022 *
1023 * Run this method when template is being saved.
1024 *
1025 * Fired by `save_post` action.
1026 *
1027 * @since 1.0.1
1028 * @access public
1029 *
1030 * @param int $post_id Post ID.
1031 * @param \WP_Post $post The current post object.
1032 */
1033 public function on_save_post( $post_id, \WP_Post $post ) {
1034 if ( self::CPT !== $post->post_type ) {
1035 return;
1036 }
1037
1038 if ( self::get_template_type( $post_id ) ) { // It's already with a type
1039 return;
1040 }
1041
1042 // Don't save type on import, the importer will do it.
1043 if ( did_action( 'import_start' ) ) {
1044 return;
1045 }
1046
1047 $this->save_item_type( $post_id, 'page' );
1048 }
1049
1050 /**
1051 * Save item type.
1052 *
1053 * When saving/updating templates, this method is used to update the post
1054 * meta data and the taxonomy.
1055 *
1056 * @since 1.0.1
1057 * @access private
1058 *
1059 * @param int $post_id Post ID.
1060 * @param string $type Item type.
1061 */
1062 private function save_item_type( $post_id, $type ) {
1063 update_post_meta( $post_id, Document::TYPE_META_KEY, $type );
1064
1065 wp_set_object_terms( $post_id, $type, self::TAXONOMY_TYPE_SLUG );
1066 }
1067
1068 /**
1069 * Bulk export action.
1070 *
1071 * Adds an 'Export' action to the Bulk Actions drop-down in the template
1072 * library.
1073 *
1074 * Fired by `bulk_actions-edit-elementor_library` filter.
1075 *
1076 * @since 1.6.0
1077 * @access public
1078 *
1079 * @param array $actions An array of the available bulk actions.
1080 *
1081 * @return array An array of the available bulk actions.
1082 */
1083 public function admin_add_bulk_export_action( $actions ) {
1084 $actions[ self::BULK_EXPORT_ACTION ] = __( 'Export', 'elementor' );
1085
1086 return $actions;
1087 }
1088
1089 /**
1090 * Add bulk export action.
1091 *
1092 * Handles the template library bulk export action.
1093 *
1094 * Fired by `handle_bulk_actions-edit-elementor_library` filter.
1095 *
1096 * @since 1.6.0
1097 * @access public
1098 *
1099 * @param string $redirect_to The redirect URL.
1100 * @param string $action The action being taken.
1101 * @param array $post_ids The items to take the action on.
1102 */
1103 public function admin_export_multiple_templates( $redirect_to, $action, $post_ids ) {
1104 if ( self::BULK_EXPORT_ACTION === $action ) {
1105 $result = $this->export_multiple_templates( $post_ids );
1106
1107 // If you reach this line, the export failed
1108 wp_die( $result->get_error_message() );
1109 }
1110 }
1111
1112 /**
1113 * Print admin tabs.
1114 *
1115 * Used to output the template library tabs with their labels.
1116 *
1117 * Fired by `views_edit-elementor_library` filter.
1118 *
1119 * @since 2.0.0
1120 * @access public
1121 *
1122 * @param array $views An array of available list table views.
1123 *
1124 * @return array An updated array of available list table views.
1125 */
1126 public function admin_print_tabs( $views ) {
1127 $current_type = '';
1128 $active_class = ' nav-tab-active';
1129 $current_tabs_group = $this->get_current_tab_group();
1130
1131 if ( ! empty( $_REQUEST[ self::TAXONOMY_TYPE_SLUG ] ) ) {
1132 $current_type = $_REQUEST[ self::TAXONOMY_TYPE_SLUG ];
1133 $active_class = '';
1134 }
1135
1136 $url_args = [
1137 'post_type' => self::CPT,
1138 'tabs_group' => $current_tabs_group,
1139 ];
1140
1141 $baseurl = add_query_arg( $url_args, admin_url( 'edit.php' ) );
1142
1143 $filter = [
1144 'admin_tab_group' => $current_tabs_group,
1145 ];
1146 $operator = 'and';
1147
1148 if ( empty( $current_tabs_group ) ) {
1149 // Don't include 'not-supported' or other templates that don't set their `admin_tab_group`.
1150 $operator = 'NOT';
1151 }
1152
1153 $doc_types = Plugin::$instance->documents->get_document_types( $filter, $operator );
1154
1155 if ( 1 >= count( $doc_types ) ) {
1156 return $views;
1157 }
1158
1159 ?>
1160 <div id="elementor-template-library-tabs-wrapper" class="nav-tab-wrapper">
1161 <a class="nav-tab<?php echo $active_class; ?>" href="<?php echo $baseurl; ?>">
1162 <?php
1163 $all_title = $this->get_library_title();
1164 if ( ! $all_title ) {
1165 $all_title = __( 'All', 'elementor' );
1166 }
1167 echo $all_title; ?>
1168 </a>
1169 <?php
1170 foreach ( $doc_types as $type => $class_name ) :
1171 $active_class = '';
1172
1173 if ( $current_type === $type ) {
1174 $active_class = ' nav-tab-active';
1175 }
1176
1177 $type_url = add_query_arg( self::TAXONOMY_TYPE_SLUG, $type, $baseurl );
1178 $type_label = $this->get_template_label_by_type( $type );
1179
1180 echo "<a class='nav-tab{$active_class}' href='{$type_url}'>{$type_label}</a>";
1181 endforeach;
1182 ?>
1183 </div>
1184 <?php
1185 return $views;
1186 }
1187
1188 /**
1189 * Maybe render blank state.
1190 *
1191 * When the template library has no saved templates, display a blank admin page offering
1192 * to create the very first template.
1193 *
1194 * Fired by `manage_posts_extra_tablenav` action.
1195 *
1196 * @since 2.0.0
1197 * @access public
1198 *
1199 * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
1200 * @param array $args
1201 */
1202 public function maybe_render_blank_state( $which, array $args = [] ) {
1203 global $post_type;
1204
1205 $args = wp_parse_args( $args, [
1206 'cpt' => self::CPT,
1207 'post_type' => get_query_var( 'elementor_library_type' ),
1208 ] );
1209
1210 if ( $args['cpt'] !== $post_type || 'bottom' !== $which ) {
1211 return;
1212 }
1213
1214 global $wp_list_table;
1215
1216 $total_items = $wp_list_table->get_pagination_arg( 'total_items' );
1217
1218 if ( ! empty( $total_items ) || ! empty( $_REQUEST['s'] ) ) {
1219 return;
1220 }
1221
1222 $current_type = $args['post_type'];
1223
1224 $document_types = Plugin::instance()->documents->get_document_types();
1225
1226 if ( empty( $document_types[ $current_type ] ) ) {
1227 return;
1228 }
1229
1230 // TODO: Better way to exclude widget type.
1231 if ( 'widget' === $current_type ) {
1232 return;
1233 }
1234
1235 // TODO: This code maybe unreachable see if above `if ( empty( $document_types[ $current_type ] ) )`.
1236 if ( empty( $current_type ) ) {
1237 $counts = (array) wp_count_posts( self::CPT );
1238 unset( $counts['auto-draft'] );
1239 $count = array_sum( $counts );
1240
1241 if ( 0 < $count ) {
1242 return;
1243 }
1244
1245 $current_type = 'template';
1246
1247 $args['additional_inline_style'] = '#elementor-template-library-tabs-wrapper {display: none;}';
1248 }
1249
1250 $this->render_blank_state( $current_type, $args );
1251 }
1252
1253 private function render_blank_state( $current_type, array $args = [] ) {
1254 $current_type_label = $this->get_template_label_by_type( $current_type );
1255 $inline_style = '#posts-filter .wp-list-table, #posts-filter .tablenav.top, .tablenav.bottom .actions, .wrap .subsubsub { display:none;}';
1256
1257 $args = wp_parse_args( $args, [
1258 'additional_inline_style' => '',
1259 'href' => '',
1260 'description' => __( 'Add templates and reuse them across your website. Easily export and import them to any other project, for an optimized workflow.', 'elementor' ),
1261 ] );
1262 $inline_style .= $args['additional_inline_style'];
1263 ?>
1264 <style type="text/css"><?php echo $inline_style; ?></style>
1265 <div class="elementor-template_library-blank_state">
1266 <?php $this->print_blank_state_template( $current_type_label, $args['href'], $args['description'] ); ?>
1267 </div>
1268 <?php
1269 }
1270
1271 /**
1272 * Print Blank State Template
1273 *
1274 * When the an entity (CPT, Taxonomy...etc) has no saved items, print a blank admin page offering
1275 * to create the very first item.
1276 *
1277 * This method is public because it needs to be accessed from outside the Source_Local
1278 *
1279 * @since 3.1.0
1280 * @access public
1281 *
1282 * @param string $current_type_label The Entity title
1283 * @param string $href The URL for the 'Add New' button
1284 * @param string $description The sub title describing the Entity (Post Type, Taxonomy, etc.)
1285 */
1286 public function print_blank_state_template( $current_type_label, $href, $description ) {
1287 ?>
1288 <div class="elementor-blank_state">
1289 <i class="eicon-folder"></i>
1290 <h2>
1291 <?php
1292 /* translators: %s: Template type label. */
1293 printf( __( 'Create Your First %s', 'elementor' ), $current_type_label );
1294 ?>
1295 </h2>
1296 <p><?php echo $description; ?></p>
1297 <a id="elementor-template-library-add-new" class="elementor-button elementor-button-success" href="<?php echo $href; ?>">
1298 <?php
1299 /* translators: %s: Template type label. */
1300 printf( __( 'Add New %s', 'elementor' ), $current_type_label );
1301 ?>
1302 </a>
1303 </div>
1304 <?php
1305 }
1306
1307 public function add_filter_by_category( $post_type ) {
1308 if ( self::CPT !== $post_type ) {
1309 return;
1310 }
1311
1312 $all_items = get_taxonomy( self::TAXONOMY_CATEGORY_SLUG )->labels->all_items;
1313
1314 $dropdown_options = array(
1315 'show_option_all' => $all_items,
1316 'show_option_none' => $all_items,
1317 'hide_empty' => 0,
1318 'hierarchical' => 1,
1319 'show_count' => 0,
1320 'orderby' => 'name',
1321 'value_field' => 'slug',
1322 'taxonomy' => self::TAXONOMY_CATEGORY_SLUG,
1323 'name' => self::TAXONOMY_CATEGORY_SLUG,
1324 'selected' => empty( $_GET[ self::TAXONOMY_CATEGORY_SLUG ] ) ? '' : $_GET[ self::TAXONOMY_CATEGORY_SLUG ],
1325 );
1326
1327 echo '<label class="screen-reader-text" for="cat">' . _x( 'Filter by category', 'Template Library', 'elementor' ) . '</label>';
1328 wp_dropdown_categories( $dropdown_options );
1329 }
1330
1331 /**
1332 * Import single template.
1333 *
1334 * Import template from a file to the database.
1335 *
1336 * @since 1.6.0
1337 * @access private
1338 *
1339 * @param string $file_path File name.
1340 *
1341 * @return \WP_Error|int|array Local template array, or template ID, or
1342 * `WP_Error`.
1343 */
1344 private function import_single_template( $file_path ) {
1345 $data = json_decode( file_get_contents( $file_path ), true );
1346
1347 if ( empty( $data ) ) {
1348 return new \WP_Error( 'file_error', 'Invalid File' );
1349 }
1350
1351 $content = $data['content'];
1352
1353 if ( ! is_array( $content ) ) {
1354 return new \WP_Error( 'file_error', 'Invalid Content In File' );
1355 }
1356
1357 $content = $this->process_export_import_content( $content, 'on_import' );
1358
1359 $page_settings = [];
1360
1361 if ( ! empty( $data['page_settings'] ) ) {
1362 $page = new Model( [
1363 'id' => 0,
1364 'settings' => $data['page_settings'],
1365 ] );
1366
1367 $page_settings_data = $this->process_element_export_import_content( $page, 'on_import' );
1368
1369 if ( ! empty( $page_settings_data['settings'] ) ) {
1370 $page_settings = $page_settings_data['settings'];
1371 }
1372 }
1373
1374 $template_id = $this->save_item( [
1375 'content' => $content,
1376 'title' => $data['title'],
1377 'type' => $data['type'],
1378 'page_settings' => $page_settings,
1379 ] );
1380
1381 // Remove the temporary file, now that we're done with it.
1382 Plugin::$instance->uploads_manager->remove_file_or_dir( $file_path );
1383
1384 if ( is_wp_error( $template_id ) ) {
1385 return $template_id;
1386 }
1387
1388 return $this->get_item( $template_id );
1389 }
1390
1391 /**
1392 * Prepare template to export.
1393 *
1394 * Retrieve the relevant template data and return them as an array.
1395 *
1396 * @since 1.6.0
1397 * @access private
1398 *
1399 * @param int $template_id The template ID.
1400 *
1401 * @return \WP_Error|array Exported template data.
1402 */
1403 private function prepare_template_export( $template_id ) {
1404 $document = Plugin::$instance->documents->get( $template_id );
1405
1406 $template_data = $document->get_export_data();
1407
1408 if ( empty( $template_data['content'] ) ) {
1409 return new \WP_Error( 'empty_template', 'The template is empty' );
1410 }
1411
1412 $export_data = [
1413 'content' => $template_data['content'],
1414 'page_settings' => $template_data['settings'],
1415 'version' => DB::DB_VERSION,
1416 'title' => get_the_title( $template_id ),
1417 'type' => self::get_template_type( $template_id ),
1418 ];
1419
1420 return [
1421 'name' => 'elementor-' . $template_id . '-' . gmdate( 'Y-m-d' ) . '.json',
1422 'content' => wp_json_encode( $export_data ),
1423 ];
1424 }
1425
1426 /**
1427 * Send file headers.
1428 *
1429 * Set the file header when export template data to a file.
1430 *
1431 * @since 1.6.0
1432 * @access private
1433 *
1434 * @param string $file_name File name.
1435 * @param int $file_size File size.
1436 */
1437 private function send_file_headers( $file_name, $file_size ) {
1438 header( 'Content-Type: application/octet-stream' );
1439 header( 'Content-Disposition: attachment; filename=' . $file_name );
1440 header( 'Expires: 0' );
1441 header( 'Cache-Control: must-revalidate' );
1442 header( 'Pragma: public' );
1443 header( 'Content-Length: ' . $file_size );
1444 }
1445
1446 /**
1447 * Get template label by type.
1448 *
1449 * Retrieve the template label for any given template type.
1450 *
1451 * @since 2.0.0
1452 * @access private
1453 *
1454 * @param string $template_type Template type.
1455 *
1456 * @return string Template label.
1457 */
1458 private function get_template_label_by_type( $template_type ) {
1459 $document_types = Plugin::instance()->documents->get_document_types();
1460
1461 if ( isset( $document_types[ $template_type ] ) ) {
1462 $template_label = call_user_func( [ $document_types[ $template_type ], 'get_title' ] );
1463 } else {
1464 $template_label = ucwords( str_replace( [ '_', '-' ], ' ', $template_type ) );
1465 }
1466
1467 /**
1468 * Template label by template type.
1469 *
1470 * Filters the template label by template type in the template library .
1471 *
1472 * @since 2.0.0
1473 *
1474 * @param string $template_label Template label.
1475 * @param string $template_type Template type.
1476 */
1477 $template_label = apply_filters( 'elementor/template-library/get_template_label_by_type', $template_label, $template_type );
1478
1479 return $template_label;
1480 }
1481
1482 /**
1483 * Filter template types in admin query.
1484 *
1485 * Update the template types in the main admin query.
1486 *
1487 * Fired by `parse_query` action.
1488 *
1489 * @since 2.4.0
1490 * @access public
1491 *
1492 * @param \WP_Query $query The `WP_Query` instance.
1493 */
1494 public function admin_query_filter_types( \WP_Query $query ) {
1495 if ( ! $this->is_current_screen() || ! empty( $query->query_vars['meta_key'] ) ) {
1496 return;
1497 }
1498
1499 $current_tabs_group = $this->get_current_tab_group();
1500
1501 if ( isset( $query->query_vars[ self::TAXONOMY_CATEGORY_SLUG ] ) && '-1' === $query->query_vars[ self::TAXONOMY_CATEGORY_SLUG ] ) {
1502 unset( $query->query_vars[ self::TAXONOMY_CATEGORY_SLUG ] );
1503 }
1504
1505 if ( empty( $current_tabs_group ) ) {
1506 return;
1507 }
1508
1509 $doc_types = Plugin::$instance->documents->get_document_types( [
1510 'admin_tab_group' => $current_tabs_group,
1511 ] );
1512
1513 $query->query_vars['meta_key'] = Document::TYPE_META_KEY;
1514 $query->query_vars['meta_value'] = array_keys( $doc_types );
1515 }
1516
1517 /**
1518 * Add template library actions.
1519 *
1520 * Register filters and actions for the template library.
1521 *
1522 * @since 2.0.0
1523 * @access private
1524 */
1525 private function add_actions() {
1526 if ( is_admin() ) {
1527 add_action( 'admin_menu', [ $this, 'admin_menu' ] );
1528 add_action( 'admin_menu', [ $this, 'admin_menu_reorder' ], 800 );
1529 add_filter( 'admin_title', [ $this, 'admin_title' ], 10, 2 );
1530 add_action( 'all_admin_notices', [ $this, 'replace_admin_heading' ] );
1531 add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 );
1532 add_action( 'admin_footer', [ $this, 'admin_import_template_form' ] );
1533 add_action( 'save_post', [ $this, 'on_save_post' ], 10, 2 );
1534 add_filter( 'display_post_states', [ $this, 'remove_elementor_post_state_from_library' ], 11, 2 );
1535
1536 add_action( 'parse_query', [ $this, 'admin_query_filter_types' ] );
1537
1538 // Template filter by category.
1539 add_action( 'restrict_manage_posts', [ $this, 'add_filter_by_category' ] );
1540
1541 // Template type column.
1542 add_action( 'manage_' . self::CPT . '_posts_columns', [ $this, 'admin_columns_headers' ] );
1543 add_action( 'manage_' . self::CPT . '_posts_custom_column', [ $this, 'admin_columns_content' ], 10, 2 );
1544
1545 // Template library bulk actions.
1546 add_filter( 'bulk_actions-edit-elementor_library', [ $this, 'admin_add_bulk_export_action' ] );
1547 add_filter( 'handle_bulk_actions-edit-elementor_library', [ $this, 'admin_export_multiple_templates' ], 10, 3 );
1548
1549 // Print template library tabs.
1550 add_filter( 'views_edit-' . self::CPT, [ $this, 'admin_print_tabs' ] );
1551
1552 // Show blank state.
1553 add_action( 'manage_posts_extra_tablenav', [ $this, 'maybe_render_blank_state' ] );
1554 }
1555
1556 add_action( 'template_redirect', [ $this, 'block_template_frontend' ] );
1557 }
1558
1559 /**
1560 * @since 2.0.6
1561 * @access public
1562 */
1563 public function admin_columns_content( $column_name, $post_id ) {
1564 if ( 'elementor_library_type' === $column_name ) {
1565 /** @var Document $document */
1566 $document = Plugin::$instance->documents->get( $post_id );
1567
1568 if ( $document && $document instanceof Library_Document ) {
1569 $document->print_admin_column_type();
1570 }
1571 }
1572 }
1573
1574 /**
1575 * @since 2.0.6
1576 * @access public
1577 */
1578 public function admin_columns_headers( $posts_columns ) {
1579 // Replace original column that bind to the taxonomy - with another column.
1580 unset( $posts_columns['taxonomy-elementor_library_type'] );
1581
1582 $offset = 2;
1583
1584 $posts_columns = array_slice( $posts_columns, 0, $offset, true ) + [
1585 'elementor_library_type' => __( 'Type', 'elementor' ),
1586 ] + array_slice( $posts_columns, $offset, null, true );
1587
1588 return $posts_columns;
1589 }
1590
1591 public function get_current_tab_group( $default = '' ) {
1592 $current_tabs_group = $default;
1593
1594 if ( ! empty( $_REQUEST[ self::TAXONOMY_TYPE_SLUG ] ) ) {
1595 $doc_type = Plugin::$instance->documents->get_document_type( $_REQUEST[ self::TAXONOMY_TYPE_SLUG ], '' );
1596 if ( $doc_type ) {
1597 $current_tabs_group = $doc_type::get_property( 'admin_tab_group' );
1598 }
1599 } elseif ( ! empty( $_REQUEST['tabs_group'] ) ) {
1600 $current_tabs_group = $_REQUEST['tabs_group'];
1601 }
1602
1603 return $current_tabs_group;
1604 }
1605
1606 private function get_library_title() {
1607 $title = '';
1608
1609 if ( $this->is_current_screen() ) {
1610 $current_tab_group = $this->get_current_tab_group();
1611
1612 if ( $current_tab_group ) {
1613 $titles = [
1614 'library' => __( 'Saved Templates', 'elementor' ),
1615 'theme' => __( 'Theme Builder', 'elementor' ),
1616 'popup' => __( 'Popups', 'elementor' ),
1617 ];
1618
1619 if ( ! empty( $titles[ $current_tab_group ] ) ) {
1620 $title = $titles[ $current_tab_group ];
1621 }
1622 }
1623 }
1624
1625 return $title;
1626 }
1627
1628 private function is_current_screen() {
1629 global $pagenow, $typenow;
1630
1631 return 'edit.php' === $pagenow && self::CPT === $typenow;
1632 }
1633
1634 /**
1635 * Template library local source constructor.
1636 *
1637 * Initializing the template library local source base by registering custom
1638 * template data and running custom actions.
1639 *
1640 * @since 1.0.0
1641 * @access public
1642 */
1643 public function __construct() {
1644 parent::__construct();
1645
1646 $this->add_actions();
1647 }
1648 }
1649