Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"Bash(docker logs:*)",
"Bash(docker-compose:*)",
"Bash(docker volume rm:*)",
"Bash(composer install:*)"
"Bash(composer install:*)",
"Bash(php:*)"
],
"deny": [],
"ask": []
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ services:
@ini_set('post_max_size', '128M');
volumes:
- ./wp-content:/var/www/html/wp-content
- ./php-uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
- wordpress_data:/var/www/html
depends_on:
db:
Expand Down
5 changes: 5 additions & 0 deletions php-uploads.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
upload_max_filesize = 256M
post_max_size = 256M
max_execution_time = 300
memory_limit = 512M
max_input_time = 300
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/**
* Migration: Bulk Jobs Tables
* -----------------------------------------------------------------------------
* Creates tables for bulk upload job management (psai-bulk):
* - psai_bulk_jobs: Job records
* - psai_bulk_items: Individual file items per job
*
* @package PostSecret\Admin
*/

namespace PostSecret\Admin\Migrations;

/**
* Run the migration.
*
* @global \wpdb $wpdb
*/
function up_005_bulk_jobs(): void
{
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();

// Jobs table
$table_jobs = $wpdb->prefix . 'psai_bulk_jobs';
$sql_jobs = "CREATE TABLE {$table_jobs} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
uuid varchar(36) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'new',
source varchar(255) NOT NULL,
staging_path varchar(500) NOT NULL,
total_items int(11) unsigned NOT NULL DEFAULT 0,
processed_items int(11) unsigned NOT NULL DEFAULT 0,
success_count int(11) unsigned NOT NULL DEFAULT 0,
fail_count int(11) unsigned NOT NULL DEFAULT 0,
last_error text,
settings text,
started_at datetime DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uuid (uuid),
KEY status (status),
KEY created_at (created_at)
) {$charset_collate};";

// Items table
$table_items = $wpdb->prefix . 'psai_bulk_items';
$sql_items = "CREATE TABLE {$table_items} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
job_id bigint(20) unsigned NOT NULL,
file_path varchar(500) NOT NULL,
sha256 varchar(64) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'pending',
attachment_id bigint(20) unsigned DEFAULT NULL,
attempts int(11) unsigned NOT NULL DEFAULT 0,
last_error text,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
KEY job_id (job_id),
KEY status (status),
KEY sha256 (sha256),
KEY attachment_id (attachment_id)
) {$charset_collate};";

require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql_jobs);
dbDelta($sql_items);
}
110 changes: 110 additions & 0 deletions wp-content/plugins/postsecret-ai/bulk-debug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/**
* Bulk Upload Debug Page
* Shows recent errors and allows test uploads
*/

require_once __DIR__ . '/../../../wp-load.php';

if (!current_user_can('manage_options')) {
wp_die('Unauthorized');
}

// Enable error display
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

echo "<h1>Bulk Upload Debug</h1>\n";
echo "<style>body{font-family:sans-serif;padding:20px;} pre{background:#f5f5f5;padding:10px;overflow:auto;} .error{color:red;} .ok{color:green;}</style>\n";

// Check if we're processing a test upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_FILES['test_file'])) {
echo "<h2>Processing Upload...</h2>\n";
echo "<pre>";

try {
echo "Files received:\n";
print_r($_FILES);
echo "\n\n";

echo "Calling BulkJobService::create_job()...\n";
$result = \PSAI\BulkJobService::create_job($_FILES);

echo "\n\nResult:\n";
print_r($result);

if ($result['success']) {
echo "\n\n<span class='ok'>✓ SUCCESS! Job ID: {$result['job_id']}</span>\n";
} else {
echo "\n\n<span class='error'>✗ FAILED: {$result['error']}</span>\n";
}
} catch (\Throwable $e) {
echo "\n\n<span class='error'>✗ EXCEPTION: {$e->getMessage()}</span>\n\n";
echo "Trace:\n{$e->getTraceAsString()}\n";
}

echo "</pre>";
echo "<hr>\n";
}

// Check transient error
$transient_error = get_transient('_ps_bulk_error');
if ($transient_error) {
echo "<div style='background:#fee;padding:10px;border:1px solid #c00;'>";
echo "<strong>Last Error:</strong> " . esc_html($transient_error);
echo "</div><br>\n";
}

// Check database tables
global $wpdb;
$table_jobs = $wpdb->prefix . 'psai_bulk_jobs';
$table_items = $wpdb->prefix . 'psai_bulk_items';

echo "<h2>Database Status</h2>\n";
$jobs_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_jobs}'") === $table_jobs;
$items_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_items}'") === $table_items;

echo "<p>Jobs table: " . ($jobs_exists ? "<span class='ok'>EXISTS</span>" : "<span class='error'>NOT FOUND</span>") . "</p>\n";
echo "<p>Items table: " . ($items_exists ? "<span class='ok'>EXISTS</span>" : "<span class='error'>NOT FOUND</span>") . "</p>\n";

if ($jobs_exists) {
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_jobs}");
echo "<p>Total jobs: {$count}</p>\n";

if ($count > 0) {
echo "<h3>Recent Jobs:</h3>\n";
$jobs = $wpdb->get_results("SELECT * FROM {$table_jobs} ORDER BY created_at DESC LIMIT 5", ARRAY_A);
echo "<pre>" . print_r($jobs, true) . "</pre>\n";
}
}

// Test upload form
echo "<hr>\n";
echo "<h2>Test Upload</h2>\n";
echo "<form method='post' enctype='multipart/form-data'>\n";
echo "<p><input type='file' name='test_file[]' accept='.zip,.jpg,.jpeg,.png,.webp' multiple /></p>\n";
echo "<p><button type='submit' class='button button-primary'>Test Upload</button></p>\n";
echo "</form>\n";

