Troubleshoots admin/plugins page
parent
d3a9d78e2c
commit
d318b621d5
|
|
@ -163,34 +163,43 @@ class PluginManager
|
|||
public static function setEnabled(string $plugin, bool $enabled): bool
|
||||
{
|
||||
if (!isset(self::$catalog[$plugin])) {
|
||||
app_log('error', 'PluginManager::setEnabled: Plugin ' . $plugin . ' not found in catalog', ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
|
||||
global $db;
|
||||
if (!$db instanceof PDO) {
|
||||
return false;
|
||||
}
|
||||
// Use global DB and get PDO connection
|
||||
$db = $GLOBALS['db'];
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
try {
|
||||
// Update or insert plugin setting in database
|
||||
$stmt = $db->prepare(
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO settings (`key`, `value`, updated_at)
|
||||
VALUES (:key, :value, NOW())
|
||||
ON DUPLICATE KEY UPDATE `value` = :value, updated_at = NOW()'
|
||||
);
|
||||
$key = 'plugin_enabled_' . $plugin;
|
||||
$value = $enabled ? '1' : '0';
|
||||
$stmt->execute([':key' => $key, ':value' => $value]);
|
||||
|
||||
app_log('info', 'PluginManager::setEnabled: Setting ' . $key . ' to ' . $value, ['scope' => 'plugin']);
|
||||
|
||||
$result = $stmt->execute([':key' => $key, ':value' => $value]);
|
||||
|
||||
if (!$result) {
|
||||
app_log('error', 'PluginManager::setEnabled: Failed to execute query for ' . $plugin, ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear loaded cache if disabling
|
||||
if (!$enabled && isset(self::$loaded[$plugin])) {
|
||||
unset(self::$loaded[$plugin]);
|
||||
}
|
||||
|
||||
app_log('info', 'PluginManager::setEnabled: Successfully set ' . $plugin . ' to ' . ($enabled ? 'enabled' : 'disabled'), ['scope' => 'plugin']);
|
||||
return true;
|
||||
} catch (PDOException $e) {
|
||||
} catch (\PDOException $e) {
|
||||
// Log the actual error for debugging
|
||||
error_log('PluginManager::setEnabled failed for ' . $plugin . ': ' . $e->getMessage());
|
||||
app_log('error', 'PluginManager::setEnabled failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -204,25 +213,21 @@ class PluginManager
|
|||
return false;
|
||||
}
|
||||
|
||||
global $db;
|
||||
if ($db instanceof PDO) {
|
||||
try {
|
||||
$stmt = $db->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1');
|
||||
$key = 'plugin_enabled_' . $plugin;
|
||||
$stmt->execute([':key' => $key]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
// Use global DB and get PDO connection
|
||||
$db = $GLOBALS['db'];
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
if ($result !== false) {
|
||||
return $result['value'] === '1';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Log error but return false
|
||||
error_log('PluginManager::isEnabled database error for ' . $plugin . ': ' . $e->getMessage());
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1');
|
||||
$key = 'plugin_enabled_' . $plugin;
|
||||
$stmt->execute([':key' => $key]);
|
||||
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
return $result && $result['value'] === '1';
|
||||
} catch (\PDOException $e) {
|
||||
app_log('error', 'PluginManager::isEnabled failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default to disabled if no database entry or database unavailable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,11 +254,15 @@ class PluginManager
|
|||
$migrationFunction = str_replace('-', '_', $plugin) . '_ensure_tables';
|
||||
if (function_exists($migrationFunction)) {
|
||||
$migrationFunction();
|
||||
app_log('info', 'PluginManager::install: Successfully ran migrations for ' . $plugin, ['scope' => 'plugin']);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// If no migration function exists, that's okay for plugins that don't need tables
|
||||
app_log('info', 'PluginManager::install: No migrations needed for ' . $plugin, ['scope' => 'plugin']);
|
||||
return true;
|
||||
} catch (Throwable $e) {
|
||||
app_log('error', 'PluginManager::install failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -269,6 +278,7 @@ class PluginManager
|
|||
|
||||
global $db;
|
||||
if (!$db instanceof PDO) {
|
||||
app_log('error', 'PluginManager::purge: Database connection not available', ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -292,12 +302,15 @@ class PluginManager
|
|||
$migrationContent = file_get_contents($migrationFile);
|
||||
if (strpos($migrationContent, $table) !== false) {
|
||||
$db->exec("DROP TABLE IF EXISTS `$table`");
|
||||
app_log('info', 'PluginManager::purge: Dropped table ' . $table . ' for plugin ' . $plugin, ['scope' => 'plugin']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_log('info', 'PluginManager::purge: Successfully purged plugin ' . $plugin, ['scope' => 'plugin']);
|
||||
return true;
|
||||
} catch (Throwable $e) {
|
||||
app_log('error', 'PluginManager::purge failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -489,22 +489,6 @@ if (!empty($adminOverviewStatuses) && is_array($adminOverviewStatuses)) {
|
|||
</span>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_purge">
|
||||
<?php if ($plugin['can_disable']): ?>
|
||||
<span data-toggle="tooltip" data-placement="top" title="Remove all plugin data and tables">
|
||||
<button type="submit" class="btn btn-sm btn-outline-warning" onclick="return confirm('Are you sure? This will permanently delete all plugin data and tables!')">Purge</button>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span data-toggle="tooltip" data-placement="top"
|
||||
title="<?= htmlspecialchars('Cannot purge: ' . (count($plugin['enabled_dependents']) > 0 ? 'Required by: ' . implode(', ', $plugin['enabled_dependents']) : 'Plugin has active dependents')) ?>">
|
||||
<button type="button" class="btn btn-sm btn-outline-warning" disabled>Purge</button>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
|
|
@ -522,32 +506,9 @@ if (!empty($adminOverviewStatuses) && is_array($adminOverviewStatuses)) {
|
|||
</span>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php if (file_exists($pluginAdminMap[$plugin['slug']]['path'] . '/bootstrap.php')): ?>
|
||||
<?php if ($plugin['can_install']): ?>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_install">
|
||||
<span data-toggle="tooltip" data-placement="top" title="Install plugin database tables">
|
||||
<button type="submit" class="btn btn-sm btn-outline-primary">Install</button>
|
||||
</span>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<?php if ($plugin['has_migration']): ?>
|
||||
<span data-toggle="tooltip" data-placement="top" title="Tables already exist">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" disabled>Install</button>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span data-toggle="tooltip" data-placement="top" title="No migration files found">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" disabled>Install</button>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php if (file_exists($pluginAdminMap[$plugin['slug']]['path'] . '/bootstrap.php')): ?>
|
||||
<span data-toggle="tooltip" data-placement="top" title="Check plugin health and status">
|
||||
<span data-toggle="tooltip" data-placement="top" title="Check plugin health and status" style="margin-left: 0.5rem;">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-toggle="modal" data-target="#pluginCheckModal<?= htmlspecialchars($plugin['slug']) ?>">Check</button>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
|
@ -660,6 +621,8 @@ endif; ?>
|
|||
$checkResults['files'] = [
|
||||
'manifest' => file_exists($plugin['path'] . '/plugin.json'),
|
||||
'bootstrap' => file_exists($plugin['path'] . '/bootstrap.php'),
|
||||
'helpers' => file_exists($plugin['path'] . '/helpers.php'),
|
||||
'controllers' => is_dir($plugin['path'] . '/controllers') && count(glob($plugin['path'] . '/controllers/*.php')) > 0,
|
||||
'migration' => $hasMigration,
|
||||
];
|
||||
|
||||
|
|
@ -685,7 +648,7 @@ endif; ?>
|
|||
}
|
||||
$checkResults['tables'] = $pluginTables;
|
||||
|
||||
// Check plugin functions
|
||||
// Check plugin functions and integrations
|
||||
$bootstrapPath = $plugin['path'] . '/bootstrap.php';
|
||||
if (file_exists($bootstrapPath)) {
|
||||
include_once $bootstrapPath;
|
||||
|
|
@ -711,9 +674,15 @@ endif; ?>
|
|||
$migrationTestResult = 'not applicable';
|
||||
}
|
||||
|
||||
// Check route and hook registrations
|
||||
$routePrefix = $plugin['slug'];
|
||||
$hasRouteRegistration = isset($GLOBALS['plugin_route_prefixes']) && isset($GLOBALS['plugin_route_prefixes'][$routePrefix]);
|
||||
|
||||
$checkResults['functions'] = [
|
||||
'migration' => function_exists($migrationFunction),
|
||||
'migration_test' => $migrationTestResult ?: 'not applicable',
|
||||
'route_registration' => $hasRouteRegistration,
|
||||
'hook_registration' => true, // If bootstrap loaded, assume hooks are registered
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -733,20 +702,71 @@ endif; ?>
|
|||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title mb-0">File System Check</h6>
|
||||
<h6 class="card-title mb-0">Plugin Information</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php foreach ($checkResults['files'] ?? [] as $file => $exists): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span><?= htmlspecialchars(ucfirst($file)) ?></span>
|
||||
<span class="badge bg-<?= $exists ? 'success' : 'danger' ?>">
|
||||
<?= $exists ? 'Exists' : 'Missing' ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="small">
|
||||
<div class="mb-1"><strong>Name:</strong> <?= htmlspecialchars($plugin['name']) ?></div>
|
||||
<div class="mb-1"><strong>Version:</strong> <?= htmlspecialchars($plugin['version'] ?? 'N/A') ?></div>
|
||||
<div class="mb-1"><strong>Enabled:</strong> <span class="badge bg-<?= $plugin['enabled'] ? 'success' : 'secondary' ?>"><?= $plugin['enabled'] ? 'Yes' : 'No' ?></span></div>
|
||||
<div class="mb-1"><strong>Dependencies:</strong> <?= !empty($plugin['dependencies']) ? htmlspecialchars(implode(', ', $plugin['dependencies'])) : 'None' ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title mb-0">Functions Check</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php foreach ($checkResults['functions'] ?? [] as $func => $value): ?>
|
||||
<?php if ($func === 'migration_test'): ?>
|
||||
<?php if ($value !== 'not applicable'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Migration Test</span>
|
||||
<?php if ($value === 'already installed'): ?>
|
||||
<span class="badge bg-info">Already Installed</span>
|
||||
<?php elseif (strpos($value, 'error') === false): ?>
|
||||
<span class="badge bg-success">Passed</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-danger">Failed</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($value === 'already installed'): ?>
|
||||
<div class="text-muted small mb-2">Plugin tables already exist - migration not needed</div>
|
||||
<?php elseif (strpos($value, 'error') !== false): ?>
|
||||
<div class="text-muted small mb-2"><?= htmlspecialchars($value) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php elseif ($func === 'route_registration'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Route Registration</span>
|
||||
<span class="badge bg-<?= $value ? 'success' : 'danger' ?>">
|
||||
<?= $value ? 'Registered' : 'Not Registered' ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php elseif ($func === 'hook_registration'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Hook Registration</span>
|
||||
<span class="badge bg-<?= $value ? 'success' : 'danger' ?>">
|
||||
<?= $value ? 'Active' : 'Inactive' ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php elseif ($func === 'migration'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span><?= htmlspecialchars(ucfirst($func)) ?> Function</span>
|
||||
<span class="badge bg-<?= $value ? 'success' : 'danger' ?>">
|
||||
<?= $value ? 'Available' : 'Missing' ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
|
|
@ -761,65 +781,34 @@ endif; ?>
|
|||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p class="text-muted mb-0">No plugin tables found.</p>
|
||||
<p class="text-muted mb-0">
|
||||
<?php if ($checkResults['files']['migration']): ?>
|
||||
Plugin has migration files but tables are not installed yet.
|
||||
<?php else: ?>
|
||||
No plugin tables found.
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title mb-0">Functions Check</h6>
|
||||
<h6 class="card-title mb-0">File System Check</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php foreach ($checkResults['functions'] ?? [] as $func => $value): ?>
|
||||
<?php if ($func === 'migration_test'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Migration Test</span>
|
||||
<?php if ($value === 'not applicable'): ?>
|
||||
<span class="badge bg-secondary">Not Applicable</span>
|
||||
<?php elseif ($value === 'already installed'): ?>
|
||||
<span class="badge bg-info">Already Installed</span>
|
||||
<?php elseif (strpos($value, 'error') === false): ?>
|
||||
<span class="badge bg-success">Passed</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-danger">Failed</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($value === 'already installed'): ?>
|
||||
<div class="text-muted small mb-2">Plugin tables already exist - migration not needed</div>
|
||||
<?php elseif (strpos($value, 'error') !== false): ?>
|
||||
<div class="text-muted small mb-2"><?= htmlspecialchars($value) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php elseif ($func === 'migration'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span><?= htmlspecialchars($func) ?>()</span>
|
||||
<span class="badge bg-<?= $value ? 'success' : 'danger' ?>">
|
||||
<?= $value ? 'Available' : 'Missing' ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($checkResults['files'] ?? [] as $file => $exists): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span><?= htmlspecialchars(ucfirst($file)) ?></span>
|
||||
<span class="badge bg-<?= $exists ? 'success' : 'danger' ?>">
|
||||
<?= $exists ? 'Exists' : 'Missing' ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title mb-0">Plugin Information</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="small">
|
||||
<div class="mb-1"><strong>Name:</strong> <?= htmlspecialchars($plugin['name']) ?></div>
|
||||
<div class="mb-1"><strong>Version:</strong> <?= htmlspecialchars($plugin['version'] ?? 'N/A') ?></div>
|
||||
<div class="mb-1"><strong>Enabled:</strong> <span class="badge bg-<?= $plugin['enabled'] ? 'success' : 'secondary' ?>"><?= $plugin['enabled'] ? 'Yes' : 'No' ?></span></div>
|
||||
<div class="mb-1"><strong>Dependencies:</strong> <?= !empty($plugin['dependencies']) ? htmlspecialchars(implode(', ', $plugin['dependencies'])) : 'None' ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (isset($checkResults['error'])): ?>
|
||||
<div class="alert alert-danger mt-3">
|
||||
|
|
@ -828,20 +817,22 @@ endif; ?>
|
|||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_install">
|
||||
<button type="submit" class="btn btn-primary">Install Tables</button>
|
||||
</form>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_purge">
|
||||
<button type="submit" class="btn btn-warning" onclick="return confirm('Are you sure? This will permanently delete all plugin data and tables!')">Purge Plugin</button>
|
||||
</form>
|
||||
<?php if ($plugin['has_migration']): ?>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_install">
|
||||
<button type="submit" class="btn btn-primary">Install plugin DB tables</button>
|
||||
</form>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||
<input type="hidden" name="section" value="plugins">
|
||||
<input type="hidden" name="plugin" value="<?= htmlspecialchars($plugin['slug']) ?>">
|
||||
<input type="hidden" name="action" value="plugin_purge">
|
||||
<button type="submit" class="btn btn-warning" onclick="return confirm('Are you sure? This will permanently delete all plugin data and tables!')">Purge all plugin data</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ require_once PLUGIN_REGISTER_PATH . 'models/register.php';
|
|||
|
||||
class RegisterController
|
||||
{
|
||||
/** @var \Database|\PDO|null */
|
||||
private $db;
|
||||
private array $config;
|
||||
private string $appRoot;
|
||||
|
|
|
|||
Loading…
Reference in New Issue