Upload via GUI (267 Dateien)
This commit is contained in:
@@ -127,6 +127,7 @@ function plugin_filter_permission(string $hook): ?string
|
||||
{
|
||||
if ($hook === 'ticket.view.sidebar') return 'ui.sidebar';
|
||||
if (str_starts_with($hook, 'ticket.')) return 'hooks.tickets';
|
||||
if ($hook === 'backup.list.rows') return 'ui.menu'; // wer einen Reiter unter „Backup“ anbieten darf, darf auch die Liste der Backups ergänzen
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -146,14 +147,18 @@ function plugin_add_filter(string $hook, callable $fn, int $priority = 10): void
|
||||
$state['filters'][$hook][$priority][] = ['fn' => $fn, 'plugin' => $state['current']];
|
||||
}
|
||||
|
||||
/** Menüpunkt im Mitarbeiterbereich. $page ist der Dateiname unter plugins/<slug>/pages/ (ohne .php). */
|
||||
function plugin_register_menu(string $label, string $page, ?string $permission = null): void
|
||||
/**
|
||||
* Menüpunkt im Mitarbeiterbereich. $page ist der Dateiname unter plugins/<slug>/pages/ (ohne .php).
|
||||
* Mit $belongsTo (zum Beispiel 'backup', siehe plugin_parent_pages()) erscheint kein eigener Eintrag in der Seitenleiste: Die Seite wird
|
||||
* stattdessen als Reiter innerhalb dieser Seite angeboten (zum Beispiel unter „Backup“). Dafür ist keine zusätzliche Berechtigung nötig (ui.menu genügt).
|
||||
*/
|
||||
function plugin_register_menu(string $label, string $page, ?string $permission = null, ?string $belongsTo = null): void
|
||||
{
|
||||
if (!plugin_permission_allowed('ui.menu')) return;
|
||||
$state = &plugin_state();
|
||||
$slug = $state['current'];
|
||||
if ($slug === null || !preg_match('/^[a-z0-9_-]+$/i', $page)) return;
|
||||
$state['menus'][] = ['plugin' => $slug, 'label' => mb_substr($label, 0, 60), 'page' => $page, 'permission' => $permission];
|
||||
$state['menus'][] = ['plugin' => $slug, 'label' => mb_substr($label, 0, 60), 'page' => $page, 'permission' => $permission, 'belongs_to' => ($belongsTo !== null && isset(plugin_parent_pages()[$belongsTo])) ? $belongsTo : null];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,11 +311,45 @@ function plugin_menu_items(?array $staff): array
|
||||
$items = [];
|
||||
foreach (plugin_state()['menus'] as $m) {
|
||||
if ($m['permission'] !== null && (!$staff || !has_permission($staff, $m['permission']))) continue;
|
||||
if (!empty($m['belongs_to'])) continue; // erscheint als Reiter in der übergeordneten Seite, nicht in der Seitenleiste
|
||||
$items[] = $m + ['url' => '/staff/plugin.php?p=' . rawurlencode($m['plugin']) . '&page=' . rawurlencode($m['page'])];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/** Seiten, unter denen Plugins Reiter anbieten dürfen: Schlüssel => [Bezeichnung des ersten Reiters, Adresse]. */
|
||||
function plugin_parent_pages(): array
|
||||
{
|
||||
return ['backup' => ['Backups', '/staff/backup.php']];
|
||||
}
|
||||
|
||||
/** Plugin-Seiten (mit Rechteprüfung), die als Reiter zu einer übergeordneten Seite gehören. */
|
||||
function plugin_subpages(string $parent, ?array $staff): array
|
||||
{
|
||||
$items = [];
|
||||
foreach (plugin_state()['menus'] as $m) {
|
||||
if (($m['belongs_to'] ?? null) !== $parent) continue;
|
||||
if ($m['permission'] !== null && (!$staff || !has_permission($staff, $m['permission']))) continue;
|
||||
$items[] = $m + ['url' => '/staff/plugin.php?p=' . rawurlencode($m['plugin']) . '&page=' . rawurlencode($m['page'])];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/** Reiterleiste einer übergeordneten Seite (leer, wenn kein Plugin Reiter anbietet). $active: 'parent' oder '<plugin>:<seite>'. */
|
||||
function plugin_subpage_tabs(string $parent, ?array $staff, string $active = 'parent'): string
|
||||
{
|
||||
$parents = plugin_parent_pages();
|
||||
$subs = plugin_subpages($parent, $staff);
|
||||
if (!$subs || !isset($parents[$parent])) return '';
|
||||
$tab = function (string $label, string $url, bool $on): string {
|
||||
return '<a href="' . htmlspecialchars($url, ENT_QUOTES) . '" style="padding:8px 16px;border-radius:8px 8px 0 0;text-decoration:none;font-size:.9rem;'
|
||||
. ($on ? 'background:var(--card-bg,#fff);border:1px solid var(--border);border-bottom-color:var(--card-bg,#fff);font-weight:600;color:inherit;position:relative;top:1px' : 'color:var(--muted);border:1px solid transparent') . '">' . htmlspecialchars($label) . '</a>';
|
||||
};
|
||||
$html = '<div class="plugin-tabs" style="display:flex;gap:4px;flex-wrap:wrap;border-bottom:1px solid var(--border);margin:0 0 18px">' . $tab($parents[$parent][0], $parents[$parent][1], $active === 'parent');
|
||||
foreach ($subs as $s) $html .= $tab($s['label'], $s['url'], $active === $s['plugin'] . ':' . $s['page']);
|
||||
return $html . '</div>';
|
||||
}
|
||||
|
||||
// ─── Verwaltung (Installieren, Aktivieren, Entfernen) ────────────────────────
|
||||
|
||||
function plugins_installed(PDO $pdo): array
|
||||
@@ -785,10 +824,13 @@ function plugin_run_migrations(PDO $pdo, string $slug): array
|
||||
if (in_array($key, $done, true)) continue;
|
||||
$sql = preg_replace('/^\s*--.*$/m', '', (string) file_get_contents($file));
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
// SQLite kann Tabellen-Änderungen in einer Transaktion zurückrollen. MySQL beendet bei CREATE/ALTER TABLE jede offene
|
||||
// Transaktion von selbst ("There is no active transaction"), dort läuft die Datei deshalb ohne Transaktion.
|
||||
$useTransaction = $driver === 'sqlite';
|
||||
if ($useTransaction) $pdo->beginTransaction();
|
||||
foreach (array_filter(array_map('trim', explode(';', $sql))) as $stmt) $pdo->exec($stmt);
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute([$key]);
|
||||
$pdo->commit();
|
||||
if ($useTransaction && $pdo->inTransaction()) $pdo->commit();
|
||||
$applied++;
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
|
||||
Reference in New Issue
Block a user