=' ) ) {
add_filter( 'get_post_metadata', [ $this, 'maybeDisableWpBakeryMetaTags' ], 10, 3 );
}
if ( ! aioseo()->postSettings->canAddPageBuilderMetabox( get_post_type( $this->getPostId() ) ) ) {
return;
}
add_action( 'vc_frontend_editor_enqueue_js_css', [ $this, 'enqueue' ] );
add_action( 'vc_backend_editor_enqueue_js_css', [ $this, 'enqueue' ] );
add_filter( 'vc_nav_front_controls', [ $this, 'addNavbarControls' ] );
add_filter( 'vc_nav_controls', [ $this, 'addNavbarControls' ] );
}
/**
* Maybe disable WP Bakery meta tags.
*
* @since 4.7.1
*
* @param mixed $value The value of the meta.
* @param int $objectId The object ID.
* @param string $metaKey The meta key.
* @return mixed The value of the meta.
*/
public function maybeDisableWpBakeryMetaTags( $value, $objectId, $metaKey ) {
if ( is_singular() && '_wpb_post_custom_seo_settings' === $metaKey ) {
return null;
}
return $value;
}
/**
* Add AIOSEO controls to the WPBakery navbar.
*
* @since 4.5.2
*
* @param array $controlList The control list.
* @return array The control list.
*/
public function addNavbarControls( $controlList ) {
if ( ! $controlList ) {
return $controlList;
}
remove_filter( 'vc_nav_front_controls', [ $this, 'addNavbarControls' ] );
remove_filter( 'vc_nav_controls', [ $this, 'addNavbarControls' ] );
$controlList[] = [
'aioseo',
'
'
];
return $controlList;
}
/**
* Returns the attribute names that hold visible copy on the given tag.
*
* Read from WPBakery's own element map, which is registered on front-end requests too, so the
* copy-bearing attributes never have to be listed here and cannot fall behind new elements.
*
* @since 5.0.3
*
* @param string $tag The shortcode tag.
* @return array The attribute names.
*/
protected function getTextAttributes( $tag ) {
static $attributes = [];
if ( isset( $attributes[ $tag ] ) ) {
return $attributes[ $tag ];
}
$attributes[ $tag ] = parent::getTextAttributes( $tag );
$params = $this->getMappedParams( $tag );
if ( empty( $params ) ) {
return $attributes[ $tag ];
}
$names = [];
foreach ( $params as $param ) {
$name = isset( $param['param_name'] ) ? $param['param_name'] : '';
$type = isset( $param['type'] ) ? $param['type'] : '';
if ( '' === $name || ! in_array( $type, $this->textParamTypes, true ) ) {
continue;
}
if ( preg_match( $this->machineParamPattern, $name ) ) {
continue;
}
$names[] = $name;
}
if ( ! empty( $names ) ) {
$attributes[ $tag ] = $names;
}
return $attributes[ $tag ];
}
/**
* Returns whether the given tag stores its enclosed body encoded or as code rather than as copy.
*
* Raw HTML and Raw JS store their body base64 encoded and decode it in their own render template,
* so the blob would otherwise survive into the description once the tags around it are dropped.
*
* @since 5.0.3
*
* @param string $tag The shortcode tag.
* @param string $body The enclosed body.
* @return bool Whether the body is unreadable.
*/
protected function isRawBody( $tag, $body ) { // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
static $raw = [];
if ( isset( $raw[ $tag ] ) ) {
return $raw[ $tag ];
}
$raw[ $tag ] = false;
foreach ( $this->getMappedParams( $tag ) as $param ) {
$isBodyParam = 'content' === ( isset( $param['param_name'] ) ? $param['param_name'] : '' ) || ! empty( $param['holder'] );
if ( $isBodyParam && in_array( isset( $param['type'] ) ? $param['type'] : '', $this->rawParamTypes, true ) ) {
$raw[ $tag ] = true;
}
}
return $raw[ $tag ];
}
/**
* Returns whether the given attribute value is visible copy.
*
* WPBakery's map marks a param's type but not whether it is shown to a reader, so its text types
* still carry sizes, counts and delays. A value with no letter in it, or a bare CSS length or
* duration such as `32px` or `0.3s`, is one of those. On a heading or label param only the CSS
* length or duration is dropped, so a heading like `2024` or `100%` is kept.
*
* NOTE: Byte-safe on purpose. A letter in any script falls outside the classes below, and adding a
* /u flag would let invalid UTF-8 fail the match and silently empty the description.
*
* @since 5.0.3
*
* @param string $value The attribute value.
* @param string $attribute The attribute name.
* @return bool Whether the value is copy.
*/
protected function isCopyValue( $value, $attribute ) {
if ( preg_match( $this->headingParamPattern, $attribute ) ) {
return ! preg_match( '/^[0-9.]+(?:px|r?em|vw|vh|ms|s)$/i', $value );
}
return ! preg_match( '/^[0-9\s\-_.,:;#%\/()\[\]|+*]+(?:px|r?em|vw|vh|ms|s)?$/i', $value );
}
/**
* Returns the mapped params for the given tag, or an empty array when WPBakery has not mapped it.
*
* @since 5.0.3
*
* @param string $tag The shortcode tag.
* @return array The params.
*/
private function getMappedParams( $tag ) {
if ( ! method_exists( '\WPBMap', 'getShortCode' ) ) {
return [];
}
$definition = \WPBMap::getShortCode( $tag );
return empty( $definition['params'] ) ? [] : (array) $definition['params'];
}
/**
* Returns whether or not the given Post ID was built with WPBakery.
*
* @since 4.5.2
*
* @param int $postId The Post ID.
* @return boolean Whether or not the Post was built with WPBakery.
*/
public function isBuiltWith( $postId ) {
$postObj = get_post( $postId );
if ( ! empty( $postObj ) && preg_match( '/vc_row/', (string) $postObj->post_content ) ) {
return true;
}
return false;
}
/**
* Returns whether should or not limit the modified date.
*
* @since 4.5.2
*
* @param int $postId The Post ID.
* @return boolean Whether or not sholud limit the modified date.
*/
public function limitModifiedDate( $postId ) {
// This method is supposed to be used in the `saveAjaxFe` action.
if ( empty( $_REQUEST['_vcnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_vcnonce'] ) ), 'vc-nonce-vc-admin-nonce' ) ) {
return false;
}
$editorPostId = ! empty( $_REQUEST['post_id'] ) ? intval( $_REQUEST['post_id'] ) : 0;
if ( $editorPostId !== $postId ) {
return false;
}
return ! empty( $_REQUEST['aioseo_limit_modified_date'] ) && (bool) $_REQUEST['aioseo_limit_modified_date'];
}
/**
* Returns the processed page builder content.
*
* @since 4.5.2
* @version 4.9.9 Render shortcodes on front-end views too, not just AJAX/cron/REST.
* @version 5.0.3 Extract text from the raw markup on front-end views instead of rendering it.
*
* @param int $postId The post id.
* @param mixed $content The raw content.
* @return string The processed content.
*/
public function processContent( $postId, $content = null ) {
if ( method_exists( '\WPBMap', 'addAllMappedShortcodes' ) ) {
\WPBMap::addAllMappedShortcodes();
}
$content = parent::processContent( $postId, $content ?? '' );
// WPBakery content is purely [vc_*] shortcodes; the parent only expands them in AJAX/cron/REST,
// so on front-end views they survive to strip_shortcodes() and the auto-description comes out
// empty. Read the copy straight out of the markup instead: WPBakery elements register assets and
// per-instance init state when they render, so running them here and again during the real render
// leaves tabs, tours and carousels broken.
if ( ! is_admin() && ! aioseo()->helpers->isAjaxCronRestRequest() && ! doing_filter( 'the_content' ) ) {
$content = $this->extractShortcodeText( $content );
}
return $content;
}
/**
* Returns the given attribute value with WPBakery's encoding undone.
*
* NOTE: WPBakery stores `[`, `]` and `"` in attribute values as `{`, `}` and ``. The brackets are
* decoded first, as WPBakery does, since their tokens contain the quote token.
*
* @since 5.0.3
*
* @param string $value The raw attribute value.
* @return string The decoded value.
*/
protected function decodeAttributeValue( $value ) {
return str_replace( [ '`{`', '`}`', '``' ], [ '[', ']', '"' ], $value );
}
}