Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/elementor/core/documents-manager.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + namespace Elementor\Core;
3 +
4 + use Elementor\Core\Base\Document;
5 + use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
6 + use Elementor\Core\DocumentTypes\Page;
7 + use Elementor\Core\DocumentTypes\Post;
8 + use Elementor\Plugin;
9 + use Elementor\TemplateLibrary\Source_Local;
10 + use Elementor\Utils;
11 +
12 + if ( ! defined( 'ABSPATH' ) ) {
13 + exit; // Exit if accessed directly.
14 + }
15 +
16 + /**
17 + * Elementor documents manager.
18 + *
19 + * Elementor documents manager handler class is responsible for registering and
20 + * managing Elementor documents.
21 + *
22 + * @since 2.0.0
23 + */
24 + class Documents_Manager {
25 +
26 + /**
27 + * Registered types.
28 + *
29 + * Holds the list of all the registered types.
30 + *
31 + * @since 2.0.0
32 + * @access protected
33 + *
34 + * @var Document[]
35 + */
36 + protected $types = [];
37 +
38 + /**
39 + * Registered documents.
40 + *
41 + * Holds the list of all the registered documents.
42 + *
43 + * @since 2.0.0
44 + * @access protected
45 + *
46 + * @var Document[]
47 + */
48 + protected $documents = [];
49 +
50 + /**
51 + * Current document.
52 + *
53 + * Holds the current document.
54 + *
55 + * @since 2.0.0
56 + * @access protected
57 + *
58 + * @var Document
59 + */
60 + protected $current_doc;
61 +
62 + /**
63 + * Switched data.
64 + *
65 + * Holds the current document when changing to the requested post.
66 + *
67 + * @since 2.0.0
68 + * @access protected
69 + *
70 + * @var array
71 + */
72 + protected $switched_data = [];
73 +
74 + protected $cpt = [];
75 +
76 + /**
77 + * Documents manager constructor.
78 + *
79 + * Initializing the Elementor documents manager.
80 + *
81 + * @since 2.0.0
82 + * @access public
83 + */
84 + public function __construct() {
85 + add_action( 'elementor/documents/register', [ $this, 'register_default_types' ], 0 );
86 + add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
87 + add_filter( 'post_row_actions', [ $this, 'filter_post_row_actions' ], 11, 2 );
88 + add_filter( 'page_row_actions', [ $this, 'filter_post_row_actions' ], 11, 2 );
89 + add_filter( 'user_has_cap', [ $this, 'remove_user_edit_cap' ], 10, 3 );
90 + add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
91 + add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
92 + }
93 +
94 + /**
95 + * Register ajax actions.
96 + *
97 + * Process ajax action handles when saving data and discarding changes.
98 + *
99 + * Fired by `elementor/ajax/register_actions` action.
100 + *
101 + * @since 2.0.0
102 + * @access public
103 + *
104 + * @param Ajax $ajax_manager An instance of the ajax manager.
105 + */
106 + public function register_ajax_actions( $ajax_manager ) {
107 + $ajax_manager->register_ajax_action( 'save_builder', [ $this, 'ajax_save' ] );
108 + $ajax_manager->register_ajax_action( 'discard_changes', [ $this, 'ajax_discard_changes' ] );
109 + $ajax_manager->register_ajax_action( 'get_document_config', [ $this, 'ajax_get_document_config' ] );
110 + }
111 +
112 + /**
113 + * Register default types.
114 + *
115 + * Registers the default document types.
116 + *
117 + * @since 2.0.0
118 + * @access public
119 + */
120 + public function register_default_types() {
121 + $default_types = [
122 + 'post' => Post::get_class_full_name(), // BC.
123 + 'wp-post' => Post::get_class_full_name(),
124 + 'wp-page' => Page::get_class_full_name(),
125 + ];
126 +
127 + foreach ( $default_types as $type => $class ) {
128 + $this->register_document_type( $type, $class );
129 + }
130 + }
131 +
132 + /**
133 + * Register document type.
134 + *
135 + * Registers a single document.
136 + *
137 + * @since 2.0.0
138 + * @access public
139 + *
140 + * @param string $type Document type name.
141 + * @param string $class_name The name of the class that registers the document type.
142 + * Full name with the namespace.
143 + *
144 + * @return Documents_Manager The updated document manager instance.
145 + */
146 + public function register_document_type( $type, $class_name ) {
147 + $this->types[ $type ] = $class_name;
148 +
149 + $cpt = $class_name::get_property( 'cpt' );
150 +
151 + if ( $cpt ) {
152 + foreach ( $cpt as $post_type ) {
153 + $this->cpt[ $post_type ] = $type;
154 + }
155 + }
156 +
157 + if ( $class_name::get_property( 'register_type' ) ) {
158 + Source_Local::add_template_type( $type );
159 + }
160 +
161 + return $this;
162 + }
163 +
164 + /**
165 + * Get document.
166 + *
167 + * Retrieve the document data based on a post ID.
168 + *
169 + * @since 2.0.0
170 + * @access public
171 + *
172 + * @param int $post_id Post ID.
173 + * @param bool $from_cache Optional. Whether to retrieve cached data. Default is true.
174 + *
175 + * @return false|Document Document data or false if post ID was not entered.
176 + */
177 + public function get( $post_id, $from_cache = true ) {
178 + $this->register_types();
179 +
180 + $post_id = absint( $post_id );
181 +
182 + if ( ! $post_id || ! get_post( $post_id ) ) {
183 + return false;
184 + }
185 +
186 + /**
187 + * Retrieve document post ID.
188 + *
189 + * Filters the document post ID.
190 + *
191 + * @since 2.0.7
192 + *
193 + * @param int $post_id The post ID of the document.
194 + */
195 + $post_id = apply_filters( 'elementor/documents/get/post_id', $post_id );
196 +
197 + if ( ! $from_cache || ! isset( $this->documents[ $post_id ] ) ) {
198 + $doc_type = $this->get_doc_type_by_id( $post_id );
199 + $doc_type_class = $this->get_document_type( $doc_type );
200 +
201 + $this->documents[ $post_id ] = new $doc_type_class( [
202 + 'post_id' => $post_id,
203 + ] );
204 + }
205 +
206 + return $this->documents[ $post_id ];
207 + }
208 +
209 + /**
210 + * Retrieve a document after checking it exist and allowed to edit.
211 + *
212 + * @param string $id
213 + * @return Document
214 + * @throws \Exception If the document is not found or the current user is not allowed to edit it.
215 + * @since 3.13.0
216 + */
217 + public function get_with_permissions( $id ): Document {
218 + $document = $this->get( $id );
219 +
220 + if ( ! $document ) {
221 + throw new \Exception( 'Not found.' );
222 + }
223 +
224 + if ( ! $document->is_editable_by_current_user() ) {
225 + throw new \Exception( 'Access denied.' );
226 + }
227 +
228 + return $document;
229 + }
230 +
231 + /**
232 + * A `void` version for `get_with_permissions`.
233 + *
234 + * @param string $id
235 + * @return void
236 + * @throws \Exception If the document is not found or the current user is not allowed to edit it.
237 + */
238 + public function check_permissions( $id ) {
239 + $this->get_with_permissions( $id );
240 + }
241 +
242 + /**
243 + * Get document or autosave.
244 + *
245 + * Retrieve either the document or the autosave.
246 + *
247 + * @since 2.0.0
248 + * @access public
249 + *
250 + * @param int $id Optional. Post ID. Default is `0`.
251 + * @param int $user_id Optional. User ID. Default is `0`.
252 + *
253 + * @return false|Document The document if it exist, False otherwise.
254 + */
255 + public function get_doc_or_auto_save( $id, $user_id = 0 ) {
256 + $document = $this->get( $id );
257 + if ( $document && $document->get_autosave_id( $user_id ) ) {
258 + $document = $document->get_autosave( $user_id );
259 + }
260 +
261 + return $document;
262 + }
263 +
264 + /**
265 + * Get document for frontend.
266 + *
267 + * Retrieve the document for frontend use.
268 + *
269 + * @since 2.0.0
270 + * @access public
271 + *
272 + * @param int $post_id Optional. Post ID. Default is `0`.
273 + *
274 + * @return false|Document The document if it exist, False otherwise.
275 + */
276 + public function get_doc_for_frontend( $post_id ) {
277 + $preview_id = (int) Utils::get_super_global_value( $_GET, 'preview_id' );
278 + $is_preview = is_preview() && $post_id === $preview_id;
279 + $is_nonce_verify = wp_verify_nonce( Utils::get_super_global_value( $_GET, 'preview_nonce' ), 'post_preview_' . $preview_id );
280 +
281 + if ( ( $is_preview && $is_nonce_verify ) || Plugin::$instance->preview->is_preview_mode() ) {
282 + $document = $this->get_doc_or_auto_save( $post_id, get_current_user_id() );
283 + } else {
284 + $document = $this->get( $post_id );
285 + }
286 +
287 + return $document;
288 + }
289 +
290 + /**
291 + * Get document type.
292 + *
293 + * Retrieve the type of any given document.
294 + *
295 + * @since 2.0.0
296 + * @access public
297 + *
298 + * @param string $type
299 + *
300 + * @param string $fallback
301 + *
302 + * @return Document|bool The type of the document.
303 + */
304 + public function get_document_type( $type, $fallback = 'post' ) {
305 + $types = $this->get_document_types();
306 +
307 + if ( isset( $types[ $type ] ) ) {
308 + return $types[ $type ];
309 + }
310 +
311 + if ( isset( $types[ $fallback ] ) ) {
312 + return $types[ $fallback ];
313 + }
314 +
315 + return false;
316 + }
317 +
318 + /**
319 + * Get document types.
320 + *
321 + * Retrieve the all the registered document types.
322 + *
323 + * @since 2.0.0
324 + * @access public
325 + *
326 + * @param array $args Optional. An array of key => value arguments to match against
327 + * the properties. Default is empty array.
328 + * @param string $operator Optional. The logical operation to perform. 'or' means only one
329 + * element from the array needs to match; 'and' means all elements
330 + * must match; 'not' means no elements may match. Default 'and'.
331 + *
332 + * @return Document[] All the registered document types.
333 + */
334 + public function get_document_types( $args = [], $operator = 'and' ) {
335 + $this->register_types();
336 +
337 + if ( ! empty( $args ) ) {
338 + $types_properties = $this->get_types_properties();
339 +
340 + $filtered = wp_filter_object_list( $types_properties, $args, $operator );
341 +
342 + return array_intersect_key( $this->types, $filtered );
343 + }
344 +
345 + return $this->types;
346 + }
347 +
348 + /**
349 + * Get document types with their properties.
350 + *
351 + * @return array A list of properties arrays indexed by the type.
352 + */
353 + public function get_types_properties() {
354 + $types_properties = [];
355 +
356 + foreach ( $this->get_document_types() as $type => $class ) {
357 + $types_properties[ $type ] = $class::get_properties();
358 + }
359 + return $types_properties;
360 + }
361 +
362 + /**
363 + * Create a document.
364 + *
365 + * Create a new document using any given parameters.
366 + *
367 + * @since 2.0.0
368 + * @access public
369 + *
370 + * @param string $type Document type.
371 + * @param array $post_data An array containing the post data.
372 + * @param array $meta_data An array containing the post meta data.
373 + *
374 + * @return Document The type of the document.
375 + */
376 + public function create( $type, $post_data = [], $meta_data = [] ) {
377 + $class = $this->get_document_type( $type, false );
378 +
379 + if ( ! $class ) {
380 + return new \WP_Error( 500, sprintf( 'Type %s does not exist.', $type ) );
381 + }
382 +
383 + if ( empty( $post_data['post_title'] ) ) {
384 + $post_data['post_title'] = esc_html__( 'Elementor', 'elementor' );
385 + if ( 'post' !== $type ) {
386 + $post_data['post_title'] = sprintf(
387 + /* translators: %s: Document title. */
388 + __( 'Elementor %s', 'elementor' ),
389 + call_user_func( [ $class, 'get_title' ] )
390 + );
391 + }
392 + $update_title = true;
393 + }
394 +
395 + $meta_data['_elementor_edit_mode'] = 'builder';
396 +
397 + // Save the type as-is for plugins that hooked at `wp_insert_post`.
398 + $meta_data[ Document::TYPE_META_KEY ] = $type;
399 +
400 + $post_data['meta_input'] = $meta_data;
401 +
402 + $post_types = $class::get_property( 'cpt' );
403 +
404 + if ( ! empty( $post_types[0] ) && empty( $post_data['post_type'] ) ) {
405 + $post_data['post_type'] = $post_types[0];
406 + }
407 +
408 + $post_id = wp_insert_post( $post_data );
409 +
410 + if ( ! empty( $update_title ) ) {
411 + $post_data['ID'] = $post_id;
412 + $post_data['post_title'] .= ' #' . $post_id;
413 +
414 + // The meta doesn't need update.
415 + unset( $post_data['meta_input'] );
416 +
417 + wp_update_post( $post_data );
418 + }
419 +
420 + /** @var Document $document */
421 + $document = new $class( [
422 + 'post_id' => $post_id,
423 + ] );
424 +
425 + // Let the $document to re-save the template type by his way + version.
426 + $document->save( [] );
427 +
428 + return $document;
429 + }
430 +
431 + /**
432 + * Remove user edit capabilities if document is not editable.
433 + *
434 + * Filters the user capabilities to disable editing in admin.
435 + *
436 + * @param array $allcaps An array of all the user's capabilities.
437 + * @param array $caps Actual capabilities for meta capability.
438 + * @param array $args Optional parameters passed to has_cap(), typically object ID.
439 + *
440 + * @return array
441 + */
442 + public function remove_user_edit_cap( $allcaps, $caps, $args ) {
443 + global $pagenow;
444 +
445 + if ( ! in_array( $pagenow, [ 'post.php', 'edit.php' ], true ) ) {
446 + return $allcaps;
447 + }
448 +
449 + // Don't touch not existing or not allowed caps.
450 + if ( empty( $caps[0] ) || empty( $allcaps[ $caps[0] ] ) ) {
451 + return $allcaps;
452 + }
453 +
454 + $capability = $args[0];
455 +
456 + if ( 'edit_post' !== $capability ) {
457 + return $allcaps;
458 + }
459 +
460 + if ( empty( $args[2] ) ) {
461 + return $allcaps;
462 + }
463 +
464 + $post_id = $args[2];
465 +
466 + $document = Plugin::$instance->documents->get( $post_id );
467 +
468 + if ( ! $document ) {
469 + return $allcaps;
470 + }
471 +
472 + $allcaps[ $caps[0] ] = $document::get_property( 'is_editable' );
473 +
474 + return $allcaps;
475 + }
476 +
477 + /**
478 + * Filter Post Row Actions.
479 + *
480 + * Let the Document to filter the array of row action links on the Posts list table.
481 + *
482 + * @param array $actions
483 + * @param \WP_Post $post
484 + *
485 + * @return array
486 + */
487 + public function filter_post_row_actions( $actions, $post ) {
488 + $document = $this->get( $post->ID );
489 +
490 + if ( $document ) {
491 + $actions = $document->filter_admin_row_actions( $actions );
492 + }
493 +
494 + return $actions;
495 + }
496 +
497 + /**
498 + * Save document data using ajax.
499 + *
500 + * Save the document on the builder using ajax, when saving the changes, and refresh the editor.
501 + *
502 + * @since 2.0.0
503 + * @access public
504 + *
505 + * @param array $request Post ID.
506 + *
507 + * @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
508 + *
509 + * @return array The document data after saving.
510 + */
511 + public function ajax_save( $request ) {
512 + $document = $this->get( $request['editor_post_id'] );
513 +
514 + if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
515 + throw new \Exception( 'Access denied.' );
516 + }
517 +
518 + $this->switch_to_document( $document );
519 +
520 + // Set the post as global post.
521 + Plugin::$instance->db->switch_to_post( $document->get_post()->ID );
522 +
523 + $status = Document::STATUS_DRAFT;
524 +
525 + if ( isset( $request['status'] ) && in_array( $request['status'], [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE, Document::STATUS_PENDING, Document::STATUS_AUTOSAVE ], true ) ) {
526 + $status = $request['status'];
527 + }
528 +
529 + if ( Document::STATUS_AUTOSAVE === $status ) {
530 + // If the post is a draft - save the `autosave` to the original draft.
531 + // Allow a revision only if the original post is already published.
532 + if ( in_array( $document->get_post()->post_status, [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE ], true ) ) {
533 + $document = $document->get_autosave( 0, true );
534 + }
535 + }
536 +
537 + // Set default page template because the footer-saver doesn't send default values,
538 + // But if the template was changed from canvas to default - it needed to save.
539 + if ( Utils::is_cpt_custom_templates_supported() && ! isset( $request['settings']['template'] ) ) {
540 + $request['settings']['template'] = 'default';
541 + }
542 +
543 + $data = [
544 + 'elements' => $request['elements'],
545 + 'settings' => $request['settings'],
546 + ];
547 +
548 + $document->save( $data );
549 +
550 + $post = $document->get_post();
551 + $main_post = $document->get_main_post();
552 +
553 + // Refresh after save.
554 + $document = $this->get( $post->ID, false );
555 +
556 + $return_data = [
557 + 'status' => $post->post_status,
558 + 'config' => [
559 + 'document' => [
560 + 'last_edited' => $document->get_last_edited(),
561 + 'urls' => [
562 + 'wp_preview' => $document->get_wp_preview_url(),
563 + ],
564 + ],
565 + ],
566 + ];
567 +
568 + $post_status_object = get_post_status_object( $main_post->post_status );
569 +
570 + if ( $post_status_object ) {
571 + $return_data['config']['document']['status'] = [
572 + 'value' => $post_status_object->name,
573 + 'label' => $post_status_object->label,
574 + ];
575 + }
576 +
577 + /**
578 + * Returned documents ajax saved data.
579 + *
580 + * Filters the ajax data returned when saving the post on the builder.
581 + *
582 + * @since 2.0.0
583 + *
584 + * @param array $return_data The returned data.
585 + * @param Document $document The document instance.
586 + */
587 + $return_data = apply_filters( 'elementor/documents/ajax_save/return_data', $return_data, $document );
588 +
589 + return $return_data;
590 + }
591 +
592 + /**
593 + * Ajax discard changes.
594 + *
595 + * Load the document data from an autosave, deleting unsaved changes.
596 + *
597 + * @param array $request
598 + *
599 + * @return bool True if changes discarded, False otherwise.
600 + * @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
601 + *
602 + * @since 2.0.0
603 + * @access public
604 + */
605 + public function ajax_discard_changes( $request ) {
606 + $document = $this->get_with_permissions( $request['editor_post_id'] );
607 +
608 + $autosave = $document->get_autosave();
609 +
610 + if ( $autosave ) {
611 + $success = $autosave->delete();
612 + } else {
613 + $success = true;
614 + }
615 +
616 + return $success;
617 + }
618 +
619 + public function ajax_get_document_config( $request ) {
620 + $post_id = absint( $request['id'] );
621 +
622 + Plugin::$instance->editor->set_post_id( $post_id );
623 +
624 + $document = $this->get_doc_or_auto_save( $post_id );
625 +
626 + if ( ! $document ) {
627 + throw new \Exception( 'Not found.' );
628 + }
629 +
630 + if ( ! $document->is_editable_by_current_user() ) {
631 + throw new \Exception( 'Access denied.' );
632 + }
633 +
634 + // Set the global data like $post, $authordata and etc
635 + Plugin::$instance->db->switch_to_post( $post_id );
636 +
637 + $this->switch_to_document( $document );
638 +
639 + // Change mode to Builder
640 + $document->set_is_built_with_elementor( true );
641 +
642 + $doc_config = $document->get_config();
643 +
644 + return $doc_config;
645 + }
646 +
647 + /**
648 + * Switch to document.
649 + *
650 + * Change the document to any new given document type.
651 + *
652 + * @since 2.0.0
653 + * @access public
654 + *
655 + * @param Document $document The document to switch to.
656 + */
657 + public function switch_to_document( $document ) {
658 + // If is already switched, or is the same post, return.
659 + if ( $this->current_doc === $document ) {
660 + $this->switched_data[] = false;
661 + return;
662 + }
663 +
664 + $this->switched_data[] = [
665 + 'switched_doc' => $document,
666 + 'original_doc' => $this->current_doc, // Note, it can be null if the global isn't set
667 + ];
668 +
669 + $this->current_doc = $document;
670 + }
671 +
672 + /**
673 + * Restore document.
674 + *
675 + * Rollback to the original document.
676 + *
677 + * @since 2.0.0
678 + * @access public
679 + */
680 + public function restore_document() {
681 + $data = array_pop( $this->switched_data );
682 +
683 + // If not switched, return.
684 + if ( ! $data ) {
685 + return;
686 + }
687 +
688 + $this->current_doc = $data['original_doc'];
689 + }
690 +
691 + /**
692 + * Get current document.
693 + *
694 + * Retrieve the current document.
695 + *
696 + * @since 2.0.0
697 + * @access public
698 + *
699 + * @return Document The current document.
700 + */
701 + public function get_current() {
702 + return $this->current_doc;
703 + }
704 +
705 + public function localize_settings( $settings ) {
706 + $translations = [];
707 +
708 + foreach ( $this->get_document_types() as $type => $class ) {
709 + $translations[ $type ] = $class::get_title();
710 + }
711 +
712 + return array_replace_recursive( $settings, [
713 + 'i18n' => $translations,
714 + ] );
715 + }
716 +
717 + private function register_types() {
718 + if ( ! did_action( 'elementor/documents/register' ) ) {
719 + /**
720 + * Register Elementor documents.
721 + *
722 + * @since 2.0.0
723 + *
724 + * @param Documents_Manager $this The document manager instance.
725 + */
726 + do_action( 'elementor/documents/register', $this );
727 + }
728 + }
729 +
730 + /**
731 + * Get create new post URL.
732 + *
733 + * Retrieve a custom URL for creating a new post/page using Elementor.
734 + *
735 + * @param string $post_type Optional. Post type slug. Default is 'page'.
736 + * @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
737 + *
738 + * @return string A URL for creating new post using Elementor.
739 + */
740 + public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
741 + $query_args = [
742 + 'action' => 'elementor_new_post',
743 + 'post_type' => $post_type,
744 + ];
745 +
746 + if ( $template_type ) {
747 + $query_args['template_type'] = $template_type;
748 + }
749 +
750 + $new_post_url = add_query_arg( $query_args, admin_url( 'edit.php' ) );
751 +
752 + $new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
753 +
754 + return $new_post_url;
755 + }
756 +
757 + private function get_doc_type_by_id( $post_id ) {
758 + // Auto-save inherits from the original post.
759 + if ( wp_is_post_autosave( $post_id ) ) {
760 + $post_id = wp_get_post_parent_id( $post_id );
761 + }
762 +
763 + // Content built with Elementor.
764 + $template_type = get_post_meta( $post_id, Document::TYPE_META_KEY, true );
765 +
766 + if ( $template_type && isset( $this->types[ $template_type ] ) ) {
767 + return $template_type;
768 + }
769 +
770 + // Elementor installation on a site with existing content (which doesn't contain Elementor's meta).
771 + $post_type = get_post_type( $post_id );
772 +
773 + return $this->cpt[ $post_type ] ?? 'post';
774 + }
775 +
776 + public function register_rest_routes() {
777 + register_rest_route('elementor/v1/documents', '/(?P<id>\d+)/media/import', [
778 + 'methods' => \WP_REST_Server::CREATABLE,
779 + 'callback' => function( $request ) {
780 + $post_id = $request->get_param( 'id' );
781 +
782 + try {
783 + $document = $this->get_with_permissions( $post_id );
784 +
785 + $elements_data = $document->get_elements_data();
786 +
787 + $import_data = $document->get_import_data( [
788 + 'content' => $elements_data,
789 + ] );
790 +
791 + $document->save( [
792 + 'elements' => $import_data['content'],
793 + ] );
794 +
795 + return new \WP_REST_Response( [
796 + 'success' => true,
797 + 'document_saved' => true,
798 + ], 200 );
799 +
800 + } catch ( \Exception $e ) {
801 + return new \WP_Error(
802 + 'elementor_import_error',
803 + $e->getMessage(),
804 + [ 'status' => 500 ]
805 + );
806 + }
807 + },
808 + 'permission_callback' => function() {
809 + return current_user_can( 'manage_options' );
810 + },
811 + 'args' => [
812 + 'id' => [
813 + 'required' => true,
814 + 'validate_callback' => function( $param ) {
815 + return is_numeric( $param );
816 + },
817 + ],
818 + ],
819 + ]);
820 + }
821 + }
822 +