option( 'rank-math-options-titles' ); } public function counts(): array { $counts = parent::counts(); $counts['redirects'] = $this->count_redirects(); return $counts; } private function table(): string { global $wpdb; return $wpdb->prefix . 'rank_math_redirections'; } private function count_redirects(): int { global $wpdb; if ( ! $this->table_exists( $this->table() ) ) { return 0; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE status = 'active'", $this->table() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name from this plugin's own Database class/adapter, never from input; values go through $wpdb->prepare(). } /** * @return array */ private function record( int $id, bool $term ): array { $robots = $this->robots_from( $term ? get_term_meta( $id, 'rank_math_robots', true ) : get_post_meta( $id, 'rank_math_robots', true ) ); $kw = $this->meta( $id, 'rank_math_focus_keyword', $term ); // "primary, secondary, third" — the first is the focus keyword. $keywords = array_values( array_filter( array_map( 'trim', explode( ',', $kw ) ) ) ); $record = array( 'title' => $this->vars( $this->meta( $id, 'rank_math_title', $term ) ), 'desc' => $this->vars( $this->meta( $id, 'rank_math_description', $term ) ), 'keywords' => $keywords, 'canonical' => $this->meta( $id, 'rank_math_canonical_url', $term ), 'robots' => $robots, 'breadcrumb_title' => $this->meta( $id, 'rank_math_breadcrumb_title', $term ), 'social' => array( 'og_title' => $this->meta( $id, 'rank_math_facebook_title', $term ), 'og_desc' => $this->meta( $id, 'rank_math_facebook_description', $term ), 'og_image' => $this->meta( $id, 'rank_math_facebook_image', $term ), 'tw_title' => $this->meta( $id, 'rank_math_twitter_title', $term ), 'tw_desc' => $this->meta( $id, 'rank_math_twitter_description', $term ), 'tw_image' => $this->meta( $id, 'rank_math_twitter_image', $term ), ), ); if ( ! $term ) { $snippet = $this->meta( $id, 'rank_math_rich_snippet' ); if ( '' !== $snippet && 'off' !== $snippet ) { $record['schema_type'] = $snippet; } if ( '' !== $this->meta( $id, 'rank_math_advanced_robots' ) ) { $record['robots'] = array_values( array_unique( array_merge( $record['robots'], $this->robots_from( get_post_meta( $id, 'rank_math_advanced_robots', true ) ) ) ) ); } } return $this->clean( $record ); } public function post_record( int $post_id ): array { return $this->record( $post_id, false ); } public function term_record( int $term_id ): array { return $this->record( $term_id, true ); } public function settings(): array { $titles = $this->option( 'rank-math-options-titles' ); $sitemap = $this->option( 'rank-math-options-sitemap' ); if ( empty( $titles ) && empty( $sitemap ) ) { return array(); } $out = array( 'separator' => \AiRankSuite\Modules\ImportExport\Services\Variables::separator( (string) ( $titles['title_separator'] ?? '' ) ), 'home_title' => $this->vars( (string) ( $titles['homepage_title'] ?? '' ) ), 'home_desc' => $this->vars( (string) ( $titles['homepage_description'] ?? '' ) ), ); $templates = array(); $noindex_pt = array(); foreach ( get_post_types( array( 'public' => true ) ) as $type ) { $title = $this->vars( (string) ( $titles[ 'pt_' . $type . '_title' ] ?? '' ) ); $desc = $this->vars( (string) ( $titles[ 'pt_' . $type . '_description' ] ?? '' ) ); if ( '' !== $title || '' !== $desc ) { $templates[ $type ] = array( 'title' => $title, 'desc' => $desc ); } if ( in_array( 'noindex', (array) ( $titles[ 'pt_' . $type . '_robots' ] ?? array() ), true ) ) { $noindex_pt[] = $type; } } $tax_templates = array(); $noindex_tax = array(); foreach ( get_taxonomies( array( 'public' => true ) ) as $tax ) { $title = $this->vars( (string) ( $titles[ 'tax_' . $tax . '_title' ] ?? '' ) ); $desc = $this->vars( (string) ( $titles[ 'tax_' . $tax . '_description' ] ?? '' ) ); if ( '' !== $title || '' !== $desc ) { $tax_templates[ $tax ] = array( 'title' => $title, 'desc' => $desc ); } if ( in_array( 'noindex', (array) ( $titles[ 'tax_' . $tax . '_robots' ] ?? array() ), true ) ) { $noindex_tax[] = $tax; } } $out['templates'] = $templates; $out['noindex_types'] = $noindex_pt; $out['taxonomy_templates'] = $tax_templates; $out['noindex_taxonomies'] = $noindex_tax; $out['author_enabled'] = 'on' !== (string) ( $titles['disable_author_archives'] ?? '' ); $out['author_title'] = $this->vars( (string) ( $titles['author_archive_title'] ?? '' ) ); $out['author_noindex'] = in_array( 'noindex', (array) ( $titles['author_robots'] ?? array() ), true ); $out['date_enabled'] = 'on' !== (string) ( $titles['disable_date_archives'] ?? '' ); $out['date_noindex'] = in_array( 'noindex', (array) ( $titles['date_archive_robots'] ?? array() ), true ); $out['search_title'] = $this->vars( (string) ( $titles['search_title'] ?? '' ) ); $out['notfound_title'] = $this->vars( (string) ( $titles['404_title'] ?? '' ) ); $out['attachment_redirect'] = 'on' === (string) ( $titles['attachment_redirect_urls'] ?? 'on' ); $out['organisation'] = array( 'name' => (string) ( $titles['knowledgegraph_name'] ?? '' ), 'logo' => is_array( $titles['knowledgegraph_logo'] ?? null ) ? (string) ( $titles['knowledgegraph_logo']['url'] ?? '' ) : (string) ( $titles['knowledgegraph_logo'] ?? '' ), 'type' => 'person' === (string) ( $titles['knowledgegraph_type'] ?? '' ) ? 'person' : 'organization', ); $out['twitter_site'] = ltrim( (string) ( $titles['twitter_author_names'] ?? '' ), '@' ); $out['og_default_image'] = is_array( $titles['open_graph_image'] ?? null ) ? (string) ( $titles['open_graph_image']['url'] ?? '' ) : (string) ( $titles['open_graph_image'] ?? '' ); // Sitemap exclusions: Rank Math stores excluded post ids and terms. $out['sitemap_exclude_posts'] = array_values( array_filter( array_map( 'intval', preg_split( '/[\s,]+/', (string) ( $sitemap['exclude_posts'] ?? '' ) ) ?: array() ) ) ); $out['sitemap_exclude_terms'] = array_values( array_filter( array_map( 'intval', preg_split( '/[\s,]+/', (string) ( $sitemap['exclude_terms'] ?? '' ) ) ?: array() ) ) ); // Local SEO lives in the same titles option on the free plugin. $local = array( 'name' => (string) ( $titles['local_seo_company_name'] ?? $titles['knowledgegraph_name'] ?? '' ), 'phone' => is_array( $titles['phone_numbers'] ?? null ) ? (string) ( $titles['phone_numbers'][0]['number'] ?? '' ) : '', 'street' => (string) ( $titles['local_address']['streetAddress'] ?? '' ), 'city' => (string) ( $titles['local_address']['addressLocality'] ?? '' ), 'zip' => (string) ( $titles['local_address']['postalCode'] ?? '' ), 'country' => (string) ( $titles['local_address']['addressCountry'] ?? '' ), ); if ( '' !== implode( '', array_map( 'strval', $local ) ) ) { $out['local'] = $local; } return $out; } public function redirects( int $after, int $limit ): array { global $wpdb; if ( ! $this->table_exists( $this->table() ) ) { return array(); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id, sources, url_to, header_code FROM %i WHERE status = 'active' AND id > %d ORDER BY id ASC LIMIT %d", $this->table(), $after, max( 1, $limit ) ) ); $out = array(); foreach ( (array) $rows as $row ) { $sources = maybe_unserialize( $row->sources ); foreach ( (array) $sources as $source ) { $pattern = is_array( $source ) ? (string) ( $source['pattern'] ?? '' ) : (string) $source; if ( '' === $pattern ) { continue; } $out[] = array( 'id' => (int) $row->id, 'source' => $pattern, 'target' => (string) $row->url_to, 'type' => (int) $row->header_code, ); } if ( empty( $sources ) ) { // A row we cannot read still has to advance the cursor. $out[] = array( 'id' => (int) $row->id, 'source' => '', 'target' => '', 'type' => 301 ); } } return $out; } }