array( 'post', 'page' ), 'post_status' => 'publish', 'posts_per_page' => 300, // cap protects large sites from a heavy run 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true, ) ); $rader = array(); $poster = array_values( array_filter( $q->posts, function ( $p ) { return ! tedrix_ar_noindex( $p ); } ) ); foreach ( $poster as $post ) { $titel = tedrix_las_seo_titel( $post ); $beskr = tedrix_las_seo_beskrivning( $post ); $problem = array(); if ( mb_strlen( $titel ) > TEDRIX_TITEL_MAX ) { $problem[] = array( 'titel_lang', mb_strlen( $titel ) ); } if ( '' === trim( $beskr ) ) { $problem[] = array( 'beskr_saknas', 0 ); } elseif ( mb_strlen( $beskr ) > TEDRIX_BESKR_MAX ) { $problem[] = array( 'beskr_lang', mb_strlen( $beskr ) ); } elseif ( mb_strlen( $beskr ) < TEDRIX_BESKR_MIN ) { $problem[] = array( 'beskr_kort', mb_strlen( $beskr ) ); } if ( $problem ) { $rader[] = array( 'id' => $post->ID, 'titel' => $titel, 'url' => get_permalink( $post ), 'problem' => $problem, ); } } /* Duplicates: two pages with the same title compete with each other, and * that is invisible when looking at one page at a time. */ $sedda = array(); foreach ( $poster as $post ) { $t = mb_strtolower( tedrix_las_seo_titel( $post ) ); if ( '' === $t ) { continue; } $sedda[ $t ][] = $post->ID; } foreach ( $sedda as $ids ) { if ( count( $ids ) < 2 ) { continue; } foreach ( $ids as $id ) { $i = tedrix_rad_index( $rader, $id ); $text = array( 'dubblett', count( $ids ) - 1 ); if ( null === $i ) { $rader[] = array( 'id' => $id, 'titel' => tedrix_las_seo_titel( get_post( $id ) ), 'url' => get_permalink( $id ), 'problem' => array( $text ), ); } else { $rader[ $i ]['problem'][] = $text; } } } $ut = array( 'rader' => $rader, 'antal' => count( $rader ), 'granskade' => count( $poster ), ); set_transient( 'tedrix_kollen_v3', $ut, HOUR_IN_SECONDS ); return $ut; } /** * A problem as text, in the viewer's language. The cache stores only the * kind and the number, so switching language never shows stale text. * * @param array $p array( kind, number ). * @return string */ function tedrix_problem_text( $p ) { list( $typ, $n ) = $p; switch ( $typ ) { case 'titel_lang': /* translators: %d: number of characters */ return sprintf( __( 'Title %d characters, cut off in Google.', 'tedrix' ), $n ); case 'beskr_saknas': return __( 'No description, so Google writes its own.', 'tedrix' ); case 'beskr_lang': /* translators: %d: number of characters */ return sprintf( __( 'Description %d characters, cut off in Google.', 'tedrix' ), $n ); case 'beskr_kort': /* translators: %d: number of characters */ return sprintf( __( 'Description only %d characters.', 'tedrix' ), $n ); case 'dubblett': /* translators: %d: number of other pages with the same title */ return sprintf( _n( 'Same title as %d other page.', 'Same title as %d other pages.', $n, 'tedrix' ), $n ); } return ''; } /** * Index of the row for a post, or null. * * @param array $rader Rows. * @param int $id Post ID. * @return int|null */ function tedrix_rad_index( array $rader, $id ) { foreach ( $rader as $i => $r ) { if ( (int) $r['id'] === (int) $id ) { return $i; } } return null; } /** * Plain text the way Google sees it: no tags, and entities such as ’ * (which get_the_title() adds for curly quotes) turned back into characters * so they count as one character, not seven. * * @param string $s Text. * @return string */ function tedrix_ren( $s ) { return trim( html_entity_decode( wp_strip_all_tags( (string) $s ), ENT_QUOTES, 'UTF-8' ) ); } /** * SEO plugins let a title or description contain variables such as * %%title%% (Yoast), %title% (Rank Math) or #post_title (All in One SEO). * Fill in the common ones and drop the rest, so the length is close to what * is printed instead of counting the variable names. * * @param string $v Stored value. * @param WP_Post $post Post. * @return string */ function tedrix_fyll_variabler( $v, $post ) { if ( false === strpos( $v, '%' ) && false === strpos( $v, '#' ) ) { return $v; } $titel = tedrix_ren( $post->post_title ); $sajt = tedrix_ren( get_bloginfo( 'name' ) ); $utdrag = tedrix_ren( has_excerpt( $post ) ? $post->post_excerpt : wp_trim_words( $post->post_content, 25, '' ) ); $ersatt = array( '%%title%%' => $titel, '%%sitename%%' => $sajt, '%%sep%%' => '-', '%%excerpt%%' => $utdrag, '%%post_title%%' => $titel, '%%sitetitle%%' => $sajt, '%title%' => $titel, '%sitename%' => $sajt, '%sep%' => '-', '%excerpt%' => $utdrag, '#post_title' => $titel, '#site_title' => $sajt, '#separator_sa' => '-', '#post_excerpt' => $utdrag, ); $v = strtr( $v, $ersatt ); $v = preg_replace( '/%%[a-z0-9_]+%%|%[a-z0-9_]+%|#(?:post|site|separator|tagline|category|author|current|page|permalink|taxonomy)[a-z0-9_]*/i', '', $v ); return trim( preg_replace( '/\s{2,}/', ' ', $v ) ); } /** * The All in One SEO row for a post (title, description, noindex), read from * its own table. Cached per request. Empty array when AIOSEO is not in use. * * @param int $id Post ID. * @return array */ function tedrix_aioseo_rad( $id ) { static $tabell = null, $rader = array(); global $wpdb; if ( null === $tabell ) { $tabell = ''; if ( tedrix_active_seo_plugins() && in_array( 'aioseo', tedrix_active_seo_plugins(), true ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- one lookup per request, result kept in a static. $finns = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'aioseo_posts' ) ) ); $tabell = ( $wpdb->prefix . 'aioseo_posts' === $finns ) ? $finns : ''; } } if ( '' === $tabell ) { return array(); } $id = (int) $id; if ( ! array_key_exists( $id, $rader ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- AIOSEO keeps its fields in its own table; the name is the site prefix plus a fixed string, checked above. Kept in a static per request. $rad = $wpdb->get_row( $wpdb->prepare( "SELECT title, description, robots_noindex FROM {$tabell} WHERE post_id = %d", $id ), ARRAY_A ); $rader[ $id ] = is_array( $rad ) ? $rad : array(); } return $rader[ $id ]; } /** * Whether an SEO plugin tells search engines to skip this page. Such pages * never show in Google, so their titles do not matter. * * @param WP_Post $post Post. * @return bool */ function tedrix_ar_noindex( $post ) { if ( '1' === (string) get_post_meta( $post->ID, '_yoast_wpseo_meta-robots-noindex', true ) ) { return true; } $rm = get_post_meta( $post->ID, 'rank_math_robots', true ); if ( is_array( $rm ) && in_array( 'noindex', $rm, true ) ) { return true; } if ( 'yes' === (string) get_post_meta( $post->ID, '_seopress_robots_index', true ) ) { return true; } if ( '1' === (string) get_post_meta( $post->ID, '_genesis_noindex', true ) ) { return true; } $aio = tedrix_aioseo_rad( $post->ID ); return ! empty( $aio['robots_noindex'] ); } /** * The title that actually reaches Google: the SEO plugin's field if set, * otherwise the post title. * * @param WP_Post $post Post. * @return string */ function tedrix_las_seo_titel( $post ) { foreach ( tedrix_seo_meta_keys() as $keys ) { if ( empty( $keys['title'] ) ) { continue; } $v = get_post_meta( $post->ID, $keys['title'], true ); if ( is_string( $v ) && '' !== trim( $v ) ) { return tedrix_ren( tedrix_fyll_variabler( $v, $post ) ); } } $aio = tedrix_aioseo_rad( $post->ID ); if ( ! empty( $aio['title'] ) ) { $v = tedrix_ren( tedrix_fyll_variabler( (string) $aio['title'], $post ) ); if ( '' !== $v ) { return $v; } } return tedrix_ren( get_the_title( $post ) ); } /** * The description that reaches Google, from whichever SEO plugin is * installed. Empty string means none is set. * * @param WP_Post $post Post. * @return string */ function tedrix_las_seo_beskrivning( $post ) { foreach ( tedrix_seo_meta_keys() as $keys ) { if ( empty( $keys['desc'] ) ) { continue; } $v = get_post_meta( $post->ID, $keys['desc'], true ); if ( is_string( $v ) && '' !== trim( $v ) ) { return tedrix_ren( tedrix_fyll_variabler( $v, $post ) ); } } $aio = tedrix_aioseo_rad( $post->ID ); if ( ! empty( $aio['description'] ) ) { return tedrix_ren( tedrix_fyll_variabler( (string) $aio['description'], $post ) ); } return ''; } /* Clear the cache whenever something is published or edited. */ add_action( 'save_post', function () { delete_transient( 'tedrix_kollen_v3' ); } ); /** * Renders the titles and descriptions section of the admin page. */ function tedrix_kollen_sektion() { $tvinga = isset( $_GET['tedrix_om'] ) && check_admin_referer( 'tedrix_om' ); $r = tedrix_kollen( $tvinga ); ?>
| ', array_map( 'esc_html', array_map( 'tedrix_problem_text', $rad['problem'] ) ) ), array( 'br' => array() ) ); ?> |