postSettings->canAddPageBuilderMetabox( get_post_type( $this->getPostId() ) ) ) { return; } if ( ! did_action( 'elementor/init' ) ) { add_action( 'elementor/init', [ $this, 'addPanelTab' ] ); } else { $this->addPanelTab(); } add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'addInlineScript' ] ); add_action( 'elementor/documents/register_controls', [ $this, 'registerDocumentControls' ] ); add_action( 'elementor/editor/footer', [ $this, 'addContainers' ] ); // Add the SEO tab to the main Elementor panel. add_action( 'elementor/editor/footer', [ $this, 'startCapturing' ], 0 ); add_action( 'elementor/editor/footer', [ $this, 'endCapturing' ], 999 ); } /** * Add the inline script to the Elementor editor. * We do this because our scripts are loaded with the `type="module"` attribute. * This means that the script is not executed until the DOM is fully loaded. * This is a hack to add the button to the Elementor editor. * * @since 4.9.2 * * @return void */ public function addInlineScript() { $title = esc_js( __( 'Save (Don\'t Modify Date)', 'all-in-one-seo-pack' ) ); $script = " (function($) { $(window).on('elementor:init', () => { if(!window?.elementorV2) { return } window.elementorV2.editorAppBar.documentOptionsMenu.registerToggleAction({ priority : 10, useProps : () => { const currentDocument = window.elementor?.documents?.getCurrent() || null; const isChanged = currentDocument?.editor?.isChanged ?? true; const isSaving = currentDocument?.editor?.isSaving ?? false; return { title : '{$title}', icon : window.elementorV2.icons.CalendarIcon, onClick : () => { document.dispatchEvent(new Event('aioseo-limit-modified-date-save')) }, disabled : !isChanged || isSaving } } }) }) })(window.jQuery) "; wp_add_inline_script( 'elementor-editor', $script, 'before' ); } /** * Start capturing buffer. * * @since 4.3.5 * * @return void */ public function startCapturing() { ob_start(); } /** * End capturing buffer and add button. * This is a hack to add the SEO tab to the main Elementor panel. * We need to do this because Elementor doesn't provide a filter to add tabs to the main panel. * * @since 4.3.5 * * @return void */ public function endCapturing() { $output = ob_get_clean(); $search = '/(
.*<\/div>)/m'; $replace = '${1}
SEO
'; echo preg_replace( $search, $replace, $output ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Add the AIOSEO Panel Tab on Elementor. * * @since 4.1.7 * * @return void */ public function addPanelTab() { ControlsManager::add_tab( 'aioseo', AIOSEO_PLUGIN_SHORT_NAME ); } /** * Register the Elementor Document Controls. * * @since 4.1.7 * * @return void */ public function registerDocumentControls( $document ) { // PageBase is the base class for documents like `post` `page` and etc. if ( ! $document instanceof PageBase || ! $document::get_property( 'has_elements' ) ) { return; } // This is needed to get the tab to appear, but will be overwritten in the JavaScript. $document->start_controls_section( 'aioseo_section', [ 'label' => AIOSEO_PLUGIN_SHORT_NAME, 'tab' => 'aioseo', ] ); $document->end_controls_section(); } /** * Returns whether or not the given Post ID was built with Elementor. * * @since 4.1.7 * * @param int $postId The Post ID. * @return boolean Whether or not the Post was built with Elementor. */ public function isBuiltWith( $postId ) { $document = $this->getElementorDocument( $postId ); if ( ! $document ) { return false; } return $document->is_built_with_elementor(); } /** * Returns the content TruSEO analyzes: the HTML Elementor saves to `post_content`, rendered. * * NOTE: Elementor's `the_content` filter swaps in the global post's front-end render, menus and * post loops included, so it is held off; uncached, as {@see WpContext::getPostContent()} may hold it. * * @since 5.0.4 * * @param \WP_Post $post The post. * @return string The content. */ public function getAnalysisContent( $post ) { $frontend = class_exists( '\Elementor\Plugin' ) ? ( \Elementor\Plugin::instance()->frontend ?? null ) : null; $callback = [ $frontend, 'apply_builder_in_content' ]; $priority = is_object( $frontend ) && ! doing_filter( 'the_content' ) ? has_filter( 'the_content', $callback ) : false; if ( false !== $priority ) { remove_filter( 'the_content', $callback, $priority ); } try { $content = aioseo()->helpers->theContent( $this->processContent( $post->ID, $post->post_content ) ); if ( apply_filters( 'aioseo_description_include_custom_fields', true, $post ) ) { $content .= aioseo()->helpers->theContent( aioseo()->helpers->getPostCustomFieldsContent( $post ) ); } return $content; } finally { if ( false !== $priority ) { add_filter( 'the_content', $callback, $priority ); } } } /** * Returns the Elementor edit url for the given Post ID. * * @since 4.3.1 * * @param int $postId The Post ID. * @return string The Edit URL. */ public function getEditUrl( $postId ) { $document = $this->getElementorDocument( $postId ); if ( ! $document || ! $document->is_editable_by_current_user() ) { return ''; } return esc_url( $document->get_edit_url() ); } /** * Add the containers to mount our panel. * * @since 4.1.9 * * @return void */ public function addContainers() { echo '
'; } /** * Returns the Elementor Document instance for the given Post ID. * * @since 4.3.5 * * @param int $postId The Post ID. * @return object The Elementor Document instance. */ private function getElementorDocument( $postId ) { if ( ! class_exists( '\Elementor\Plugin' ) || ! is_object( \Elementor\Plugin::instance()->documents ) || ! method_exists( \Elementor\Plugin::instance()->documents, 'get' ) ) { return false; } $elementorDocument = \Elementor\Plugin::instance()->documents->get( $postId ); if ( empty( $elementorDocument ) ) { return false; } return $elementorDocument; } /** * Checks whether or not we should prevent the date from being modified. * This method is supposed to be used in the `wp_ajax_seedprod_pro_save_lpage` action. * * @since 4.5.2 * * @param int $postId The Post ID. * @return bool Whether or not we should prevent the date from being modified. */ public function limitModifiedDate( $postId ) { // This method is supposed to be used in the `wp_ajax_elementor_ajax` action. if ( empty( $_REQUEST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_nonce'] ) ), 'elementor_ajax' ) ) { return false; } $editorPostId = ! empty( $_REQUEST['editor_post_id'] ) ? (int) $_REQUEST['editor_post_id'] : false; if ( $editorPostId !== $postId ) { return false; } return ! empty( $_REQUEST['aioseo_limit_modified_date'] ); } /** * Get the post ID. * * @since 4.6.9 * * @return int|null The post ID or null. */ public function getPostId() { // phpcs:disable HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended if ( aioseo()->helpers->isAjaxCronRestRequest() ) { foreach ( [ 'editor_post_id', 'initial_document_id' ] as $key ) { if ( ! empty( $_REQUEST[ $key ] ) ) { return intval( wp_unslash( $_REQUEST[ $key ] ) ); } } } // phpcs:enable return parent::getPostId(); } }