send(); } /** * Send the digest if there are new problems since the last one. * * @return bool Whether an email was sent. */ public function send(): bool { if ( ! Settings::get( 'email_digest' ) ) { return false; } $since = (int) get_option( self::LAST_SENT, 0 ); $new = $this->new_problems( $since ); if ( empty( $new ) ) { return false; } $lines = array( /* translators: 1: number of links, 2: site name. */ sprintf( _n( '%1$d new broken link was found on %2$s:', '%1$d new broken links were found on %2$s:', count( $new ), 'unbreak' ), count( $new ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) ), '', ); $query = new LinkQuery(); $locs = $query->locations( array_map( 'intval', wp_list_pluck( $new, 'id' ) ) ); foreach ( array_slice( $new, 0, 100 ) as $link ) { $lines[] = sprintf( '- %s (%s%s)', $link->url, self::status_label( (string) $link->status ), $link->http_code ? ' ' . $link->http_code : '' ); foreach ( array_slice( $locs[ (int) $link->id ] ?? array(), 0, 3 ) as $loc ) { /* translators: %s: where the link appears (post title, menu, widget). */ $lines[] = ' ' . sprintf( __( 'in: %s', 'unbreak' ), $loc['title'] ); } } $lines[] = ''; $lines[] = __( 'Review and fix them here:', 'unbreak' ) . ' ' . admin_url( 'tools.php?page=unbreak' ); $lines[] = ''; $lines[] = __( 'You receive this email because the weekly digest is enabled in Unbreak settings.', 'unbreak' ); /* translators: %s: site name. */ $subject = sprintf( __( '[%s] New broken links found', 'unbreak' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) ); $sent = wp_mail( (string) get_option( 'admin_email' ), $subject, implode( "\n", $lines ) ); if ( $sent ) { update_option( self::LAST_SENT, Clock::now(), false ); } return (bool) $sent; } /** * Translated label for a problem status. * * @param string $status Status slug. */ public static function status_label( string $status ): string { $labels = array( 'broken' => __( 'Broken', 'unbreak' ), 'server_error' => __( 'Server error', 'unbreak' ), 'timeout' => __( 'Timeout', 'unbreak' ), 'ssl_error' => __( 'SSL error', 'unbreak' ), ); return $labels[ $status ] ?? $status; } /** * Links that turned into a confirmed problem since a timestamp. * * @param int $since Timestamp. * @return array */ public function new_problems( int $since ): array { global $wpdb; $table = Schema::table( 'links' ); $statuses = StatusPolicy::problem_statuses(); $in = implode( ',', array_fill( 0, count( $statuses ), '%s' ) ); $params = array_merge( array( $table ), $statuses, array( Clock::to_mysql( $since ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $in only holds %s placeholders generated above. return (array) $wpdb->get_results( $wpdb->prepare( "SELECT id, url, status, http_code FROM %i WHERE dismissed = 0 AND status IN ({$in}) AND status_changed_at > %s ORDER BY status_changed_at DESC LIMIT 500", $params ) ); } }