*/ public static function frequencies() { return array( '' => __( 'Not specified', 'maintained-log' ), 'hourly' => __( 'Hourly', 'maintained-log' ), 'daily' => __( 'Daily', 'maintained-log' ), 'weekly' => __( 'Weekly', 'maintained-log' ), 'monthly' => __( 'Monthly', 'maintained-log' ), ); } /** * Reminder intervals in days. * * @return array */ public static function reminder_intervals() { return array( '0' => __( 'Never', 'maintained-log' ), '30' => __( 'Monthly', 'maintained-log' ), '90' => __( 'Quarterly', 'maintained-log' ), ); } /** * Whether a policy has been declared. * * @return bool */ public static function has_policy() { return '' !== trim( (string) Settings::get( 'backup_provider' ) ); } /** * One sentence describing the declared policy, for reports. * * @return string Empty when no policy is declared. */ public static function policy_sentence() { if ( ! self::has_policy() ) { return ''; } $provider = Settings::get( 'backup_provider' ); $frequency = Settings::get( 'backup_frequency' ); $retention = (int) Settings::get( 'backup_retention' ); $labels = array( 'hourly' => __( 'Hourly backups', 'maintained-log' ), 'daily' => __( 'Daily backups', 'maintained-log' ), 'weekly' => __( 'Weekly backups', 'maintained-log' ), 'monthly' => __( 'Monthly backups', 'maintained-log' ), ); $what = isset( $labels[ $frequency ] ) ? $labels[ $frequency ] : __( 'Backups', 'maintained-log' ); /* translators: 1: "Daily backups", 2: provider, e.g. "the hosting provider". */ $sentence = sprintf( __( '%1$s managed by %2$s', 'maintained-log' ), $what, $provider ); if ( $retention ) { /* translators: %d: number of days. */ $sentence .= ', ' . sprintf( _n( 'kept for %d day', 'kept for %d days', $retention, 'maintained-log' ), $retention ); } return $sentence . '.'; } /** * Latest confirmation entry of a kind, optionally before a date. * * @param string $kind KIND_CHECKED or KIND_RESTORE_TEST. Empty for any. * @param string|null $before UTC upper bound, exclusive. * @return object|null */ public static function last_confirmation( $kind = '', $before = null ) { $page = 1; do { $entries = Log::query( array( 'type' => Log::TYPE_MANUAL, 'category' => self::CATEGORY, 'to' => $before, 'per_page' => 50, 'page' => $page, ) ); foreach ( $entries as $entry ) { if ( Log::STATUS_ERROR === $entry->status ) { continue; } $entry_kind = isset( $entry->details['backup'] ) ? $entry->details['backup'] : self::KIND_CHECKED; if ( '' === $kind || $kind === $entry_kind ) { return $entry; } } ++$page; } while ( count( $entries ) === 50 ); return null; } /** * Days since the last confirmation, or null if never confirmed. * * @param string $kind Kind. * @return int|null */ public static function days_since( $kind = '' ) { $entry = self::last_confirmation( $kind ); if ( ! $entry ) { return null; } return (int) floor( ( time() - strtotime( $entry->created_at . ' UTC' ) ) / DAY_IN_SECONDS ); } /** * Whether a confirmation is overdue according to the reminder setting. * * @return bool */ public static function is_overdue() { $interval = (int) Settings::get( 'backup_reminder' ); if ( ! $interval ) { return false; } $days = self::days_since( self::KIND_CHECKED ); return null === $days || $days >= $interval; } /** * Logs a confirmation. * * @param string $kind KIND_CHECKED or KIND_RESTORE_TEST. * @param string $notes Optional notes. * @return int|false */ public static function confirm( $kind, $notes = '' ) { $titles = array( self::KIND_CHECKED => __( 'Backups checked: recent copies are available', 'maintained-log' ), self::KIND_RESTORE_TEST => __( 'Restore test completed successfully', 'maintained-log' ), ); if ( ! isset( $titles[ $kind ] ) ) { return false; } $details = array( 'backup' => $kind ); if ( '' !== $notes ) { $details['notes'] = $notes; } if ( self::has_policy() ) { $details['policy'] = self::policy_sentence(); } return Log::add( array( 'type' => Log::TYPE_MANUAL, 'category' => self::CATEGORY, 'status' => Log::STATUS_SUCCESS, 'title' => $titles[ $kind ], 'details' => $details, 'source' => Log::SOURCE_MANUAL, ) ); } /** * Emails the administrator when a confirmation is overdue, at most weekly. * * @return bool Whether an email was sent. */ public static function maybe_send_reminder() { if ( ! self::is_overdue() ) { return false; } $last = (int) get_option( self::LAST_REMINDER_OPTION, 0 ); if ( time() - $last < WEEK_IN_SECONDS - HOUR_IN_SECONDS ) { return false; } $to = Settings::parse_emails( Settings::get( 'admin_recipients' ) ); if ( ! $to ) { return false; } $days = self::days_since( self::KIND_CHECKED ); if ( null === $days ) { $when = __( 'Backups have not been confirmed yet.', 'maintained-log' ); } else { /* translators: %d: number of days. */ $when = sprintf( _n( 'Backups were last confirmed %d day ago.', 'Backups were last confirmed %d days ago.', $days, 'maintained-log' ), $days ); } $policy = self::has_policy() ? self::policy_sentence() : __( 'No backup policy is declared in the settings.', 'maintained-log' ); $message = $when . "\n\n" . $policy . "\n\n" . sprintf( /* translators: %s: admin URL. */ __( "Please check in your hosting panel that recent backups exist, then confirm it here with one click:\n%s\n\nThe confirmation is recorded in the log and shown in the client report.", 'maintained-log' ), admin_url( 'admin.php?page=maintained-log' ) ); /* translators: %s: site name. */ $subject = sprintf( __( '[%s] Time to check the backups', 'maintained-log' ), get_bloginfo( 'name' ) ); $sent = wp_mail( $to, $subject, $message ); if ( $sent ) { update_option( self::LAST_REMINDER_OPTION, time(), false ); } return $sent; } }