Cloudflare R2 media offload manager for peptiderepo.com. Built on WP Offload Media Lite. Supersedes the one-off migration script and mu-plugin peptiderepo-r2-endpoint.php. Features: - Admin page under Peptide Repo > R2 Media Manager (shared umbrella menu) - ensure_umbrella_menu() guard — safe for multi-plugin umbrella sharing - Batch AJAX migration to R2 with pause/resume and progress bar - Remove-local-copies batch with physical disk presence check - Activity log table (wp_pr_r2_log) with self-healing on plugins_loaded - Test R2 connection button - R2 endpoint injection via as3cf_aws_s3_client_args filter
132 lines
4.1 KiB
PHP
Executable File
132 lines
4.1 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* What: Manages the wp_pr_r2_log table used to track every offload and
|
|
* remove-local operation: success, error, or skipped. Surfaces the
|
|
* most recent entries on the admin page for visibility.
|
|
* Who: PR_R2_Log::create_table() called on plugin activation.
|
|
* PR_R2_Log::write() called by PR_R2_Ajax after each file operation.
|
|
* PR_R2_Log::get_recent() called by PR_R2_Admin_Page::render().
|
|
* Depends: WordPress $wpdb global.
|
|
*
|
|
* @see includes/class-r2-ajax.php (writes log rows after each operation)
|
|
* @see includes/class-r2-admin-page.php (reads log rows for display)
|
|
* @see peptiderepo-r2-manager.php (register_activation_hook + self-heal)
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
class PR_R2_Log {
|
|
|
|
const TABLE_NAME = 'pr_r2_log';
|
|
const STATUS_OK = 'success';
|
|
const STATUS_ERROR = 'error';
|
|
const STATUS_SKIP = 'skipped';
|
|
|
|
/**
|
|
* Create the log table. Uses CREATE TABLE IF NOT EXISTS for idempotency.
|
|
* Safe to call repeatedly — no-op if the table already exists.
|
|
*
|
|
* @return void
|
|
* Side effects: may CREATE wp_pr_r2_log in the WordPress database.
|
|
*/
|
|
public static function create_table(): void {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . self::TABLE_NAME;
|
|
$charset_cs = $wpdb->get_charset_collate();
|
|
$wpdb->query(
|
|
"CREATE TABLE IF NOT EXISTS {$table} (
|
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
attachment_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
|
file_path VARCHAR(500) NOT NULL DEFAULT '',
|
|
operation VARCHAR(20) NOT NULL DEFAULT '',
|
|
status ENUM('success','error','skipped') NOT NULL DEFAULT 'success',
|
|
error_msg TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
KEY idx_attachment_id (attachment_id),
|
|
KEY idx_created_at (created_at)
|
|
) {$charset_cs}"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Insert one log row.
|
|
*
|
|
* @param int $attachment_id WP attachment post ID.
|
|
* @param string $file_path Relative file path (e.g. 2025/11/photo.jpg).
|
|
* @param string $operation 'migrate' | 'remove-local'.
|
|
* @param string $status One of the STATUS_* constants.
|
|
* @param string $error_msg Human-readable error on failure; empty otherwise.
|
|
* @return void
|
|
* Side effects: inserts a row into wp_pr_r2_log.
|
|
*/
|
|
public static function write(
|
|
int $attachment_id,
|
|
string $file_path,
|
|
string $operation,
|
|
string $status,
|
|
string $error_msg = ''
|
|
): void {
|
|
global $wpdb;
|
|
$wpdb->insert(
|
|
$wpdb->prefix . self::TABLE_NAME,
|
|
[
|
|
'attachment_id' => $attachment_id,
|
|
'file_path' => $file_path,
|
|
'operation' => $operation,
|
|
'status' => $status,
|
|
'error_msg' => $error_msg,
|
|
'created_at' => current_time( 'mysql' ),
|
|
],
|
|
[ '%d', '%s', '%s', '%s', '%s', '%s' ]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return the most recent log rows for display on the admin page.
|
|
*
|
|
* @param int $limit Number of rows to return (default 20).
|
|
* @return array<int,object>
|
|
*/
|
|
public static function get_recent( int $limit = 20 ): array {
|
|
global $wpdb;
|
|
return $wpdb->get_results( $wpdb->prepare(
|
|
'SELECT * FROM ' . $wpdb->prefix . self::TABLE_NAME . ' ORDER BY id DESC LIMIT %d',
|
|
$limit
|
|
) );
|
|
}
|
|
|
|
/**
|
|
* Delete all rows from the log table.
|
|
*
|
|
* @return void
|
|
* Side effects: truncates wp_pr_r2_log.
|
|
*/
|
|
public static function clear(): void {
|
|
global $wpdb;
|
|
$wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . self::TABLE_NAME );
|
|
}
|
|
|
|
/**
|
|
* Count rows by status for a given operation.
|
|
*
|
|
* @param string $operation 'migrate' | 'remove-local' | '' for all.
|
|
* @return array{success:int,error:int,skipped:int}
|
|
*/
|
|
public static function count_by_status( string $operation = '' ): array {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . self::TABLE_NAME;
|
|
$where = $operation ? $wpdb->prepare( 'WHERE operation = %s', $operation ) : '';
|
|
$rows = $wpdb->get_results(
|
|
"SELECT status, COUNT(*) AS cnt FROM {$table} {$where} GROUP BY status"
|
|
);
|
|
$out = [ 'success' => 0, 'error' => 0, 'skipped' => 0 ];
|
|
foreach ( $rows as $row ) {
|
|
$out[ $row->status ] = (int) $row->cnt;
|
|
}
|
|
return $out;
|
|
}
|
|
}
|