rows(); if ( empty( $rows ) ) { printf( '

%s

', sprintf( // Translators: 1 - A number of days. esc_html__( 'No 404s in the last %1$d days.', '404-to-301' ), (int) self::DAYS ) ); } else { echo '
'; foreach ( $rows as $row ) { printf( '
%1$s%2$s
', esc_html( $row['url'] ), esc_html( sprintf( // Translators: 1 - A number of hits. _n( '%1$s hit', '%1$s hits', (int) $row['hits'], '404-to-301' ), number_format_i18n( (int) $row['hits'] ) ) ) ); } echo '
'; } $this->footer( ! empty( $rows ) ); } /** * The footer: a link into the logs, plus the promo when there is something to promote. * * @since 4.0.4 * * @param bool $hasRows Whether any 404s were listed. * @return void */ private function footer( bool $hasRows ): void { echo '
'; printf( '%2$s', esc_url( Plugin::getUrl( $hasRows ? 'logs' : 'redirects' ) ), $hasRows ? esc_html__( 'View all 404s', '404-to-301' ) // Translators: 1 - The plugin name. : esc_html( sprintf( __( 'Open %1$s', '404-to-301' ), Plugin::name() ) ) ); /* * Nothing to promote when the log is empty or the plugin is already running. * The `install_plugins` check mirrors the landing page's own registration: a * host with DISALLOW_FILE_MODS leaves an admin holding `manage_options` but * not `install_plugins`, and the link would lead to an unregistered page. */ $plugins = aioseo404To301()->helpers->getPluginData(); if ( $hasRows && current_user_can( 'install_plugins' ) && empty( $plugins['brokenLinkChecker']['activated'] ) ) { printf( '%3$s', esc_url( plugins_url( 'static/blc.svg', AIOSEO_404_TO_301_FILE ) ), esc_url( Plugin::getUrl( 'blc' ) ), esc_html__( 'Find the links causing these', '404-to-301' ) ); } echo '
'; } /** * Top 404s by hits from the last 30 days. * * Cached because the Dashboard is a hot page and this is decoration, not a * source of truth — a few minutes of staleness costs nothing. * * @since 4.0.4 * * @return array[] Rows of url and hits. */ private function rows(): array { $cached = get_transient( self::CACHE ); if ( is_array( $cached ) ) { return $cached; } $logs = Log::paginate( [ 'number' => self::ROWS, 'orderby' => 'hits', 'order' => 'DESC', 'status' => Log::STATUS_OPEN, 'date_query' => [ [ 'column' => 'updated_at', 'after' => gmdate( 'Y-m-d H:i:s', strtotime( '-' . self::DAYS . ' days' ) ), ], ], ] ); $rows = []; foreach ( $logs['items'] as $log ) { $rows[] = [ 'url' => (string) $log->url, 'hits' => (int) $log->hits, ]; } set_transient( self::CACHE, $rows, 15 * MINUTE_IN_SECONDS ); return $rows; } }