count' => [ 'description' => __( 'Whether to create a new user account as part of order processing.', 'woocommerce' ), 'type' => 'boolean', 'context' => [ 'view', 'edit' ], ], 'payment_result' => [ 'description' => __( 'Result of payment processing, or false if not yet processed.', 'woocommerce' ), 'type' => 'object', 'context' => [ 'view', 'edit' ], 'readonly' => true, 'properties' => [ 'payment_status' => [ 'description' => __( 'Status of the payment returned by the gateway. One of success, pending, failure, error.', 'woocommerce' ), 'readonly' => true, 'type' => 'string', ], 'payment_details' => [ 'description' => __( 'An array of data being returned from the payment gateway.', 'woocommerce' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'key' => [ 'type' => 'string', ], 'value' => [ 'type' => 'string', ], ], ], ], 'redirect_url' => [ 'description' => __( 'A URL to redirect the customer after checkout. This could be, for example, a link to the payment processors website.', 'woocommerce' ), 'readonly' => true, 'type' => 'string', ], ], ], self::EXTENDING_KEY => $this->get_extended_schema( self::IDENTIFIER ), ]; } /** * Return the response for checkout. * * @param object $item Results from checkout action. * @return array */ public function get_item_response( $item ) { return $this->get_checkout_response( $item->order, $item->payment_result ); } /** * Get the checkout response based on the current order and any payments. * * @param \WC_Order $order Order object. * @param PaymentResult $payment_result Payment result object. * @return array */ protected function get_checkout_response( \WC_Order $order, PaymentResult $payment_result = null ) { return [ 'order_id' => $order->get_id(), 'status' => $order->get_status(), 'order_key' => $order->get_order_key(), 'customer_note' => $order->get_customer_note(), 'customer_id' => $order->get_customer_id(), 'billing_address' => $this->billing_address_schema->get_item_response( $order ), 'shipping_address' => $this->shipping_address_schema->get_item_response( $order ), 'payment_method' => $order->get_payment_method(), 'payment_result' => [ 'payment_status' => $payment_result->status, 'payment_details' => $this->prepare_payment_details_for_response( $payment_result->payment_details ), 'redirect_url' => $payment_result->redirect_url, ], self::EXTENDING_KEY => $this->get_extended_data( self::IDENTIFIER ), ]; } /** * This prepares the payment details for the response so it's following the * schema where it's an array of objects. * * @param array $payment_details An array of payment details from the processed payment. * * @return array An array of objects where each object has the key and value * as distinct properties. */ protected function prepare_payment_details_for_response( array $payment_details ) { return array_map( function( $key, $value ) { return (object) [ 'key' => $key, 'value' => $value, ]; }, array_keys( $payment_details ), $payment_details ); } } ce 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'; } }