ajax, when saving the changes, and refresh the editor.
*
* @since 2.0.0
* @access public
*
* @param $request Post ID.
*
* @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
*
* @return array The document data after saving.
*/
public function ajax_save( $request ) {
$document = $this->get( $request['editor_post_id'] );
if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
throw new \Exception( 'Access denied.' );
}
$this->switch_to_document( $document );
// Set the post as global post.
Plugin::$instance->db->switch_to_post( $document->get_post()->ID );
$status = Document::STATUS_DRAFT;
if ( isset( $request['status'] ) && in_array( $request['status'], [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE, Document::STATUS_PENDING, Document::STATUS_AUTOSAVE ], true ) ) {
$status = $request['status'];
}
if ( Document::STATUS_AUTOSAVE === $status ) {
// If the post is a draft - save the `autosave` to the original draft.
// Allow a revision only if the original post is already published.
if ( in_array( $document->get_post()->post_status, [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE ], true ) ) {
$document = $document->get_autosave( 0, true );
}
}
// Set default page template because the footer-saver doesn't send default values,
// But if the template was changed from canvas to default - it needed to save.
if ( Utils::is_cpt_custom_templates_supported() && ! isset( $request['settings']['template'] ) ) {
$request['settings']['template'] = 'default';
}
$data = [
'elements' => $request['elements'],
'settings' => $request['settings'],
];
$document->save( $data );
$post = $document->get_post();
$main_post = $document->get_main_post();
// Refresh after save.
$document = $this->get( $post->ID, false );
$return_data = [
'status' => $post->post_status,
'config' => [
'document' => [
'last_edited' => $document->get_last_edited(),
'urls' => [
'wp_preview' => $document->get_wp_preview_url(),
],
],
],
];
$post_status_object = get_post_status_object( $main_post->post_status );
if ( $post_status_object ) {
$return_data['config']['document']['status'] = [
'value' => $post_status_object->name,
'label' => $post_status_object->label,
];
}
/**
* Returned documents ajax saved data.
*
* Filters the ajax data returned when saving the post on the builder.
*
* @since 2.0.0
*
* @param array $return_data The returned data.
* @param Document $document The document instance.
*/
$return_data = apply_filters( 'elementor/documents/ajax_save/return_data', $return_data, $document );
return $return_data;
}
/**
* Ajax discard changes.
*
* Load the document data from an autosave, deleting unsaved changes.
*
* @param $request
*
* @return bool True if changes discarded, False otherwise.
* @throws \Exception
*
* @since 2.0.0
* @access public
*
*/
public function ajax_discard_changes( $request ) {
$document = $this->get_with_permissions( $request['editor_post_id'] );
$autosave = $document->get_autosave();
if ( $autosave ) {
$success = $autosave->delete();
} else {
$success = true;
}
return $success;
}
public function ajax_get_document_config( $request ) {
$post_id = absint( $request['id'] );
Plugin::$instance->editor->set_post_id( $post_id );
$document = $this->get_doc_or_auto_save( $post_id );
if ( ! $document ) {
throw new \Exception( 'Not found.' );
}
if ( ! $document->is_editable_by_current_user() ) {
throw new \Exception( 'Access denied.' );
}
// Set the global data like $post, $authordata and etc
Plugin::$instance->db->switch_to_post( $post_id );
$this->switch_to_document( $document );
// Change mode to Builder
$document->set_is_built_with_elementor( true );
$doc_config = $document->get_config();
return $doc_config;
}
/**
* Switch to document.
*
* Change the document to any new given document type.
*
* @since 2.0.0
* @access public
*
* @param Document $document The document to switch to.
*/
public function switch_to_document( $document ) {
// If is already switched, or is the same post, return.
if ( $this->current_doc === $document ) {
$this->switched_data[] = false;
return;
}
$this->switched_data[] = [
'switched_doc' => $document,
'original_doc' => $this->current_doc, // Note, it can be null if the global isn't set
];
$this->current_doc = $document;
}
/**
* Restore document.
*
* Rollback to the original document.
*
* @since 2.0.0
* @access public
*/
public function restore_document() {
$data = array_pop( $this->switched_data );
// If not switched, return.
if ( ! $data ) {
return;
}
$this->current_doc = $data['original_doc'];
}
/**
* Get current document.
*
* Retrieve the current document.
*
* @since 2.0.0
* @access public
*
* @return Document The current document.
*/
public function get_current() {
return $this->current_doc;
}
public function localize_settings( $settings ) {
$translations = [];
foreach ( $this->get_document_types() as $type => $class ) {
$translations[ $type ] = $class::get_title();
}
return array_replace_recursive( $settings, [
'i18n' => $translations,
] );
}
private function register_types() {
if ( ! did_action( 'elementor/documents/register' ) ) {
/**
* Register Elementor documents.
*
* @since 2.0.0
*
* @param Documents_Manager $this The document manager instance.
*/
do_action( 'elementor/documents/register', $this );
}
}
/**
* Get create new post URL.
*
* Retrieve a custom URL for creating a new post/page using Elementor.
*
* @param string $post_type Optional. Post type slug. Default is 'page'.
* @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
*
* @return string A URL for creating new post using Elementor.
*/
public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
$query_args = [
'action' => 'elementor_new_post',
'post_type' => $post_type,
];
if ( $template_type ) {
$query_args['template_type'] = $template_type;
}
$new_post_url = add_query_arg( $query_args, admin_url( 'edit.php' ) );
$new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
return $new_post_url;
}
private function get_doc_type_by_id( $post_id ) {
// Auto-save inherits from the original post.
if ( wp_is_post_autosave( $post_id ) ) {
$post_id = wp_get_post_parent_id( $post_id );
}
// Content built with Elementor.
$template_type = get_post_meta( $post_id, Document::TYPE_META_KEY, true );
if ( $template_type && isset( $this->types[ $template_type ] ) ) {
return $template_type;
}
// Elementor installation on a site with existing content (which doesn't contain Elementor's meta).
$post_type = get_post_type( $post_id );
return $this->cpt[ $post_type ] ?? 'post';
}
public function register_rest_routes() {
register_rest_route('elementor/v1/documents', '/(?P\d+)/media/import', [
'methods' => \WP_REST_Server::CREATABLE,
'callback' => function( $request ) {
$post_id = $request->get_param( 'id' );
try {
$document = $this->get_with_permissions( $post_id );
$elements_data = $document->get_elements_data();
$import_data = $document->get_import_data( [
'content' => $elements_data,
] );
$document->save( [
'elements' => $import_data['content'],
] );
return new \WP_REST_Response( [
'success' => true,
'document_saved' => true,
], 200 );
} catch ( \Exception $e ) {
return new \WP_Error(
'elementor_import_error',
$e->getMessage(),
[ 'status' => 500 ]
);
}
},
'permission_callback' => function() {
return current_user_can( 'manage_options' );
},
'args' => [
'id' => [
'required' => true,
'validate_callback' => function( $param ) {
return is_numeric( $param );
},
],
],
]);
}
}