impact = $impact; $this->suggester = $suggester; } /** * Registers hooks. */ public function register() { add_action( 'rest_api_init', array( $this, 'routes' ) ); } /** * Registers the routes. */ public function routes() { $args = array( 'id' => array( 'validate_callback' => function ( $value ) { return is_numeric( $value ); }, ), ); $can = function ( WP_REST_Request $request ) { return current_user_can( 'edit_post', (int) $request['id'] ); }; register_rest_route( 'orbitstem-seo/v1', '/impact/(?P\d+)', array( 'methods' => 'GET', 'callback' => array( $this, 'impact' ), 'permission_callback' => $can, 'args' => $args, ) ); register_rest_route( 'orbitstem-seo/v1', '/links/(?P\d+)', array( 'methods' => 'POST', 'callback' => array( $this, 'links' ), 'permission_callback' => $can, 'args' => $args + array( 'content' => array( 'type' => 'string', 'default' => null, ), 'title' => array( 'type' => 'string', 'default' => '', ), 'seo_title' => array( 'type' => 'string', 'default' => '', ), ), ) ); } /** * Returns the impact of one post. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ public function impact( WP_REST_Request $request ) { $post = get_post( (int) $request['id'] ); if ( ! $post ) { return new WP_REST_Response( array( 'message' => __( 'Not found.', 'orbitstem-seo' ) ), 404 ); } $impact = $this->impact->for_post( $post ); $impact['consequences'] = Impact::removal_consequences( $impact ); return new WP_REST_Response( $impact, 200 ); } /** * Links into the post, its broken links (from the last scan) and suggested links. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ public function links( WP_REST_Request $request ) { global $wpdb; $post = get_post( (int) $request['id'] ); if ( ! $post ) { return new WP_REST_Response( array( 'message' => __( 'Not found.', 'orbitstem-seo' ) ), 404 ); } $inbound = array(); $broken = array(); if ( Tables::exist_cached() ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery $rows = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT l.source_id FROM %i l INNER JOIN {$wpdb->posts} p ON p.ID = l.source_id AND p.post_status = 'publish' WHERE l.target_id = %d AND l.kind = 'link' AND l.source_id <> %d ORDER BY l.source_id DESC LIMIT 20", Tables::links(), $post->ID, $post->ID ) ); foreach ( $rows as $row ) { $inbound[] = array( 'title' => html_entity_decode( get_the_title( (int) $row->source_id ), ENT_QUOTES | ENT_HTML5, 'UTF-8' ), 'edit' => (string) get_edit_post_link( (int) $row->source_id, 'raw' ), ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery $broken = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT u.url FROM %i l INNER JOIN %i u ON u.url_hash = l.url_hash WHERE l.source_id = %d AND u.status IN (404, 410) LIMIT 20', Tables::links(), Tables::urls(), $post->ID ) ); } $content = $request['content']; $suggestions = $this->suggester ? $this->suggester->for_source( $post, 5, is_string( $content ) ? $content : null ) : array(); return new WP_REST_Response( array( 'inbound' => $inbound, 'broken' => array_values( $broken ), 'suggestions' => $suggestions, 'duplicates' => $this->duplicates( $post, (string) $request['title'], (string) $request['seo_title'] ), ), 200 ); } /** * Other published items with the same title or the same custom SEO title, * which would compete in search results. * * @param \WP_Post $post Post. * @param string $title Post title as typed (unsaved). * @param string $seo_title Custom SEO title as typed (unsaved). * @return array */ private function duplicates( $post, $title, $seo_title ) { global $wpdb; $title = trim( $title ); $seo_title = trim( $seo_title ); $ids = array(); if ( '' !== $title ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery $ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type = %s AND ID <> %d AND post_title = %s LIMIT 5", $post->post_type, $post->ID, $title ) ); } if ( '' !== $seo_title ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery $ids = array_merge( $ids, $wpdb->get_col( $wpdb->prepare( "SELECT m.post_id FROM {$wpdb->postmeta} m INNER JOIN {$wpdb->posts} p ON p.ID = m.post_id AND p.post_status = 'publish' WHERE m.meta_key = %s AND m.meta_value = %s AND m.post_id <> %d LIMIT 5", '_orbitstem_seo_title', $seo_title, $post->ID ) ) ); } $out = array(); foreach ( array_unique( array_map( 'intval', $ids ) ) as $id ) { $out[] = array( 'title' => html_entity_decode( get_the_title( $id ), ENT_QUOTES | ENT_HTML5, 'UTF-8' ), 'edit' => (string) get_edit_post_link( $id, 'raw' ), ); } return $out; } }