// Check upload directory
echo "<hr>\n";
echo "<h2>Upload Directory</h2>\n";
$upload_dir = wp_upload_dir();
$staging_base = trailingslashit($upload_dir['basedir']) . 'psai-bulk-staging';

echo "<p>Upload base: <code>{$upload_dir['basedir']}</code></p>\n";
echo "<p>Staging dir: <code>{$staging_base}</code></p>\n";
echo "<p>Staging exists: " . (is_dir($staging_base) ? "<span class='ok'>YES</span>" : "NO (will be created)") . "</p>\n";
echo "<p>Parent writable: " . (is_writable($upload_dir['basedir']) ? "<span class='ok'>YES</span>" : "<span class='error'>NO</span>") . "</p>\n";

// Check PHP settings
echo "<hr>\n";
echo "<h2>PHP Settings</h2>\n";
echo "<p>upload_max_filesize: " . ini_get('upload_max_filesize') . "</p>\n";
echo "<p>post_max_size: " . ini_get('post_max_size') . "</p>\n";
echo "<p>max_file_uploads: " . ini_get('max_file_uploads') . "</p>\n";
echo "<p>ZipArchive available: " . (class_exists('ZipArchive') ? "<span class='ok'>YES</span>" : "<span class='error'>NO</span>") . "</p>\n";

echo "<hr>\n";
echo "<p><a href='" . admin_url('admin.php?page=psai_bulk_upload') . "'>← Back to Bulk Upload</a></p>\n";
87 changes: 87 additions & 0 deletions wp-content/plugins/postsecret-ai/check-bulk-setup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/**
* Diagnostic: Check if bulk upload tables exist
*
* Visit this file to verify database setup
*/

require_once __DIR__ . '/../../../wp-load.php';

if (!current_user_can('manage_options')) {
wp_die('Unauthorized');
}

global $wpdb;

echo "<h1>PostSecret Bulk Upload - Setup Check</h1>\n";
echo "<style>body{font-family:sans-serif;padding:20px;} .ok{color:green;} .error{color:red;} pre{background:#f5f5f5;padding:10px;}</style>\n";

// Check tables
$table_jobs = $wpdb->prefix . 'psai_bulk_jobs';
$table_items = $wpdb->prefix . 'psai_bulk_items';

echo "<h2>Database Tables</h2>\n";

$jobs_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_jobs}'") === $table_jobs;
$items_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_items}'") === $table_items;

if ($jobs_exists) {
echo "<p class='ok'>✓ Table <code>{$table_jobs}</code> exists</p>\n";
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_jobs}");
echo "<p>Jobs in table: {$count}</p>\n";

if ($count > 0) {
echo "<h3>Recent Jobs:</h3>\n";
$jobs = $wpdb->get_results("SELECT * FROM {$table_jobs} ORDER BY created_at DESC LIMIT 5", ARRAY_A);
echo "<pre>" . print_r($jobs, true) . "</pre>\n";
}
} else {
echo "<p class='error'>✗ Table <code>{$table_jobs}</code> does NOT exist</p>\n";
echo "<p><strong>Action needed:</strong> Run migrations at <a href='" . plugins_url('postsecret-admin/run-migrations.php') . "'>postsecret-admin/run-migrations.php</a></p>\n";
}

if ($items_exists) {
echo "<p class='ok'>✓ Table <code>{$table_items}</code> exists</p>\n";
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_items}");
echo "<p>Items in table: {$count}</p>\n";
} else {
echo "<p class='error'>✗ Table <code>{$table_items}</code> does NOT exist</p>\n";
echo "<p><strong>Action needed:</strong> Run migrations at <a href='" . plugins_url('postsecret-admin/run-migrations.php') . "'>postsecret-admin/run-migrations.php</a></p>\n";
}

// Check upload directory
echo "<h2>Upload Directory</h2>\n";
$upload_dir = wp_upload_dir();
$staging_base = trailingslashit($upload_dir['basedir']) . 'psai-bulk-staging';

if (is_dir($staging_base)) {
echo "<p class='ok'>✓ Staging directory exists: <code>{$staging_base}</code></p>\n";
echo "<p>Writable: " . (is_writable($staging_base) ? '<span class="ok">Yes</span>' : '<span class="error">No</span>') . "</p>\n";
} else {
echo "<p>Staging directory will be created on first upload: <code>{$staging_base}</code></p>\n";
echo "<p>Parent writable: " . (is_writable($upload_dir['basedir']) ? '<span class="ok">Yes</span>' : '<span class="error">No</span>') . "</p>\n";
}

// Test AJAX endpoint
echo "<h2>AJAX Endpoints</h2>\n";
echo "<p>Testing <code>psai_bulk_list_jobs</code>...</p>\n";

$_REQUEST['action'] = 'psai_bulk_list_jobs';
ob_start();
do_action('wp_ajax_psai_bulk_list_jobs');
$response = ob_get_clean();

echo "<p>Response:</p>\n";
echo "<pre>" . esc_html($response) . "</pre>\n";

if ($jobs_exists && $items_exists) {
echo "<h2>✓ Setup Complete</h2>\n";
echo "<p><a href='" . admin_url('admin.php?page=psai_bulk_upload') . "'>Go to Bulk Upload Page</a></p>\n";
} else {
echo "<h2>Setup Required</h2>\n";
echo "<p><strong>1. Run migrations:</strong> <a href='" . plugins_url('postsecret-admin/run-migrations.php') . "'>Click here to run migrations</a></p>\n";
echo "<p><strong>2. Refresh this page</strong> to verify setup</p>\n";
}

echo "<hr>\n";
echo "<p><a href='" . admin_url() . "'>← Back to Dashboard</a></p>\n";
Loading