Files
peptiderepo-r2-manager/includes/class-r2-log.php
peptiderepo 3cb8cd7c7d feat: initial release v1.1.0
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
2026-04-25 08:48:01 +00:00

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;
}
}