*/ public static function allowed_intervals(): array { return array( self::INTERVAL_DISABLED, 'hourly', 'twicedaily', 'daily', 'weekly', ); } /** * Default option payload. * * @return array */ public static function defaults(): array { return array( 'retention_count' => 3, 'encryption_enabled' => false, 'working_dir_rel' => 'brverify-working', 'delete_data_on_uninstall' => false, // Default OFF: uninstall keeps options, tables and backups. 'schedule_interval' => 'daily', 'alert_email' => '', // Phase K: opt-in local backup right before plugin/theme/core updates (default OFF). 'pre_update_enabled' => false, 'pre_update_retention_count' => 3, // Default: local only. Extensions may expand via brverify_backup_destinations. 'destinations' => array( 'local' ), ); } /** * Merged settings (defaults + stored). * * @return array */ public static function all(): array { $stored = get_option( self::OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } return array_merge( self::defaults(), $stored ); } /** * Retention count N (keep last N local artifacts). Minimum 1. */ public static function retention_count(): int { $settings = self::all(); $n = isset( $settings['retention_count'] ) ? (int) $settings['retention_count'] : 3; $n = (int) apply_filters( 'brverify_retention_count', $n ); return max( 1, min( 100, $n ) ); } /** * Whether automatic pre-update backups are enabled (opt-in, default OFF). */ public static function pre_update_enabled(): bool { $settings = self::all(); return ! empty( $settings['pre_update_enabled'] ); } /** * Separate retention cap for pre-update backups (default 3, 1–50). * * Pre-update artifacts are counted against this cap only — they never * crowd out scheduled/manual backups, and vice versa. */ public static function pre_update_retention_count(): int { $settings = self::all(); $n = isset( $settings['pre_update_retention_count'] ) ? (int) $settings['pre_update_retention_count'] : 3; /** * Filter the pre-update backup retention cap. * * @param int $n Cap. */ $n = (int) apply_filters( 'brverify_pre_update_retention_count', $n ); return max( 1, min( 50, $n ) ); } /** * Backup schedule interval key. */ public static function schedule_interval(): string { $settings = self::all(); $key = isset( $settings['schedule_interval'] ) ? (string) $settings['schedule_interval'] : 'daily'; if ( ! in_array( $key, self::allowed_intervals(), true ) ) { return 'daily'; } return $key; } /** * Failure alert recipient (empty → admin_email). */ public static function alert_email(): string { $settings = self::all(); $raw = isset( $settings['alert_email'] ) ? trim( (string) $settings['alert_email'] ) : ''; if ( '' === $raw ) { return (string) get_option( 'admin_email' ); } $sanitized = sanitize_email( $raw ); return is_email( $sanitized ) ? $sanitized : (string) get_option( 'admin_email' ); } /** * Whether uninstall should delete ALL plugin data (options, tables and the backup files). * * Default OFF. When OFF, uninstall only removes cron events and transients: backups, * history and settings survive deleting the plugin. The pre-release key * `delete_artifacts_on_uninstall` is honoured as an alias. */ public static function delete_data_on_uninstall(): bool { $settings = self::all(); return ! empty( $settings['delete_data_on_uninstall'] ) || ! empty( $settings['delete_artifacts_on_uninstall'] ); } /** * Persist a partial update (merge into existing). * * @param array $patch Fields to update. */ public static function update( array $patch ): void { $current = self::all(); $merged = array_merge( $current, $patch ); $clean = self::sanitize( $merged ); update_option( self::OPTION, $clean, false ); } /** * Sanitize a full settings array. * * @param array $input Raw. * @return array */ public static function sanitize( array $input ): array { $out = self::defaults(); if ( isset( $input['retention_count'] ) ) { $out['retention_count'] = max( 1, min( 100, (int) $input['retention_count'] ) ); } if ( isset( $input['encryption_enabled'] ) ) { $out['encryption_enabled'] = (bool) $input['encryption_enabled']; } if ( isset( $input['working_dir_rel'] ) ) { $rel = sanitize_file_name( (string) $input['working_dir_rel'] ); $out['working_dir_rel'] = '' !== $rel ? $rel : 'brverify-working'; } if ( isset( $input['delete_data_on_uninstall'] ) ) { $out['delete_data_on_uninstall'] = (bool) $input['delete_data_on_uninstall']; } elseif ( isset( $input['delete_artifacts_on_uninstall'] ) ) { $out['delete_data_on_uninstall'] = (bool) $input['delete_artifacts_on_uninstall']; // Pre-release alias. } if ( isset( $input['schedule_interval'] ) ) { $key = (string) $input['schedule_interval']; $out['schedule_interval'] = in_array( $key, self::allowed_intervals(), true ) ? $key : 'daily'; } if ( isset( $input['alert_email'] ) ) { $email = sanitize_email( (string) $input['alert_email'] ); $out['alert_email'] = is_email( $email ) ? $email : ''; } if ( isset( $input['pre_update_enabled'] ) ) { $out['pre_update_enabled'] = (bool) $input['pre_update_enabled']; } if ( isset( $input['pre_update_retention_count'] ) ) { $out['pre_update_retention_count'] = max( 1, min( 50, (int) $input['pre_update_retention_count'] ) ); } if ( isset( $input['destinations'] ) ) { $raw = $input['destinations']; if ( is_string( $raw ) ) { $raw = array_map( 'trim', explode( ',', $raw ) ); } if ( ! is_array( $raw ) ) { $raw = array( 'local' ); } $clean_dest = array(); foreach ( $raw as $id ) { $id = sanitize_key( (string) $id ); if ( '' === $id ) { continue; } $clean_dest[] = $id; } $clean_dest = array_values( array_unique( $clean_dest ) ); // Free alone always keeps at least local. if ( empty( $clean_dest ) ) { $clean_dest = array( 'local' ); } $out['destinations'] = $clean_dest; } return $out; } /** * Selected backup destination adapter ids (default: local only). * * Free edition stores a single destination (local). When extended edition registers * remote adapters, the list may include multiple ids. extended edition (or other * extensions) may override via the `brverify_backup_destinations` filter. * * @return list */ public static function backup_destinations(): array { $settings = self::all(); $ids = isset( $settings['destinations'] ) && is_array( $settings['destinations'] ) ? $settings['destinations'] : array( 'local' ); $clean = array(); foreach ( $ids as $id ) { $id = sanitize_key( (string) $id ); if ( '' !== $id ) { $clean[] = $id; } } $clean = array_values( array_unique( $clean ) ); if ( empty( $clean ) ) { $clean = array( 'local' ); } /** * Filter backup destination adapter ids for the next / current job. * * Free default is `array( 'local' )`. the extended edition expands this * by the extended edition to enable multi-destination (local + S3 + SFTP). * * @param list $destinations Adapter ids. */ $filtered = apply_filters( 'brverify_backup_destinations', $clean ); if ( ! is_array( $filtered ) ) { return $clean; } $out = array(); foreach ( $filtered as $id ) { $id = sanitize_key( (string) $id ); if ( '' !== $id ) { $out[] = $id; } } $out = array_values( array_unique( $out ) ); return empty( $out ) ? array( 'local' ) : $out; } /** * Human labels for schedule intervals. * * @return array */ public static function interval_labels(): array { return array( self::INTERVAL_DISABLED => __( 'Disabled (manual only)', '007aj-backup-restore-verifier' ), 'hourly' => __( 'Hourly', '007aj-backup-restore-verifier' ), 'twicedaily' => __( 'Twice daily', '007aj-backup-restore-verifier' ), 'daily' => __( 'Daily', '007aj-backup-restore-verifier' ), 'weekly' => __( 'Weekly', '007aj-backup-restore-verifier' ), ); } }