$status ) ); } function dashe_seo_admin_menu() { add_options_page( 'Dashe SEO', 'Dashe SEO', 'manage_options', 'dashe-seo', 'dashe_seo_admin' ); } add_action( 'admin_menu', 'dashe_seo_admin_menu' ); function dashe_seo_admin() { if ( ! current_user_can( 'manage_options' ) ) { return; } $c = dashe_seo_config(); $security = dashe_seo_host_security(); ?>

Dashe SEO

Connect this website to your Dashe workspace to manage approved SEO fields and publish articles. Chat settings remain independent.

PHP host check: — PHP security guidance

Dashe receives PHP and WordPress versions, operating-system family, session-upload status and cURL Brotli support/version during authenticated connection and security checks. It does not receive phpinfo, environment secrets or the full server configuration.

Dashe receives public page content, metadata, selected author/category names and publication receipts. Content may be processed by your workspace's configured AI and search-data providers. Privacy · Service terms

SEO connection active. Changes remain limited to the permissions selected here and in Dashe.



Only list images your business has permission to publish. Add accurate titles and alt text in the Media Library. Dashe may use these as article featured images; no image is selected by default.

URL changes, redirects, indexing controls, prices, inventory, theme files and checkout are excluded. You can disconnect at any time. Updating this plugin never enables SEO automatically.

403 ) ); } check_admin_referer( 'dashe_seo_connect' ); $token = isset( $_POST['pairing_code'] ) ? sanitize_text_field( wp_unslash( $_POST['pairing_code'] ) ) : ''; if ( ! preg_match( '/^[a-f0-9]{64}$/D', $token ) ) { wp_die( 'Use a fresh connection code from Dashe.' ); } $previous = dashe_seo_config(); $c = array( 'enabled' => true, 'secret' => bin2hex( random_bytes( 32 ) ), 'user_id' => get_current_user_id(), 'metadata' => ! empty( $_POST['metadata'] ), 'publish' => ! empty( $_POST['publish'] ) ); $c['content'] = ! empty( $_POST['content'] ); $c['approved_media'] = array_slice( array_filter( array_map( 'absint', explode( ',', sanitize_text_field( wp_unslash( $_POST['approved_media'] ?? '' ) ) ) ) ), 0, 20 ); update_option( 'dashe_seo_connection', $c, false ); $response = wp_remote_post( 'https://www.getdashe.com/api/seo/pair', array( 'timeout' => 30, 'redirection' => 0, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'url' => trailingslashit( home_url() ), 'endpoint' => rest_url( 'dashe-seo/v1/exchange' ), 'token' => $token, 'secret' => $c['secret'] ) ) ) ); $ok = ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) && ! empty( json_decode( wp_remote_retrieve_body( $response ), true )['ok'] ); if ( ! $ok ) { update_option( 'dashe_seo_connection', $previous, false ); } wp_safe_redirect( admin_url( 'options-general.php?page=dashe-seo&dashe_seo_status=' . ( $ok ? 'connected' : 'failed' ) ) ); exit; } add_action( 'admin_post_dashe_seo_connect', 'dashe_seo_connect' ); function dashe_seo_disconnect() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( 'Not authorized.', '', array( 'response' => 403 ) ); } check_admin_referer( 'dashe_seo_disconnect' ); delete_option( 'dashe_seo_connection' ); wp_safe_redirect( admin_url( 'options-general.php?page=dashe-seo&dashe_seo_status=disconnected' ) ); exit; } add_action( 'admin_post_dashe_seo_disconnect', 'dashe_seo_disconnect' ); function dashe_seo_permission( $request ) { $c = dashe_seo_config(); if ( empty( $c['enabled'] ) || empty( $c['secret'] ) || ! user_can( $c['user_id'], 'manage_options' ) ) { return dashe_seo_error( 'SEO access is disabled.', 403 ); } $stamp = $request->get_header( 'x-dashe-timestamp' ); $nonce = $request->get_header( 'x-dashe-nonce' ); $signature = $request->get_header( 'x-dashe-signature' ); if ( ! ctype_digit( $stamp ) || abs( time() - (int) $stamp ) > 120 || ! preg_match( '/^[a-f0-9]{40}$/D', $nonce ) || strlen( $request->get_body() ) > 2000000 ) { return dashe_seo_error( 'Invalid signed request.', 403 ); } $expected = hash_hmac( 'sha256', $stamp . "\n" . $nonce . "\n" . $request->get_body(), $c['secret'] ); if ( ! hash_equals( $expected, $signature ) ) { return dashe_seo_error( 'Invalid signature.', 403 ); } $GLOBALS['dashe_seo_request_secret'] = $c['secret']; // Unique option insertion is atomic on both MySQL and WordPress SQLite. if ( ! add_option( 'dashe_seo_nonce_' . $nonce, time(), '', false ) ) { return dashe_seo_error( 'Request already used.', 409 ); } global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Expired nonce housekeeping; nonce entries are never read from cache. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s AND CAST(option_value AS UNSIGNED) < %d", $wpdb->esc_like( 'dashe_seo_nonce_' ) . '%', time() - 300 ) ); return true; } add_action( 'rest_api_init', function () { register_rest_route( 'dashe-seo/v1', '/exchange', array( 'methods' => 'POST', 'callback' => 'dashe_seo_exchange', 'permission_callback' => 'dashe_seo_permission' ) ); } ); function dashe_seo_adapter() { if ( ( defined( 'WPSEO_VERSION' ) && defined( 'RANK_MATH_VERSION' ) ) || defined( 'SEOPRESS_VERSION' ) || defined( 'AIOSEO_VERSION' ) || defined( 'THE_SEO_FRAMEWORK_VERSION' ) ) { return 'unsupported'; } if ( defined( 'RANK_MATH_VERSION' ) && class_exists( '\RankMath\Helper' ) && \RankMath\Helper::is_invalid_registration() ) { return 'setup_required'; } return defined( 'WPSEO_VERSION' ) ? 'yoast' : ( defined( 'RANK_MATH_VERSION' ) ? 'rank_math' : 'dashe' ); } function dashe_seo_keys() { $a = dashe_seo_adapter(); return 'yoast' === $a ? array( '_yoast_wpseo_title', '_yoast_wpseo_metadesc' ) : ( 'rank_math' === $a ? array( 'rank_math_title', 'rank_math_description' ) : array( '_dashe_seo_title', '_dashe_seo_description' ) ); } function dashe_seo_snapshot( $id ) { $p = get_post( $id ); if ( ! $p ) { return null; } $keys = dashe_seo_keys(); $builder = get_post_meta( $id, '_elementor_edit_mode', true ) || get_post_meta( $id, '_et_pb_use_builder', true ); $s = array( 'id' => (int) $id, 'url' => get_permalink( $id ), 'type' => $p->post_type, 'status' => $p->post_status, 'title' => $p->post_title, 'content' => $p->post_content, 'excerpt' => $p->post_excerpt, 'seo_title' => get_post_meta( $id, $keys[0], true ), 'description' => get_post_meta( $id, $keys[1], true ), 'editable' => ! $builder, 'adapter' => dashe_seo_adapter(), 'modified' => $p->post_modified_gmt, 'author' => (int) $p->post_author ); $s['featured_media'] = (int) get_post_thumbnail_id( $id ); $s['fragments'] = ! $builder && 'product' !== $p->post_type ? array_map( function ( $f ) { return array( 'index' => $f['index'], 'text' => $f['text'] ); }, dashe_seo_fragments( $p->post_content ) ) : array(); $s['hash'] = hash( 'sha256', wp_json_encode( $s ) ); return $s; } function dashe_seo_fragments( $content ) { // Preserve all HTML, block delimiters, attributes, links and unknown storage. foreach ( parse_blocks( $content ) as $block ) { if ( ! empty( $block['blockName'] ) && ! in_array( $block['blockName'], array( 'core/paragraph', 'core/heading', 'core/list', 'core/image' ), true ) ) { return array(); } } if ( preg_match( '/\[[a-zA-Z]|<(script|style|textarea|pre|code|iframe)\b/i', $content ) ) { return array(); } preg_match_all( '#]*)?>([^<]*)

#u', $content, $matches, PREG_OFFSET_CAPTURE ); $fragments = array(); foreach ( $matches[1] as $match ) { $text = html_entity_decode( $match[0], ENT_QUOTES, 'UTF-8' ); if ( strlen( $text ) < 40 || strlen( $text ) > 1800 || preg_match( '/[0-9$€£¥]/u', $text ) ) { continue; } $fragments[] = array( 'index' => count( $fragments ), 'text' => $text, 'offset' => $match[1], 'length' => strlen( $match[0] ) ); if ( count( $fragments ) >= 40 ) { break; } } return $fragments; } function dashe_seo_protected( $p ) { if ( ! $p || ! in_array( $p->post_type, array( 'post', 'page', 'product' ), true ) || $p->post_password || in_array( $p->post_status, array( 'private', 'trash' ), true ) ) { return true; } if ( 'publish' !== $p->post_status && ! get_post_meta( $p->ID, '_dashe_seo_managed', true ) ) { return true; } if ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $p->ID ) { return true; } foreach ( array( 'woocommerce_cart_page_id', 'woocommerce_checkout_page_id', 'woocommerce_myaccount_page_id', 'woocommerce_terms_page_id' ) as $option ) { if ( (int) get_option( $option ) === (int) $p->ID ) { return true; } } return (bool) preg_match( '#/(cart|checkout|my-account|privacy|terms)(/|$)#i', wp_parse_url( get_permalink( $p ), PHP_URL_PATH ) ); } function dashe_seo_html( $doc ) { if ( ! is_array( $doc ) || empty( $doc['blocks'] ) || count( $doc['blocks'] ) > 80 || strlen( wp_json_encode( $doc ) ) > 60000 ) { return dashe_seo_error( 'Invalid article document.' ); } $html = ''; foreach ( $doc['blocks'] as $block ) { $type = isset( $block['type'] ) ? $block['type'] : ''; if ( 'heading' === $type ) { $html .= '

' . esc_html( $block['text'] ) . '

' . "\n"; } elseif ( 'paragraph' === $type ) { $html .= '

' . esc_html( $block['text'] ) . '

' . "\n"; } elseif ( 'link' === $type && isset( $block['url'], $block['text'] ) && wp_parse_url( $block['url'], PHP_URL_HOST ) === wp_parse_url( home_url(), PHP_URL_HOST ) && in_array( wp_parse_url( $block['url'], PHP_URL_SCHEME ), array( 'https', 'http' ), true ) ) { $html .= '

' . esc_html( $block['text'] ) . '

' . "\n"; } elseif ( 'list' === $type && is_array( $block['items'] ) && count( $block['items'] ) <= 20 ) { $html .= '' . "\n"; } else { return dashe_seo_error( 'Unsupported article block.' ); } } return $html; } // Direct uncached SQL is required below for row locks and atomic post/receipt // commits. Dynamic values use placeholders; table identifiers come from wpdb. // phpcs:disable WordPress.DB.DirectDatabaseQuery function dashe_seo_locked_option( $key, $sqlite ) { global $wpdb; if ( $sqlite ) { return $wpdb->get_var( $wpdb->prepare( 'SELECT option_value FROM %i WHERE option_name=%s', $wpdb->options, $key ) ); } return $wpdb->get_var( $wpdb->prepare( 'SELECT option_value FROM %i WHERE option_name=%s FOR UPDATE', $wpdb->options, $key ) ); } function dashe_seo_lock_post( $id, $sqlite ) { if ( $sqlite ) { return; } // START TRANSACTION provides the SQLite write boundary. global $wpdb; $wpdb->get_row( $wpdb->prepare( 'SELECT ID FROM %i WHERE ID=%d FOR UPDATE', $wpdb->posts, $id ) ); $wpdb->get_results( $wpdb->prepare( 'SELECT meta_id FROM %i WHERE post_id=%d FOR UPDATE', $wpdb->postmeta, $id ) ); } function dashe_seo_transaction( $input, $callback ) { global $wpdb; $security = dashe_seo_host_security(); if ( 'passed' !== $security['status'] ) { return dashe_seo_error( $security['message'], 409 ); } $id = isset( $input['operationId'] ) ? $input['operationId'] : ''; if ( ! preg_match( '/^[a-f0-9-]{36}$/D', $id ) ) { return dashe_seo_error( 'Invalid operation ID.' ); } $key = 'dashe_seo_receipt_' . $id; $digest = hash( 'sha256', wp_json_encode( $input ) ); $sqlite = defined( 'DB_ENGINE' ) && 'sqlite' === DB_ENGINE; if ( ! $sqlite ) { // Refuse writes on nontransactional storage: a receipt must commit with its post. foreach ( array( $wpdb->posts, $wpdb->postmeta, $wpdb->options, $wpdb->terms, $wpdb->term_taxonomy, $wpdb->term_relationships ) as $table ) { $engine = $wpdb->get_row( $wpdb->prepare( 'SHOW TABLE STATUS WHERE Name = %s', $table ) ); if ( ! $engine || 'innodb' !== strtolower( $engine->Engine ) ) { return dashe_seo_error( 'SEO writes require transactional InnoDB tables.', 409 ); } } } if ( false === $wpdb->query( 'START TRANSACTION' ) ) { return dashe_seo_error( 'Could not start the write transaction.', 503 ); } try { $fresh = maybe_unserialize( dashe_seo_locked_option( 'dashe_seo_connection', $sqlite ) ); if ( ! is_array( $fresh ) || empty( $fresh['enabled'] ) || empty( $GLOBALS['dashe_seo_request_secret'] ) || ! hash_equals( $fresh['secret'], $GLOBALS['dashe_seo_request_secret'] ) || ! user_can( $fresh['user_id'], 'manage_options' ) ) { $wpdb->query( 'ROLLBACK' ); return dashe_seo_error( 'SEO access changed.', 403 ); } // The placeholder row serializes retries of the same external operation. if ( false === $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->options} (option_name,option_value,autoload) VALUES (%s,%s,'off')", $key, wp_json_encode( array( 'digest' => $digest, 'pending' => true ) ) ) ) ) { throw new Exception( 'Receipt insert failed.' ); } $stored = dashe_seo_locked_option( $key, $sqlite ); $receipt = json_decode( $stored, true ); if ( ! $receipt || $receipt['digest'] !== $digest ) { $wpdb->query( 'ROLLBACK' ); return dashe_seo_error( 'Operation ID already belongs to another request.', 409 ); } if ( isset( $receipt['result'] ) ) { $wpdb->query( 'COMMIT' ); return $receipt['result']; } $GLOBALS['dashe_seo_writing'] = true; $result = call_user_func( $callback, $sqlite ); $GLOBALS['dashe_seo_writing'] = false; if ( is_wp_error( $result ) ) { $wpdb->query( 'ROLLBACK' ); return $result; } if ( false === $wpdb->update( $wpdb->options, array( 'option_value' => wp_json_encode( array( 'digest' => $digest, 'result' => $result ) ) ), array( 'option_name' => $key ) ) || false === $wpdb->query( 'COMMIT' ) ) { throw new Exception( 'Receipt commit failed.' ); } wp_cache_delete( $key, 'options' ); if ( ! empty( $result['after']['id'] ) ) { dashe_seo_purge( $result['after']['id'] ); } return $result; } catch ( Throwable $e ) { $GLOBALS['dashe_seo_writing'] = false; $wpdb->query( 'ROLLBACK' ); return dashe_seo_error( 'The operation could not be committed.', 500 ); } } function dashe_seo_metadata( $id, $title, $description ) { global $wpdb; $keys = dashe_seo_keys(); $values = array( $keys[0] => $title, $keys[1] => $description, '_dashe_seo_title' => $title, '_dashe_seo_description' => $description ); foreach ( $values as $key => $value ) { update_post_meta( $id, $key, sanitize_text_field( $value ) ); if ( $wpdb->last_error ) { throw new Exception( 'Metadata write failed.' ); } } // Render filters make the new values effective even when an SEO plugin caches its index. clean_post_cache( $id ); } function dashe_seo_purge( $id ) { clean_post_cache( $id ); if ( function_exists( 'wp_cache_post_change' ) ) { wp_cache_post_change( $id ); } if ( function_exists( 'rocket_clean_post' ) ) { rocket_clean_post( $id ); } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Documented third-party cache purge hook. do_action( 'litespeed_purge_post', $id ); } function dashe_seo_exchange( $request ) { $in = $request->get_json_params(); $action = isset( $in['action'] ) ? $in['action'] : ''; $c = dashe_seo_config(); if ( 'handshake' === $action ) { $authors = array(); foreach ( get_users( array( 'capability' => 'publish_posts', 'number' => 100 ) ) as $u ) { $authors[] = array( 'id' => $u->ID, 'name' => $u->display_name ); } $categories = array(); foreach ( get_categories( array( 'hide_empty' => false, 'number' => 100 ) ) as $cat ) { $categories[] = array( 'id' => $cat->term_id, 'name' => $cat->name ); } $media = array(); foreach ( $c['approved_media'] ?? array() as $id ) { if ( wp_attachment_is_image( $id ) && 'image/svg+xml' !== get_post_mime_type( $id ) ) { $media[] = array( 'id' => (int) $id, 'title' => get_the_title( $id ), 'alt' => get_post_meta( $id, '_wp_attachment_image_alt', true ), 'url' => wp_get_attachment_image_url( $id, 'large' ) ); } } return array( 'security' => dashe_seo_host_report(), 'protocol' => 1, 'version' => DASHE_CHAT_VERSION, 'url' => trailingslashit( home_url() ), 'challenge' => sanitize_text_field( $in['challenge'] ), 'metadata' => ! empty( $c['metadata'] ) && ! in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ), 'content' => ! empty( $c['content'] ), 'publish' => ! empty( $c['publish'] ) && ! in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ), 'adapter' => dashe_seo_adapter(), 'editor' => 'gutenberg-classic', 'authors' => $authors, 'categories' => $categories, 'media' => $media ); } if ( 'inventory' === $action ) { $page = max( 1, min( 20, (int) ( $in['page'] ?? 1 ) ) ); $q = new WP_Query( array( 'post_type' => array( 'post', 'page', 'product' ), 'post_status' => 'publish', 'posts_per_page' => 25, 'paged' => $page, 'orderby' => 'ID', 'order' => 'ASC' ) ); $pages = array(); foreach ( $q->posts as $post ) { if ( ! dashe_seo_protected( $post ) && strlen( $post->post_content ) <= 80000 ) { $pages[] = dashe_seo_snapshot( $post->ID ); } } return array( 'pages' => $pages, 'more' => $q->max_num_pages > $page ); } if ( 'receipt' === $action ) { $id = $in['operationId'] ?? ''; if ( ! preg_match( '/^[a-f0-9-]{36}$/D', $id ) ) { return dashe_seo_error( 'Invalid operation ID.' ); } global $wpdb; $r = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM {$wpdb->options} WHERE option_name=%s", 'dashe_seo_receipt_' . $id ) ); return $r ? json_decode( $r, true ) : array( 'found' => false ); } if ( 'snapshot' === $action ) { $p = get_post( (int) ( $in['id'] ?? 0 ) ); if ( dashe_seo_protected( $p ) ) { return dashe_seo_error( 'Protected or missing page.', 403 ); } return dashe_seo_snapshot( $p->ID ); } if ( in_array( $action, array( 'metadata', 'rollback' ), true ) ) { if ( empty( $c['metadata'] ) || in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ) ) { return dashe_seo_error( 'Metadata editing is disabled.', 403 ); } return dashe_seo_transaction( $in, function ( $sqlite ) use ( $in ) { global $wpdb; $id = (int) ( $in['id'] ?? 0 ); dashe_seo_lock_post( $id, $sqlite ); clean_post_cache( $id ); $before = dashe_seo_snapshot( $id ); if ( ! $before || dashe_seo_protected( get_post( $id ) ) ) { return dashe_seo_error( 'Protected or missing page.', 403 ); } if ( ! hash_equals( $before['hash'], (string) ( $in['expectedHash'] ?? '' ) ) ) { return dashe_seo_error( 'This page changed. Review a fresh proposal.', 409 ); } if ( ! isset( $in['title'], $in['description'] ) || strlen( $in['title'] ) > 400 || strlen( $in['description'] ) > 800 ) { return dashe_seo_error( 'Invalid metadata.' ); } dashe_seo_metadata( $id, $in['title'], $in['description'] ); return array( 'before' => $before, 'after' => dashe_seo_snapshot( $id ), 'operationId' => $in['operationId'] ); } ); } if ( in_array( $action, array( 'content', 'restore_content' ), true ) ) { if ( empty( $c['content'] ) ) { return dashe_seo_error( 'Paragraph editing is disabled.', 403 ); } return dashe_seo_transaction( $in, function ( $sqlite ) use ( $in ) { global $wpdb; $id = absint( $in['id'] ?? 0 ); dashe_seo_lock_post( $id, $sqlite ); clean_post_cache( $id ); $p = get_post( $id ); $before = dashe_seo_snapshot( $id ); if ( dashe_seo_protected( $p ) || 'product' === $p->post_type || ! $before['editable'] || empty( $before['fragments'] ) ) { return dashe_seo_error( 'This body content is protected.', 403 ); } if ( ! hash_equals( $before['hash'], (string) ( $in['expectedHash'] ?? '' ) ) ) { return dashe_seo_error( 'Page changed. Review a fresh proposal.', 409 ); } $fragments = dashe_seo_fragments( $p->post_content ); $index = $in['index'] ?? -1; $text = $in['text'] ?? ''; if ( ! is_int( $index ) || ! isset( $fragments[$index] ) || ! is_string( $text ) || strlen( $text ) < 40 || strlen( $text ) > 1800 || preg_match( '/[0-9$€£¥<>]/u', $text ) ) { return dashe_seo_error( 'Unsupported paragraph change.' ); } $f = $fragments[$index]; $content = substr_replace( $p->post_content, esc_html( $text ), $f['offset'], $f['length'] ); $result = wp_update_post( wp_slash( array( 'ID' => $id, 'post_content' => $content ) ), true ); if ( is_wp_error( $result ) ) { return $result; } clean_post_cache( $id );return array( 'before' => $before, 'after' => dashe_seo_snapshot( $id ), 'operationId' => $in['operationId'] ); } ); } if ( in_array( $action, array( 'draft', 'publish', 'unpublish' ), true ) ) { if ( empty( $c['publish'] ) || in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ) ) { return dashe_seo_error( 'Publishing is disabled.', 403 ); } return dashe_seo_transaction( $in, function ( $sqlite ) use ( $in, $action, $c ) { global $wpdb; $id = (int) ( $in['id'] ?? 0 ); $before = null; if ( $id ) { dashe_seo_lock_post( $id, $sqlite ); clean_post_cache( $id ); $before = dashe_seo_snapshot( $id ); if ( ! $before || ! get_post_meta( $id, '_dashe_seo_managed', true ) || dashe_seo_protected( get_post( $id ) ) ) { return dashe_seo_error( 'Only Dashe-created posts can be published by this operation.', 403 ); } if ( ! hash_equals( $before['hash'], (string) ( $in['expectedHash'] ?? '' ) ) ) { return dashe_seo_error( 'The draft was edited in WordPress. Review it again.', 409 ); } } if ( 'unpublish' === $action ) { if ( ! $id ) { return dashe_seo_error( 'Missing post ID.' ); } $result = wp_update_post( array( 'ID' => $id, 'post_status' => 'draft' ), true ); } else { $doc = $in['document'] ?? array(); $html = dashe_seo_html( $doc ); if ( is_wp_error( $html ) ) { return $html; } $media = absint( $doc['featuredMedia'] ?? 0 ); if ( $media && ( ! in_array( $media, $c['approved_media'] ?? array(), true ) || ! wp_attachment_is_image( $media ) || 'image/svg+xml' === get_post_mime_type( $media ) ) ) { return dashe_seo_error( 'This image has not been approved for publication.', 403 ); } $author = ! empty( $in['author'] ) ? (int) $in['author'] : (int) $c['user_id']; if ( ! user_can( $author, 'publish_posts' ) || empty( $doc['title'] ) || strlen( $doc['title'] ) > 400 ) { return dashe_seo_error( 'Invalid author or title.' ); } $post = array( 'post_type' => 'post', 'post_status' => 'publish' === $action ? 'publish' : 'draft', 'post_title' => sanitize_text_field( $doc['title'] ), 'post_content' => $html, 'post_excerpt' => sanitize_text_field( $doc['description'] ?? '' ), 'post_author' => $author ); if ( $id ) { $post['ID'] = $id; } $result = wp_insert_post( wp_slash( $post ), true ); } if ( is_wp_error( $result ) ) { return $result; } $id = (int) $result; update_post_meta( $id, '_dashe_seo_managed', 1 ); if ( $wpdb->last_error ) { throw new Exception( 'Post ownership write failed.' ); } if ( 'unpublish' !== $action ) { if ( $media ) { set_post_thumbnail( $id, $media ); } else { delete_post_thumbnail( $id ); } if ( $wpdb->last_error ) { throw new Exception( 'Featured image write failed.' ); } dashe_seo_metadata( $id, $doc['title'], $doc['description'] ?? '' ); if ( isset( $in['categories'] ) && is_array( $in['categories'] ) && is_wp_error( wp_set_post_categories( $id, array_slice( array_map( 'absint', $in['categories'] ), 0, 10 ) ) ) ) { throw new Exception( 'Category write failed.' ); } } clean_post_cache( $id ); return array( 'before' => $before, 'after' => dashe_seo_snapshot( $id ), 'operationId' => $in['operationId'] ); } ); } return dashe_seo_error( 'Unsupported operation.' ); } function dashe_seo_title( $title ) { if ( in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ) ) { return $title; } if ( is_singular() ) { $v = get_post_meta( get_queried_object_id(), '_dashe_seo_title', true ); if ( '' !== $v ) { return $v; } } return $title; } function dashe_seo_description( $description ) { if ( in_array( dashe_seo_adapter(), array( 'unsupported', 'setup_required' ), true ) ) { return $description; } if ( is_singular() ) { $v = get_post_meta( get_queried_object_id(), '_dashe_seo_description', true ); if ( '' !== $v ) { return $v; } } return $description; } add_filter( 'pre_get_document_title', 'dashe_seo_title', 99 ); add_filter( 'wpseo_title', 'dashe_seo_title', 99 ); add_filter( 'wpseo_metadesc', 'dashe_seo_description', 99 ); add_filter( 'rank_math/frontend/title', 'dashe_seo_title', 99 ); add_filter( 'rank_math/frontend/description', 'dashe_seo_description', 99 ); add_action( 'wp_head', function () { if ( 'dashe' === dashe_seo_adapter() && is_singular() ) { $d = dashe_seo_description( '' ); if ( $d ) { echo '' . "\n"; } } }, 2 ); // Human changes take precedence over generated render overrides. add_action( 'save_post', function ( $id ) { if ( empty( $GLOBALS['dashe_seo_writing'] ) && 'dashe' !== dashe_seo_adapter() ) { delete_post_meta( $id, '_dashe_seo_title' ); delete_post_meta( $id, '_dashe_seo_description' ); } }, 100 ); add_action( 'updated_post_meta', function ( $meta_id, $id, $key ) { if ( empty( $GLOBALS['dashe_seo_writing'] ) && in_array( $key, array( '_yoast_wpseo_title', '_yoast_wpseo_metadesc', 'rank_math_title', 'rank_math_description' ), true ) ) { delete_post_meta( $id, '_dashe_seo_title' ); delete_post_meta( $id, '_dashe_seo_description' ); } }, 100, 3 ); // phpcs:enable WordPress.DB.DirectDatabaseQuery // Existing SEO plugins own their graphs. For Dashe-created posts without one, // add an Article only when the rendered theme has supplied no JSON-LD at all. function dashe_seo_article_schema( $html ) { $id = get_queried_object_id(); if ( strlen( $html ) > 2000000 || false === stripos( $html, '' ) || preg_match( '#]*application/ld\+json#i', $html ) ) { return $html; } $p = get_post( $id ); if ( ! $p || 'publish' !== $p->post_status || ! get_post_meta( $id, '_dashe_seo_managed', true ) ) { return $html; } $schema = array( '@context' => 'https://schema.org', '@type' => 'Article', '@id' => get_permalink( $id ) . '#article', 'mainEntityOfPage' => get_permalink( $id ), 'headline' => $p->post_title, 'description' => $p->post_excerpt, 'datePublished' => get_post_time( DATE_W3C, true, $p ), 'dateModified' => get_post_modified_time( DATE_W3C, true, $p ), 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta( 'display_name', $p->post_author ) ) ); $image = get_the_post_thumbnail_url( $id, 'full' ); if ( $image ) { $schema['image'] = $image; } $script = '' . "\n"; return preg_replace_callback( '##i', function () use ( $script ) { return $script . ''; }, $html, 1 ); } add_action( 'template_redirect', function () { if ( ! is_admin() && is_singular( 'post' ) && 'dashe' === dashe_seo_adapter() && get_post_meta( get_queried_object_id(), '_dashe_seo_managed', true ) ) { ob_start( 'dashe_seo_article_schema' ); } }, 1 );