settings = $settings; $this->issues = $issues; } /** * Registers hooks. */ public function register() { add_action( 'orbitstem_seo_daily', array( $this, 'maybe_send' ) ); add_action( 'update_option_' . Settings::OPTION, array( $this, 'on_settings' ), 10, 2 ); } /** * Switching the email on or changing how often starts a new period, so the * first report never covers the months it was off. * * @param mixed $old_value Old settings. * @param mixed $value New settings. */ public function on_settings( $old_value, $value ) { $was = is_array( $old_value ) && isset( $old_value['health_email'] ) ? $old_value['health_email'] : 'off'; $is = is_array( $value ) && isset( $value['health_email'] ) ? $value['health_email'] : 'off'; if ( $was !== $is ) { delete_option( self::LAST ); } } /** * Sends when a week or a month has passed since the last email. * * @return bool Whether an email was sent. */ public function maybe_send() { $frequency = $this->settings->get( 'health_email' ); if ( 'off' === $frequency ) { delete_option( self::LAST ); return false; } $last = (int) get_option( self::LAST, 0 ); $every = 'monthly' === $frequency ? 30 * DAY_IN_SECONDS : 7 * DAY_IN_SECONDS; if ( ! $last ) { update_option( self::LAST, time(), false ); // First period starts now. return false; } if ( time() - $last < $every - HOUR_IN_SECONDS ) { return false; } $sent = $this->send( $last ); update_option( self::LAST, time(), false ); return $sent; } /** * Builds and sends the email for the period since a time. * * @param int $since Unix time. * @return bool */ public function send( $since ) { $report = $this->report( $since ); $name = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); /* translators: %s: site name */ return wp_mail( (string) get_option( 'admin_email' ), sprintf( __( '[%s] Your SEO health report', 'orbitstem-seo' ), $name ), $report ); } /** * The email text. * * @param int $since Unix time. * @return string */ public function report( $since ) { global $wpdb; $from = gmdate( 'Y-m-d H:i:s', (int) $since ); $fixed = 0; $changes = 0; $events = array(); if ( Tables::v3_ready() ) { // phpcs:disable WordPress.DB.DirectDatabaseQuery $fixed = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE created_at >= %s AND fixer IN ('redirected_links','redirect_chains','broken_links','outdated_links','orphan_links') AND status = 'applied'", Tables::changes(), $from ) ); $changes = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE created_at >= %s AND fixer NOT IN ('site_event') AND status = 'applied'", Tables::changes(), $from ) ); // Changes undone since do not count. $events = $wpdb->get_col( $wpdb->prepare( "SELECT summary FROM %i WHERE created_at >= %s AND fixer = 'site_event' ORDER BY id DESC LIMIT 10", Tables::changes(), $from ) ); // phpcs:enable } $summary = $this->issues->summary(); $lines = array( /* translators: 1: site name, 2: start date */ sprintf( __( 'SEO health for %1$s since %2$s', 'orbitstem-seo' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), wp_date( get_option( 'date_format' ), (int) $since ) ), '', __( 'Fixed', 'orbitstem-seo' ), /* translators: %d: number of repairs */ '- ' . sprintf( _n( '%d link or redirect repaired.', '%d links or redirects repaired.', $fixed, 'orbitstem-seo' ), $fixed ), /* translators: %d: number of recorded changes */ '- ' . sprintf( _n( '%d SEO change recorded (each can be undone).', '%d SEO changes recorded (each can be undone).', $changes, 'orbitstem-seo' ), $changes ), '', ); if ( $events ) { $lines[] = __( 'Site updates', 'orbitstem-seo' ); foreach ( $events as $event ) { $lines[] = '- ' . $event; } $lines[] = ''; } $lines[] = __( 'Needs attention', 'orbitstem-seo' ); /* translators: 1: errors, 2: warnings */ $lines[] = '- ' . sprintf( __( 'Errors: %1$d. Warnings: %2$d.', 'orbitstem-seo' ), (int) $summary['error'], (int) $summary['warning'] ); foreach ( array_slice( (array) $summary['top'], 0, 3 ) as $top ) { $lines[] = '- ' . ( isset( $top['title'] ) ? $top['title'] : '' ) . ( isset( $top['count'] ) ? ' (' . (int) $top['count'] . ')' : '' ); } if ( ! $summary['error'] && ! $summary['warning'] ) { $lines[] = '- ' . __( 'Nothing urgent. Well done.', 'orbitstem-seo' ); } $lines[] = ''; $lines[] = admin_url( 'admin.php?page=orbitstem-seo' ); $lines[] = ''; $lines[] = __( 'You get this email because "SEO health email" is on in Orbitstem SEO > Settings > Checks & history.', 'orbitstem-seo' ); return implode( "\n", $lines ); } }