diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 5f3e6ba8..e18add25 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,2 +1,158 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +# Pinakes — CodeRabbit Configuration +# PHP/Slim 4 library management system with MySQL + +language: "it-IT" + +tone_instructions: | + Sii conciso e diretto. Concentrati su bug reali, vulnerabilità di sicurezza + e violazioni delle regole del progetto. Evita suggerimenti stilistici minori. + +early_access: true + reviews: - max_files: 200 + profile: "assertive" + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: true + + # ── File Filters ────────────────────────────────────────────────── + path_filters: + exclude: + - "vendor/**" + - "node_modules/**" + - "public/assets/tinymce/**" + - "public/assets/fontawesome/**" + - "public/assets/choices/**" + - "public/assets/flatpickr/**" + - "public/assets/sweetalert2/**" + - "*.min.js" + - "*.min.css" + - "*.map" + - "pinakes-*.zip" + - "pinakes-*.sha256" + - "test-results/**" + + # ── Path-Specific Review Instructions ────────────────────────────── + path_instructions: + # Controllers — input validation, auth, soft-delete + - path: "app/Controllers/**" + instructions: | + - CRITICO: ogni query sulla tabella `libri` DEVE avere `AND deleted_at IS NULL` + - Verifica che `getParsedBody()` non sia usato per JSON — serve `json_decode((string)$request->getBody())` + - Input utente: validare e sanitizzare PRIMA dell'uso + - Sessione: `$_SESSION['user']['id']` (NON `$_SESSION['user_id']`) + - Eccezioni: catturare `\Throwable` non `\Exception` (strict_types TypeError extends \Error) + - Logging: `SecureLogger::error()` non `error_log()` per contesti sensibili + - Route: mai hardcodare percorsi URL, usare `route_path('key')` o `RouteTranslator::route('key')` + - Export CSV: tipo_media deve essere incluso, usare stringa vuota come fallback (non 'libro') + + # Models / Repository — query safety + - path: "app/Models/**" + instructions: | + - CRITICO: ogni SELECT/UPDATE/DELETE sulla tabella `libri` DEVE avere `AND deleted_at IS NULL` + - Soft-delete: nullificare isbn10, isbn13, ean quando si fa soft-delete (prevent unique constraint violations) + - Transaction safety: mai annidare `begin_transaction()` in mysqli (causa commit implicito) + - Pattern: verificare `@@autocommit` per rilevare transazioni in corso + - hasColumn() guard per colonne aggiunte in migrazioni recenti (backward compat) + - tipo_media: usare `array_key_exists` guard, non sovrascrivere il valore se non esplicitamente fornito + + # Views — escaping, XSS prevention + - path: "app/Views/**" + instructions: | + - CRITICO: `htmlspecialchars(url(...), ENT_QUOTES, 'UTF-8')` in TUTTI gli attributi HTML (href, action, src) + - `route_path()` richiede lo stesso escaping negli attributi HTML + - PHP->JS: usare `json_encode(..., JSON_HEX_TAG)` per qualsiasi dato PHP inserito in JavaScript + - TinyMCE: SEMPRE includere `model: 'dom'` e `license_key: 'gpl'` in ogni `tinymce.init({})` + - Mai usare `HtmlHelper::e()` nelle view — usare `htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` + - Schema.org: ogni tipo_media deve avere il proprio branch con proprietà specifiche (non mescolare Book con CreativeWork) + - DataTable: ogni valore da API deve passare per `escapeHtml()` prima del rendering + + # Support classes — helpers, utilities + - path: "app/Support/**" + instructions: | + - MediaLabels: `isMusic()` deve essere autoritativo su tipo_media quando impostato + - `inferTipoMedia()`: attenzione ai false positive su token corti ('cd' matcha 'CD-ROM', 'lp' matcha parole con 'lp') + - `formatTracklist()`: deve rilevare HTML pre-formattato (`
    `) e restituirlo as-is + - PluginManager: usare `\Throwable` non `\Exception`, `BundledPlugins::LIST` centralizzato + - Route translation: mai hardcodare percorsi, usare `RouteTranslator::route('key')` + + # Plugins — API safety, rate limiting + - path: "storage/plugins/**" + instructions: | + - SICUREZZA: ogni chiamata curl DEVE avere CURLOPT_PROTOCOLS (HTTP/HTTPS only), CURLOPT_MAXREDIRS, CURLOPT_CONNECTTIMEOUT, CURLOPT_SSL_VERIFYPEER + - SSRF: validare/castare ID esterni (es. releaseId a int) prima di usarli in URL + - Rate limiting: deve essere elapsed-based (microtime) e static (persistere tra istanze) + - Ogni `curl_exec()` deve avere `curl_error()` check con logging + - Hook registration: transazione + rethrow on failure + - Non enrichire dati di libri con cover musicali (gate su resolveTipoMedia) + + # Migrations — versioning, idempotency + - path: "installer/database/migrations/**" + instructions: | + - CRITICO: il nome del file di migrazione DEVE avere versione <= version.json (altrimenti viene silenziosamente saltata) + - L'updater usa `version_compare($migrationVersion, $toVersion, '<=')` — versioni superiori sono IGNORATE + - Ogni migrazione DEVE essere completamente idempotente (IF NOT EXISTS, IF @col_exists = 0, etc.) + - LIKE patterns: evitare `%cd%` e `%lp%` che matchano false positive ('CD-ROM', parole con 'lp') — usare REGEXP word boundaries + - Se servono più migrazioni per una release: unirle in UN file con la versione della release + + # Translations — completeness + - path: "locale/**" + instructions: | + - Ogni chiave presente in it_IT.json DEVE essere presente anche in en_US.json e de_DE.json + - Le chiavi di traduzione devono corrispondere esattamente (case-sensitive) + - I placeholder (%s, %d) devono essere preservati in tutte le lingue + - Nuove chiavi aggiunte nel codice PHP/JS devono essere aggiunte in TUTTE le lingue + + # Tests — E2E patterns + - path: "tests/**" + instructions: | + - I test E2E richiedono `/tmp/run-e2e.sh` per credenziali DB/admin + - `--workers=1` obbligatorio per esecuzione seriale + - SweetAlert: dopo form submit, verificare e cliccare `.swal2-confirm` + - Choices.js: usare `fill` + `waitForTimeout` + click suggestion + - Flatpickr: interagire via JS evaluate, non click diretto + - Pulizia dati test: FK-safe order (prima tabelle figlie, poi padri) + + # Release scripts + - path: "scripts/**" + instructions: | + - MAI creare ZIP manualmente — SEMPRE usare `create-release.sh` + - Lo script verifica 9 file critici nel ZIP prima del rilascio + - `git archive` usa file COMMITTATI, non la working directory + - Verificare che `public/assets/tinymce/models/dom/model.min.js` sia nel ZIP + + # ── Auto Review Settings ─────────────────────────────────────────── + auto_review: + enabled: true + drafts: false + + # ── Tools ────────────────────────────────────────────────────────── + tools: + phpstan: + enabled: true + config_file: "phpstan.neon" + shellcheck: + enabled: true + semgrep: + enabled: true + gitleaks: + enabled: true + yamllint: + enabled: true + +# ── Chat ────────────────────────────────────────────────────────────── +chat: + auto_reply: true + +# ── Knowledge Base ──────────────────────────────────────────────────── +knowledge_base: + opt_out: false + learnings: + scope: "local" + enabled: true + issues: + scope: "auto" + pull_requests: + scope: "auto" diff --git a/.gitignore b/.gitignore index 29892a41..f3bc4aba 100644 --- a/.gitignore +++ b/.gitignore @@ -138,6 +138,13 @@ storage/plugins/goodlib/* !storage/plugins/goodlib/*.md !storage/plugins/goodlib/views/ !storage/plugins/goodlib/views/*.php +!storage/plugins/discogs/ +storage/plugins/discogs/* +!storage/plugins/discogs/*.php +!storage/plugins/discogs/*.json +!storage/plugins/discogs/*.md +!storage/plugins/discogs/views/ +!storage/plugins/discogs/views/*.php # Premium plugin - never track (private/commercial) storage/plugins/scraping-pro/ @@ -204,6 +211,7 @@ desktop.ini # Test Artifacts # ======================================== .playwright-mcp/ +test-results/ # ======================================== # Development Documentation (not for distribution) diff --git a/app/Controllers/CsvImportController.php b/app/Controllers/CsvImportController.php index 4b3625f1..89b221a9 100644 --- a/app/Controllers/CsvImportController.php +++ b/app/Controllers/CsvImportController.php @@ -17,6 +17,28 @@ class CsvImportController */ private const CHUNK_SIZE = 10; + /** @var bool|null Cached result of tipo_media column existence check */ + private ?bool $cachedHasTipoMedia = null; + + /** + * Check if tipo_media column exists (cached per controller instance). + */ + private function hasTipoMediaColumn(\mysqli $db): bool + { + if ($this->cachedHasTipoMedia === null) { + try { + $checkCol = $db->query("SHOW COLUMNS FROM libri LIKE 'tipo_media'"); + $this->cachedHasTipoMedia = $checkCol !== false && $checkCol->num_rows > 0; + if ($checkCol instanceof \mysqli_result) { + $checkCol->free(); + } + } catch (\Throwable $e) { + $this->cachedHasTipoMedia = false; + } + } + return $this->cachedHasTipoMedia; + } + /** * Write log message to import log file */ @@ -770,6 +792,9 @@ private function parseCsvRow(array $row): array 'genere' => !empty($row['genere']) ? trim($row['genere']) : null, 'descrizione' => !empty($row['descrizione']) ? trim($row['descrizione']) : null, 'formato' => !empty($row['formato']) ? trim($row['formato']) : 'cartaceo', + 'tipo_media' => array_key_exists('tipo_media', $row) && trim((string) ($row['tipo_media'] ?? '')) !== '' + ? \App\Support\MediaLabels::normalizeTipoMedia(trim((string) $row['tipo_media'])) + : null, 'prezzo' => $this->validatePrice($row['prezzo'] ?? ''), 'copie_totali' => !empty($row['copie_totali']) ? (int)$row['copie_totali'] : 1, 'collana' => !empty($row['collana']) ? trim($row['collana']) : null, @@ -1022,6 +1047,7 @@ private function mapColumnHeaders(array $headers): array 'genere' => ['genere', 'genre', 'género', 'category', 'categoria'], 'descrizione' => ['descrizione', 'description', 'descripción', 'summary', 'riassunto', 'abstract'], 'formato' => ['formato', 'format', 'media', 'binding', 'physical description'], + 'tipo_media' => ['tipo_media', 'media_type', 'type', 'medientyp'], 'prezzo' => ['prezzo', 'price', 'precio', 'prix', 'preis', 'list price', 'purchase price'], 'copie_totali' => ['copie_totali', 'copie', 'copies', 'quantity', 'quantità', 'cantidad'], 'collana' => ['collana', 'series', 'collection', 'collections', 'colección', 'reihe'], @@ -1224,6 +1250,9 @@ private function findExistingBook(\mysqli $db, array $data): ?int */ private function updateBook(\mysqli $db, int $bookId, array $data, ?int $editorId, ?int $genreId): void { + $hasTipoMedia = $this->hasTipoMediaColumn($db); + $tipoMediaSet = $hasTipoMedia ? ', tipo_media = COALESCE(?, tipo_media)' : ''; + $stmt = $db->prepare(" UPDATE libri SET isbn10 = ?, @@ -1237,7 +1266,7 @@ private function updateBook(\mysqli $db, int $bookId, array $data, ?int $editorI numero_pagine = ?, genere_id = ?, descrizione = ?, - formato = ?, + formato = ?{$tipoMediaSet}, prezzo = ?, editore_id = ?, collana = ?, @@ -1247,7 +1276,7 @@ private function updateBook(\mysqli $db, int $bookId, array $data, ?int $editorI parole_chiave = ?, classificazione_dewey = ?, updated_at = NOW() - WHERE id = ? + WHERE id = ? AND deleted_at IS NULL "); $isbn10 = !empty($data['isbn10']) ? $data['isbn10'] : null; @@ -1269,30 +1298,30 @@ classificazione_dewey = ?, $paroleChiave = !empty($data['parole_chiave'] ?? null) ? $data['parole_chiave'] : null; $dewey = !empty($data['classificazione_dewey'] ?? null) ? $data['classificazione_dewey'] : null; - $stmt->bind_param( - 'sssssissiissdissssssi', - $isbn10, - $isbn13, - $ean, - $titolo, - $sottotitolo, - $anno, - $lingua, - $edizione, - $pagine, - $genreId, - $descrizione, - $formato, - $prezzo, - $editorId, - $collana, - $numeroSerie, - $traduttore, - $illustratore, - $paroleChiave, - $dewey, - $bookId - ); + $params = [ + $isbn10, $isbn13, $ean, $titolo, $sottotitolo, + $anno, $lingua, $edizione, $pagine, $genreId, + $descrizione, $formato, + ]; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::normalizeTipoMedia($data['tipo_media'] ?? null); + } + $params = array_merge($params, [ + $prezzo, $editorId, $collana, $numeroSerie, + $traduttore, $illustratore, $paroleChiave, $dewey, $bookId, + ]); + + $types = ''; + foreach ($params as $p) { + if (is_int($p)) { + $types .= 'i'; + } elseif (is_float($p)) { + $types .= 'd'; + } else { + $types .= 's'; + } + } + $stmt->bind_param($types, ...$params); $stmt->execute(); $stmt->close(); @@ -1323,17 +1352,21 @@ private function upsertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr */ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genreId): int { + $hasTipoMedia = $this->hasTipoMediaColumn($db); + $tipoMediaCol = $hasTipoMedia ? ', tipo_media' : ''; + $tipoMediaVal = $hasTipoMedia ? ', ?' : ''; + $stmt = $db->prepare(" INSERT INTO libri ( isbn10, isbn13, ean, titolo, sottotitolo, anno_pubblicazione, lingua, edizione, numero_pagine, genere_id, - descrizione, formato, prezzo, copie_totali, copie_disponibili, + descrizione, formato{$tipoMediaCol}, prezzo, copie_totali, copie_disponibili, editore_id, collana, numero_serie, traduttore, illustratore, parole_chiave, classificazione_dewey, stato, created_at ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, + ?, ?{$tipoMediaVal}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'disponibile', NOW() ) @@ -1365,31 +1398,31 @@ classificazione_dewey, stato, created_at $paroleChiave = !empty($data['parole_chiave'] ?? null) ? $data['parole_chiave'] : null; $dewey = !empty($data['classificazione_dewey'] ?? null) ? $data['classificazione_dewey'] : null; - $stmt->bind_param( - 'sssssissiissdiiissssss', - $isbn10, - $isbn13, - $ean, - $titolo, - $sottotitolo, - $anno, - $lingua, - $edizione, - $pagine, - $genreId, - $descrizione, - $formato, - $prezzo, - $copie, - $copie, - $editorId, - $collana, - $numeroSerie, - $traduttore, - $illustratore, - $paroleChiave, - $dewey - ); + $params = [ + $isbn10, $isbn13, $ean, $titolo, $sottotitolo, $anno, + $lingua, $edizione, $pagine, $genreId, + $descrizione, $formato, + ]; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::resolveTipoMedia($formato, $data['tipo_media'] ?? null); + } + $params = array_merge($params, [ + $prezzo, $copie, $copie, + $editorId, $collana, $numeroSerie, $traduttore, $illustratore, $paroleChiave, + $dewey, + ]); + + $types = ''; + foreach ($params as $p) { + if (is_int($p)) { + $types .= 'i'; + } elseif (is_float($p)) { + $types .= 'd'; + } else { + $types .= 's'; + } + } + $stmt->bind_param($types, ...$params); $stmt->execute(); $bookId = $db->insert_id; @@ -1553,7 +1586,7 @@ private function enrichBookWithScrapedData(\mysqli $db, int $bookId, array $csvD // Update libro if we have data if (!empty($updates)) { - $sql = "UPDATE libri SET " . implode(', ', $updates) . " WHERE id = ?"; + $sql = "UPDATE libri SET " . implode(', ', $updates) . " WHERE id = ? AND deleted_at IS NULL"; $params[] = $bookId; $types .= 'i'; @@ -1594,7 +1627,7 @@ private function enrichBookWithScrapedData(\mysqli $db, int $bookId, array $csvD $publisherResult = $this->getOrCreatePublisher($db, $scrapedData['publisher']); $editorId = $publisherResult['id']; - $stmt = $db->prepare("UPDATE libri SET editore_id = ? WHERE id = ?"); + $stmt = $db->prepare("UPDATE libri SET editore_id = ? WHERE id = ? AND deleted_at IS NULL"); $stmt->bind_param('ii', $editorId, $bookId); $stmt->execute(); $stmt->close(); diff --git a/app/Controllers/FrontendController.php b/app/Controllers/FrontendController.php index ec2e9b7f..2da82cc5 100644 --- a/app/Controllers/FrontendController.php +++ b/app/Controllers/FrontendController.php @@ -734,6 +734,10 @@ private function getFilters(array $params): array { // Support both 'q' (header form) and 'search' (hero form) parameters $searchTerm = $params['q'] ?? $params['search'] ?? ''; + $rawTipoMedia = $params['tipo_media'] ?? ''; + if (is_array($rawTipoMedia)) { + $rawTipoMedia = $rawTipoMedia[0] ?? ''; + } return [ 'search' => $searchTerm, @@ -742,6 +746,7 @@ private function getFilters(array $params): array 'editore' => $params['editore'] ?? '', 'anno_min' => $params['anno_min'] ?? '', 'anno_max' => $params['anno_max'] ?? '', + 'tipo_media' => trim((string) $rawTipoMedia), 'sort' => $params['sort'] ?? 'newest' ]; } @@ -896,6 +901,12 @@ private function buildWhereConditions(array $filters, mysqli $db): array $types .= 'i'; } + if (!empty($filters['tipo_media']) && $this->hasLibriColumn($db, 'tipo_media')) { + $conditions[] = "l.tipo_media = ?"; + $params[] = $filters['tipo_media']; + $types .= 's'; + } + return [ 'conditions' => $conditions, 'params' => $params, @@ -903,6 +914,18 @@ private function buildWhereConditions(array $filters, mysqli $db): array ]; } + private function hasLibriColumn(mysqli $db, string $column): bool + { + static $columnCache = []; + + if (!array_key_exists($column, $columnCache)) { + $result = $db->query("SHOW COLUMNS FROM libri LIKE '" . $db->real_escape_string($column) . "'"); + $columnCache[$column] = $result !== false && $result->num_rows > 0; + } + + return $columnCache[$column]; + } + private function buildOrderBy(string $sort): string { switch ($sort) { diff --git a/app/Controllers/LibraryThingImportController.php b/app/Controllers/LibraryThingImportController.php index d760c8fc..792ef14d 100644 --- a/app/Controllers/LibraryThingImportController.php +++ b/app/Controllers/LibraryThingImportController.php @@ -35,6 +35,28 @@ class LibraryThingImportController /** @var bool|null Cached result of descrizione_plain column existence check */ private ?bool $cachedHasDescPlain = null; + /** @var bool|null Cached result of tipo_media column existence check */ + private ?bool $cachedHasTipoMedia = null; + + /** + * Check if tipo_media column exists (cached per controller instance). + */ + private function hasTipoMediaColumn(\mysqli $db): bool + { + if ($this->cachedHasTipoMedia === null) { + try { + $checkCol = $db->query("SHOW COLUMNS FROM libri LIKE 'tipo_media'"); + $this->cachedHasTipoMedia = $checkCol !== false && $checkCol->num_rows > 0; + if ($checkCol instanceof \mysqli_result) { + $checkCol->free(); + } + } catch (\Throwable $e) { + $this->cachedHasTipoMedia = false; + } + } + return $this->cachedHasTipoMedia; + } + /** * Check if descrizione_plain column exists (cached per controller instance). */ @@ -911,6 +933,10 @@ private function parseLibraryThingRow(array $data): array } } + // Infer tipo_media from Media field (null when empty to avoid overwriting) + $mediaRaw = trim((string) ($data['Media'] ?? '')); + $result['tipo_media'] = $mediaRaw !== '' ? \App\Support\MediaLabels::inferTipoMedia($mediaRaw) : null; + // Genre/Subjects $result['genere'] = !empty($data['Subjects']) ? trim(explode(',', $data['Subjects'])[0]) : ''; @@ -1276,13 +1302,17 @@ private function updateBook(\mysqli $db, int $bookId, array $data, ?int $editorI $hasDescPlain = $this->hasDescrizionePlainColumn($db); $descPlainSet = $hasDescPlain ? ', descrizione_plain = ?' : ''; + // Check if tipo_media column exists (cached per controller instance) + $hasTipoMedia = $this->hasTipoMediaColumn($db); + $tipoMediaSet = $hasTipoMedia ? ', tipo_media = COALESCE(?, tipo_media)' : ''; + if ($hasLTFields) { // Full update with all LibraryThing fields $stmt = $db->prepare(" UPDATE libri SET isbn10 = ?, isbn13 = ?, ean = ?, titolo = ?, sottotitolo = ?, anno_pubblicazione = ?, lingua = ?, edizione = ?, numero_pagine = ?, - genere_id = ?, descrizione = ?{$descPlainSet}, formato = ?, prezzo = ?, editore_id = ?, + genere_id = ?, descrizione = ?{$descPlainSet}, formato = ?{$tipoMediaSet}, prezzo = ?, editore_id = ?, collana = ?, numero_serie = ?, traduttore = ?, parole_chiave = ?, classificazione_dewey = ?, peso = ?, dimensioni = ?, data_acquisizione = ?, review = ?, rating = ?, comment = ?, private_comment = ?, @@ -1315,8 +1345,11 @@ classificazione_dewey = ?, peso = ?, dimensioni = ?, data_acquisizione = ?, if ($hasDescPlain) { $params[] = !empty($data['descrizione_plain']) ? $data['descrizione_plain'] : null; } + $params[] = !empty($data['formato']) ? $data['formato'] : 'cartaceo'; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::normalizeTipoMedia($data['tipo_media'] ?? null); + } $params = array_merge($params, [ - !empty($data['formato']) ? $data['formato'] : 'cartaceo', !empty($data['prezzo']) ? (float) str_replace(',', '.', $data['prezzo']) : null, $editorId, !empty($data['collana']) ? $data['collana'] : null, @@ -1363,7 +1396,7 @@ classificazione_dewey = ?, peso = ?, dimensioni = ?, data_acquisizione = ?, UPDATE libri SET isbn10 = ?, isbn13 = ?, ean = ?, titolo = ?, sottotitolo = ?, anno_pubblicazione = ?, lingua = ?, edizione = ?, numero_pagine = ?, - genere_id = ?, descrizione = ?{$descPlainSet}, formato = ?, prezzo = ?, editore_id = ?, + genere_id = ?, descrizione = ?{$descPlainSet}, formato = ?{$tipoMediaSet}, prezzo = ?, editore_id = ?, collana = ?, numero_serie = ?, traduttore = ?, parole_chiave = ?, classificazione_dewey = ?, updated_at = NOW() WHERE id = ? AND deleted_at IS NULL @@ -1387,8 +1420,11 @@ classificazione_dewey = ?, updated_at = NOW() if ($hasDescPlain) { $params[] = !empty($data['descrizione_plain']) ? $data['descrizione_plain'] : null; } + $params[] = !empty($data['formato']) ? $data['formato'] : 'cartaceo'; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::normalizeTipoMedia($data['tipo_media'] ?? null); + } $params = array_merge($params, [ - !empty($data['formato']) ? $data['formato'] : 'cartaceo', !empty($data['prezzo']) ? (float) str_replace(',', '.', $data['prezzo']) : null, $editorId, !empty($data['collana']) ? $data['collana'] : null, @@ -1434,6 +1470,11 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr $descPlainCol = $hasDescPlain ? ', descrizione_plain' : ''; $descPlainVal = $hasDescPlain ? ', ?' : ''; + // Check if tipo_media column exists (cached per controller instance) + $hasTipoMedia = $this->hasTipoMediaColumn($db); + $tipoMediaCol = $hasTipoMedia ? ', tipo_media' : ''; + $tipoMediaVal = $hasTipoMedia ? ', ?' : ''; + $copie = !empty($data['copie_totali']) ? (int) $data['copie_totali'] : 1; if ($copie < 1) { $copie = 1; @@ -1446,7 +1487,7 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr $stmt = $db->prepare(" INSERT INTO libri ( isbn10, isbn13, ean, titolo, sottotitolo, anno_pubblicazione, - lingua, edizione, numero_pagine, genere_id, descrizione{$descPlainCol}, formato, + lingua, edizione, numero_pagine, genere_id, descrizione{$descPlainCol}, formato{$tipoMediaCol}, prezzo, copie_totali, copie_disponibili, editore_id, collana, numero_serie, traduttore, parole_chiave, classificazione_dewey, peso, dimensioni, data_acquisizione, @@ -1460,7 +1501,7 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr value, condition_lt, entry_date, stato, created_at ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?{$descPlainVal}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?{$descPlainVal}, ?{$tipoMediaVal}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, @@ -1492,8 +1533,12 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr if ($hasDescPlain) { $params[] = !empty($data['descrizione_plain']) ? $data['descrizione_plain'] : null; } + $formato = !empty($data['formato']) ? $data['formato'] : 'cartaceo'; + $params[] = $formato; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::resolveTipoMedia($data['formato'] ?? null, $data['tipo_media'] ?? null); + } $params = array_merge($params, [ - !empty($data['formato']) ? $data['formato'] : 'cartaceo', !empty($data['prezzo']) ? (float) str_replace(',', '.', $data['prezzo']) : null, $copie, $copie, @@ -1540,12 +1585,12 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr $stmt = $db->prepare(" INSERT INTO libri ( isbn10, isbn13, ean, titolo, sottotitolo, anno_pubblicazione, - lingua, edizione, numero_pagine, genere_id, descrizione{$descPlainCol}, formato, + lingua, edizione, numero_pagine, genere_id, descrizione{$descPlainCol}, formato{$tipoMediaCol}, prezzo, copie_totali, copie_disponibili, editore_id, collana, numero_serie, traduttore, parole_chiave, classificazione_dewey, stato, created_at ) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?{$descPlainVal}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'disponibile', NOW() + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?{$descPlainVal}, ?{$tipoMediaVal}, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'disponibile', NOW() ) "); @@ -1567,8 +1612,12 @@ private function insertBook(\mysqli $db, array $data, ?int $editorId, ?int $genr if ($hasDescPlain) { $params[] = !empty($data['descrizione_plain']) ? $data['descrizione_plain'] : null; } + $formato = !empty($data['formato']) ? $data['formato'] : 'cartaceo'; + $params[] = $formato; + if ($hasTipoMedia) { + $params[] = \App\Support\MediaLabels::resolveTipoMedia($data['formato'] ?? null, $data['tipo_media'] ?? null); + } $params = array_merge($params, [ - !empty($data['formato']) ? $data['formato'] : 'cartaceo', !empty($data['prezzo']) ? (float) str_replace(',', '.', $data['prezzo']) : null, $copie, $copie, diff --git a/app/Controllers/LibriApiController.php b/app/Controllers/LibriApiController.php index e15a1c65..19793ff3 100644 --- a/app/Controllers/LibriApiController.php +++ b/app/Controllers/LibriApiController.php @@ -32,6 +32,7 @@ public function list(Request $request, Response $response, mysqli $db): Response $anno_from = trim((string) ($q['anno_from'] ?? '')); $anno_to = trim((string) ($q['anno_to'] ?? '')); $collana = trim((string) ($q['collana'] ?? '')); + $tipo_media = trim((string) ($q['tipo_media'] ?? '')); // Build WHERE clause with prepared statement parameters $where = 'WHERE l.deleted_at IS NULL '; @@ -137,19 +138,24 @@ public function list(Request $request, Response $response, mysqli $db): Response $params[] = '%' . $collana . '%'; $types .= 's'; } + if ($tipo_media !== '' && $this->hasColumn($db, 'tipo_media')) { + $where .= " AND l.tipo_media = ?"; + $params[] = $tipo_media; + $types .= 's'; + } // Parse DataTables sorting parameters (with robust null checks to avoid notices) $order = $q['order'][0] ?? null; - $orderColumn = isset($order['column']) ? (int) $order['column'] : 3; // Default to Info column (title) + $orderColumn = isset($order['column']) ? (int) $order['column'] : 4; // Default to Info column (title) $orderDir = (isset($order['dir']) && strtoupper(trim($order['dir'])) === 'DESC') ? 'DESC' : 'ASC'; // Map column indices to database fields - // Columns: 0=checkbox, 1=status, 2=cover, 3=info(title), 4=genre, 5=position, 6=year, 7=actions + // Columns: 0=checkbox, 1=status, 2=media, 3=cover, 4=info(title), 5=genre, 6=position, 7=year, 8=actions $orderByMap = [ - 3 => 'l.titolo', // Info column - sort by title - 4 => 'g.nome', // Genre column - 5 => 's.codice, m.numero_livello, COALESCE(l.posizione_progressiva, p.ordine)', // Position - 6 => 'l.anno_pubblicazione', // Year column + 4 => 'l.titolo', // Info column - sort by title + 5 => 'g.nome', // Genre column + 6 => 's.codice, m.numero_livello, COALESCE(l.posizione_progressiva, p.ordine)', // Position + 7 => 'l.anno_pubblicazione', // Year column ]; $orderByClause = $orderByMap[$orderColumn] ?? 'l.titolo'; diff --git a/app/Controllers/LibriController.php b/app/Controllers/LibriController.php index 59d0de22..a6927aca 100644 --- a/app/Controllers/LibriController.php +++ b/app/Controllers/LibriController.php @@ -676,6 +676,7 @@ public function store(Request $request, Response $response, mysqli $db): Respons 'illustratore' => '', 'curatore' => '', 'numero_pagine' => null, + 'tipo_media' => '', ]; // Merge LibraryThing fields defaults only if plugin installed @@ -1216,6 +1217,7 @@ public function update(Request $request, Response $response, mysqli $db, int $id 'illustratore' => '', 'curatore' => '', 'numero_pagine' => null, + 'tipo_media' => '', ]; // Merge LibraryThing fields defaults only if plugin installed @@ -2998,6 +3000,7 @@ public function exportCsv(Request $request, Response $response, mysqli $db): Res 'numero_pagine', 'genere', 'formato', + 'tipo_media', 'prezzo', 'copie_totali', 'collana', @@ -3035,6 +3038,7 @@ public function exportCsv(Request $request, Response $response, mysqli $db): Res $libro['numero_pagine'] ?? '', $libro['genere_nome'] ?? '', $libro['formato'] ?? '', + $libro['tipo_media'] ?? '', $libro['prezzo'] ?? '', $libro['copie_totali'] ?? '1', $libro['collana'] ?? '', diff --git a/app/Controllers/PluginController.php b/app/Controllers/PluginController.php index 98620e0c..e173ce33 100644 --- a/app/Controllers/PluginController.php +++ b/app/Controllers/PluginController.php @@ -53,6 +53,12 @@ public function index(Request $request, Response $response): Response $settings['api_key'] = $settings['api_key_exists'] ? '••••••••' : ''; } + // Redact Discogs token — never expose to template + if ($plugin['name'] === 'discogs' && array_key_exists('api_token', $settings)) { + $settings['api_token_exists'] = $settings['api_token'] !== ''; + $settings['api_token'] = ''; + } + $pluginSettings[$plugin['id']] = $settings; } @@ -214,6 +220,49 @@ public function uninstall(Request $request, Response $response, array $args): Re return $response->withHeader('Content-Type', 'application/json'); } + public function settingsPage(Request $request, Response $response, array $args): Response + { + if (!isset($_SESSION['user']) || $_SESSION['user']['tipo_utente'] !== 'admin') { + return $response->withStatus(403); + } + + $pluginId = (int) $args['id']; + $plugin = $this->pluginManager->getPlugin($pluginId); + if (!$plugin) { + return $response->withStatus(404); + } + + $pluginInstance = $this->pluginManager->getPluginInstance($pluginId); + if ($pluginInstance === null || !is_callable([$pluginInstance, 'hasSettingsPage']) || !$pluginInstance->hasSettingsPage()) { + return $response->withStatus(404); + } + + if (!is_callable([$pluginInstance, 'getSettingsViewPath'])) { + return $response->withStatus(404); + } + + $settingsViewPath = $pluginInstance->getSettingsViewPath(); + if (!is_string($settingsViewPath) || !is_file($settingsViewPath)) { + return $response->withStatus(404); + } + + if (!isset($GLOBALS['plugins'])) { + $GLOBALS['plugins'] = []; + } + $GLOBALS['plugins'][$plugin['name']] = $pluginInstance; + + ob_start(); + require $settingsViewPath; + $content = ob_get_clean(); + + ob_start(); + require __DIR__ . '/../Views/layout.php'; + $html = ob_get_clean(); + + $response->getBody()->write($html); + return $response; + } + /** * Update plugin settings (limited to supported plugins) */ @@ -391,6 +440,23 @@ public function updateSettings(Request $request, Response $response, array $args 'message' => __('Impostazioni GoodLib salvate correttamente.'), 'data' => $normalizedDomains, ])); + } elseif ($plugin['name'] === 'discogs') { + // Discogs: personal access token + $apiToken = trim((string) ($settings['api_token'] ?? '')); + + $saved = $this->pluginManager->setSetting($pluginId, 'api_token', $apiToken, true); + if (!$saved) { + $response->getBody()->write(json_encode(['success' => false, 'message' => __('Errore durante il salvataggio.')])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(500); + } + + $response->getBody()->write(json_encode([ + 'success' => true, + 'message' => __('Impostazioni Discogs salvate correttamente.'), + 'data' => [ + 'has_token' => $apiToken !== '' + ] + ])); } else { // Plugin not supported error_log('[PluginController] Plugin does not support settings: ' . $plugin['name']); diff --git a/app/Controllers/PublicApiController.php b/app/Controllers/PublicApiController.php index 40fa6114..f81308c1 100644 --- a/app/Controllers/PublicApiController.php +++ b/app/Controllers/PublicApiController.php @@ -114,6 +114,7 @@ private function findBooks(mysqli $db, ?string $ean, ?string $isbn13, ?string $i l.descrizione, l.parole_chiave, l.formato, + l.tipo_media, l.peso, l.dimensioni, l.prezzo, @@ -191,6 +192,7 @@ private function findBooks(mysqli $db, ?string $ean, ?string $isbn13, ?string $i 'descrizione' => $row['descrizione'], 'parole_chiave' => $row['parole_chiave'], 'formato' => $row['formato'], + 'tipo_media' => $row['tipo_media'] ?? 'libro', 'peso' => $row['peso'] !== null ? (float)$row['peso'] : null, 'dimensioni' => $row['dimensioni'], 'prezzo' => $row['prezzo'] !== null ? (float)$row['prezzo'] : null, diff --git a/app/Controllers/ScrapeController.php b/app/Controllers/ScrapeController.php index fa3431af..5261b777 100644 --- a/app/Controllers/ScrapeController.php +++ b/app/Controllers/ScrapeController.php @@ -145,6 +145,8 @@ public function byIsbn(Request $request, Response $response): Response $payload = $this->enrichWithSbnData($payload, $cleanIsbn); } + $payload = $this->ensureTipoMedia($payload); + // Normalize ISBN fields (auto-calculate missing isbn10/isbn13) $payload = $this->normalizeIsbnFields($payload, $cleanIsbn); @@ -213,6 +215,8 @@ public function byIsbn(Request $request, Response $response): Response $fallbackData = $this->enrichWithSbnData($fallbackData, $cleanIsbn); } + $fallbackData = $this->ensureTipoMedia($fallbackData); + // Normalize ISBN fields (auto-calculate missing isbn10/isbn13) $fallbackData = $this->normalizeIsbnFields($fallbackData, $cleanIsbn); @@ -763,6 +767,18 @@ private function enrichWithSbnData(array $data, string $originalIsbn): array */ private function normalizeIsbnFields(array $data, string $originalIsbn): array { + $resolvedTipoMedia = \App\Support\MediaLabels::resolveTipoMedia( + $data['format'] ?? $data['formato'] ?? null, + $data['tipo_media'] ?? null + ); + $data['tipo_media'] = $resolvedTipoMedia; + + // Skip ISBN auto-population for non-book media. + // The barcode is an EAN, not an ISBN — don't stuff it into isbn13/isbn10. + if ($resolvedTipoMedia !== 'libro') { + return $data; + } + // First, try to get variants from original search term $variants = IsbnFormatter::getAllVariants($originalIsbn); @@ -803,4 +819,14 @@ private function normalizeIsbnFields(array $data, string $originalIsbn): array return $data; } + + private function ensureTipoMedia(array $payload): array + { + $payload['tipo_media'] = \App\Support\MediaLabels::resolveTipoMedia( + $payload['format'] ?? $payload['formato'] ?? null, + $payload['tipo_media'] ?? null + ); + + return $payload; + } } diff --git a/app/Models/BookRepository.php b/app/Models/BookRepository.php index 22262b9b..dbcc5bac 100644 --- a/app/Models/BookRepository.php +++ b/app/Models/BookRepository.php @@ -300,6 +300,13 @@ public function createBasic(array $data): int if ($this->hasColumn('formato')) { $addField('formato', 's', $data['formato'] ?? null); } + if ($this->hasColumn('tipo_media')) { + $val = \App\Support\MediaLabels::resolveTipoMedia( + $data['formato'] ?? null, + $data['tipo_media'] ?? null + ); + $addField('tipo_media', 's', $this->normalizeEnumValue($val, 'tipo_media', 'libro')); + } if ($this->hasColumn('peso')) { $addField('peso', 'd', $peso); } @@ -639,6 +646,10 @@ public function updateBasic(int $id, array $data): bool if ($this->hasColumn('formato')) { $addSet('formato', 's', $data['formato'] ?? null); } + if ($this->hasColumn('tipo_media') && array_key_exists('tipo_media', $data)) { + $val = is_string($data['tipo_media']) ? $data['tipo_media'] : null; + $addSet('tipo_media', 's', $this->normalizeEnumValue($val, 'tipo_media', 'libro')); + } if ($this->hasColumn('peso')) { $addSet('peso', 'd', $peso); } @@ -989,7 +1000,7 @@ public function delete(int $id): bool public function updateOptionals(int $bookId, array $data): void { $cols = []; - foreach (['numero_pagine', 'ean', 'data_pubblicazione', 'anno_pubblicazione', 'traduttore', 'illustratore', 'curatore', 'collana', 'edizione'] as $c) { + foreach (['numero_pagine', 'ean', 'data_pubblicazione', 'anno_pubblicazione', 'traduttore', 'illustratore', 'curatore', 'collana', 'edizione', 'tipo_media', 'parole_chiave'] as $c) { if ($this->hasColumn($c) && array_key_exists($c, $data) && $data[$c] !== '' && $data[$c] !== null) { if ($c === 'numero_pagine') { $validated = filter_var($data[$c], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]); @@ -1001,6 +1012,8 @@ public function updateOptionals(int $bookId, array $data): void if ($validated !== false) { $cols[$c] = $validated; } + } elseif ($c === 'tipo_media') { + $cols[$c] = $this->normalizeEnumValue((string) $data[$c], 'tipo_media', 'libro'); } elseif (in_array($c, ['traduttore', 'illustratore', 'curatore'], true)) { $cols[$c] = \App\Support\AuthorNormalizer::normalize((string) $data[$c]); } else { @@ -1044,6 +1057,22 @@ public function updateOptionals(int $bookId, array $data): void if ($this->hasColumn('illustratore') && !isset($cols['illustratore']) && !empty($data['scraped_illustrator'])) { $cols['illustratore'] = \App\Support\AuthorNormalizer::normalize((string) $data['scraped_illustrator']); } + if ($this->hasColumn('tipo_media') && !array_key_exists('tipo_media', $cols)) { + $formato = trim((string) ($data['formato'] ?? ($data['scraped_formato'] ?? ''))); + $scrapedTipoMedia = trim((string) ($data['scraped_tipo_media'] ?? '')); + $hasMediaSignal = $formato !== '' || $scrapedTipoMedia !== ''; + + if ($hasMediaSignal) { + $val = \App\Support\MediaLabels::resolveTipoMedia( + $formato !== '' ? $formato : null, + $scrapedTipoMedia !== '' ? $scrapedTipoMedia : null + ); + $normalized = $this->normalizeEnumValue((string) $val, 'tipo_media', 'libro'); + if ($normalized !== '') { + $cols['tipo_media'] = $normalized; + } + } + } if (!$cols) return; $set = []; diff --git a/app/Routes/web.php b/app/Routes/web.php index df218415..76a4b113 100644 --- a/app/Routes/web.php +++ b/app/Routes/web.php @@ -2867,6 +2867,12 @@ return $controller->uninstall($request, $response, $args); })->add(new CsrfMiddleware())->add(new AdminAuthMiddleware()); + $app->get('/admin/plugins/{id}/settings', function ($request, $response, $args) use ($app) { + $pluginManager = $app->getContainer()->get('pluginManager'); + $controller = new \App\Controllers\PluginController($pluginManager); + return $controller->settingsPage($request, $response, $args); + })->add(new AdminAuthMiddleware()); + // Plugin settings update $app->post('/admin/plugins/{id}/settings', function ($request, $response, $args) use ($app) { $pluginManager = $app->getContainer()->get('pluginManager'); diff --git a/app/Support/BundledPlugins.php b/app/Support/BundledPlugins.php new file mode 100644 index 00000000..039bfe12 --- /dev/null +++ b/app/Support/BundledPlugins.php @@ -0,0 +1,21 @@ + + */ + private static function normalizedCandidates(?string $value): array + { + if ($value === null) { + return []; + } + + $trimmed = trim($value); + if ($trimmed === '') { + return []; + } + + $lower = strtolower($trimmed); + $underscore = preg_replace('/[\s-]+/u', '_', $lower) ?? $lower; + $collapsed = preg_replace('/[\s\-_]+/u', '', $lower) ?? $lower; + + return array_values(array_unique([$lower, $underscore, $collapsed])); + } + + /** + * Normalize explicit tipo_media or common aliases to the canonical enum. + */ + public static function normalizeTipoMedia(?string $tipoMedia): ?string + { + foreach (self::normalizedCandidates($tipoMedia) as $candidate) { + if (isset(self::allTypes()[$candidate])) { + return $candidate; + } + + if (in_array($candidate, ['book', 'books', 'paperback', 'hardcover', 'hardback', 'cartaceo', 'print', 'printed'], true)) { + return 'libro'; + } + + if (in_array($candidate, ['disc', 'record', 'album', 'cd', 'cdaudio', 'compactdisc', 'vinyl', 'vinile', 'lp', 'cassette', 'cassetta', 'audiocassetta'], true)) { + return 'disco'; + } + + if (in_array($candidate, ['audiobook', 'audiobooks', 'audiolibro'], true)) { + return 'audiolibro'; + } + + if (in_array($candidate, ['dvd', 'bluray', 'blu_ray', 'movie', 'film'], true)) { + return 'dvd'; + } + + if (in_array($candidate, ['altro', 'other'], true)) { + return 'altro'; + } + } + + return null; + } + + /** + * Resolve the effective tipo_media, preferring an explicit valid value. + */ + public static function resolveTipoMedia(?string $formato, ?string $tipoMedia = null): string + { + $normalized = self::normalizeTipoMedia($tipoMedia); + if ($normalized !== null) { + return $normalized; + } + + return self::inferTipoMedia($formato); + } + + /** + * Check if a format string indicates music media. + */ + public static function isMusic(?string $formato, ?string $tipoMedia = null): bool + { + $normalizedTipoMedia = self::normalizeTipoMedia($tipoMedia); + if ($normalizedTipoMedia !== null) { + return $normalizedTipoMedia === 'disco'; + } + + return self::inferTipoMedia($formato) === 'disco'; + } + + /** + * Map of internal format keys to translatable display names. + */ + private static array $formatDisplayNames = [ + 'cartaceo' => 'Cartaceo', + 'ebook' => 'eBook', + 'audiolibro' => 'Audiolibro', + 'audiobook' => 'Audiolibro', + 'cd_audio' => 'CD Audio', + 'cd' => 'CD', + 'vinile' => 'Vinile', + 'vinyl' => 'Vinile', + 'lp' => 'LP', + 'cassetta' => 'Cassetta', + 'cassette' => 'Cassetta', + 'audiocassetta' => 'Audiocassetta', + 'dvd' => 'DVD', + 'blu-ray' => 'Blu-ray', + 'blu_ray' => 'Blu-ray', + 'digitale' => 'Digitale', + 'altro' => 'Altro', + ]; + + /** + * Get human-readable display name for a format value. + * Returns the translated display name, or the raw value titlecased if unknown. + */ + public static function formatDisplayName(?string $formato): string + { + if ($formato === null || $formato === '') { + return ''; + } + + foreach (self::normalizedCandidates($formato) as $candidate) { + if (isset(self::$formatDisplayNames[$candidate])) { + return __(self::$formatDisplayNames[$candidate]); + } + } + + // Return the original value with first letter uppercase + return ucfirst($formato); + } + + /** + * Format a tracklist string into an HTML ordered list. + * Detects "1. Track (3:45) 2. Track (2:30)" patterns and converts to
      . + */ + public static function formatTracklist(string $text): string + { + $text = trim($text); + if ($text === '') { + return ''; + } + + // If already formatted as HTML ordered list, return as-is + if (str_contains($text, '')) { + return $text; + } + + // Remove "Tracklist:" prefix if present + $text = preg_replace('/^Tracklist\s*:\s*/i', '', $text) ?? $text; + + // Try to split on numbered tracks: "1. Title (3:45)" pattern + $tracks = preg_split('/(?<=\))\s+(?=\d+\.\s)/', $text); + if ($tracks === false || count($tracks) < 2) { + // Fallback: split on "N. " pattern + $tracks = preg_split('/\s+(?=\d+\.\s)/', $text); + } + + if ($tracks === false || count($tracks) < 2) { + return nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'), false); + } + + $items = []; + foreach ($tracks as $track) { + $track = trim($track); + // Remove leading "N. " numbering + $track = preg_replace('/^\d+\.\s*/', '', $track) ?? $track; + if ($track !== '') { + $items[] = $track; + } + } + + if (empty($items)) { + return nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'), false); + } + + $html = '
        '; + foreach ($items as $item) { + $html .= '
      1. ' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '
      2. '; + } + $html .= '
      '; + return $html; + } + + /** + * All valid tipo_media values with their metadata. + * @return array + */ + public static function allTypes(): array + { + return [ + 'libro' => ['icon' => 'fa-book', 'schema' => 'Book', 'label' => 'Libro'], + 'disco' => ['icon' => 'fa-compact-disc', 'schema' => 'MusicAlbum', 'label' => 'Disco'], + 'audiolibro' => ['icon' => 'fa-headphones', 'schema' => 'Audiobook', 'label' => 'Audiolibro'], + 'dvd' => ['icon' => 'fa-film', 'schema' => 'Movie', 'label' => 'DVD'], + 'altro' => ['icon' => 'fa-box', 'schema' => 'CreativeWork', 'label' => 'Altro'], + ]; + } + + public static function icon(?string $tipoMedia): string + { + $types = self::allTypes(); + $resolved = self::normalizeTipoMedia($tipoMedia) ?? 'libro'; + return $types[$resolved]['icon'] ?? 'fa-book'; + } + + public static function schemaOrgType(?string $tipoMedia): string + { + $types = self::allTypes(); + $resolved = self::normalizeTipoMedia($tipoMedia) ?? 'libro'; + return $types[$resolved]['schema'] ?? 'Book'; + } + + public static function tipoMediaDisplayName(?string $tipoMedia): string + { + $types = self::allTypes(); + $resolved = self::normalizeTipoMedia($tipoMedia) ?? 'libro'; + $label = $types[$resolved]['label'] ?? 'Libro'; + return __($label); + } + + /** + * Infer tipo_media from formato field (for backward compat / migration). + */ + public static function inferTipoMedia(?string $formato): string + { + if ($formato === null || $formato === '') { + return 'libro'; + } + + $normalized = self::normalizeTipoMedia($formato); + if ($normalized !== null) { + return $normalized; + } + + foreach (self::normalizedCandidates($formato) as $candidate) { + // Check audiobook BEFORE music tokens to prevent "Audiobook CD" matching as disco + if (str_contains($candidate, 'audiolibro') || str_contains($candidate, 'audiobook')) { + return 'audiolibro'; + } + + // Long tokens: safe for substring match (unique enough) + foreach (['cdaudio', 'compactdisc', 'vinile', 'vinyl', 'cassetta', 'cassette', 'audiocassetta'] as $musicToken) { + if (str_contains($candidate, $musicToken)) { + return 'disco'; + } + } + // Short tokens: exact match only to avoid false positives + // ('cd' would match 'cdrom', 'lp' would match 'help') + if ($candidate === 'cd' || $candidate === 'lp') { + return 'disco'; + } + + if (str_contains($candidate, 'dvd') || str_contains($candidate, 'bluray') || str_contains($candidate, 'blu_ray')) { + return 'dvd'; + } + + if (str_contains($candidate, 'other') || str_contains($candidate, 'altro')) { + return 'altro'; + } + + if (str_contains($candidate, 'book') || str_contains($candidate, 'paperback') || str_contains($candidate, 'hardcover') || str_contains($candidate, 'hardback')) { + return 'libro'; + } + } + + foreach (self::normalizedCandidates($formato) as $candidate) { + if (preg_match('/\b(?:music|musik)\b/i', $candidate) === 1) { + return 'disco'; + } + } + + return 'libro'; + } + + /** + * Get the appropriate label for a field based on format. + * Returns the music label if format is music, otherwise the default. + */ + public static function label(string $field, ?string $formato = null, ?string $tipoMedia = null): string + { + $isMusic = self::isMusic($formato, $tipoMedia); + + return match ($field) { + 'autore', 'author' => $isMusic ? __('Artista') : __('Autore'), + 'autori', 'authors' => $isMusic ? __('Artisti') : __('Autori'), + 'editore', 'publisher' => $isMusic ? __('Etichetta') : __('Editore'), + 'anno_pubblicazione', 'year' => $isMusic ? __('Anno di Uscita') : __('Anno di Pubblicazione'), + 'numero_pagine', 'pages' => $isMusic ? __('Tracce') : __('Numero di Pagine'), + 'isbn13' => $isMusic ? __('Barcode') : 'ISBN-13', + 'ean' => $isMusic ? __('Barcode') : 'EAN', + 'descrizione', 'description' => $isMusic ? __('Tracklist') : __('Descrizione'), + 'collana', 'series' => $isMusic ? __('Discografia') : __('Collana'), + 'formato', 'format' => __('Formato'), + default => __($field), + }; + } +} diff --git a/app/Support/PluginManager.php b/app/Support/PluginManager.php index 22c2f659..880e26b7 100644 --- a/app/Support/PluginManager.php +++ b/app/Support/PluginManager.php @@ -38,19 +38,6 @@ public function __construct(mysqli $db, HookManager $hookManager) } } - /** - * Bundled plugins that ship with Pinakes - * These are auto-registered and activated if their folders exist - */ - private const BUNDLED_PLUGINS = [ - 'open-library', - 'z39-server', - 'api-book-scraper', - 'digital-library', - 'dewey-editor', - 'goodlib', - ]; - /** * Auto-register bundled plugins that exist on disk but not in database * This ensures bundled plugins survive updates even if DB entries were lost @@ -61,7 +48,7 @@ public function autoRegisterBundledPlugins(): int { $registered = 0; - foreach (self::BUNDLED_PLUGINS as $pluginName) { + foreach (BundledPlugins::LIST as $pluginName) { $pluginPath = $this->pluginsDir . '/' . $pluginName; $jsonPath = $pluginPath . '/plugin.json'; @@ -137,12 +124,16 @@ public function autoRegisterBundledPlugins(): int continue; } + // Optional plugins (e.g. network-backed scrapers) start inactive + $isOptional = !empty($pluginMeta['metadata']['optional']); + $isActiveValue = $isOptional ? 0 : 1; + // Insert into database $stmt = $this->db->prepare(" INSERT INTO plugins ( name, display_name, description, version, author, author_url, plugin_url, is_active, path, main_file, requires_php, requires_app, metadata, installed_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, NOW()) + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) "); $metadata = json_encode($pluginMeta['metadata'] ?? []); @@ -159,7 +150,7 @@ public function autoRegisterBundledPlugins(): int $requiresApp = $pluginMeta['requires_app'] ?? ''; $stmt->bind_param( - 'ssssssssssss', + 'ssssssssissss', $name, $displayName, $description, @@ -167,6 +158,7 @@ public function autoRegisterBundledPlugins(): int $author, $authorUrl, $pluginUrl, + $isActiveValue, $path, $mainFile, $requiresPhp, @@ -177,7 +169,8 @@ public function autoRegisterBundledPlugins(): int if ($stmt->execute()) { $pluginId = $this->db->insert_id; $registered++; - error_log("[PluginManager] Auto-registered bundled plugin: $pluginName (ID: $pluginId, active)"); + $activeLabel = $isOptional ? 'inactive (optional)' : 'active'; + error_log("[PluginManager] Auto-registered bundled plugin: $pluginName (ID: $pluginId, $activeLabel)"); // Run onInstall if exists try { @@ -192,11 +185,13 @@ public function autoRegisterBundledPlugins(): int error_log("[PluginManager] Note: onInstall failed for $pluginName: " . $e->getMessage()); } - // Register hooks for active plugin - try { - $this->runPluginMethod($pluginName, 'onActivate'); - } catch (\Throwable $e) { - error_log("[PluginManager] Note: onActivate failed for $pluginName: " . $e->getMessage()); + // Register hooks only for active (non-optional) plugins + if (!$isOptional) { + try { + $this->runPluginMethod($pluginName, 'onActivate'); + } catch (\Throwable $e) { + error_log("[PluginManager] Note: onActivate failed for $pluginName: " . $e->getMessage()); + } } } else { error_log("[PluginManager] Failed to auto-register $pluginName: " . $this->db->error); @@ -366,6 +361,24 @@ public function getPluginByName(string $name): ?array return $plugin ?: null; } + public function getPluginInstance(int $pluginId): ?object + { + $plugin = $this->getPlugin($pluginId); + if ($plugin === null) { + return null; + } + + try { + return $this->instantiatePlugin($plugin); + } catch (\Throwable $e) { + SecureLogger::warning("[PluginManager] Failed to instantiate plugin {$plugin['name']}", [ + 'plugin_id' => $pluginId, + 'error' => $e->getMessage(), + ]); + return null; + } + } + /** * Install plugin from uploaded ZIP file * @@ -1184,10 +1197,18 @@ public function loadActivePlugins(): void * @return void */ private function loadPlugin(array $plugin): void + { + $instance = $this->instantiatePlugin($plugin); + + // Load and register hooks for this plugin + $this->registerPluginHooks((int) $plugin['id'], $instance); + } + + private function instantiatePlugin(array $plugin): object { // Save plugin data to prefixed variables before require_once // This prevents plugin files from overwriting $plugin variable (which some do) - $_pluginId = (int)$plugin['id']; + $_pluginId = (int) $plugin['id']; $_pluginName = $plugin['name']; $_pluginPath = $this->pluginsDir . '/' . $plugin['path']; $_mainFile = $_pluginPath . '/' . $plugin['main_file']; @@ -1196,21 +1217,15 @@ private function loadPlugin(array $plugin): void throw new \Exception("Main file not found: {$_mainFile}"); } - // Load plugin main file - // NOTE: Plugin files may define variables that collide with local scope require_once $_mainFile; - // Get plugin class name $className = $this->getPluginClassName($_pluginName); - if (!class_exists($className)) { throw new \Exception("Plugin class not found: {$className}"); } - // Instantiate plugin $instance = new $className($this->db, $this->hookManager); - // Pass plugin ID to the instance when supported (needed for plugin settings) if (is_callable([$instance, 'setPluginId'])) { try { $instance->setPluginId($_pluginId); @@ -1221,8 +1236,7 @@ private function loadPlugin(array $plugin): void } } - // Load and register hooks for this plugin - $this->registerPluginHooks($_pluginId, $instance); + return $instance; } /** diff --git a/app/Support/RouteTranslator.php b/app/Support/RouteTranslator.php index 4a9dc4d4..d95587b8 100644 --- a/app/Support/RouteTranslator.php +++ b/app/Support/RouteTranslator.php @@ -46,6 +46,7 @@ class RouteTranslator 'catalog_legacy' => '/catalog.php', 'book' => '/book', 'book_legacy' => '/book-detail.php', + 'plugins' => '/admin/plugins', 'author' => '/author', 'publisher' => '/publisher', 'genre' => '/genre', diff --git a/app/Support/Updater.php b/app/Support/Updater.php index 061262f5..cfe62733 100644 --- a/app/Support/Updater.php +++ b/app/Support/Updater.php @@ -31,20 +31,6 @@ class Updater private string $tempPath; private string $githubToken = ''; - /** - * Bundled plugins that are updated during app updates. - * scraping-pro is NOT bundled — it's a premium add-on managed separately. - * @var array - */ - private const BUNDLED_PLUGINS = [ - 'api-book-scraper', - 'dewey-editor', - 'digital-library', - 'goodlib', - 'open-library', - 'z39-server', - ]; - /** @var array Files/directories to preserve during update */ private array $preservePaths = [ '.env', @@ -2217,7 +2203,7 @@ private function copyDirectoryRecursive(string $source, string $dest): void /** * Update bundled plugins from the release package. * copyDirectory() skips storage/plugins (preservePaths), so bundled plugins - * must be updated separately. Only plugins listed in BUNDLED_PLUGINS are + * must be updated separately. Only plugins listed in BundledPlugins::LIST are * updated — user-installed and premium plugins (scraping-pro) are untouched. */ private function updateBundledPlugins(string $sourcePath): void @@ -2238,22 +2224,114 @@ private function updateBundledPlugins(string $sourcePath): void } $updated = 0; - foreach (self::BUNDLED_PLUGINS as $pluginName) { - $sourcePluginPath = $sourcePluginsDir . '/' . $pluginName; + $targetPluginsDirReal = realpath($targetPluginsDir); + if ($targetPluginsDirReal === false) { + throw new Exception(__('Impossibile risolvere il percorso della directory plugins.')); + } + + foreach (BundledPlugins::LIST as $pluginName) { + $pluginSlug = $this->normalizeBundledPluginSlug($pluginName); + $sourcePluginPath = $sourcePluginsDir . '/' . $pluginSlug; if (!is_dir($sourcePluginPath)) { - $this->debugLog('DEBUG', 'Plugin bundled non presente nel pacchetto', ['plugin' => $pluginName]); + $this->debugLog('DEBUG', 'Plugin bundled non presente nel pacchetto', ['plugin' => $pluginSlug]); continue; } - $targetPluginPath = $targetPluginsDir . '/' . $pluginName; - $this->debugLog('INFO', 'Aggiornamento plugin bundled', ['plugin' => $pluginName]); - $this->copyDirectoryRecursive($sourcePluginPath, $targetPluginPath); + $targetPluginPath = $targetPluginsDirReal . '/' . $pluginSlug; + $stagingPath = $targetPluginsDirReal . '/.' . $pluginSlug . '.tmp-' . bin2hex(random_bytes(4)); + $backupPath = $targetPluginsDirReal . '/.' . $pluginSlug . '.bak-' . bin2hex(random_bytes(4)); + + $this->debugLog('INFO', 'Aggiornamento plugin bundled', ['plugin' => $pluginSlug]); + + try { + $this->copyDirectoryRecursive($sourcePluginPath, $stagingPath); + + if (is_dir($targetPluginPath) && !rename($targetPluginPath, $backupPath)) { + $this->removeDirectoryTree($stagingPath); + throw new Exception(sprintf(__('Impossibile creare il backup del plugin: %s'), $pluginSlug)); + } + + if (!rename($stagingPath, $targetPluginPath)) { + if (is_dir($backupPath) && !rename($backupPath, $targetPluginPath)) { + throw new Exception(sprintf(__('Impossibile ripristinare il plugin precedente: %s'), $pluginSlug)); + } + throw new Exception(sprintf(__('Impossibile attivare la nuova versione del plugin: %s'), $pluginSlug)); + } + + if (is_dir($backupPath)) { + try { + $this->removeDirectoryTree($backupPath); + } catch (\Throwable $cleanupError) { + $this->debugLog('WARNING', 'Impossibile rimuovere backup plugin', [ + 'plugin' => $pluginSlug, + 'backup' => $backupPath, + 'error' => $cleanupError->getMessage(), + ]); + } + } + } catch (\Throwable $e) { + if (is_dir($stagingPath)) { + $this->removeDirectoryTree($stagingPath); + } + if (is_dir($backupPath) && !is_dir($targetPluginPath)) { + if (!rename($backupPath, $targetPluginPath)) { + $this->debugLog('ERROR', 'Impossibile ripristinare il plugin dal backup', [ + 'plugin' => $pluginSlug, + 'backup' => $backupPath, + 'target' => $targetPluginPath, + ]); + } + } + throw $e; + } + $updated++; } $this->debugLog('INFO', 'Plugin bundled aggiornati', ['count' => $updated]); } + private function normalizeBundledPluginSlug(string $pluginName): string + { + $pluginSlug = trim($pluginName); + if ($pluginSlug === '' || preg_match('/^[a-z0-9][a-z0-9-]*$/', $pluginSlug) !== 1) { + throw new Exception(sprintf(__('Slug plugin bundled non valido: %s'), $pluginName)); + } + + return $pluginSlug; + } + + private function removeDirectoryTree(string $path): void + { + if (!file_exists($path)) { + return; + } + if (!is_dir($path)) { + throw new Exception(sprintf(__('Percorso plugin non valido: %s'), $path)); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($iterator as $item) { + if ($item->isDir()) { + if (!rmdir($item->getPathname())) { + throw new Exception(sprintf(__('Impossibile rimuovere directory: %s'), $item->getPathname())); + } + } else { + if (!unlink($item->getPathname())) { + throw new Exception(sprintf(__('Impossibile rimuovere file: %s'), $item->getPathname())); + } + } + } + + if (!rmdir($path)) { + throw new Exception(sprintf(__('Impossibile rimuovere directory: %s'), $path)); + } + } + /** * Clean up orphan files */ diff --git a/app/Views/admin/plugins.php b/app/Views/admin/plugins.php index 4326a050..238173b0 100644 --- a/app/Views/admin/plugins.php +++ b/app/Views/admin/plugins.php @@ -241,7 +241,7 @@ class="px-4 py-2 " data-plugin-name="" data-plugin-type="api-book-scraper" data-has-config="" - data-settings-url="" + data-settings-url="" data-api-endpoint="" data-timeout="" data-enabled="" onclick="openApiBookScraperModal(this)"> @@ -296,6 +296,13 @@ class="px-4 py-2 bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 tr + + + + + + + + + + +

      + + + + discogs.com/settings/developers + +

      +

      + + +

      + + + + + +
      + + + + + + + + + + +
      + + + + diff --git a/storage/plugins/discogs/wrapper.php b/storage/plugins/discogs/wrapper.php new file mode 100644 index 00000000..f8e7d9d2 --- /dev/null +++ b/storage/plugins/discogs/wrapper.php @@ -0,0 +1,105 @@ +instance = new \App\Plugins\Discogs\DiscogsPlugin($db, $hookManager); + } + + /** + * Activate the plugin + */ + public function activate(): void + { + if (method_exists($this->instance, 'activate')) { + $this->instance->activate(); + } + } + + /** + * Deactivate the plugin (called by PluginManager) + */ + public function onDeactivate(): void + { + if (method_exists($this->instance, 'onDeactivate')) { + $this->instance->onDeactivate(); + } + \App\Support\SecureLogger::debug('[Discogs] Plugin deactivated'); + } + + /** + * Called when plugin is installed (by PluginManager) + */ + public function onInstall(): void + { + if (method_exists($this->instance, 'onInstall')) { + $this->instance->onInstall(); + } + \App\Support\SecureLogger::debug('[Discogs] Plugin installed'); + } + + /** + * Called when plugin is activated (by PluginManager) + */ + public function onActivate(): void + { + if (method_exists($this->instance, 'onActivate')) { + $this->instance->onActivate(); + } elseif (method_exists($this->instance, 'activate')) { + $this->instance->activate(); + } + \App\Support\SecureLogger::debug('[Discogs] Plugin activated'); + } + + /** + * Called when plugin is uninstalled (by PluginManager) + */ + public function onUninstall(): void + { + if (method_exists($this->instance, 'onUninstall')) { + $this->instance->onUninstall(); + } + \App\Support\SecureLogger::debug('[Discogs] Plugin uninstalled'); + } + + /** + * Set the plugin ID (called by PluginManager after installation) + */ + public function setPluginId(int $pluginId): void + { + $this->instance->setPluginId($pluginId); + } + + /** + * Forward all method calls to the namespaced instance + */ + public function __call($method, $args) + { + if (method_exists($this->instance, $method)) { + return call_user_func_array([$this->instance, $method], $args); + } + + throw new \BadMethodCallException("Method {$method} does not exist"); + } + } +} diff --git a/tests/discogs-advanced.spec.js b/tests/discogs-advanced.spec.js new file mode 100644 index 00000000..1d787afa --- /dev/null +++ b/tests/discogs-advanced.spec.js @@ -0,0 +1,230 @@ +// @ts-check +/** + * Advanced Discogs tests: tipo_media filtering, CSV export, Schema.org, + * tracklist rendering, and edit persistence. + * Requires: app installed, admin user, Discogs plugin active, music records in DB. + */ +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; +const DB_USER = process.env.E2E_DB_USER || ''; +const DB_PASS = process.env.E2E_DB_PASS || ''; +const DB_NAME = process.env.E2E_DB_NAME || ''; +const DB_SOCKET = process.env.E2E_DB_SOCKET || ''; +const RUN_ID = Date.now(); +const SEEDED_MUSIC_EAN = `2${String(RUN_ID).slice(-12)}`; +const SEEDED_BOOK_ISBN = `978${String(RUN_ID).slice(-10)}`; + +function dbQuery(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-N', '-B', '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + return execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }).trim(); +} + +function dbExec(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }); +} + +test.describe.serial('Discogs Advanced Tests', () => { + /** @type {import('@playwright/test').Page} */ + let page; + /** @type {import('@playwright/test').BrowserContext} */ + let context; + let musicBookId = ''; + let bookBookId = ''; + + test.beforeAll(async ({ browser }) => { + test.skip(!ADMIN_EMAIL || !ADMIN_PASS || !DB_USER || !DB_PASS || !DB_NAME, 'Missing E2E env vars'); + context = await browser.newContext(); + page = await context.newPage(); + + // Login + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); + + // Seed a music record and a book for comparison + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, ean, copie_totali, copie_disponibili, descrizione, note_varie, created_at, updated_at) " + + "VALUES ('E2E_ADV_CD_" + RUN_ID + "', 'cd_audio', 'disco', '" + SEEDED_MUSIC_EAN + "', 1, 1, " + + "'Track One - Track Two', 'Cat: TEST-001', NOW(), NOW())" + ); + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, isbn13, copie_totali, copie_disponibili, descrizione, created_at, updated_at) " + + "VALUES ('E2E_ADV_Book_" + RUN_ID + "', 'cartaceo', 'libro', '" + SEEDED_BOOK_ISBN + "', 1, 1, " + + "'A test book description', NOW(), NOW())" + ); + + musicBookId = dbQuery(`SELECT id FROM libri WHERE titolo = 'E2E_ADV_CD_${RUN_ID}' AND deleted_at IS NULL LIMIT 1`); + bookBookId = dbQuery(`SELECT id FROM libri WHERE titolo = 'E2E_ADV_Book_${RUN_ID}' AND deleted_at IS NULL LIMIT 1`); + }); + + test.afterAll(async () => { + try { + dbExec( + `DELETE FROM libri + WHERE id IN (${Number(musicBookId) || 0}, ${Number(bookBookId) || 0}) + OR ean = '${SEEDED_MUSIC_EAN}' + OR isbn13 = '${SEEDED_BOOK_ISBN}'` + ); + } catch {} + await context?.close(); + }); + + // ═══════════════════════════════════════════════════════════════════ + // Test 1: tipo_media filter in admin book list + // ═══════════════════════════════════════════════════════════════════ + test('1. Admin list filters by tipo_media=disco', async () => { + // Fetch the DataTable API with tipo_media filter + const resp = await page.request.get(`${BASE}/api/libri?tipo_media=disco&start=0&length=100&search_text=E2E_ADV`); + expect(resp.status()).toBe(200); + const data = await resp.json(); + + // Should find the CD but not the book + const titles = (data.data || []).map((r) => r.titolo || r.info || ''); + const flatTitles = titles.join(' '); + + expect(flatTitles).toContain(`E2E_ADV_CD_${RUN_ID}`); + expect(flatTitles).not.toContain(`E2E_ADV_Book_${RUN_ID}`); + }); + + // ═══════════════════════════════════════════════════════════════════ + // Test 2: CSV export includes tipo_media column + // ═══════════════════════════════════════════════════════════════════ + test('2. CSV export includes tipo_media for music records', async () => { + const resp = await page.request.get(`${BASE}/admin/libri/export/csv?ids=${musicBookId}`); + expect(resp.status()).toBe(200); + const body = await resp.text(); + + // Parse header + const lines = body.split('\n'); + const header = lines[0].replace(/^\uFEFF/, ''); + expect(header).toContain('tipo_media'); + + // Parse the data row + const headerFields = header.split(';'); + const tipoMediaIdx = headerFields.indexOf('tipo_media'); + expect(tipoMediaIdx).toBeGreaterThan(-1); + + if (lines.length > 1 && lines[1].trim()) { + const dataFields = lines[1].split(';'); + expect(dataFields[tipoMediaIdx]).toBe('disco'); + } + }); + + // ═══════════════════════════════════════════════════════════════════ + // Test 3: Schema.org uses MusicAlbum for disco, Book for libro + // ═══════════════════════════════════════════════════════════════════ + test('3. Schema.org JSON-LD type is MusicAlbum for disco', async () => { + const musicResp = await page.request.get(`${BASE}/libro/${musicBookId}`); + expect(musicResp.status()).toBe(200); + const musicHtml = await musicResp.text(); + + const jsonLdBlocks = Array.from( + musicHtml.matchAll(/]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi), + (match) => match[1] + ); + const schemas = jsonLdBlocks.flatMap((block) => { + try { + const parsed = JSON.parse(block.trim()); + return Array.isArray(parsed) ? parsed : [parsed]; + } catch { + return []; + } + }); + + const musicSchema = schemas.find((schema) => schema && schema['@type'] === 'MusicAlbum'); + expect(musicSchema, 'Frontend JSON-LD is missing MusicAlbum for disco').toBeTruthy(); + + const tipoMedia = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${musicBookId}`); + expect(tipoMedia).toBe('disco'); + + const bookTipoMedia = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${bookBookId}`); + expect(bookTipoMedia).toBe('libro'); + }); + + // ═══════════════════════════════════════════════════════════════════ + // Test 4: Tracklist renders as
        in admin detail (music) + // vs prose

        for regular books + // ═══════════════════════════════════════════════════════════════════ + test('4. Admin detail: music shows tracklist, book shows prose description', async () => { + // Music book + await page.goto(`${BASE}/admin/libri/${musicBookId}`); + await page.waitForLoadState('domcontentloaded'); + const musicContent = await page.content(); + + // Should have tracklist HTML + expect(musicContent).toContain('Track One'); + expect(musicContent).toContain('Track Two'); + + // Should show music-specific labels + const hasEtichetta = musicContent.includes('Etichetta') || musicContent.includes('Label'); + const hasAnnoUscita = musicContent.includes('Anno di Uscita') || musicContent.includes('Release Year'); + expect(hasEtichetta || hasAnnoUscita).toBe(true); + + // Should have the media type badge + expect(musicContent).toContain('fa-compact-disc'); + + // Regular book + await page.goto(`${BASE}/admin/libri/${bookBookId}`); + await page.waitForLoadState('domcontentloaded'); + const bookContent = await page.content(); + + // Should have standard labels + const hasEditore = bookContent.includes('Editore') || bookContent.includes('Publisher'); + expect(hasEditore).toBe(true); + + // Should have book icon badge + expect(bookContent).toContain('fa-book'); + }); + + // ═══════════════════════════════════════════════════════════════════ + // Test 5: Edit a CD — tipo_media persists after save + // ═══════════════════════════════════════════════════════════════════ + test('5. Edit music record: tipo_media persists after save', async () => { + await page.goto(`${BASE}/admin/libri/modifica/${musicBookId}`); + await page.waitForLoadState('domcontentloaded'); + + // Verify tipo_media select shows "disco" (or equivalent) + const tipoSelect = page.locator('#tipo_media'); + if (await tipoSelect.isVisible({ timeout: 3000 }).catch(() => false)) { + const currentValue = await tipoSelect.inputValue(); + expect(currentValue).toBe('disco'); + + // Change the title slightly + const titleInput = page.locator('input[name="titolo"]'); + const currentTitle = await titleInput.inputValue(); + await titleInput.fill(currentTitle + ' (edited)'); + + // Submit + await page.locator('button[type="submit"]').first().click(); + const swalConfirm = page.locator('.swal2-confirm'); + if (await swalConfirm.isVisible({ timeout: 3000 }).catch(() => false)) { + await swalConfirm.click(); + } + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }).catch(() => {}); + + // Verify tipo_media was NOT overwritten to 'libro' + const tipoAfter = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${musicBookId}`); + expect(tipoAfter).toBe('disco'); + + // Verify title was updated + const titleAfter = dbQuery(`SELECT titolo FROM libri WHERE id = ${musicBookId}`); + expect(titleAfter).toContain('(edited)'); + + // Clean up title + dbExec(`UPDATE libri SET titolo = 'E2E_ADV_CD_${RUN_ID}' WHERE id = ${musicBookId}`); + } else { + // tipo_media column might not exist yet (pre-migration) + const tipoMedia = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${musicBookId}`); + expect(tipoMedia).toBe('disco'); + } + }); +}); diff --git a/tests/discogs-import.spec.js b/tests/discogs-import.spec.js new file mode 100644 index 00000000..5a5acd6c --- /dev/null +++ b/tests/discogs-import.spec.js @@ -0,0 +1,175 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; +const DB_USER = process.env.E2E_DB_USER || ''; +const DB_PASS = process.env.E2E_DB_PASS || ''; +const DB_NAME = process.env.E2E_DB_NAME || ''; +const DB_SOCKET = process.env.E2E_DB_SOCKET || ''; + +function dbQuery(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-N', '-B', '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + return execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }).trim(); +} + +function dbExec(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }); +} + +// Nirvana - Nevermind (very common CD, reliable on Discogs) +const TEST_BARCODE = '0720642442524'; + +test.describe.serial('Discogs Import: full scraping flow', () => { + /** @type {import('@playwright/test').Page} */ + let page; + /** @type {import('@playwright/test').BrowserContext} */ + let context; + let createdId = ''; + + test.beforeAll(async ({ browser }) => { + test.skip( + !ADMIN_EMAIL || !ADMIN_PASS || !DB_USER || !DB_PASS || !DB_NAME, + 'Missing E2E env vars' + ); + context = await browser.newContext(); + page = await context.newPage(); + + // Login + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); + }); + + test.afterAll(async () => { + // Cleanup test data + try { + if (createdId !== '') { + dbExec(`DELETE FROM libri WHERE id = ${Number(createdId)} AND deleted_at IS NULL`); + } + } catch {} + await context?.close(); + }); + + test('1. Verify Discogs plugin is active', async () => { + const isActive = dbQuery("SELECT COUNT(*) FROM plugins WHERE name = 'discogs' AND is_active = 1"); + if (parseInt(isActive) === 0) { + // Activate it + await page.goto(`${BASE}/admin/plugins`); + await page.waitForLoadState('domcontentloaded'); + // The plugin is bundled, so it should auto-register on page visit + await page.waitForTimeout(2000); + const isActiveNow = dbQuery("SELECT COUNT(*) FROM plugins WHERE name = 'discogs' AND is_active = 1"); + expect(parseInt(isActiveNow, 10), 'Discogs plugin could not be activated').toBeGreaterThan(0); + } + }); + + test('2. Import CD via barcode in book form', async () => { + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + // Find the ISBN import field and button + const importField = page.locator('#importIsbn'); + const importBtn = page.locator('#btnImportIsbn'); + + await expect(importBtn, 'Import button not visible — scraping flow unavailable').toBeVisible({ timeout: 5000 }); + + // Enter barcode and trigger import + await importField.fill(TEST_BARCODE); + await importBtn.click(); + + // Wait for scraping response (Discogs needs time + rate limits) + // The scraping service tries multiple sources — wait up to 20s + await page.waitForTimeout(8000); + + // Check if title was populated + const titleField = page.locator('input[name="titolo"]'); + const titleValue = await titleField.inputValue(); + + expect(titleValue.trim().length, 'Scraping did not return a title for the Discogs barcode').toBeGreaterThan(0); + + // Title should contain "Nevermind" (the album name) + expect(titleValue.toLowerCase()).toContain('nevermind'); + }); + + test('3. Verify scraped fields are populated', async () => { + // After successful scraping, check multiple fields + const titleValue = await page.locator('input[name="titolo"]').inputValue(); + expect(titleValue.trim().length, 'No scraped data available after Discogs import').toBeGreaterThan(0); + + // Author/Artist should be populated + // Choices.js creates items — check if any author is selected + const authorItems = page.locator('#autori-wrapper .choices__item--selectable, .choices__item.choices__item--selectable'); + const authorCount = await authorItems.count().catch(() => 0); + + // At minimum, title should be populated + expect(titleValue.length).toBeGreaterThan(0); + + // Check EAN field has the barcode + const eanValue = await page.locator('input[name="ean"]').inputValue(); + // The barcode might be in isbn13 or ean depending on the scraper + const isbn13Value = await page.locator('input[name="isbn13"]').inputValue(); + expect(eanValue === TEST_BARCODE || isbn13Value === TEST_BARCODE || + eanValue.includes('720642442524') || isbn13Value.includes('720642442524')).toBe(true); + }); + + test('4. Save the imported CD', async () => { + const titleValue = await page.locator('input[name="titolo"]').inputValue(); + expect(titleValue.trim().length, 'No scraped data to save').toBeGreaterThan(0); + + // Set copies (required field) + const copieInput = page.locator('input[name="copie_totali"]'); + const copieVal = await copieInput.inputValue(); + if (!copieVal || copieVal === '0') { + await copieInput.fill('1'); + } + + // Submit the form (triggers SweetAlert confirmation) + await page.locator('button[type="submit"]').first().click(); + + // Wait for and confirm SweetAlert dialog + const swalConfirm = page.locator('.swal2-confirm'); + await expect(swalConfirm).toBeVisible({ timeout: 5000 }); + await swalConfirm.click(); + + // Wait for navigation after save + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }); + const finalUrl = page.url(); + expect(/\/admin\/libri\/\d+/.test(finalUrl)).toBe(true); + const createdIdMatch = finalUrl.match(/\/admin\/libri\/(\d+)/); + expect(createdIdMatch, 'Could not resolve created record id from save redirect').not.toBeNull(); + createdId = createdIdMatch?.[1] ?? ''; + }); + + test('5. Verify saved CD in database', async () => { + expect(createdId, 'Created record id not captured during save').not.toBe(''); + const book = dbQuery( + `SELECT titolo, COALESCE(ean, ''), COALESCE(isbn13, ''), formato FROM libri WHERE id = ${Number(createdId)} AND deleted_at IS NULL LIMIT 1` + ); + expect(book, 'CD not found in database after import/save flow').not.toBe(''); + expect(book.toLowerCase()).toContain('nevermind'); + }); + + test('6. Verify music labels on saved CD detail page', async () => { + const bookId = createdId; + expect(bookId, 'CD not found for label check').not.toBe(''); + + await page.goto(`${BASE}/admin/libri/${bookId}`); + await page.waitForLoadState('domcontentloaded'); + const content = await page.content(); + + const tipoMedia = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${bookId}`); + expect(tipoMedia).toBe('disco'); + + const hasMusicLabel = content.includes('Etichetta') || content.includes('Label') || + content.includes('Anno di Uscita') || content.includes('Release Year'); + expect(hasMusicLabel).toBe(true); + }); +}); diff --git a/tests/discogs-plugin.spec.js b/tests/discogs-plugin.spec.js new file mode 100644 index 00000000..f802891a --- /dev/null +++ b/tests/discogs-plugin.spec.js @@ -0,0 +1,246 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; +const DB_USER = process.env.E2E_DB_USER || ''; +const DB_PASS = process.env.E2E_DB_PASS || ''; +const DB_NAME = process.env.E2E_DB_NAME || ''; +const DB_SOCKET = process.env.E2E_DB_SOCKET || ''; + +function dbQuery(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-N', '-B', '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + return execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }).trim(); +} + +function dbExec(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }); +} + +// Unique barcode for testing (not used by seeded records) +const TEST_BARCODE = '9999999999901'; +const TEST_ARTIST = 'Pink Floyd'; + +test.describe.serial('Discogs Plugin (#87)', () => { + /** @type {import('@playwright/test').BrowserContext} */ + let context; + /** @type {import('@playwright/test').Page} */ + let page; + let pluginActivated = false; + let discogsPluginId = ''; + + test.beforeAll(async ({ browser }) => { + test.skip( + !ADMIN_EMAIL || !ADMIN_PASS || !DB_USER || !DB_PASS || !DB_NAME, + 'Missing E2E env vars' + ); + context = await browser.newContext(); + page = await context.newPage(); + + // Login + await page.goto(`${BASE}/admin/dashboard`); + const emailField = page.locator('input[name="email"]'); + if (await emailField.isVisible({ timeout: 3000 }).catch(() => false)) { + await emailField.fill(ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); + } + }); + + test.afterAll(async () => { + // Cleanup: remove test books + try { dbExec("DELETE FROM libri WHERE titolo LIKE '%E2E_DISCOGS_%'"); } catch {} + await context?.close(); + }); + + test('1. Discogs plugin files exist in storage', async () => { + // Verify plugin is shipped (via DB — plugins table may have it installed) + const pluginExists = dbQuery( + "SELECT COUNT(*) FROM plugins WHERE name = 'discogs'" + ); + + // If not installed, check if plugin.json is accessible via the admin page + await page.goto(`${BASE}/admin/plugins`); + await page.waitForLoadState('domcontentloaded'); + const pageContent = await page.content(); + const discogsCard = page.locator('div[data-plugin-id]').filter({ + has: page.getByRole('heading', { name: /discogs/i }), + }).first(); + await expect(discogsCard).toBeVisible({ timeout: 5000 }); + + // Plugin should appear in the list (installed or available) + expect( + pageContent.toLowerCase().includes('discogs') || parseInt(pluginExists) > 0 + ).toBe(true); + }); + + test('2. Activate Discogs plugin', async () => { + await page.goto(`${BASE}/admin/plugins`); + await page.waitForLoadState('domcontentloaded'); + + // Check if already active + const isActive = dbQuery( + "SELECT COUNT(*) FROM plugins WHERE name = 'discogs' AND is_active = 1" + ); + if (parseInt(isActive) > 0) { + pluginActivated = true; + return; + } + + discogsPluginId = dbQuery("SELECT id FROM plugins WHERE name = 'discogs' LIMIT 1"); + expect(discogsPluginId).not.toBe(''); + + const discogsCard = page.locator('div[data-plugin-id]').filter({ + has: page.getByRole('heading', { name: /discogs/i }), + }).first(); + await expect(discogsCard, 'Discogs card not found on the plugins page').toBeVisible({ timeout: 5000 }); + + const activateBtn = discogsCard.getByRole('button', { name: /^Attiva$/ }).first(); + if (await activateBtn.isVisible({ timeout: 3000 }).catch(() => false)) { + await activateBtn.click(); + await page.waitForLoadState('domcontentloaded'); + await page.waitForTimeout(2000); + } + + // Verify activation + const activeNow = dbQuery( + "SELECT COUNT(*) FROM plugins WHERE name = 'discogs' AND is_active = 1" + ); + expect(parseInt(activeNow, 10), 'Discogs plugin failed to activate').toBeGreaterThan(0); + pluginActivated = true; + }); + + test('3. Plugin settings page loads', async () => { + test.skip(!pluginActivated, 'Discogs plugin not activated'); + if (!discogsPluginId) { + discogsPluginId = dbQuery("SELECT id FROM plugins WHERE name = 'discogs' LIMIT 1"); + } + expect(discogsPluginId).not.toBe(''); + + await page.goto(`${BASE}/admin/plugins/${discogsPluginId}/settings`); + await page.waitForLoadState('domcontentloaded'); + + const tokenField = page.locator('input[name="api_token"]'); + await expect(tokenField).toBeVisible({ timeout: 3000 }); + }); + + test('4. MediaLabels: book with music format shows adapted labels', async () => { + // Create a test book with music format via DB + dbExec(` + INSERT INTO libri (titolo, formato, copie_totali, copie_disponibili, created_at, updated_at) + VALUES ('E2E_DISCOGS_MediaLabel_Test', 'cd_audio', 1, 1, NOW(), NOW()) + `); + const bookId = dbQuery( + "SELECT id FROM libri WHERE titolo = 'E2E_DISCOGS_MediaLabel_Test' AND deleted_at IS NULL LIMIT 1" + ); + expect(bookId).not.toBe(''); + + // Visit the admin book detail page + await page.goto(`${BASE}/admin/libri/${bookId}`); + await page.waitForLoadState('domcontentloaded'); + const adminContent = await page.content(); + + // Labels should be music-aware (check for at least one adapted label) + // "Etichetta" instead of "Editore", or "Anno di Uscita" instead of "Anno di Pubblicazione" + const hasEtichetta = adminContent.includes('Etichetta') || adminContent.includes('Label'); + const hasAnnoUscita = adminContent.includes('Anno di Uscita') || adminContent.includes('Release Year'); + expect(hasEtichetta || hasAnnoUscita).toBe(true); + }); + + test('5. MediaLabels: regular book keeps standard labels', async () => { + // Create a regular book + dbExec(` + INSERT INTO libri (titolo, formato, copie_totali, copie_disponibili, created_at, updated_at) + VALUES ('E2E_DISCOGS_RegularBook', 'cartaceo', 1, 1, NOW(), NOW()) + `); + const bookId = dbQuery( + "SELECT id FROM libri WHERE titolo = 'E2E_DISCOGS_RegularBook' AND deleted_at IS NULL LIMIT 1" + ); + + await page.goto(`${BASE}/admin/libri/${bookId}`); + await page.waitForLoadState('domcontentloaded'); + const content = await page.content(); + + // Should have standard labels (Editore, not Etichetta) + // Won't have "Etichetta" unless there's music data + const hasEditore = content.includes('Editore') || content.includes('Publisher'); + expect(hasEditore).toBe(true); + }); + + test('6. Frontend: music book shows Barcode instead of ISBN-13', async () => { + // Create a music book with EAN + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, ean, copie_totali, copie_disponibili, created_at, updated_at) " + + "VALUES ('E2E_DISCOGS_Frontend_CD', 'vinile', 'disco', '" + TEST_BARCODE + "', 1, 1, NOW(), NOW())" + ); + const bookId = dbQuery( + "SELECT id FROM libri WHERE titolo = 'E2E_DISCOGS_Frontend_CD' AND deleted_at IS NULL LIMIT 1" + ); + + const resp = await page.request.get(`${BASE}/libro/${bookId}`); + expect(resp.status()).toBe(200); + + // Check that the frontend music page uses the barcode label path + const html = await resp.text(); + const hasBarcode = html.includes('Barcode'); + const hasMusicLabel = html.includes('Etichetta') || html.includes('Label') || + html.includes('Anno di Uscita') || html.includes('Release Year'); + expect(hasBarcode).toBe(true); + expect(hasMusicLabel).toBe(true); + }); + + test('7. Discogs scraping via ISBN import (if plugin active)', async () => { + test.skip(!pluginActivated, 'Discogs plugin not activated'); + + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + const importBtn = page.locator('#btnImportIsbn'); + if (!await importBtn.isVisible({ timeout: 3000 }).catch(() => false)) { + test.skip(true, 'Import button not visible'); + return; + } + + // Try importing with a known CD barcode + await page.locator('#importIsbn').fill(TEST_BARCODE); + await importBtn.click(); + + // Wait for response (up to 15s — Discogs can be slow) + await page.waitForTimeout(5000); + + // Check if any fields were populated + const titleField = page.locator('input[name="titolo"]'); + const titleValue = await titleField.inputValue().catch(() => ''); + + if (titleValue !== '') { + // Scraping succeeded — verify some data + expect(titleValue.length).toBeGreaterThan(0); + + // Check if format was set to a music type + const formatField = page.locator('input[name="formato"]'); + const formatValue = await formatField.inputValue().catch(() => ''); + // Format might be populated from Discogs + + // Check description (should contain tracklist) + const descFrame = page.frameLocator('.tox-edit-area__iframe').first(); + if (await descFrame.locator('body').isVisible({ timeout: 2000 }).catch(() => false)) { + const descText = await descFrame.locator('body').textContent().catch(() => ''); + // If Discogs returned tracklist, description should have content + if (descText) { + expect(descText.length).toBeGreaterThan(0); + } + } + } else { + // Scraping might have failed (rate limit, network) — that's OK for CI + // Just verify no JS errors occurred + const logs = []; + page.on('console', msg => { if (msg.type() === 'error') logs.push(msg.text()); }); + } + }); +}); diff --git a/tests/multisource-scraping.spec.js b/tests/multisource-scraping.spec.js new file mode 100644 index 00000000..583b3ed1 --- /dev/null +++ b/tests/multisource-scraping.spec.js @@ -0,0 +1,377 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; +const DB_USER = process.env.E2E_DB_USER || ''; +const DB_PASS = process.env.E2E_DB_PASS || ''; +const DB_NAME = process.env.E2E_DB_NAME || ''; +const DB_SOCKET = process.env.E2E_DB_SOCKET || ''; +const RUN_ID = Date.now().toString(); +const RUN_TAG = `E2E_MULTI_${RUN_ID}`; + +const OPEN_LIBRARY_ISBN = '9780140328721'; +const ITALIAN_ISBN = '9788804671664'; +const NEVERMIND_BARCODE = '0720642442524'; +const MEDDLE_BARCODE = '5099902894225'; + +const MANUAL_BOOK_TITLE = `${RUN_TAG} BOOK MANUAL`; +const IMPORTED_BOOK_TITLE = `${RUN_TAG} BOOK IMPORT`; +const MANUAL_DISC_TITLE = `${RUN_TAG} DISC MANUAL`; +const IMPORTED_DISC_1_TITLE = `${RUN_TAG} DISC NEVERMIND`; +const IMPORTED_DISC_2_TITLE = `${RUN_TAG} DISC MEDDLE`; + +const MANUAL_BOOK_ISBN13 = `9781234${RUN_ID.slice(-6)}`; +const MANUAL_DISC_EAN = `999${RUN_ID.slice(-10)}`; +const IMPORTED_BOOK_ISBN13 = `9782234${RUN_ID.slice(-6)}`; +const IMPORTED_DISC_1_EAN = `888${RUN_ID.slice(-10)}`; +const IMPORTED_DISC_2_EAN = `777${RUN_ID.slice(-10)}`; + +function mysqlArgs(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-N', '-B', '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + return args; +} + +function dbQuery(sql) { + return execFileSync('mysql', mysqlArgs(sql), { + encoding: 'utf-8', + timeout: 10000, + }).trim(); +} + +function sqlEscape(value) { + return String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} + +async function loginAsAdmin(page) { + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); +} + +async function openCreateForm(page) { + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); +} + +async function importIdentifier(page, identifier) { + await page.locator('#importIsbn').fill(identifier); + await page.locator('#btnImportIsbn').click(); + await expect(page.locator('input[name="titolo"]')).not.toHaveValue('', { timeout: 20000 }); + + const sourceNameLocator = page.locator('#scrapeSourceName'); + await expect.poll( + async () => ((await sourceNameLocator.textContent().catch(() => '')) || '').trim(), + { timeout: 5000 } + ).not.toBe(''); + const sourceName = await sourceNameLocator.textContent().catch(() => ''); + return (sourceName || '').trim(); +} + +async function clearImportedEan(page) { + const scrapedEan = page.locator('#scraped_ean'); + if (await scrapedEan.count()) { + await scrapedEan.evaluate((node) => { + node.value = ''; + }); + } +} + +async function saveCurrentForm(page) { + await page.locator('button[type="submit"]').first().click(); + + const swalConfirm = page.locator('.swal2-confirm'); + if (await swalConfirm.isVisible({ timeout: 5000 }).catch(() => false)) { + await swalConfirm.click(); + } + + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }); + const match = page.url().match(/\/admin\/libri\/(\d+)/); + if (!match) { + throw new Error(`Could not resolve saved book id from URL: ${page.url()}`); + } + + return Number(match[1]); +} + +async function getScrapePayload(page, identifier) { + const response = await page.request.get(`${BASE}/api/scrape/isbn?isbn=${encodeURIComponent(identifier)}`); + expect(response.status(), `Unexpected scrape status for ${identifier}`).toBe(200); + return response.json(); +} + +test.describe.serial('Multi-source scraping and creation flows', () => { + /** @type {import('@playwright/test').BrowserContext} */ + let context; + /** @type {import('@playwright/test').Page} */ + let page; + + let manualBookId = 0; + let importedBookId = 0; + let manualDiscId = 0; + let importedDisc1Id = 0; + let importedDisc2Id = 0; + + test.beforeAll(async ({ browser }) => { + test.skip( + !ADMIN_EMAIL || !ADMIN_PASS || !DB_USER || !DB_PASS || !DB_NAME, + 'Missing E2E env vars' + ); + + context = await browser.newContext(); + page = await context.newPage(); + await loginAsAdmin(page); + }); + + test.afterAll(async () => { + await context?.close(); + }); + + test('1. Scraping plugins are active with the expected priority order', async () => { + const activePlugins = dbQuery( + "SELECT GROUP_CONCAT(name ORDER BY name SEPARATOR ',') FROM plugins WHERE name IN ('discogs','open-library','z39-server') AND is_active = 1" + ); + expect(activePlugins).toContain('discogs'); + expect(activePlugins).toContain('open-library'); + expect(activePlugins).toContain('z39-server'); + + const hookOrder = dbQuery( + "SELECT GROUP_CONCAT(CONCAT(p.name, ':', ph.priority) ORDER BY ph.priority SEPARATOR ',') " + + "FROM plugin_hooks ph JOIN plugins p ON p.id = ph.plugin_id " + + "WHERE ph.hook_name = 'scrape.fetch.custom' AND ph.is_active = 1 AND p.name IN ('z39-server','open-library','discogs')" + ); + expect(hookOrder).toBe('z39-server:3,open-library:5,discogs:8'); + }); + + test('2. Scrape API returns Open Library book data for a known ISBN', async () => { + const payload = await getScrapePayload(page, OPEN_LIBRARY_ISBN); + + expect(payload.title).toContain('Fantastic Mr. Fox'); + expect(payload.tipo_media).toBe('libro'); + expect(payload.source).toContain('openlibrary.org'); + expect(Array.isArray(payload.authors) ? payload.authors.length : 0).toBeGreaterThan(0); + expect(payload.image).toBeTruthy(); + }); + + test('3. Scrape API returns enriched Italian metadata for a second book ISBN', async () => { + const payload = await getScrapePayload(page, ITALIAN_ISBN); + + expect(payload.title).toBeTruthy(); + expect(payload.tipo_media).toBe('libro'); + expect(payload.classificazione_dewey).toBe('188'); + expect(payload.isbn13).toBe(ITALIAN_ISBN); + expect((payload.collana || payload.series || '').length).toBeGreaterThan(0); + }); + + test('4. Scrape API returns Discogs music data for Nevermind barcode', async () => { + const payload = await getScrapePayload(page, NEVERMIND_BARCODE); + + expect(payload.source).toBe('discogs'); + expect(payload.title).toContain('Nevermind'); + expect(payload.tipo_media).toBe('disco'); + expect(payload.ean).toBe(NEVERMIND_BARCODE); + expect(payload.publisher).toBeTruthy(); + }); + + test('5. Scrape API returns Discogs music data for Meddle barcode', async () => { + const payload = await getScrapePayload(page, MEDDLE_BARCODE); + + expect(payload.source).toBe('discogs'); + expect(payload.title).toContain('Meddle'); + expect(payload.tipo_media).toBe('disco'); + expect(payload.ean).toBe(MEDDLE_BARCODE); + expect(payload.image).toBeTruthy(); + }); + + test('6. Admin can create a manual book from the create form', async () => { + await openCreateForm(page); + + await page.locator('input[name="titolo"]').fill(MANUAL_BOOK_TITLE); + await page.locator('input[name="isbn13"]').fill(MANUAL_BOOK_ISBN13); + await page.locator('input[name="copie_totali"]').fill('1'); + await page.locator('#tipo_media').selectOption('libro'); + await page.locator('input[name="formato"]').fill('cartaceo'); + + manualBookId = await saveCurrentForm(page); + expect(manualBookId).toBeGreaterThan(0); + }); + + test('7. The manual book is persisted as a book record', async () => { + const row = dbQuery( + `SELECT CONCAT(id, '|', titolo, '|', COALESCE(tipo_media, ''), '|', COALESCE(isbn13, '')) FROM libri WHERE titolo = '${sqlEscape(MANUAL_BOOK_TITLE)}' AND deleted_at IS NULL ORDER BY id DESC LIMIT 1` + ); + + expect(row).toContain(MANUAL_BOOK_TITLE); + expect(row).toContain('|libro|'); + expect(row).toContain(MANUAL_BOOK_ISBN13); + }); + + test('8. Admin can import and save a book from Open Library', async () => { + await openCreateForm(page); + + const sourceName = await importIdentifier(page, OPEN_LIBRARY_ISBN); + expect(sourceName).toContain('Open Library'); + + await expect(page.locator('input[name="isbn13"]')).toHaveValue(OPEN_LIBRARY_ISBN); + await page.locator('input[name="titolo"]').fill(IMPORTED_BOOK_TITLE); + await page.locator('input[name="isbn10"]').fill(''); + await page.locator('input[name="isbn13"]').fill(IMPORTED_BOOK_ISBN13); + await page.locator('input[name="ean"]').fill(''); + await clearImportedEan(page); + await page.locator('input[name="copie_totali"]').fill('1'); + await expect(page.locator('#tipo_media')).toHaveValue('libro'); + + importedBookId = await saveCurrentForm(page); + expect(importedBookId).toBeGreaterThan(0); + }); + + test('9. The imported book detail keeps book labels and ISBN metadata', async () => { + const row = dbQuery( + `SELECT CONCAT(COALESCE(tipo_media, ''), '|', COALESCE(isbn13, ''), '|', COALESCE(ean, '')) FROM libri WHERE id = ${importedBookId}` + ); + expect(row).toContain('libro'); + expect(row).toContain(IMPORTED_BOOK_ISBN13); + + await page.goto(`${BASE}/admin/libri/${importedBookId}`); + await page.waitForLoadState('domcontentloaded'); + const html = await page.content(); + + const hasBookLabel = html.includes('Editore') || html.includes('Publisher'); + const hasIsbnLabel = html.includes('ISBN-13') || html.includes(IMPORTED_BOOK_ISBN13); + expect(hasBookLabel).toBe(true); + expect(hasIsbnLabel).toBe(true); + }); + + test('10. Admin can create a manual disc from the create form', async () => { + await openCreateForm(page); + + await page.locator('input[name="titolo"]').fill(MANUAL_DISC_TITLE); + await page.locator('input[name="ean"]').fill(MANUAL_DISC_EAN); + await page.locator('input[name="copie_totali"]').fill('1'); + await page.locator('#tipo_media').selectOption('disco'); + await page.locator('input[name="formato"]').fill('cd_audio'); + await page.locator('textarea[name="descrizione"]').fill('Manual test tracklist'); + + manualDiscId = await saveCurrentForm(page); + expect(manualDiscId).toBeGreaterThan(0); + }); + + test('11. The manual disc is persisted with music-specific metadata', async () => { + const row = dbQuery( + `SELECT CONCAT(COALESCE(tipo_media, ''), '|', COALESCE(ean, ''), '|', COALESCE(formato, '')) FROM libri WHERE id = ${manualDiscId}` + ); + expect(row).toContain('disco'); + expect(row).toContain(MANUAL_DISC_EAN); + expect(row).toContain('cd_audio'); + + await page.goto(`${BASE}/admin/libri/${manualDiscId}`); + await page.waitForLoadState('domcontentloaded'); + const html = await page.content(); + + const hasMusicBadge = html.includes('fa-compact-disc') || html.includes('Barcode'); + expect(hasMusicBadge).toBe(true); + }); + + test('12. Admin can import and save a Discogs music release from Nevermind barcode', async () => { + await openCreateForm(page); + + const sourceName = await importIdentifier(page, NEVERMIND_BARCODE); + expect(sourceName.toLowerCase()).toContain('discogs'); + + await expect(page.locator('#tipo_media')).toHaveValue('disco'); + await expect(page.locator('input[name="ean"]')).toHaveValue(NEVERMIND_BARCODE); + await page.locator('input[name="titolo"]').fill(IMPORTED_DISC_1_TITLE); + await page.locator('input[name="ean"]').fill(IMPORTED_DISC_1_EAN); + await page.locator('input[name="isbn10"]').fill(''); + await page.locator('input[name="isbn13"]').fill(''); + await page.locator('input[name="copie_totali"]').fill('1'); + + importedDisc1Id = await saveCurrentForm(page); + expect(importedDisc1Id).toBeGreaterThan(0); + }); + + test('13. The first imported disc exposes music labels and Discogs metadata', async () => { + const row = dbQuery( + `SELECT CONCAT(COALESCE(tipo_media, ''), '|', COALESCE(ean, ''), '|', COALESCE(editore_id, 0), '|', COALESCE(anno_pubblicazione, '')) FROM libri WHERE id = ${importedDisc1Id}` + ); + expect(row).toContain('disco'); + expect(row).toContain(IMPORTED_DISC_1_EAN); + + await page.goto(`${BASE}/admin/libri/${importedDisc1Id}`); + await page.waitForLoadState('domcontentloaded'); + const html = await page.content(); + + const hasMusicLabel = html.includes('Etichetta') || html.includes('Label'); + const hasBarcode = html.includes('Barcode') || html.includes(NEVERMIND_BARCODE); + expect(hasMusicLabel || hasBarcode).toBe(true); + }); + + test('14. Admin can import and save a second Discogs release from Meddle barcode', async () => { + await openCreateForm(page); + + const sourceName = await importIdentifier(page, MEDDLE_BARCODE); + expect(sourceName.toLowerCase()).toContain('discogs'); + + await expect(page.locator('#tipo_media')).toHaveValue('disco'); + await expect(page.locator('input[name="ean"]')).toHaveValue(MEDDLE_BARCODE); + await page.locator('input[name="titolo"]').fill(IMPORTED_DISC_2_TITLE); + await page.locator('input[name="ean"]').fill(IMPORTED_DISC_2_EAN); + await page.locator('input[name="isbn10"]').fill(''); + await page.locator('input[name="isbn13"]').fill(''); + await page.locator('input[name="copie_totali"]').fill('1'); + + importedDisc2Id = await saveCurrentForm(page); + expect(importedDisc2Id).toBeGreaterThan(0); + + const row = dbQuery( + `SELECT CONCAT(COALESCE(tipo_media, ''), '|', COALESCE(ean, ''), '|', COALESCE(titolo, '')) FROM libri WHERE id = ${importedDisc2Id}` + ); + expect(row).toContain('disco'); + expect(row).toContain(IMPORTED_DISC_2_EAN); + expect(row).toContain(IMPORTED_DISC_2_TITLE); + }); + + test('15. Admin filters and public search distinguish the new books and discs', async () => { + const adminResponse = await page.request.get( + `${BASE}/api/libri?tipo_media=disco&start=0&length=200&search_text=${encodeURIComponent(RUN_TAG)}` + ); + expect(adminResponse.status()).toBe(200); + const adminData = await adminResponse.json(); + const adminText = JSON.stringify(adminData.data || []); + + expect(adminText).toContain(MANUAL_DISC_TITLE); + expect(adminText).toContain(IMPORTED_DISC_1_TITLE); + expect(adminText).toContain(IMPORTED_DISC_2_TITLE); + expect(adminText).not.toContain(MANUAL_BOOK_TITLE); + expect(adminText).not.toContain(IMPORTED_BOOK_TITLE); + + const publicResponse = await page.request.get(`${BASE}/api/catalog?q=${encodeURIComponent(RUN_TAG)}`); + expect(publicResponse.status()).toBe(200); + const publicData = await publicResponse.json(); + const publicHtml = publicData.html || ''; + + expect(publicHtml).toContain(MANUAL_BOOK_TITLE); + expect(publicHtml).toContain(IMPORTED_BOOK_TITLE); + expect(publicHtml).toContain(MANUAL_DISC_TITLE); + expect(publicHtml).toContain(IMPORTED_DISC_1_TITLE); + expect(publicHtml).toContain(IMPORTED_DISC_2_TITLE); + + const publicDiscResponse = await page.request.get( + `${BASE}/api/catalog?q=${encodeURIComponent(RUN_TAG)}&tipo_media=disco` + ); + expect(publicDiscResponse.status()).toBe(200); + const publicDiscData = await publicDiscResponse.json(); + const publicDiscHtml = publicDiscData.html || ''; + + expect(publicDiscHtml).toContain(MANUAL_DISC_TITLE); + expect(publicDiscHtml).toContain(IMPORTED_DISC_1_TITLE); + expect(publicDiscHtml).toContain(IMPORTED_DISC_2_TITLE); + expect(publicDiscHtml).not.toContain(MANUAL_BOOK_TITLE); + expect(publicDiscHtml).not.toContain(IMPORTED_BOOK_TITLE); + }); +}); diff --git a/tests/playwright.config.js b/tests/playwright.config.js index 987e830b..6a37cb7c 100644 --- a/tests/playwright.config.js +++ b/tests/playwright.config.js @@ -6,6 +6,7 @@ module.exports = defineConfig({ timeout: 120_000, expect: { timeout: 15_000 }, reporter: 'list', + workers: 1, use: { baseURL: process.env.APP_URL || 'http://localhost:8081', headless: true, diff --git a/tests/pr100-media-types.spec.js b/tests/pr100-media-types.spec.js new file mode 100644 index 00000000..07d36784 --- /dev/null +++ b/tests/pr100-media-types.spec.js @@ -0,0 +1,241 @@ +// @ts-check +/** + * PR #100 Feature Tests: Media Types, Discogs Plugin, Dynamic Labels + * 10 reusable tests covering the tipo_media system end-to-end. + * Requires: app installed, admin user, tipo_media column in DB. + */ +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; +const DB_USER = process.env.E2E_DB_USER || ''; +const DB_PASS = process.env.E2E_DB_PASS || ''; +const DB_NAME = process.env.E2E_DB_NAME || ''; +const DB_SOCKET = process.env.E2E_DB_SOCKET || ''; +const RUN_ID = Date.now(); + +function dbQuery(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-N', '-B', '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + return execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }).trim(); +} + +function dbExec(sql) { + const args = ['-u', DB_USER, `-p${DB_PASS}`, DB_NAME, '-e', sql]; + if (DB_SOCKET) args.splice(3, 0, '-S', DB_SOCKET); + execFileSync('mysql', args, { encoding: 'utf-8', timeout: 10000 }); +} + +test.describe.serial('PR #100: Media Types System', () => { + /** @type {import('@playwright/test').Page} */ + let page; + /** @type {import('@playwright/test').BrowserContext} */ + let context; + let cdId = '', bookId = '', audiobookId = '', dvdId = ''; + + test.beforeAll(async ({ browser }) => { + test.skip(!ADMIN_EMAIL || !ADMIN_PASS || !DB_USER || !DB_PASS || !DB_NAME, 'Missing env vars'); + context = await browser.newContext(); + page = await context.newPage(); + + // Login + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); + + // Seed 4 media types — use RUN_ID in EAN/ISBN to avoid collisions + const eanSuffix = String(RUN_ID).slice(-10).padStart(12, '0'); + const isbnSuffix = '978' + String(RUN_ID).slice(-10).padStart(10, '0'); + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, ean, copie_totali, copie_disponibili, created_at, updated_at) " + + "VALUES ('PR100_CD_" + RUN_ID + "', 'cd_audio', 'disco', '" + eanSuffix + "1', 1, 1, NOW(), NOW())" + ); + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, isbn13, copie_totali, copie_disponibili, created_at, updated_at) " + + "VALUES ('PR100_Book_" + RUN_ID + "', 'cartaceo', 'libro', '" + isbnSuffix + "', 1, 1, NOW(), NOW())" + ); + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, copie_totali, copie_disponibili, created_at, updated_at) " + + "VALUES ('PR100_Audiobook_" + RUN_ID + "', 'audiolibro', 'audiolibro', 1, 1, NOW(), NOW())" + ); + dbExec( + "INSERT INTO libri (titolo, formato, tipo_media, copie_totali, copie_disponibili, created_at, updated_at) " + + "VALUES ('PR100_DVD_" + RUN_ID + "', 'dvd', 'dvd', 1, 1, NOW(), NOW())" + ); + + cdId = dbQuery("SELECT id FROM libri WHERE titolo = 'PR100_CD_" + RUN_ID + "' LIMIT 1"); + bookId = dbQuery("SELECT id FROM libri WHERE titolo = 'PR100_Book_" + RUN_ID + "' LIMIT 1"); + audiobookId = dbQuery("SELECT id FROM libri WHERE titolo = 'PR100_Audiobook_" + RUN_ID + "' LIMIT 1"); + dvdId = dbQuery("SELECT id FROM libri WHERE titolo = 'PR100_DVD_" + RUN_ID + "' LIMIT 1"); + }); + + test.afterAll(async () => { + try { dbExec("UPDATE libri SET deleted_at = NOW(), ean = NULL, isbn13 = NULL WHERE titolo LIKE 'PR100_%_" + RUN_ID + "' AND deleted_at IS NULL"); } catch {} + await context?.close(); + }); + + // ═══════════════════════════════════════════════════════ + // 1. tipo_media column exists in DB + // ═══════════════════════════════════════════════════════ + test('1. tipo_media column exists with correct ENUM values', async () => { + const colType = dbQuery("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='libri' AND COLUMN_NAME='tipo_media'"); + expect(colType).toContain('libro'); + expect(colType).toContain('disco'); + expect(colType).toContain('audiolibro'); + expect(colType).toContain('dvd'); + expect(colType).toContain('altro'); + }); + + // ═══════════════════════════════════════════════════════ + // 2. Admin book form has tipo_media dropdown + // ═══════════════════════════════════════════════════════ + test('2. Book form has tipo_media dropdown with all options', async () => { + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + const select = page.locator('#tipo_media'); + await expect(select).toBeVisible(); + + const options = await select.locator('option').allTextContents(); + expect(options.length).toBeGreaterThanOrEqual(5); + }); + + // ═══════════════════════════════════════════════════════ + // 3. Admin list shows tipo_media icon column + // ═══════════════════════════════════════════════════════ + test('3. Admin list has media type icon column', async () => { + await page.goto(`${BASE}/admin/libri`); + await page.waitForLoadState('domcontentloaded'); + await page.waitForTimeout(2000); + + const content = await page.content(); + // Should have the icon column header + expect(content).toContain('fa-compact-disc'); + }); + + // ═══════════════════════════════════════════════════════ + // 4. Admin list filters by tipo_media + // ═══════════════════════════════════════════════════════ + test('4. API filters by tipo_media=disco', async () => { + const resp = await page.request.get(`${BASE}/api/libri?tipo_media=disco&start=0&length=100&search_text=PR100`); + expect(resp.status()).toBe(200); + const data = await resp.json(); + const records = data.data || []; + + // Should find CD but not book/audiobook/dvd + const titles = records.map((r) => r.titolo || '').join(' '); + expect(titles).toContain('PR100_CD_'); + expect(titles).not.toContain('PR100_Book_'); + }); + + // ═══════════════════════════════════════════════════════ + // 5. CD shows music labels (Etichetta, Anno di Uscita) + // ═══════════════════════════════════════════════════════ + test('5. CD admin detail shows music-specific labels', async () => { + await page.goto(`${BASE}/admin/libri/${cdId}`); + await page.waitForLoadState('domcontentloaded'); + const content = await page.content(); + + const hasEtichetta = content.includes('Etichetta') || content.includes('Label'); + const hasAnnoUscita = content.includes('Anno di Uscita') || content.includes('Release Year'); + expect(hasEtichetta || hasAnnoUscita).toBe(true); + expect(content).toContain('fa-compact-disc'); + }); + + // ═══════════════════════════════════════════════════════ + // 6. Book shows standard labels (Editore, Anno Pubblicazione) + // ═══════════════════════════════════════════════════════ + test('6. Book admin detail shows standard labels', async () => { + await page.goto(`${BASE}/admin/libri/${bookId}`); + await page.waitForLoadState('domcontentloaded'); + const content = await page.content(); + + const hasEditore = content.includes('Editore') || content.includes('Publisher'); + expect(hasEditore).toBe(true); + expect(content).toContain('fa-book'); + }); + + // ═══════════════════════════════════════════════════════ + // 7. Edit CD — tipo_media persists as 'disco' + // ═══════════════════════════════════════════════════════ + test('7. Edit CD preserves tipo_media=disco', async () => { + await page.goto(`${BASE}/admin/libri/modifica/${cdId}`); + await page.waitForLoadState('domcontentloaded'); + + const select = page.locator('#tipo_media'); + if (await select.isVisible({ timeout: 3000 }).catch(() => false)) { + expect(await select.inputValue()).toBe('disco'); + + // Change title, save + await page.locator('input[name="titolo"]').fill('PR100_CD_' + RUN_ID + '_edited'); + await page.locator('button[type="submit"]').first().click(); + const swal = page.locator('.swal2-confirm'); + if (await swal.isVisible({ timeout: 3000 }).catch(() => false)) await swal.click(); + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }).catch(() => {}); + + // Verify DB + const tipo = dbQuery(`SELECT tipo_media FROM libri WHERE id = ${cdId}`); + expect(tipo).toBe('disco'); + + // Restore title + dbExec("UPDATE libri SET titolo = 'PR100_CD_" + RUN_ID + "' WHERE id = " + cdId); + } + }); + + // ═══════════════════════════════════════════════════════ + // 8. CSV export includes tipo_media column + // ═══════════════════════════════════════════════════════ + test('8. CSV export includes tipo_media', async () => { + const resp = await page.request.get(`${BASE}/admin/libri/export/csv?ids=${cdId},${bookId}`); + expect(resp.status()).toBe(200); + const body = await resp.text(); + const header = body.split('\n')[0].replace(/^\uFEFF/, ''); + + expect(header).toContain('tipo_media'); + + const fields = header.split(';'); + const idx = fields.indexOf('tipo_media'); + expect(idx).toBeGreaterThan(-1); + + // Data rows + const lines = body.split('\n').filter(l => l.trim()); + if (lines.length > 1) { + const row1 = lines[1].split(';'); + // One should be disco, one empty/libro + const tipos = lines.slice(1).map(l => l.split(';')[idx]); + expect(tipos.some(t => t === 'disco')).toBe(true); + } + }); + + // ═══════════════════════════════════════════════════════ + // 9. Format display name: "cd_audio" → "CD Audio" + // ═══════════════════════════════════════════════════════ + test('9. Format shows human-readable name, not raw key', async () => { + await page.goto(`${BASE}/admin/libri/${cdId}`); + await page.waitForLoadState('domcontentloaded'); + const content = await page.content(); + + // Should NOT show raw "cd_audio" + // Should show "CD Audio" (or translated equivalent) + const hasRaw = content.includes('>cd_audio<'); + const hasFormatted = content.includes('CD Audio') || content.includes('Audio CD') || content.includes('Audio-CD'); + expect(hasRaw).toBe(false); + expect(hasFormatted).toBe(true); + }); + + // ═══════════════════════════════════════════════════════ + // 10. Discogs plugin is bundled and registered + // ═══════════════════════════════════════════════════════ + test('10. Discogs plugin registered as bundled', async () => { + const exists = dbQuery("SELECT COUNT(*) FROM plugins WHERE name = 'discogs'"); + expect(parseInt(exists)).toBeGreaterThan(0); + + // Plugin display name should be updated + const displayName = dbQuery("SELECT display_name FROM plugins WHERE name = 'discogs' LIMIT 1"); + expect(displayName.toLowerCase()).toContain('music'); + }); +}); diff --git a/tests/seed-catalog.spec.js b/tests/seed-catalog.spec.js new file mode 100644 index 00000000..cb524c8b --- /dev/null +++ b/tests/seed-catalog.spec.js @@ -0,0 +1,162 @@ +// @ts-check +/** + * Seed the catalog with books and music records. + * This is a SEEDER — it does NOT clean up. Records persist for manual testing. + * Run: /tmp/run-e2e.sh tests/seed-catalog.spec.js --config=tests/playwright.config.js --workers=1 + */ +const { test, expect } = require('@playwright/test'); +const { execFileSync } = require('child_process'); + +const BASE = process.env.E2E_BASE_URL || 'http://localhost:8081'; +const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || ''; +const ADMIN_PASS = process.env.E2E_ADMIN_PASS || ''; + +// 10 music records via Discogs barcode scraping +const MUSIC_BARCODES = [ + { barcode: '0720642442524', note: 'Nirvana - Nevermind' }, + { barcode: '5099902894225', note: 'Pink Floyd - Meddle' }, + { barcode: '0094638246824', note: 'Beatles - Abbey Road' }, + { barcode: '0888837168625', note: 'Daft Punk - RAM' }, + { barcode: '5099751076322', note: 'AC/DC' }, + { barcode: '0602547428714', note: 'Adele - 25' }, + { barcode: '0602537615810', note: 'Arctic Monkeys - AM' }, + { barcode: '0886971592924', note: 'Muse - The Resistance' }, + { barcode: '0602527947747', note: 'Coldplay - Mylo Xyloto' }, + { barcode: '0602557048032', note: 'Metallica - Hardwired' }, +]; + +// 5 books via ISBN scraping +const BOOK_ISBNS = [ + { isbn: '9780061120084', note: 'To Kill a Mockingbird' }, + { isbn: '9780451524935', note: '1984' }, + { isbn: '9780141439518', note: 'Pride and Prejudice' }, + { isbn: '9780060935467', note: 'Don Quixote' }, + { isbn: '9780142437230', note: 'Moby Dick' }, +]; + +// 1 manual entry (punk split without barcode) +const MANUAL_ENTRIES = [ + { titolo: 'Zeromila / Orsetti HC — Split', formato: 'vinile', tipo_media: 'disco' }, +]; + +test.describe.serial('Seed Catalog (books + music)', () => { + /** @type {import('@playwright/test').Page} */ + let page; + /** @type {import('@playwright/test').BrowserContext} */ + let context; + + test.beforeAll(async ({ browser }) => { + test.skip(!ADMIN_EMAIL || !ADMIN_PASS, 'Missing env vars'); + context = await browser.newContext(); + page = await context.newPage(); + + await page.goto(`${BASE}/accedi`); + await page.fill('input[name="email"]', ADMIN_EMAIL); + await page.fill('input[name="password"]', ADMIN_PASS); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/admin\//, { timeout: 15000 }); + }); + + test.afterAll(async () => { + // DO NOT clean up — this is a seeder + await context?.close(); + }); + + // Seed music records via barcode scraping + for (let i = 0; i < MUSIC_BARCODES.length; i++) { + const rec = MUSIC_BARCODES[i]; + test(`Music ${i + 1}: ${rec.note}`, async () => { + test.setTimeout(30000); + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + const importBtn = page.locator('#btnImportIsbn'); + if (await importBtn.isVisible({ timeout: 3000 }).catch(() => false)) { + await page.fill('#importIsbn', rec.barcode); + await importBtn.click(); + await page.waitForTimeout(8000); + + const title = await page.locator('input[name="titolo"]').inputValue(); + if (!title) { + await page.locator('input[name="titolo"]').fill(`CD (${rec.barcode})`); + await page.locator('input[name="ean"]').fill(rec.barcode); + await page.locator('input[name="formato"]').fill('cd_audio'); + } + } else { + await page.locator('input[name="titolo"]').fill(`CD (${rec.barcode})`); + await page.locator('input[name="ean"]').fill(rec.barcode); + await page.locator('input[name="formato"]').fill('cd_audio'); + } + + const copie = await page.locator('input[name="copie_totali"]').inputValue(); + if (!copie || copie === '0') await page.locator('input[name="copie_totali"]').fill('1'); + + await page.locator('button[type="submit"]').first().click(); + const swal = page.locator('.swal2-confirm'); + if (await swal.isVisible({ timeout: 3000 }).catch(() => false)) await swal.click(); + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }).catch(() => {}); + console.log(` ✓ ${rec.note}`); + }); + } + + // Seed books via ISBN + for (let i = 0; i < BOOK_ISBNS.length; i++) { + const book = BOOK_ISBNS[i]; + test(`Book ${i + 1}: ${book.note}`, async () => { + test.setTimeout(30000); + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + const importBtn = page.locator('#btnImportIsbn'); + if (await importBtn.isVisible({ timeout: 3000 }).catch(() => false)) { + await page.fill('#importIsbn', book.isbn); + await importBtn.click(); + await page.waitForTimeout(8000); + + const title = await page.locator('input[name="titolo"]').inputValue(); + if (!title) { + await page.locator('input[name="titolo"]').fill(book.note); + await page.locator('input[name="isbn13"]').fill(book.isbn); + } + } else { + await page.locator('input[name="titolo"]').fill(book.note); + await page.locator('input[name="isbn13"]').fill(book.isbn); + } + + const copie = await page.locator('input[name="copie_totali"]').inputValue(); + if (!copie || copie === '0') await page.locator('input[name="copie_totali"]').fill('1'); + + await page.locator('button[type="submit"]').first().click(); + const swal = page.locator('.swal2-confirm'); + if (await swal.isVisible({ timeout: 3000 }).catch(() => false)) await swal.click(); + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }).catch(() => {}); + console.log(` ✓ ${book.note}`); + }); + } + + // Manual entries + for (let i = 0; i < MANUAL_ENTRIES.length; i++) { + const entry = MANUAL_ENTRIES[i]; + test(`Manual ${i + 1}: ${entry.titolo}`, async () => { + test.setTimeout(15000); + await page.goto(`${BASE}/admin/libri/crea`); + await page.waitForLoadState('domcontentloaded'); + + await page.locator('input[name="titolo"]').fill(entry.titolo); + await page.locator('input[name="formato"]').fill(entry.formato); + if (entry.tipo_media) { + const sel = page.locator('#tipo_media'); + if (await sel.isVisible({ timeout: 2000 }).catch(() => false)) { + await sel.selectOption(entry.tipo_media); + } + } + await page.locator('input[name="copie_totali"]').fill('1'); + + await page.locator('button[type="submit"]').first().click(); + const swal = page.locator('.swal2-confirm'); + if (await swal.isVisible({ timeout: 3000 }).catch(() => false)) await swal.click(); + await page.waitForURL(/\/admin\/libri\/\d+/, { timeout: 15000 }).catch(() => {}); + console.log(` ✓ ${entry.titolo}`); + }); + } +}); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index cde03fac..922794da 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,555 +6,13 @@ $baseDir = dirname($vendorDir); return array( - 'App\\Controllers\\Admin\\CmsAdminController' => $baseDir . '/app/Controllers/Admin/CmsAdminController.php', - 'App\\Controllers\\Admin\\LanguagesController' => $baseDir . '/app/Controllers/Admin/LanguagesController.php', - 'App\\Controllers\\Admin\\MessagesController' => $baseDir . '/app/Controllers/Admin/MessagesController.php', - 'App\\Controllers\\Admin\\NotificationsController' => $baseDir . '/app/Controllers/Admin/NotificationsController.php', - 'App\\Controllers\\Admin\\RecensioniAdminController' => $baseDir . '/app/Controllers/Admin/RecensioniAdminController.php', - 'App\\Controllers\\Admin\\StatsController' => $baseDir . '/app/Controllers/Admin/StatsController.php', - 'App\\Controllers\\AuthController' => $baseDir . '/app/Controllers/AuthController.php', - 'App\\Controllers\\AutoriApiController' => $baseDir . '/app/Controllers/AutoriApiController.php', - 'App\\Controllers\\AutoriController' => $baseDir . '/app/Controllers/AutoriController.php', - 'App\\Controllers\\CmsController' => $baseDir . '/app/Controllers/CmsController.php', - 'App\\Controllers\\CollaneController' => $baseDir . '/app/Controllers/CollaneController.php', - 'App\\Controllers\\CollocazioneController' => $baseDir . '/app/Controllers/CollocazioneController.php', - 'App\\Controllers\\ContactController' => $baseDir . '/app/Controllers/ContactController.php', - 'App\\Controllers\\CookiesController' => $baseDir . '/app/Controllers/CookiesController.php', - 'App\\Controllers\\CopyController' => $baseDir . '/app/Controllers/CopyController.php', - 'App\\Controllers\\CoverController' => $baseDir . '/app/Controllers/CoverController.php', - 'App\\Controllers\\CsvImportController' => $baseDir . '/app/Controllers/CsvImportController.php', - 'App\\Controllers\\DashboardController' => $baseDir . '/app/Controllers/DashboardController.php', - 'App\\Controllers\\DeweyApiController' => $baseDir . '/app/Controllers/DeweyApiController.php', - 'App\\Controllers\\EditoriApiController' => $baseDir . '/app/Controllers/EditoriApiController.php', - 'App\\Controllers\\EditorsController' => $baseDir . '/app/Controllers/EditorsController.php', - 'App\\Controllers\\EventsController' => $baseDir . '/app/Controllers/EventsController.php', - 'App\\Controllers\\FeedController' => $baseDir . '/app/Controllers/FeedController.php', - 'App\\Controllers\\FrontendController' => $baseDir . '/app/Controllers/FrontendController.php', - 'App\\Controllers\\GeneriApiController' => $baseDir . '/app/Controllers/GeneriApiController.php', - 'App\\Controllers\\GeneriController' => $baseDir . '/app/Controllers/GeneriController.php', - 'App\\Controllers\\ImportHistoryController' => $baseDir . '/app/Controllers/ImportHistoryController.php', - 'App\\Controllers\\LanguageController' => $baseDir . '/app/Controllers/LanguageController.php', - 'App\\Controllers\\LibraryThingImportController' => $baseDir . '/app/Controllers/LibraryThingImportController.php', - 'App\\Controllers\\LibriApiController' => $baseDir . '/app/Controllers/LibriApiController.php', - 'App\\Controllers\\LibriController' => $baseDir . '/app/Controllers/LibriController.php', - 'App\\Controllers\\LoanApprovalController' => $baseDir . '/app/Controllers/LoanApprovalController.php', - 'App\\Controllers\\MaintenanceController' => $baseDir . '/app/Controllers/MaintenanceController.php', - 'App\\Controllers\\PasswordController' => $baseDir . '/app/Controllers/PasswordController.php', - 'App\\Controllers\\PluginController' => $baseDir . '/app/Controllers/PluginController.php', - 'App\\Controllers\\PrestitiApiController' => $baseDir . '/app/Controllers/PrestitiApiController.php', - 'App\\Controllers\\PrestitiController' => $baseDir . '/app/Controllers/PrestitiController.php', - 'App\\Controllers\\PrivacyController' => $baseDir . '/app/Controllers/PrivacyController.php', - 'App\\Controllers\\ProfileController' => $baseDir . '/app/Controllers/ProfileController.php', - 'App\\Controllers\\PublicApiController' => $baseDir . '/app/Controllers/PublicApiController.php', - 'App\\Controllers\\RecensioniController' => $baseDir . '/app/Controllers/RecensioniController.php', - 'App\\Controllers\\RegistrationController' => $baseDir . '/app/Controllers/RegistrationController.php', - 'App\\Controllers\\ReservationManager' => $baseDir . '/app/Controllers/ReservationManager.php', - 'App\\Controllers\\ReservationsAdminController' => $baseDir . '/app/Controllers/ReservationsAdminController.php', - 'App\\Controllers\\ReservationsController' => $baseDir . '/app/Controllers/ReservationsController.php', - 'App\\Controllers\\ScrapeController' => $baseDir . '/app/Controllers/ScrapeController.php', - 'App\\Controllers\\SearchController' => $baseDir . '/app/Controllers/SearchController.php', - 'App\\Controllers\\SecurityLogsController' => $baseDir . '/app/Controllers/SecurityLogsController.php', - 'App\\Controllers\\SeoController' => $baseDir . '/app/Controllers/SeoController.php', - 'App\\Controllers\\SettingsController' => $baseDir . '/app/Controllers/SettingsController.php', - 'App\\Controllers\\ThemeController' => $baseDir . '/app/Controllers/ThemeController.php', - 'App\\Controllers\\UpdateController' => $baseDir . '/app/Controllers/UpdateController.php', - 'App\\Controllers\\UserActionsController' => $baseDir . '/app/Controllers/UserActionsController.php', - 'App\\Controllers\\UserDashboardController' => $baseDir . '/app/Controllers/UserDashboardController.php', - 'App\\Controllers\\UserWishlistController' => $baseDir . '/app/Controllers/UserWishlistController.php', - 'App\\Controllers\\UsersController' => $baseDir . '/app/Controllers/UsersController.php', - 'App\\Controllers\\UtentiApiController' => $baseDir . '/app/Controllers/UtentiApiController.php', - 'App\\Middleware\\AdminAuthMiddleware' => $baseDir . '/app/Middleware/AdminAuthMiddleware.php', - 'App\\Middleware\\ApiKeyMiddleware' => $baseDir . '/app/Middleware/ApiKeyMiddleware.php', - 'App\\Middleware\\AuthMiddleware' => $baseDir . '/app/Middleware/AuthMiddleware.php', - 'App\\Middleware\\BasePathMiddleware' => $baseDir . '/app/Middleware/BasePathMiddleware.php', - 'App\\Middleware\\CsrfMiddleware' => $baseDir . '/app/Middleware/CsrfMiddleware.php', - 'App\\Middleware\\RateLimitMiddleware' => $baseDir . '/app/Middleware/RateLimitMiddleware.php', - 'App\\Middleware\\RememberMeMiddleware' => $baseDir . '/app/Middleware/RememberMeMiddleware.php', - 'App\\Models\\ApiKeyRepository' => $baseDir . '/app/Models/ApiKeyRepository.php', - 'App\\Models\\AuthorRepository' => $baseDir . '/app/Models/AuthorRepository.php', - 'App\\Models\\BookRepository' => $baseDir . '/app/Models/BookRepository.php', - 'App\\Models\\CollocationRepository' => $baseDir . '/app/Models/CollocationRepository.php', - 'App\\Models\\CopyRepository' => $baseDir . '/app/Models/CopyRepository.php', - 'App\\Models\\DashboardStats' => $baseDir . '/app/Models/DashboardStats.php', - 'App\\Models\\GenereRepository' => $baseDir . '/app/Models/GenereRepository.php', - 'App\\Models\\Language' => $baseDir . '/app/Models/Language.php', - 'App\\Models\\LoanRepository' => $baseDir . '/app/Models/LoanRepository.php', - 'App\\Models\\PublisherRepository' => $baseDir . '/app/Models/PublisherRepository.php', - 'App\\Models\\SettingsRepository' => $baseDir . '/app/Models/SettingsRepository.php', - 'App\\Models\\TaxonomyRepository' => $baseDir . '/app/Models/TaxonomyRepository.php', - 'App\\Models\\UserRepository' => $baseDir . '/app/Models/UserRepository.php', - 'App\\Repositories\\RecensioniRepository' => $baseDir . '/app/Repositories/RecensioniRepository.php', - 'App\\Services\\ReservationReassignmentService' => $baseDir . '/app/Services/ReservationReassignmentService.php', - 'App\\Support\\AuthorNormalizer' => $baseDir . '/app/Support/AuthorNormalizer.php', - 'App\\Support\\AuthorizationHelper' => $baseDir . '/app/Support/AuthorizationHelper.php', - 'App\\Support\\BookDataMerger' => $baseDir . '/app/Support/BookDataMerger.php', - 'App\\Support\\Branding' => $baseDir . '/app/Support/Branding.php', - 'App\\Support\\CmsHelper' => $baseDir . '/app/Support/CmsHelper.php', - 'App\\Support\\ConfigStore' => $baseDir . '/app/Support/ConfigStore.php', - 'App\\Support\\ContentSanitizer' => $baseDir . '/app/Support/ContentSanitizer.php', - 'App\\Support\\Csrf' => $baseDir . '/app/Support/Csrf.php', - 'App\\Support\\CsrfHelper' => $baseDir . '/app/Support/CsrfHelper.php', - 'App\\Support\\DataIntegrity' => $baseDir . '/app/Support/DataIntegrity.php', - 'App\\Support\\DateHelper' => $baseDir . '/app/Support/DateHelper.php', - 'App\\Support\\DeweyAutoPopulator' => $baseDir . '/app/Support/DeweyAutoPopulator.php', - 'App\\Support\\EmailService' => $baseDir . '/app/Support/EmailService.php', - 'App\\Support\\GenreHelper' => $baseDir . '/app/Support/GenreHelper.php', - 'App\\Support\\HookManager' => $baseDir . '/app/Support/HookManager.php', - 'App\\Support\\Hooks' => $baseDir . '/app/Support/Hooks.php', - 'App\\Support\\HreflangHelper' => $baseDir . '/app/Support/HreflangHelper.php', - 'App\\Support\\HtmlHelper' => $baseDir . '/app/Support/HtmlHelper.php', - 'App\\Support\\I18n' => $baseDir . '/app/Support/I18n.php', - 'App\\Support\\IcsGenerator' => $baseDir . '/app/Support/IcsGenerator.php', - 'App\\Support\\ImportLogger' => $baseDir . '/app/Support/ImportLogger.php', - 'App\\Support\\InputValidator' => $baseDir . '/app/Support/InputValidator.php', - 'App\\Support\\IsbnFormatter' => $baseDir . '/app/Support/IsbnFormatter.php', - 'App\\Support\\LibraryThingInstaller' => $baseDir . '/app/Support/LibraryThingInstaller.php', - 'App\\Support\\LoanPdfGenerator' => $baseDir . '/app/Support/LoanPdfGenerator.php', - 'App\\Support\\Log' => $baseDir . '/app/Support/Log.php', - 'App\\Support\\Mailer' => $baseDir . '/app/Support/Mailer.php', - 'App\\Support\\MaintenanceService' => $baseDir . '/app/Support/MaintenanceService.php', - 'App\\Support\\MergeHelper' => $baseDir . '/app/Support/MergeHelper.php', - 'App\\Support\\NotificationService' => $baseDir . '/app/Support/NotificationService.php', - 'App\\Support\\PluginManager' => $baseDir . '/app/Support/PluginManager.php', - 'App\\Support\\QueryCache' => $baseDir . '/app/Support/QueryCache.php', - 'App\\Support\\RateLimiter' => $baseDir . '/app/Support/RateLimiter.php', - 'App\\Support\\RememberMeService' => $baseDir . '/app/Support/RememberMeService.php', - 'App\\Support\\RouteTranslator' => $baseDir . '/app/Support/RouteTranslator.php', - 'App\\Support\\ScrapingService' => $baseDir . '/app/Support/ScrapingService.php', - 'App\\Support\\SecureLogger' => $baseDir . '/app/Support/SecureLogger.php', - 'App\\Support\\SettingsEncryption' => $baseDir . '/app/Support/SettingsEncryption.php', - 'App\\Support\\SettingsMailTemplates' => $baseDir . '/app/Support/SettingsMailTemplates.php', - 'App\\Support\\SharingProviders' => $baseDir . '/app/Support/SharingProviders.php', - 'App\\Support\\SitemapGenerator' => $baseDir . '/app/Support/SitemapGenerator.php', - 'App\\Support\\ThemeColorizer' => $baseDir . '/app/Support/ThemeColorizer.php', - 'App\\Support\\ThemeManager' => $baseDir . '/app/Support/ThemeManager.php', - 'App\\Support\\Updater' => $baseDir . '/app/Support/Updater.php', 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', - 'DI\\Attribute\\Inject' => $vendorDir . '/php-di/php-di/src/Attribute/Inject.php', - 'DI\\Attribute\\Injectable' => $vendorDir . '/php-di/php-di/src/Attribute/Injectable.php', - 'DI\\CompiledContainer' => $vendorDir . '/php-di/php-di/src/CompiledContainer.php', - 'DI\\Compiler\\Compiler' => $vendorDir . '/php-di/php-di/src/Compiler/Compiler.php', - 'DI\\Compiler\\ObjectCreationCompiler' => $vendorDir . '/php-di/php-di/src/Compiler/ObjectCreationCompiler.php', - 'DI\\Compiler\\RequestedEntryHolder' => $vendorDir . '/php-di/php-di/src/Compiler/RequestedEntryHolder.php', - 'DI\\Container' => $vendorDir . '/php-di/php-di/src/Container.php', - 'DI\\ContainerBuilder' => $vendorDir . '/php-di/php-di/src/ContainerBuilder.php', - 'DI\\Definition\\ArrayDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ArrayDefinition.php', - 'DI\\Definition\\ArrayDefinitionExtension' => $vendorDir . '/php-di/php-di/src/Definition/ArrayDefinitionExtension.php', - 'DI\\Definition\\AutowireDefinition' => $vendorDir . '/php-di/php-di/src/Definition/AutowireDefinition.php', - 'DI\\Definition\\DecoratorDefinition' => $vendorDir . '/php-di/php-di/src/Definition/DecoratorDefinition.php', - 'DI\\Definition\\Definition' => $vendorDir . '/php-di/php-di/src/Definition/Definition.php', - 'DI\\Definition\\Dumper\\ObjectDefinitionDumper' => $vendorDir . '/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php', - 'DI\\Definition\\EnvironmentVariableDefinition' => $vendorDir . '/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php', - 'DI\\Definition\\Exception\\InvalidAttribute' => $vendorDir . '/php-di/php-di/src/Definition/Exception/InvalidAttribute.php', - 'DI\\Definition\\Exception\\InvalidDefinition' => $vendorDir . '/php-di/php-di/src/Definition/Exception/InvalidDefinition.php', - 'DI\\Definition\\ExtendsPreviousDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php', - 'DI\\Definition\\FactoryDefinition' => $vendorDir . '/php-di/php-di/src/Definition/FactoryDefinition.php', - 'DI\\Definition\\Helper\\AutowireDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php', - 'DI\\Definition\\Helper\\CreateDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php', - 'DI\\Definition\\Helper\\DefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/DefinitionHelper.php', - 'DI\\Definition\\Helper\\FactoryDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php', - 'DI\\Definition\\InstanceDefinition' => $vendorDir . '/php-di/php-di/src/Definition/InstanceDefinition.php', - 'DI\\Definition\\ObjectDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition.php', - 'DI\\Definition\\ObjectDefinition\\MethodInjection' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php', - 'DI\\Definition\\ObjectDefinition\\PropertyInjection' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php', - 'DI\\Definition\\Reference' => $vendorDir . '/php-di/php-di/src/Definition/Reference.php', - 'DI\\Definition\\Resolver\\ArrayResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ArrayResolver.php', - 'DI\\Definition\\Resolver\\DecoratorResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php', - 'DI\\Definition\\Resolver\\DefinitionResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php', - 'DI\\Definition\\Resolver\\EnvironmentVariableResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php', - 'DI\\Definition\\Resolver\\FactoryResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/FactoryResolver.php', - 'DI\\Definition\\Resolver\\InstanceInjector' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/InstanceInjector.php', - 'DI\\Definition\\Resolver\\ObjectCreator' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ObjectCreator.php', - 'DI\\Definition\\Resolver\\ParameterResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ParameterResolver.php', - 'DI\\Definition\\Resolver\\ResolverDispatcher' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php', - 'DI\\Definition\\SelfResolvingDefinition' => $vendorDir . '/php-di/php-di/src/Definition/SelfResolvingDefinition.php', - 'DI\\Definition\\Source\\AttributeBasedAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php', - 'DI\\Definition\\Source\\Autowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/Autowiring.php', - 'DI\\Definition\\Source\\DefinitionArray' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionArray.php', - 'DI\\Definition\\Source\\DefinitionFile' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionFile.php', - 'DI\\Definition\\Source\\DefinitionNormalizer' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php', - 'DI\\Definition\\Source\\DefinitionSource' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionSource.php', - 'DI\\Definition\\Source\\MutableDefinitionSource' => $vendorDir . '/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php', - 'DI\\Definition\\Source\\NoAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/NoAutowiring.php', - 'DI\\Definition\\Source\\ReflectionBasedAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php', - 'DI\\Definition\\Source\\SourceCache' => $vendorDir . '/php-di/php-di/src/Definition/Source/SourceCache.php', - 'DI\\Definition\\Source\\SourceChain' => $vendorDir . '/php-di/php-di/src/Definition/Source/SourceChain.php', - 'DI\\Definition\\StringDefinition' => $vendorDir . '/php-di/php-di/src/Definition/StringDefinition.php', - 'DI\\Definition\\ValueDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ValueDefinition.php', - 'DI\\DependencyException' => $vendorDir . '/php-di/php-di/src/DependencyException.php', - 'DI\\FactoryInterface' => $vendorDir . '/php-di/php-di/src/FactoryInterface.php', - 'DI\\Factory\\RequestedEntry' => $vendorDir . '/php-di/php-di/src/Factory/RequestedEntry.php', - 'DI\\Invoker\\DefinitionParameterResolver' => $vendorDir . '/php-di/php-di/src/Invoker/DefinitionParameterResolver.php', - 'DI\\Invoker\\FactoryParameterResolver' => $vendorDir . '/php-di/php-di/src/Invoker/FactoryParameterResolver.php', - 'DI\\NotFoundException' => $vendorDir . '/php-di/php-di/src/NotFoundException.php', - 'DI\\Proxy\\NativeProxyFactory' => $vendorDir . '/php-di/php-di/src/Proxy/NativeProxyFactory.php', - 'DI\\Proxy\\ProxyFactory' => $vendorDir . '/php-di/php-di/src/Proxy/ProxyFactory.php', - 'DI\\Proxy\\ProxyFactoryInterface' => $vendorDir . '/php-di/php-di/src/Proxy/ProxyFactoryInterface.php', 'Datamatrix' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php', - 'Dotenv\\Dotenv' => $vendorDir . '/vlucas/phpdotenv/src/Dotenv.php', - 'Dotenv\\Exception\\ExceptionInterface' => $vendorDir . '/vlucas/phpdotenv/src/Exception/ExceptionInterface.php', - 'Dotenv\\Exception\\InvalidEncodingException' => $vendorDir . '/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php', - 'Dotenv\\Exception\\InvalidFileException' => $vendorDir . '/vlucas/phpdotenv/src/Exception/InvalidFileException.php', - 'Dotenv\\Exception\\InvalidPathException' => $vendorDir . '/vlucas/phpdotenv/src/Exception/InvalidPathException.php', - 'Dotenv\\Exception\\ValidationException' => $vendorDir . '/vlucas/phpdotenv/src/Exception/ValidationException.php', - 'Dotenv\\Loader\\Loader' => $vendorDir . '/vlucas/phpdotenv/src/Loader/Loader.php', - 'Dotenv\\Loader\\LoaderInterface' => $vendorDir . '/vlucas/phpdotenv/src/Loader/LoaderInterface.php', - 'Dotenv\\Loader\\Resolver' => $vendorDir . '/vlucas/phpdotenv/src/Loader/Resolver.php', - 'Dotenv\\Parser\\Entry' => $vendorDir . '/vlucas/phpdotenv/src/Parser/Entry.php', - 'Dotenv\\Parser\\EntryParser' => $vendorDir . '/vlucas/phpdotenv/src/Parser/EntryParser.php', - 'Dotenv\\Parser\\Lexer' => $vendorDir . '/vlucas/phpdotenv/src/Parser/Lexer.php', - 'Dotenv\\Parser\\Lines' => $vendorDir . '/vlucas/phpdotenv/src/Parser/Lines.php', - 'Dotenv\\Parser\\Parser' => $vendorDir . '/vlucas/phpdotenv/src/Parser/Parser.php', - 'Dotenv\\Parser\\ParserInterface' => $vendorDir . '/vlucas/phpdotenv/src/Parser/ParserInterface.php', - 'Dotenv\\Parser\\Value' => $vendorDir . '/vlucas/phpdotenv/src/Parser/Value.php', - 'Dotenv\\Repository\\AdapterRepository' => $vendorDir . '/vlucas/phpdotenv/src/Repository/AdapterRepository.php', - 'Dotenv\\Repository\\Adapter\\AdapterInterface' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php', - 'Dotenv\\Repository\\Adapter\\ApacheAdapter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php', - 'Dotenv\\Repository\\Adapter\\ArrayAdapter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php', - 'Dotenv\\Repository\\Adapter\\EnvConstAdapter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php', - 'Dotenv\\Repository\\Adapter\\GuardedWriter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php', - 'Dotenv\\Repository\\Adapter\\ImmutableWriter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php', - 'Dotenv\\Repository\\Adapter\\MultiReader' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php', - 'Dotenv\\Repository\\Adapter\\MultiWriter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php', - 'Dotenv\\Repository\\Adapter\\PutenvAdapter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php', - 'Dotenv\\Repository\\Adapter\\ReaderInterface' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php', - 'Dotenv\\Repository\\Adapter\\ReplacingWriter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php', - 'Dotenv\\Repository\\Adapter\\ServerConstAdapter' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php', - 'Dotenv\\Repository\\Adapter\\WriterInterface' => $vendorDir . '/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php', - 'Dotenv\\Repository\\RepositoryBuilder' => $vendorDir . '/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php', - 'Dotenv\\Repository\\RepositoryInterface' => $vendorDir . '/vlucas/phpdotenv/src/Repository/RepositoryInterface.php', - 'Dotenv\\Store\\FileStore' => $vendorDir . '/vlucas/phpdotenv/src/Store/FileStore.php', - 'Dotenv\\Store\\File\\Paths' => $vendorDir . '/vlucas/phpdotenv/src/Store/File/Paths.php', - 'Dotenv\\Store\\File\\Reader' => $vendorDir . '/vlucas/phpdotenv/src/Store/File/Reader.php', - 'Dotenv\\Store\\StoreBuilder' => $vendorDir . '/vlucas/phpdotenv/src/Store/StoreBuilder.php', - 'Dotenv\\Store\\StoreInterface' => $vendorDir . '/vlucas/phpdotenv/src/Store/StoreInterface.php', - 'Dotenv\\Store\\StringStore' => $vendorDir . '/vlucas/phpdotenv/src/Store/StringStore.php', - 'Dotenv\\Util\\Regex' => $vendorDir . '/vlucas/phpdotenv/src/Util/Regex.php', - 'Dotenv\\Util\\Str' => $vendorDir . '/vlucas/phpdotenv/src/Util/Str.php', - 'Dotenv\\Validator' => $vendorDir . '/vlucas/phpdotenv/src/Validator.php', - 'Emleons\\SimRating\\Interfaces\\RendererInterface' => $vendorDir . '/emleons/sim-rating/src/Interfaces/RendererInterface.php', - 'Emleons\\SimRating\\Rating' => $vendorDir . '/emleons/sim-rating/src/Rating.php', - 'Emleons\\SimRating\\Renderer\\HtmlRenderer' => $vendorDir . '/emleons/sim-rating/src/Renderer/HtmlRenderer.php', - 'Emleons\\SimRating\\Renderer\\JsonRenderer' => $vendorDir . '/emleons/sim-rating/src/Renderer/JsonRenderer.php', - 'Emleons\\SimRating\\Renderer\\SvgRenderer' => $vendorDir . '/emleons/sim-rating/src/Renderer/SvgRenderer.php', - 'FastRoute\\BadRouteException' => $vendorDir . '/nikic/fast-route/src/BadRouteException.php', - 'FastRoute\\DataGenerator' => $vendorDir . '/nikic/fast-route/src/DataGenerator.php', - 'FastRoute\\DataGenerator\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/CharCountBased.php', - 'FastRoute\\DataGenerator\\GroupCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/GroupCountBased.php', - 'FastRoute\\DataGenerator\\GroupPosBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/GroupPosBased.php', - 'FastRoute\\DataGenerator\\MarkBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/MarkBased.php', - 'FastRoute\\DataGenerator\\RegexBasedAbstract' => $vendorDir . '/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php', - 'FastRoute\\Dispatcher' => $vendorDir . '/nikic/fast-route/src/Dispatcher.php', - 'FastRoute\\Dispatcher\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/CharCountBased.php', - 'FastRoute\\Dispatcher\\GroupCountBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/GroupCountBased.php', - 'FastRoute\\Dispatcher\\GroupPosBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/GroupPosBased.php', - 'FastRoute\\Dispatcher\\MarkBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/MarkBased.php', - 'FastRoute\\Dispatcher\\RegexBasedAbstract' => $vendorDir . '/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php', - 'FastRoute\\Route' => $vendorDir . '/nikic/fast-route/src/Route.php', - 'FastRoute\\RouteCollector' => $vendorDir . '/nikic/fast-route/src/RouteCollector.php', - 'FastRoute\\RouteParser' => $vendorDir . '/nikic/fast-route/src/RouteParser.php', - 'FastRoute\\RouteParser\\Std' => $vendorDir . '/nikic/fast-route/src/RouteParser/Std.php', - 'Fig\\Http\\Message\\RequestMethodInterface' => $vendorDir . '/fig/http-message-util/src/RequestMethodInterface.php', - 'Fig\\Http\\Message\\StatusCodeInterface' => $vendorDir . '/fig/http-message-util/src/StatusCodeInterface.php', - 'GrahamCampbell\\ResultType\\Error' => $vendorDir . '/graham-campbell/result-type/src/Error.php', - 'GrahamCampbell\\ResultType\\Result' => $vendorDir . '/graham-campbell/result-type/src/Result.php', - 'GrahamCampbell\\ResultType\\Success' => $vendorDir . '/graham-campbell/result-type/src/Success.php', - 'Invoker\\CallableResolver' => $vendorDir . '/php-di/invoker/src/CallableResolver.php', - 'Invoker\\Exception\\InvocationException' => $vendorDir . '/php-di/invoker/src/Exception/InvocationException.php', - 'Invoker\\Exception\\NotCallableException' => $vendorDir . '/php-di/invoker/src/Exception/NotCallableException.php', - 'Invoker\\Exception\\NotEnoughParametersException' => $vendorDir . '/php-di/invoker/src/Exception/NotEnoughParametersException.php', - 'Invoker\\Invoker' => $vendorDir . '/php-di/invoker/src/Invoker.php', - 'Invoker\\InvokerInterface' => $vendorDir . '/php-di/invoker/src/InvokerInterface.php', - 'Invoker\\ParameterResolver\\AssociativeArrayResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php', - 'Invoker\\ParameterResolver\\Container\\ParameterNameContainerResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php', - 'Invoker\\ParameterResolver\\Container\\TypeHintContainerResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php', - 'Invoker\\ParameterResolver\\DefaultValueResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php', - 'Invoker\\ParameterResolver\\NumericArrayResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php', - 'Invoker\\ParameterResolver\\ParameterResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/ParameterResolver.php', - 'Invoker\\ParameterResolver\\ResolverChain' => $vendorDir . '/php-di/invoker/src/ParameterResolver/ResolverChain.php', - 'Invoker\\ParameterResolver\\TypeHintResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/TypeHintResolver.php', - 'Invoker\\Reflection\\CallableReflection' => $vendorDir . '/php-di/invoker/src/Reflection/CallableReflection.php', - 'Laravel\\SerializableClosure\\Contracts\\Serializable' => $vendorDir . '/laravel/serializable-closure/src/Contracts/Serializable.php', - 'Laravel\\SerializableClosure\\Contracts\\Signer' => $vendorDir . '/laravel/serializable-closure/src/Contracts/Signer.php', - 'Laravel\\SerializableClosure\\Exceptions\\InvalidSignatureException' => $vendorDir . '/laravel/serializable-closure/src/Exceptions/InvalidSignatureException.php', - 'Laravel\\SerializableClosure\\Exceptions\\MissingSecretKeyException' => $vendorDir . '/laravel/serializable-closure/src/Exceptions/MissingSecretKeyException.php', - 'Laravel\\SerializableClosure\\Exceptions\\PhpVersionNotSupportedException' => $vendorDir . '/laravel/serializable-closure/src/Exceptions/PhpVersionNotSupportedException.php', - 'Laravel\\SerializableClosure\\SerializableClosure' => $vendorDir . '/laravel/serializable-closure/src/SerializableClosure.php', - 'Laravel\\SerializableClosure\\Serializers\\Native' => $vendorDir . '/laravel/serializable-closure/src/Serializers/Native.php', - 'Laravel\\SerializableClosure\\Serializers\\Signed' => $vendorDir . '/laravel/serializable-closure/src/Serializers/Signed.php', - 'Laravel\\SerializableClosure\\Signers\\Hmac' => $vendorDir . '/laravel/serializable-closure/src/Signers/Hmac.php', - 'Laravel\\SerializableClosure\\Support\\ClosureScope' => $vendorDir . '/laravel/serializable-closure/src/Support/ClosureScope.php', - 'Laravel\\SerializableClosure\\Support\\ClosureStream' => $vendorDir . '/laravel/serializable-closure/src/Support/ClosureStream.php', - 'Laravel\\SerializableClosure\\Support\\ReflectionClosure' => $vendorDir . '/laravel/serializable-closure/src/Support/ReflectionClosure.php', - 'Laravel\\SerializableClosure\\Support\\SelfReference' => $vendorDir . '/laravel/serializable-closure/src/Support/SelfReference.php', - 'Laravel\\SerializableClosure\\UnsignedSerializableClosure' => $vendorDir . '/laravel/serializable-closure/src/UnsignedSerializableClosure.php', - 'Monolog\\Attribute\\AsMonologProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php', - 'Monolog\\Attribute\\WithMonologChannel' => $vendorDir . '/monolog/monolog/src/Monolog/Attribute/WithMonologChannel.php', - 'Monolog\\DateTimeImmutable' => $vendorDir . '/monolog/monolog/src/Monolog/DateTimeImmutable.php', - 'Monolog\\ErrorHandler' => $vendorDir . '/monolog/monolog/src/Monolog/ErrorHandler.php', - 'Monolog\\Formatter\\ChromePHPFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php', - 'Monolog\\Formatter\\ElasticaFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php', - 'Monolog\\Formatter\\ElasticsearchFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php', - 'Monolog\\Formatter\\FlowdockFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php', - 'Monolog\\Formatter\\FluentdFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php', - 'Monolog\\Formatter\\FormatterInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php', - 'Monolog\\Formatter\\GelfMessageFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php', - 'Monolog\\Formatter\\GoogleCloudLoggingFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php', - 'Monolog\\Formatter\\HtmlFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php', - 'Monolog\\Formatter\\JsonFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php', - 'Monolog\\Formatter\\LineFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LineFormatter.php', - 'Monolog\\Formatter\\LogglyFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php', - 'Monolog\\Formatter\\LogmaticFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php', - 'Monolog\\Formatter\\LogstashFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php', - 'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php', - 'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php', - 'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php', - 'Monolog\\Formatter\\SyslogFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php', - 'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php', - 'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php', - 'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php', - 'Monolog\\Handler\\AbstractSyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php', - 'Monolog\\Handler\\AmqpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AmqpHandler.php', - 'Monolog\\Handler\\BrowserConsoleHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php', - 'Monolog\\Handler\\BufferHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/BufferHandler.php', - 'Monolog\\Handler\\ChromePHPHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php', - 'Monolog\\Handler\\CouchDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php', - 'Monolog\\Handler\\CubeHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/CubeHandler.php', - 'Monolog\\Handler\\Curl\\Util' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Curl/Util.php', - 'Monolog\\Handler\\DeduplicationHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php', - 'Monolog\\Handler\\DoctrineCouchDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php', - 'Monolog\\Handler\\DynamoDbHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php', - 'Monolog\\Handler\\ElasticaHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php', - 'Monolog\\Handler\\ElasticsearchHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php', - 'Monolog\\Handler\\ErrorLogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php', - 'Monolog\\Handler\\FallbackGroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php', - 'Monolog\\Handler\\FilterHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FilterHandler.php', - 'Monolog\\Handler\\FingersCrossedHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php', - 'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php', - 'Monolog\\Handler\\FingersCrossed\\ChannelLevelActivationStrategy' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php', - 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php', - 'Monolog\\Handler\\FirePHPHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php', - 'Monolog\\Handler\\FleepHookHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php', - 'Monolog\\Handler\\FlowdockHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php', - 'Monolog\\Handler\\FormattableHandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php', - 'Monolog\\Handler\\FormattableHandlerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php', - 'Monolog\\Handler\\GelfHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/GelfHandler.php', - 'Monolog\\Handler\\GroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/GroupHandler.php', - 'Monolog\\Handler\\Handler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Handler.php', - 'Monolog\\Handler\\HandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/HandlerInterface.php', - 'Monolog\\Handler\\HandlerWrapper' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php', - 'Monolog\\Handler\\IFTTTHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php', - 'Monolog\\Handler\\InsightOpsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php', - 'Monolog\\Handler\\LogEntriesHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php', - 'Monolog\\Handler\\LogglyHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogglyHandler.php', - 'Monolog\\Handler\\LogmaticHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php', - 'Monolog\\Handler\\MailHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MailHandler.php', - 'Monolog\\Handler\\MandrillHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MandrillHandler.php', - 'Monolog\\Handler\\MissingExtensionException' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php', - 'Monolog\\Handler\\MongoDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php', - 'Monolog\\Handler\\NativeMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php', - 'Monolog\\Handler\\NewRelicHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php', - 'Monolog\\Handler\\NoopHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NoopHandler.php', - 'Monolog\\Handler\\NullHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NullHandler.php', - 'Monolog\\Handler\\OverflowHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/OverflowHandler.php', - 'Monolog\\Handler\\PHPConsoleHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php', - 'Monolog\\Handler\\ProcessHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessHandler.php', - 'Monolog\\Handler\\ProcessableHandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php', - 'Monolog\\Handler\\ProcessableHandlerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php', - 'Monolog\\Handler\\PsrHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PsrHandler.php', - 'Monolog\\Handler\\PushoverHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PushoverHandler.php', - 'Monolog\\Handler\\RedisHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RedisHandler.php', - 'Monolog\\Handler\\RedisPubSubHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php', - 'Monolog\\Handler\\RollbarHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RollbarHandler.php', - 'Monolog\\Handler\\RotatingFileHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php', - 'Monolog\\Handler\\SamplingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SamplingHandler.php', - 'Monolog\\Handler\\SendGridHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SendGridHandler.php', - 'Monolog\\Handler\\SlackHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SlackHandler.php', - 'Monolog\\Handler\\SlackWebhookHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php', - 'Monolog\\Handler\\Slack\\SlackRecord' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php', - 'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php', - 'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php', - 'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php', - 'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php', - 'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php', - 'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php', - 'Monolog\\Handler\\SyslogUdp\\UdpSocket' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php', - 'Monolog\\Handler\\TelegramBotHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php', - 'Monolog\\Handler\\TestHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/TestHandler.php', - 'Monolog\\Handler\\WebRequestRecognizerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php', - 'Monolog\\Handler\\WhatFailureGroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php', - 'Monolog\\Handler\\ZendMonitorHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php', - 'Monolog\\JsonSerializableDateTimeImmutable' => $vendorDir . '/monolog/monolog/src/Monolog/JsonSerializableDateTimeImmutable.php', - 'Monolog\\Level' => $vendorDir . '/monolog/monolog/src/Monolog/Level.php', - 'Monolog\\LogRecord' => $vendorDir . '/monolog/monolog/src/Monolog/LogRecord.php', - 'Monolog\\Logger' => $vendorDir . '/monolog/monolog/src/Monolog/Logger.php', - 'Monolog\\Processor\\ClosureContextProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ClosureContextProcessor.php', - 'Monolog\\Processor\\GitProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/GitProcessor.php', - 'Monolog\\Processor\\HostnameProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php', - 'Monolog\\Processor\\IntrospectionProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php', - 'Monolog\\Processor\\LoadAverageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/LoadAverageProcessor.php', - 'Monolog\\Processor\\MemoryPeakUsageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php', - 'Monolog\\Processor\\MemoryProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php', - 'Monolog\\Processor\\MemoryUsageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php', - 'Monolog\\Processor\\MercurialProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php', - 'Monolog\\Processor\\ProcessIdProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php', - 'Monolog\\Processor\\ProcessorInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php', - 'Monolog\\Processor\\PsrLogMessageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php', - 'Monolog\\Processor\\TagProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/TagProcessor.php', - 'Monolog\\Processor\\UidProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/UidProcessor.php', - 'Monolog\\Processor\\WebProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/WebProcessor.php', - 'Monolog\\Registry' => $vendorDir . '/monolog/monolog/src/Monolog/Registry.php', - 'Monolog\\ResettableInterface' => $vendorDir . '/monolog/monolog/src/Monolog/ResettableInterface.php', - 'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php', - 'Monolog\\Test\\MonologTestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/MonologTestCase.php', - 'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php', - 'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php', 'PDF417' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/pdf417.php', - 'PHPMailer\\PHPMailer\\DSNConfigurator' => $vendorDir . '/phpmailer/phpmailer/src/DSNConfigurator.php', - 'PHPMailer\\PHPMailer\\Exception' => $vendorDir . '/phpmailer/phpmailer/src/Exception.php', - 'PHPMailer\\PHPMailer\\OAuth' => $vendorDir . '/phpmailer/phpmailer/src/OAuth.php', - 'PHPMailer\\PHPMailer\\OAuthTokenProvider' => $vendorDir . '/phpmailer/phpmailer/src/OAuthTokenProvider.php', - 'PHPMailer\\PHPMailer\\PHPMailer' => $vendorDir . '/phpmailer/phpmailer/src/PHPMailer.php', - 'PHPMailer\\PHPMailer\\POP3' => $vendorDir . '/phpmailer/phpmailer/src/POP3.php', - 'PHPMailer\\PHPMailer\\SMTP' => $vendorDir . '/phpmailer/phpmailer/src/SMTP.php', - 'PhpOption\\LazyOption' => $vendorDir . '/phpoption/phpoption/src/PhpOption/LazyOption.php', - 'PhpOption\\None' => $vendorDir . '/phpoption/phpoption/src/PhpOption/None.php', - 'PhpOption\\Option' => $vendorDir . '/phpoption/phpoption/src/PhpOption/Option.php', - 'PhpOption\\Some' => $vendorDir . '/phpoption/phpoption/src/PhpOption/Some.php', 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php', - 'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php', - 'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php', - 'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php', - 'Psr\\Http\\Message\\RequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/RequestFactoryInterface.php', - 'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php', - 'Psr\\Http\\Message\\ResponseFactoryInterface' => $vendorDir . '/psr/http-factory/src/ResponseFactoryInterface.php', - 'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php', - 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/ServerRequestFactoryInterface.php', - 'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php', - 'Psr\\Http\\Message\\StreamFactoryInterface' => $vendorDir . '/psr/http-factory/src/StreamFactoryInterface.php', - 'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php', - 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => $vendorDir . '/psr/http-factory/src/UploadedFileFactoryInterface.php', - 'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php', - 'Psr\\Http\\Message\\UriFactoryInterface' => $vendorDir . '/psr/http-factory/src/UriFactoryInterface.php', - 'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php', - 'Psr\\Http\\Server\\MiddlewareInterface' => $vendorDir . '/psr/http-server-middleware/src/MiddlewareInterface.php', - 'Psr\\Http\\Server\\RequestHandlerInterface' => $vendorDir . '/psr/http-server-handler/src/RequestHandlerInterface.php', - 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php', - 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php', - 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php', - 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php', - 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php', - 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php', - 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php', - 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php', 'QRcode' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/qrcode.php', - 'ReCaptcha\\ReCaptcha' => $vendorDir . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', - 'ReCaptcha\\RequestMethod' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', - 'ReCaptcha\\RequestMethod\\Curl' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', - 'ReCaptcha\\RequestMethod\\CurlPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', - 'ReCaptcha\\RequestMethod\\Post' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', - 'ReCaptcha\\RequestMethod\\Socket' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', - 'ReCaptcha\\RequestMethod\\SocketPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', - 'ReCaptcha\\RequestParameters' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', - 'ReCaptcha\\Response' => $vendorDir . '/google/recaptcha/src/ReCaptcha/Response.php', - 'Slim\\App' => $vendorDir . '/slim/slim/Slim/App.php', - 'Slim\\CallableResolver' => $vendorDir . '/slim/slim/Slim/CallableResolver.php', - 'Slim\\Csrf\\Guard' => $vendorDir . '/slim/csrf/src/Guard.php', - 'Slim\\Error\\AbstractErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/AbstractErrorRenderer.php', - 'Slim\\Error\\Renderers\\HtmlErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/Renderers/HtmlErrorRenderer.php', - 'Slim\\Error\\Renderers\\JsonErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/Renderers/JsonErrorRenderer.php', - 'Slim\\Error\\Renderers\\PlainTextErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/Renderers/PlainTextErrorRenderer.php', - 'Slim\\Error\\Renderers\\XmlErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/Renderers/XmlErrorRenderer.php', - 'Slim\\Exception\\HttpBadRequestException' => $vendorDir . '/slim/slim/Slim/Exception/HttpBadRequestException.php', - 'Slim\\Exception\\HttpException' => $vendorDir . '/slim/slim/Slim/Exception/HttpException.php', - 'Slim\\Exception\\HttpForbiddenException' => $vendorDir . '/slim/slim/Slim/Exception/HttpForbiddenException.php', - 'Slim\\Exception\\HttpGoneException' => $vendorDir . '/slim/slim/Slim/Exception/HttpGoneException.php', - 'Slim\\Exception\\HttpInternalServerErrorException' => $vendorDir . '/slim/slim/Slim/Exception/HttpInternalServerErrorException.php', - 'Slim\\Exception\\HttpMethodNotAllowedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpMethodNotAllowedException.php', - 'Slim\\Exception\\HttpNotFoundException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotFoundException.php', - 'Slim\\Exception\\HttpNotImplementedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotImplementedException.php', - 'Slim\\Exception\\HttpSpecializedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpSpecializedException.php', - 'Slim\\Exception\\HttpTooManyRequestsException' => $vendorDir . '/slim/slim/Slim/Exception/HttpTooManyRequestsException.php', - 'Slim\\Exception\\HttpUnauthorizedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php', - 'Slim\\Factory\\AppFactory' => $vendorDir . '/slim/slim/Slim/Factory/AppFactory.php', - 'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php', - 'Slim\\Factory\\Psr17\\HttpSoftPsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/HttpSoftPsr17Factory.php', - 'Slim\\Factory\\Psr17\\LaminasDiactorosPsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/LaminasDiactorosPsr17Factory.php', - 'Slim\\Factory\\Psr17\\NyholmPsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/NyholmPsr17Factory.php', - 'Slim\\Factory\\Psr17\\Psr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/Psr17Factory.php', - 'Slim\\Factory\\Psr17\\Psr17FactoryProvider' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/Psr17FactoryProvider.php', - 'Slim\\Factory\\Psr17\\ServerRequestCreator' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/ServerRequestCreator.php', - 'Slim\\Factory\\Psr17\\SlimHttpPsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/SlimHttpPsr17Factory.php', - 'Slim\\Factory\\Psr17\\SlimHttpServerRequestCreator' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/SlimHttpServerRequestCreator.php', - 'Slim\\Factory\\Psr17\\SlimPsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/SlimPsr17Factory.php', - 'Slim\\Factory\\ServerRequestCreatorFactory' => $vendorDir . '/slim/slim/Slim/Factory/ServerRequestCreatorFactory.php', - 'Slim\\Handlers\\ErrorHandler' => $vendorDir . '/slim/slim/Slim/Handlers/ErrorHandler.php', - 'Slim\\Handlers\\Strategies\\RequestHandler' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestHandler.php', - 'Slim\\Handlers\\Strategies\\RequestResponse' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestResponse.php', - 'Slim\\Handlers\\Strategies\\RequestResponseArgs' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestResponseArgs.php', - 'Slim\\Handlers\\Strategies\\RequestResponseNamedArgs' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestResponseNamedArgs.php', - 'Slim\\Interfaces\\AdvancedCallableResolverInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/AdvancedCallableResolverInterface.php', - 'Slim\\Interfaces\\CallableResolverInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/CallableResolverInterface.php', - 'Slim\\Interfaces\\DispatcherInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/DispatcherInterface.php', - 'Slim\\Interfaces\\ErrorHandlerInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/ErrorHandlerInterface.php', - 'Slim\\Interfaces\\ErrorRendererInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/ErrorRendererInterface.php', - 'Slim\\Interfaces\\InvocationStrategyInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/InvocationStrategyInterface.php', - 'Slim\\Interfaces\\MiddlewareDispatcherInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/MiddlewareDispatcherInterface.php', - 'Slim\\Interfaces\\Psr17FactoryInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/Psr17FactoryInterface.php', - 'Slim\\Interfaces\\Psr17FactoryProviderInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/Psr17FactoryProviderInterface.php', - 'Slim\\Interfaces\\RequestHandlerInvocationStrategyInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RequestHandlerInvocationStrategyInterface.php', - 'Slim\\Interfaces\\RouteCollectorInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteCollectorInterface.php', - 'Slim\\Interfaces\\RouteCollectorProxyInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteCollectorProxyInterface.php', - 'Slim\\Interfaces\\RouteGroupInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteGroupInterface.php', - 'Slim\\Interfaces\\RouteInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteInterface.php', - 'Slim\\Interfaces\\RouteParserInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteParserInterface.php', - 'Slim\\Interfaces\\RouteResolverInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteResolverInterface.php', - 'Slim\\Interfaces\\ServerRequestCreatorInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/ServerRequestCreatorInterface.php', - 'Slim\\Logger' => $vendorDir . '/slim/slim/Slim/Logger.php', - 'Slim\\MiddlewareDispatcher' => $vendorDir . '/slim/slim/Slim/MiddlewareDispatcher.php', - 'Slim\\Middleware\\BodyParsingMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/BodyParsingMiddleware.php', - 'Slim\\Middleware\\ContentLengthMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/ContentLengthMiddleware.php', - 'Slim\\Middleware\\ErrorMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/ErrorMiddleware.php', - 'Slim\\Middleware\\MethodOverrideMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/MethodOverrideMiddleware.php', - 'Slim\\Middleware\\OutputBufferingMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/OutputBufferingMiddleware.php', - 'Slim\\Middleware\\RoutingMiddleware' => $vendorDir . '/slim/slim/Slim/Middleware/RoutingMiddleware.php', - 'Slim\\Psr7\\Cookies' => $vendorDir . '/slim/psr7/src/Cookies.php', - 'Slim\\Psr7\\Environment' => $vendorDir . '/slim/psr7/src/Environment.php', - 'Slim\\Psr7\\Factory\\RequestFactory' => $vendorDir . '/slim/psr7/src/Factory/RequestFactory.php', - 'Slim\\Psr7\\Factory\\ResponseFactory' => $vendorDir . '/slim/psr7/src/Factory/ResponseFactory.php', - 'Slim\\Psr7\\Factory\\ServerRequestFactory' => $vendorDir . '/slim/psr7/src/Factory/ServerRequestFactory.php', - 'Slim\\Psr7\\Factory\\StreamFactory' => $vendorDir . '/slim/psr7/src/Factory/StreamFactory.php', - 'Slim\\Psr7\\Factory\\UploadedFileFactory' => $vendorDir . '/slim/psr7/src/Factory/UploadedFileFactory.php', - 'Slim\\Psr7\\Factory\\UriFactory' => $vendorDir . '/slim/psr7/src/Factory/UriFactory.php', - 'Slim\\Psr7\\Header' => $vendorDir . '/slim/psr7/src/Header.php', - 'Slim\\Psr7\\Headers' => $vendorDir . '/slim/psr7/src/Headers.php', - 'Slim\\Psr7\\Interfaces\\HeadersInterface' => $vendorDir . '/slim/psr7/src/Interfaces/HeadersInterface.php', - 'Slim\\Psr7\\Message' => $vendorDir . '/slim/psr7/src/Message.php', - 'Slim\\Psr7\\NonBufferedBody' => $vendorDir . '/slim/psr7/src/NonBufferedBody.php', - 'Slim\\Psr7\\Request' => $vendorDir . '/slim/psr7/src/Request.php', - 'Slim\\Psr7\\Response' => $vendorDir . '/slim/psr7/src/Response.php', - 'Slim\\Psr7\\Stream' => $vendorDir . '/slim/psr7/src/Stream.php', - 'Slim\\Psr7\\UploadedFile' => $vendorDir . '/slim/psr7/src/UploadedFile.php', - 'Slim\\Psr7\\Uri' => $vendorDir . '/slim/psr7/src/Uri.php', - 'Slim\\ResponseEmitter' => $vendorDir . '/slim/slim/Slim/ResponseEmitter.php', - 'Slim\\Routing\\Dispatcher' => $vendorDir . '/slim/slim/Slim/Routing/Dispatcher.php', - 'Slim\\Routing\\FastRouteDispatcher' => $vendorDir . '/slim/slim/Slim/Routing/FastRouteDispatcher.php', - 'Slim\\Routing\\Route' => $vendorDir . '/slim/slim/Slim/Routing/Route.php', - 'Slim\\Routing\\RouteCollector' => $vendorDir . '/slim/slim/Slim/Routing/RouteCollector.php', - 'Slim\\Routing\\RouteCollectorProxy' => $vendorDir . '/slim/slim/Slim/Routing/RouteCollectorProxy.php', - 'Slim\\Routing\\RouteContext' => $vendorDir . '/slim/slim/Slim/Routing/RouteContext.php', - 'Slim\\Routing\\RouteGroup' => $vendorDir . '/slim/slim/Slim/Routing/RouteGroup.php', - 'Slim\\Routing\\RouteParser' => $vendorDir . '/slim/slim/Slim/Routing/RouteParser.php', - 'Slim\\Routing\\RouteResolver' => $vendorDir . '/slim/slim/Slim/Routing/RouteResolver.php', - 'Slim\\Routing\\RouteRunner' => $vendorDir . '/slim/slim/Slim/Routing/RouteRunner.php', - 'Slim\\Routing\\RoutingResults' => $vendorDir . '/slim/slim/Slim/Routing/RoutingResults.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', - 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', - 'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php', - 'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php', 'TCPDF' => $vendorDir . '/tecnickcom/tcpdf/tcpdf.php', 'TCPDF2DBarcode' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_barcodes_2d.php', 'TCPDFBarcode' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_barcodes_1d.php', @@ -564,21 +22,6 @@ 'TCPDF_FONT_DATA' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_font_data.php', 'TCPDF_IMAGES' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_images.php', 'TCPDF_STATIC' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_static.php', - 'Thepixeldeveloper\\Sitemap\\ChunkedCollection' => $vendorDir . '/thepixeldeveloper/sitemap/src/ChunkedCollection.php', - 'Thepixeldeveloper\\Sitemap\\ChunkedUrlset' => $vendorDir . '/thepixeldeveloper/sitemap/src/ChunkedUrlset.php', - 'Thepixeldeveloper\\Sitemap\\Collection' => $vendorDir . '/thepixeldeveloper/sitemap/src/Collection.php', - 'Thepixeldeveloper\\Sitemap\\Drivers\\XmlWriterDriver' => $vendorDir . '/thepixeldeveloper/sitemap/src/Drivers/XmlWriterDriver.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Image' => $vendorDir . '/thepixeldeveloper/sitemap/src/Extensions/Image.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Link' => $vendorDir . '/thepixeldeveloper/sitemap/src/Extensions/Link.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Mobile' => $vendorDir . '/thepixeldeveloper/sitemap/src/Extensions/Mobile.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\News' => $vendorDir . '/thepixeldeveloper/sitemap/src/Extensions/News.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Video' => $vendorDir . '/thepixeldeveloper/sitemap/src/Extensions/Video.php', - 'Thepixeldeveloper\\Sitemap\\Interfaces\\DriverInterface' => $vendorDir . '/thepixeldeveloper/sitemap/src/Interfaces/DriverInterface.php', - 'Thepixeldeveloper\\Sitemap\\Interfaces\\VisitorInterface' => $vendorDir . '/thepixeldeveloper/sitemap/src/Interfaces/VisitorInterface.php', - 'Thepixeldeveloper\\Sitemap\\Sitemap' => $vendorDir . '/thepixeldeveloper/sitemap/src/Sitemap.php', - 'Thepixeldeveloper\\Sitemap\\SitemapIndex' => $vendorDir . '/thepixeldeveloper/sitemap/src/SitemapIndex.php', - 'Thepixeldeveloper\\Sitemap\\Url' => $vendorDir . '/thepixeldeveloper/sitemap/src/Url.php', - 'Thepixeldeveloper\\Sitemap\\Urlset' => $vendorDir . '/thepixeldeveloper/sitemap/src/Urlset.php', 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 7ca1db85..ef4949c7 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -181,555 +181,13 @@ class ComposerStaticInite58358eec498b7b6927cfe671382554c ); public static $classMap = array ( - 'App\\Controllers\\Admin\\CmsAdminController' => __DIR__ . '/../..' . '/app/Controllers/Admin/CmsAdminController.php', - 'App\\Controllers\\Admin\\LanguagesController' => __DIR__ . '/../..' . '/app/Controllers/Admin/LanguagesController.php', - 'App\\Controllers\\Admin\\MessagesController' => __DIR__ . '/../..' . '/app/Controllers/Admin/MessagesController.php', - 'App\\Controllers\\Admin\\NotificationsController' => __DIR__ . '/../..' . '/app/Controllers/Admin/NotificationsController.php', - 'App\\Controllers\\Admin\\RecensioniAdminController' => __DIR__ . '/../..' . '/app/Controllers/Admin/RecensioniAdminController.php', - 'App\\Controllers\\Admin\\StatsController' => __DIR__ . '/../..' . '/app/Controllers/Admin/StatsController.php', - 'App\\Controllers\\AuthController' => __DIR__ . '/../..' . '/app/Controllers/AuthController.php', - 'App\\Controllers\\AutoriApiController' => __DIR__ . '/../..' . '/app/Controllers/AutoriApiController.php', - 'App\\Controllers\\AutoriController' => __DIR__ . '/../..' . '/app/Controllers/AutoriController.php', - 'App\\Controllers\\CmsController' => __DIR__ . '/../..' . '/app/Controllers/CmsController.php', - 'App\\Controllers\\CollaneController' => __DIR__ . '/../..' . '/app/Controllers/CollaneController.php', - 'App\\Controllers\\CollocazioneController' => __DIR__ . '/../..' . '/app/Controllers/CollocazioneController.php', - 'App\\Controllers\\ContactController' => __DIR__ . '/../..' . '/app/Controllers/ContactController.php', - 'App\\Controllers\\CookiesController' => __DIR__ . '/../..' . '/app/Controllers/CookiesController.php', - 'App\\Controllers\\CopyController' => __DIR__ . '/../..' . '/app/Controllers/CopyController.php', - 'App\\Controllers\\CoverController' => __DIR__ . '/../..' . '/app/Controllers/CoverController.php', - 'App\\Controllers\\CsvImportController' => __DIR__ . '/../..' . '/app/Controllers/CsvImportController.php', - 'App\\Controllers\\DashboardController' => __DIR__ . '/../..' . '/app/Controllers/DashboardController.php', - 'App\\Controllers\\DeweyApiController' => __DIR__ . '/../..' . '/app/Controllers/DeweyApiController.php', - 'App\\Controllers\\EditoriApiController' => __DIR__ . '/../..' . '/app/Controllers/EditoriApiController.php', - 'App\\Controllers\\EditorsController' => __DIR__ . '/../..' . '/app/Controllers/EditorsController.php', - 'App\\Controllers\\EventsController' => __DIR__ . '/../..' . '/app/Controllers/EventsController.php', - 'App\\Controllers\\FeedController' => __DIR__ . '/../..' . '/app/Controllers/FeedController.php', - 'App\\Controllers\\FrontendController' => __DIR__ . '/../..' . '/app/Controllers/FrontendController.php', - 'App\\Controllers\\GeneriApiController' => __DIR__ . '/../..' . '/app/Controllers/GeneriApiController.php', - 'App\\Controllers\\GeneriController' => __DIR__ . '/../..' . '/app/Controllers/GeneriController.php', - 'App\\Controllers\\ImportHistoryController' => __DIR__ . '/../..' . '/app/Controllers/ImportHistoryController.php', - 'App\\Controllers\\LanguageController' => __DIR__ . '/../..' . '/app/Controllers/LanguageController.php', - 'App\\Controllers\\LibraryThingImportController' => __DIR__ . '/../..' . '/app/Controllers/LibraryThingImportController.php', - 'App\\Controllers\\LibriApiController' => __DIR__ . '/../..' . '/app/Controllers/LibriApiController.php', - 'App\\Controllers\\LibriController' => __DIR__ . '/../..' . '/app/Controllers/LibriController.php', - 'App\\Controllers\\LoanApprovalController' => __DIR__ . '/../..' . '/app/Controllers/LoanApprovalController.php', - 'App\\Controllers\\MaintenanceController' => __DIR__ . '/../..' . '/app/Controllers/MaintenanceController.php', - 'App\\Controllers\\PasswordController' => __DIR__ . '/../..' . '/app/Controllers/PasswordController.php', - 'App\\Controllers\\PluginController' => __DIR__ . '/../..' . '/app/Controllers/PluginController.php', - 'App\\Controllers\\PrestitiApiController' => __DIR__ . '/../..' . '/app/Controllers/PrestitiApiController.php', - 'App\\Controllers\\PrestitiController' => __DIR__ . '/../..' . '/app/Controllers/PrestitiController.php', - 'App\\Controllers\\PrivacyController' => __DIR__ . '/../..' . '/app/Controllers/PrivacyController.php', - 'App\\Controllers\\ProfileController' => __DIR__ . '/../..' . '/app/Controllers/ProfileController.php', - 'App\\Controllers\\PublicApiController' => __DIR__ . '/../..' . '/app/Controllers/PublicApiController.php', - 'App\\Controllers\\RecensioniController' => __DIR__ . '/../..' . '/app/Controllers/RecensioniController.php', - 'App\\Controllers\\RegistrationController' => __DIR__ . '/../..' . '/app/Controllers/RegistrationController.php', - 'App\\Controllers\\ReservationManager' => __DIR__ . '/../..' . '/app/Controllers/ReservationManager.php', - 'App\\Controllers\\ReservationsAdminController' => __DIR__ . '/../..' . '/app/Controllers/ReservationsAdminController.php', - 'App\\Controllers\\ReservationsController' => __DIR__ . '/../..' . '/app/Controllers/ReservationsController.php', - 'App\\Controllers\\ScrapeController' => __DIR__ . '/../..' . '/app/Controllers/ScrapeController.php', - 'App\\Controllers\\SearchController' => __DIR__ . '/../..' . '/app/Controllers/SearchController.php', - 'App\\Controllers\\SecurityLogsController' => __DIR__ . '/../..' . '/app/Controllers/SecurityLogsController.php', - 'App\\Controllers\\SeoController' => __DIR__ . '/../..' . '/app/Controllers/SeoController.php', - 'App\\Controllers\\SettingsController' => __DIR__ . '/../..' . '/app/Controllers/SettingsController.php', - 'App\\Controllers\\ThemeController' => __DIR__ . '/../..' . '/app/Controllers/ThemeController.php', - 'App\\Controllers\\UpdateController' => __DIR__ . '/../..' . '/app/Controllers/UpdateController.php', - 'App\\Controllers\\UserActionsController' => __DIR__ . '/../..' . '/app/Controllers/UserActionsController.php', - 'App\\Controllers\\UserDashboardController' => __DIR__ . '/../..' . '/app/Controllers/UserDashboardController.php', - 'App\\Controllers\\UserWishlistController' => __DIR__ . '/../..' . '/app/Controllers/UserWishlistController.php', - 'App\\Controllers\\UsersController' => __DIR__ . '/../..' . '/app/Controllers/UsersController.php', - 'App\\Controllers\\UtentiApiController' => __DIR__ . '/../..' . '/app/Controllers/UtentiApiController.php', - 'App\\Middleware\\AdminAuthMiddleware' => __DIR__ . '/../..' . '/app/Middleware/AdminAuthMiddleware.php', - 'App\\Middleware\\ApiKeyMiddleware' => __DIR__ . '/../..' . '/app/Middleware/ApiKeyMiddleware.php', - 'App\\Middleware\\AuthMiddleware' => __DIR__ . '/../..' . '/app/Middleware/AuthMiddleware.php', - 'App\\Middleware\\BasePathMiddleware' => __DIR__ . '/../..' . '/app/Middleware/BasePathMiddleware.php', - 'App\\Middleware\\CsrfMiddleware' => __DIR__ . '/../..' . '/app/Middleware/CsrfMiddleware.php', - 'App\\Middleware\\RateLimitMiddleware' => __DIR__ . '/../..' . '/app/Middleware/RateLimitMiddleware.php', - 'App\\Middleware\\RememberMeMiddleware' => __DIR__ . '/../..' . '/app/Middleware/RememberMeMiddleware.php', - 'App\\Models\\ApiKeyRepository' => __DIR__ . '/../..' . '/app/Models/ApiKeyRepository.php', - 'App\\Models\\AuthorRepository' => __DIR__ . '/../..' . '/app/Models/AuthorRepository.php', - 'App\\Models\\BookRepository' => __DIR__ . '/../..' . '/app/Models/BookRepository.php', - 'App\\Models\\CollocationRepository' => __DIR__ . '/../..' . '/app/Models/CollocationRepository.php', - 'App\\Models\\CopyRepository' => __DIR__ . '/../..' . '/app/Models/CopyRepository.php', - 'App\\Models\\DashboardStats' => __DIR__ . '/../..' . '/app/Models/DashboardStats.php', - 'App\\Models\\GenereRepository' => __DIR__ . '/../..' . '/app/Models/GenereRepository.php', - 'App\\Models\\Language' => __DIR__ . '/../..' . '/app/Models/Language.php', - 'App\\Models\\LoanRepository' => __DIR__ . '/../..' . '/app/Models/LoanRepository.php', - 'App\\Models\\PublisherRepository' => __DIR__ . '/../..' . '/app/Models/PublisherRepository.php', - 'App\\Models\\SettingsRepository' => __DIR__ . '/../..' . '/app/Models/SettingsRepository.php', - 'App\\Models\\TaxonomyRepository' => __DIR__ . '/../..' . '/app/Models/TaxonomyRepository.php', - 'App\\Models\\UserRepository' => __DIR__ . '/../..' . '/app/Models/UserRepository.php', - 'App\\Repositories\\RecensioniRepository' => __DIR__ . '/../..' . '/app/Repositories/RecensioniRepository.php', - 'App\\Services\\ReservationReassignmentService' => __DIR__ . '/../..' . '/app/Services/ReservationReassignmentService.php', - 'App\\Support\\AuthorNormalizer' => __DIR__ . '/../..' . '/app/Support/AuthorNormalizer.php', - 'App\\Support\\AuthorizationHelper' => __DIR__ . '/../..' . '/app/Support/AuthorizationHelper.php', - 'App\\Support\\BookDataMerger' => __DIR__ . '/../..' . '/app/Support/BookDataMerger.php', - 'App\\Support\\Branding' => __DIR__ . '/../..' . '/app/Support/Branding.php', - 'App\\Support\\CmsHelper' => __DIR__ . '/../..' . '/app/Support/CmsHelper.php', - 'App\\Support\\ConfigStore' => __DIR__ . '/../..' . '/app/Support/ConfigStore.php', - 'App\\Support\\ContentSanitizer' => __DIR__ . '/../..' . '/app/Support/ContentSanitizer.php', - 'App\\Support\\Csrf' => __DIR__ . '/../..' . '/app/Support/Csrf.php', - 'App\\Support\\CsrfHelper' => __DIR__ . '/../..' . '/app/Support/CsrfHelper.php', - 'App\\Support\\DataIntegrity' => __DIR__ . '/../..' . '/app/Support/DataIntegrity.php', - 'App\\Support\\DateHelper' => __DIR__ . '/../..' . '/app/Support/DateHelper.php', - 'App\\Support\\DeweyAutoPopulator' => __DIR__ . '/../..' . '/app/Support/DeweyAutoPopulator.php', - 'App\\Support\\EmailService' => __DIR__ . '/../..' . '/app/Support/EmailService.php', - 'App\\Support\\GenreHelper' => __DIR__ . '/../..' . '/app/Support/GenreHelper.php', - 'App\\Support\\HookManager' => __DIR__ . '/../..' . '/app/Support/HookManager.php', - 'App\\Support\\Hooks' => __DIR__ . '/../..' . '/app/Support/Hooks.php', - 'App\\Support\\HreflangHelper' => __DIR__ . '/../..' . '/app/Support/HreflangHelper.php', - 'App\\Support\\HtmlHelper' => __DIR__ . '/../..' . '/app/Support/HtmlHelper.php', - 'App\\Support\\I18n' => __DIR__ . '/../..' . '/app/Support/I18n.php', - 'App\\Support\\IcsGenerator' => __DIR__ . '/../..' . '/app/Support/IcsGenerator.php', - 'App\\Support\\ImportLogger' => __DIR__ . '/../..' . '/app/Support/ImportLogger.php', - 'App\\Support\\InputValidator' => __DIR__ . '/../..' . '/app/Support/InputValidator.php', - 'App\\Support\\IsbnFormatter' => __DIR__ . '/../..' . '/app/Support/IsbnFormatter.php', - 'App\\Support\\LibraryThingInstaller' => __DIR__ . '/../..' . '/app/Support/LibraryThingInstaller.php', - 'App\\Support\\LoanPdfGenerator' => __DIR__ . '/../..' . '/app/Support/LoanPdfGenerator.php', - 'App\\Support\\Log' => __DIR__ . '/../..' . '/app/Support/Log.php', - 'App\\Support\\Mailer' => __DIR__ . '/../..' . '/app/Support/Mailer.php', - 'App\\Support\\MaintenanceService' => __DIR__ . '/../..' . '/app/Support/MaintenanceService.php', - 'App\\Support\\MergeHelper' => __DIR__ . '/../..' . '/app/Support/MergeHelper.php', - 'App\\Support\\NotificationService' => __DIR__ . '/../..' . '/app/Support/NotificationService.php', - 'App\\Support\\PluginManager' => __DIR__ . '/../..' . '/app/Support/PluginManager.php', - 'App\\Support\\QueryCache' => __DIR__ . '/../..' . '/app/Support/QueryCache.php', - 'App\\Support\\RateLimiter' => __DIR__ . '/../..' . '/app/Support/RateLimiter.php', - 'App\\Support\\RememberMeService' => __DIR__ . '/../..' . '/app/Support/RememberMeService.php', - 'App\\Support\\RouteTranslator' => __DIR__ . '/../..' . '/app/Support/RouteTranslator.php', - 'App\\Support\\ScrapingService' => __DIR__ . '/../..' . '/app/Support/ScrapingService.php', - 'App\\Support\\SecureLogger' => __DIR__ . '/../..' . '/app/Support/SecureLogger.php', - 'App\\Support\\SettingsEncryption' => __DIR__ . '/../..' . '/app/Support/SettingsEncryption.php', - 'App\\Support\\SettingsMailTemplates' => __DIR__ . '/../..' . '/app/Support/SettingsMailTemplates.php', - 'App\\Support\\SharingProviders' => __DIR__ . '/../..' . '/app/Support/SharingProviders.php', - 'App\\Support\\SitemapGenerator' => __DIR__ . '/../..' . '/app/Support/SitemapGenerator.php', - 'App\\Support\\ThemeColorizer' => __DIR__ . '/../..' . '/app/Support/ThemeColorizer.php', - 'App\\Support\\ThemeManager' => __DIR__ . '/../..' . '/app/Support/ThemeManager.php', - 'App\\Support\\Updater' => __DIR__ . '/../..' . '/app/Support/Updater.php', 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'DI\\Attribute\\Inject' => __DIR__ . '/..' . '/php-di/php-di/src/Attribute/Inject.php', - 'DI\\Attribute\\Injectable' => __DIR__ . '/..' . '/php-di/php-di/src/Attribute/Injectable.php', - 'DI\\CompiledContainer' => __DIR__ . '/..' . '/php-di/php-di/src/CompiledContainer.php', - 'DI\\Compiler\\Compiler' => __DIR__ . '/..' . '/php-di/php-di/src/Compiler/Compiler.php', - 'DI\\Compiler\\ObjectCreationCompiler' => __DIR__ . '/..' . '/php-di/php-di/src/Compiler/ObjectCreationCompiler.php', - 'DI\\Compiler\\RequestedEntryHolder' => __DIR__ . '/..' . '/php-di/php-di/src/Compiler/RequestedEntryHolder.php', - 'DI\\Container' => __DIR__ . '/..' . '/php-di/php-di/src/Container.php', - 'DI\\ContainerBuilder' => __DIR__ . '/..' . '/php-di/php-di/src/ContainerBuilder.php', - 'DI\\Definition\\ArrayDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ArrayDefinition.php', - 'DI\\Definition\\ArrayDefinitionExtension' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ArrayDefinitionExtension.php', - 'DI\\Definition\\AutowireDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/AutowireDefinition.php', - 'DI\\Definition\\DecoratorDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/DecoratorDefinition.php', - 'DI\\Definition\\Definition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Definition.php', - 'DI\\Definition\\Dumper\\ObjectDefinitionDumper' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php', - 'DI\\Definition\\EnvironmentVariableDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php', - 'DI\\Definition\\Exception\\InvalidAttribute' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Exception/InvalidAttribute.php', - 'DI\\Definition\\Exception\\InvalidDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Exception/InvalidDefinition.php', - 'DI\\Definition\\ExtendsPreviousDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php', - 'DI\\Definition\\FactoryDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/FactoryDefinition.php', - 'DI\\Definition\\Helper\\AutowireDefinitionHelper' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php', - 'DI\\Definition\\Helper\\CreateDefinitionHelper' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php', - 'DI\\Definition\\Helper\\DefinitionHelper' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Helper/DefinitionHelper.php', - 'DI\\Definition\\Helper\\FactoryDefinitionHelper' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php', - 'DI\\Definition\\InstanceDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/InstanceDefinition.php', - 'DI\\Definition\\ObjectDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ObjectDefinition.php', - 'DI\\Definition\\ObjectDefinition\\MethodInjection' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php', - 'DI\\Definition\\ObjectDefinition\\PropertyInjection' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php', - 'DI\\Definition\\Reference' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Reference.php', - 'DI\\Definition\\Resolver\\ArrayResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/ArrayResolver.php', - 'DI\\Definition\\Resolver\\DecoratorResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php', - 'DI\\Definition\\Resolver\\DefinitionResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php', - 'DI\\Definition\\Resolver\\EnvironmentVariableResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php', - 'DI\\Definition\\Resolver\\FactoryResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/FactoryResolver.php', - 'DI\\Definition\\Resolver\\InstanceInjector' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/InstanceInjector.php', - 'DI\\Definition\\Resolver\\ObjectCreator' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/ObjectCreator.php', - 'DI\\Definition\\Resolver\\ParameterResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/ParameterResolver.php', - 'DI\\Definition\\Resolver\\ResolverDispatcher' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php', - 'DI\\Definition\\SelfResolvingDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/SelfResolvingDefinition.php', - 'DI\\Definition\\Source\\AttributeBasedAutowiring' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php', - 'DI\\Definition\\Source\\Autowiring' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/Autowiring.php', - 'DI\\Definition\\Source\\DefinitionArray' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/DefinitionArray.php', - 'DI\\Definition\\Source\\DefinitionFile' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/DefinitionFile.php', - 'DI\\Definition\\Source\\DefinitionNormalizer' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php', - 'DI\\Definition\\Source\\DefinitionSource' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/DefinitionSource.php', - 'DI\\Definition\\Source\\MutableDefinitionSource' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php', - 'DI\\Definition\\Source\\NoAutowiring' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/NoAutowiring.php', - 'DI\\Definition\\Source\\ReflectionBasedAutowiring' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php', - 'DI\\Definition\\Source\\SourceCache' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/SourceCache.php', - 'DI\\Definition\\Source\\SourceChain' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/Source/SourceChain.php', - 'DI\\Definition\\StringDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/StringDefinition.php', - 'DI\\Definition\\ValueDefinition' => __DIR__ . '/..' . '/php-di/php-di/src/Definition/ValueDefinition.php', - 'DI\\DependencyException' => __DIR__ . '/..' . '/php-di/php-di/src/DependencyException.php', - 'DI\\FactoryInterface' => __DIR__ . '/..' . '/php-di/php-di/src/FactoryInterface.php', - 'DI\\Factory\\RequestedEntry' => __DIR__ . '/..' . '/php-di/php-di/src/Factory/RequestedEntry.php', - 'DI\\Invoker\\DefinitionParameterResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Invoker/DefinitionParameterResolver.php', - 'DI\\Invoker\\FactoryParameterResolver' => __DIR__ . '/..' . '/php-di/php-di/src/Invoker/FactoryParameterResolver.php', - 'DI\\NotFoundException' => __DIR__ . '/..' . '/php-di/php-di/src/NotFoundException.php', - 'DI\\Proxy\\NativeProxyFactory' => __DIR__ . '/..' . '/php-di/php-di/src/Proxy/NativeProxyFactory.php', - 'DI\\Proxy\\ProxyFactory' => __DIR__ . '/..' . '/php-di/php-di/src/Proxy/ProxyFactory.php', - 'DI\\Proxy\\ProxyFactoryInterface' => __DIR__ . '/..' . '/php-di/php-di/src/Proxy/ProxyFactoryInterface.php', 'Datamatrix' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/datamatrix.php', - 'Dotenv\\Dotenv' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Dotenv.php', - 'Dotenv\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Exception/ExceptionInterface.php', - 'Dotenv\\Exception\\InvalidEncodingException' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php', - 'Dotenv\\Exception\\InvalidFileException' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Exception/InvalidFileException.php', - 'Dotenv\\Exception\\InvalidPathException' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Exception/InvalidPathException.php', - 'Dotenv\\Exception\\ValidationException' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Exception/ValidationException.php', - 'Dotenv\\Loader\\Loader' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Loader/Loader.php', - 'Dotenv\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Loader/LoaderInterface.php', - 'Dotenv\\Loader\\Resolver' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Loader/Resolver.php', - 'Dotenv\\Parser\\Entry' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/Entry.php', - 'Dotenv\\Parser\\EntryParser' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/EntryParser.php', - 'Dotenv\\Parser\\Lexer' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/Lexer.php', - 'Dotenv\\Parser\\Lines' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/Lines.php', - 'Dotenv\\Parser\\Parser' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/Parser.php', - 'Dotenv\\Parser\\ParserInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/ParserInterface.php', - 'Dotenv\\Parser\\Value' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Parser/Value.php', - 'Dotenv\\Repository\\AdapterRepository' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/AdapterRepository.php', - 'Dotenv\\Repository\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php', - 'Dotenv\\Repository\\Adapter\\ApacheAdapter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php', - 'Dotenv\\Repository\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php', - 'Dotenv\\Repository\\Adapter\\EnvConstAdapter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php', - 'Dotenv\\Repository\\Adapter\\GuardedWriter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php', - 'Dotenv\\Repository\\Adapter\\ImmutableWriter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php', - 'Dotenv\\Repository\\Adapter\\MultiReader' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php', - 'Dotenv\\Repository\\Adapter\\MultiWriter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php', - 'Dotenv\\Repository\\Adapter\\PutenvAdapter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php', - 'Dotenv\\Repository\\Adapter\\ReaderInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php', - 'Dotenv\\Repository\\Adapter\\ReplacingWriter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php', - 'Dotenv\\Repository\\Adapter\\ServerConstAdapter' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php', - 'Dotenv\\Repository\\Adapter\\WriterInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php', - 'Dotenv\\Repository\\RepositoryBuilder' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php', - 'Dotenv\\Repository\\RepositoryInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Repository/RepositoryInterface.php', - 'Dotenv\\Store\\FileStore' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/FileStore.php', - 'Dotenv\\Store\\File\\Paths' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/File/Paths.php', - 'Dotenv\\Store\\File\\Reader' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/File/Reader.php', - 'Dotenv\\Store\\StoreBuilder' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/StoreBuilder.php', - 'Dotenv\\Store\\StoreInterface' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/StoreInterface.php', - 'Dotenv\\Store\\StringStore' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Store/StringStore.php', - 'Dotenv\\Util\\Regex' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Util/Regex.php', - 'Dotenv\\Util\\Str' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Util/Str.php', - 'Dotenv\\Validator' => __DIR__ . '/..' . '/vlucas/phpdotenv/src/Validator.php', - 'Emleons\\SimRating\\Interfaces\\RendererInterface' => __DIR__ . '/..' . '/emleons/sim-rating/src/Interfaces/RendererInterface.php', - 'Emleons\\SimRating\\Rating' => __DIR__ . '/..' . '/emleons/sim-rating/src/Rating.php', - 'Emleons\\SimRating\\Renderer\\HtmlRenderer' => __DIR__ . '/..' . '/emleons/sim-rating/src/Renderer/HtmlRenderer.php', - 'Emleons\\SimRating\\Renderer\\JsonRenderer' => __DIR__ . '/..' . '/emleons/sim-rating/src/Renderer/JsonRenderer.php', - 'Emleons\\SimRating\\Renderer\\SvgRenderer' => __DIR__ . '/..' . '/emleons/sim-rating/src/Renderer/SvgRenderer.php', - 'FastRoute\\BadRouteException' => __DIR__ . '/..' . '/nikic/fast-route/src/BadRouteException.php', - 'FastRoute\\DataGenerator' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator.php', - 'FastRoute\\DataGenerator\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/CharCountBased.php', - 'FastRoute\\DataGenerator\\GroupCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/GroupCountBased.php', - 'FastRoute\\DataGenerator\\GroupPosBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/GroupPosBased.php', - 'FastRoute\\DataGenerator\\MarkBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/MarkBased.php', - 'FastRoute\\DataGenerator\\RegexBasedAbstract' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php', - 'FastRoute\\Dispatcher' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher.php', - 'FastRoute\\Dispatcher\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/CharCountBased.php', - 'FastRoute\\Dispatcher\\GroupCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/GroupCountBased.php', - 'FastRoute\\Dispatcher\\GroupPosBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/GroupPosBased.php', - 'FastRoute\\Dispatcher\\MarkBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/MarkBased.php', - 'FastRoute\\Dispatcher\\RegexBasedAbstract' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php', - 'FastRoute\\Route' => __DIR__ . '/..' . '/nikic/fast-route/src/Route.php', - 'FastRoute\\RouteCollector' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteCollector.php', - 'FastRoute\\RouteParser' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser.php', - 'FastRoute\\RouteParser\\Std' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser/Std.php', - 'Fig\\Http\\Message\\RequestMethodInterface' => __DIR__ . '/..' . '/fig/http-message-util/src/RequestMethodInterface.php', - 'Fig\\Http\\Message\\StatusCodeInterface' => __DIR__ . '/..' . '/fig/http-message-util/src/StatusCodeInterface.php', - 'GrahamCampbell\\ResultType\\Error' => __DIR__ . '/..' . '/graham-campbell/result-type/src/Error.php', - 'GrahamCampbell\\ResultType\\Result' => __DIR__ . '/..' . '/graham-campbell/result-type/src/Result.php', - 'GrahamCampbell\\ResultType\\Success' => __DIR__ . '/..' . '/graham-campbell/result-type/src/Success.php', - 'Invoker\\CallableResolver' => __DIR__ . '/..' . '/php-di/invoker/src/CallableResolver.php', - 'Invoker\\Exception\\InvocationException' => __DIR__ . '/..' . '/php-di/invoker/src/Exception/InvocationException.php', - 'Invoker\\Exception\\NotCallableException' => __DIR__ . '/..' . '/php-di/invoker/src/Exception/NotCallableException.php', - 'Invoker\\Exception\\NotEnoughParametersException' => __DIR__ . '/..' . '/php-di/invoker/src/Exception/NotEnoughParametersException.php', - 'Invoker\\Invoker' => __DIR__ . '/..' . '/php-di/invoker/src/Invoker.php', - 'Invoker\\InvokerInterface' => __DIR__ . '/..' . '/php-di/invoker/src/InvokerInterface.php', - 'Invoker\\ParameterResolver\\AssociativeArrayResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php', - 'Invoker\\ParameterResolver\\Container\\ParameterNameContainerResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php', - 'Invoker\\ParameterResolver\\Container\\TypeHintContainerResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php', - 'Invoker\\ParameterResolver\\DefaultValueResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php', - 'Invoker\\ParameterResolver\\NumericArrayResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php', - 'Invoker\\ParameterResolver\\ParameterResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/ParameterResolver.php', - 'Invoker\\ParameterResolver\\ResolverChain' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/ResolverChain.php', - 'Invoker\\ParameterResolver\\TypeHintResolver' => __DIR__ . '/..' . '/php-di/invoker/src/ParameterResolver/TypeHintResolver.php', - 'Invoker\\Reflection\\CallableReflection' => __DIR__ . '/..' . '/php-di/invoker/src/Reflection/CallableReflection.php', - 'Laravel\\SerializableClosure\\Contracts\\Serializable' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Contracts/Serializable.php', - 'Laravel\\SerializableClosure\\Contracts\\Signer' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Contracts/Signer.php', - 'Laravel\\SerializableClosure\\Exceptions\\InvalidSignatureException' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Exceptions/InvalidSignatureException.php', - 'Laravel\\SerializableClosure\\Exceptions\\MissingSecretKeyException' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Exceptions/MissingSecretKeyException.php', - 'Laravel\\SerializableClosure\\Exceptions\\PhpVersionNotSupportedException' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Exceptions/PhpVersionNotSupportedException.php', - 'Laravel\\SerializableClosure\\SerializableClosure' => __DIR__ . '/..' . '/laravel/serializable-closure/src/SerializableClosure.php', - 'Laravel\\SerializableClosure\\Serializers\\Native' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Serializers/Native.php', - 'Laravel\\SerializableClosure\\Serializers\\Signed' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Serializers/Signed.php', - 'Laravel\\SerializableClosure\\Signers\\Hmac' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Signers/Hmac.php', - 'Laravel\\SerializableClosure\\Support\\ClosureScope' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Support/ClosureScope.php', - 'Laravel\\SerializableClosure\\Support\\ClosureStream' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Support/ClosureStream.php', - 'Laravel\\SerializableClosure\\Support\\ReflectionClosure' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Support/ReflectionClosure.php', - 'Laravel\\SerializableClosure\\Support\\SelfReference' => __DIR__ . '/..' . '/laravel/serializable-closure/src/Support/SelfReference.php', - 'Laravel\\SerializableClosure\\UnsignedSerializableClosure' => __DIR__ . '/..' . '/laravel/serializable-closure/src/UnsignedSerializableClosure.php', - 'Monolog\\Attribute\\AsMonologProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php', - 'Monolog\\Attribute\\WithMonologChannel' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Attribute/WithMonologChannel.php', - 'Monolog\\DateTimeImmutable' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/DateTimeImmutable.php', - 'Monolog\\ErrorHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/ErrorHandler.php', - 'Monolog\\Formatter\\ChromePHPFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php', - 'Monolog\\Formatter\\ElasticaFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php', - 'Monolog\\Formatter\\ElasticsearchFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php', - 'Monolog\\Formatter\\FlowdockFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php', - 'Monolog\\Formatter\\FluentdFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php', - 'Monolog\\Formatter\\FormatterInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php', - 'Monolog\\Formatter\\GelfMessageFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php', - 'Monolog\\Formatter\\GoogleCloudLoggingFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php', - 'Monolog\\Formatter\\HtmlFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php', - 'Monolog\\Formatter\\JsonFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php', - 'Monolog\\Formatter\\LineFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LineFormatter.php', - 'Monolog\\Formatter\\LogglyFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php', - 'Monolog\\Formatter\\LogmaticFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php', - 'Monolog\\Formatter\\LogstashFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php', - 'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php', - 'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php', - 'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php', - 'Monolog\\Formatter\\SyslogFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php', - 'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php', - 'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php', - 'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php', - 'Monolog\\Handler\\AbstractSyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php', - 'Monolog\\Handler\\AmqpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AmqpHandler.php', - 'Monolog\\Handler\\BrowserConsoleHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php', - 'Monolog\\Handler\\BufferHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/BufferHandler.php', - 'Monolog\\Handler\\ChromePHPHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php', - 'Monolog\\Handler\\CouchDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php', - 'Monolog\\Handler\\CubeHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/CubeHandler.php', - 'Monolog\\Handler\\Curl\\Util' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Curl/Util.php', - 'Monolog\\Handler\\DeduplicationHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php', - 'Monolog\\Handler\\DoctrineCouchDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php', - 'Monolog\\Handler\\DynamoDbHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php', - 'Monolog\\Handler\\ElasticaHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php', - 'Monolog\\Handler\\ElasticsearchHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php', - 'Monolog\\Handler\\ErrorLogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php', - 'Monolog\\Handler\\FallbackGroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php', - 'Monolog\\Handler\\FilterHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FilterHandler.php', - 'Monolog\\Handler\\FingersCrossedHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php', - 'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php', - 'Monolog\\Handler\\FingersCrossed\\ChannelLevelActivationStrategy' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php', - 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php', - 'Monolog\\Handler\\FirePHPHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php', - 'Monolog\\Handler\\FleepHookHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php', - 'Monolog\\Handler\\FlowdockHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php', - 'Monolog\\Handler\\FormattableHandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php', - 'Monolog\\Handler\\FormattableHandlerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php', - 'Monolog\\Handler\\GelfHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/GelfHandler.php', - 'Monolog\\Handler\\GroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/GroupHandler.php', - 'Monolog\\Handler\\Handler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Handler.php', - 'Monolog\\Handler\\HandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/HandlerInterface.php', - 'Monolog\\Handler\\HandlerWrapper' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php', - 'Monolog\\Handler\\IFTTTHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php', - 'Monolog\\Handler\\InsightOpsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php', - 'Monolog\\Handler\\LogEntriesHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php', - 'Monolog\\Handler\\LogglyHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogglyHandler.php', - 'Monolog\\Handler\\LogmaticHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php', - 'Monolog\\Handler\\MailHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MailHandler.php', - 'Monolog\\Handler\\MandrillHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MandrillHandler.php', - 'Monolog\\Handler\\MissingExtensionException' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php', - 'Monolog\\Handler\\MongoDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php', - 'Monolog\\Handler\\NativeMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php', - 'Monolog\\Handler\\NewRelicHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php', - 'Monolog\\Handler\\NoopHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NoopHandler.php', - 'Monolog\\Handler\\NullHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NullHandler.php', - 'Monolog\\Handler\\OverflowHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/OverflowHandler.php', - 'Monolog\\Handler\\PHPConsoleHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php', - 'Monolog\\Handler\\ProcessHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessHandler.php', - 'Monolog\\Handler\\ProcessableHandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php', - 'Monolog\\Handler\\ProcessableHandlerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php', - 'Monolog\\Handler\\PsrHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PsrHandler.php', - 'Monolog\\Handler\\PushoverHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PushoverHandler.php', - 'Monolog\\Handler\\RedisHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RedisHandler.php', - 'Monolog\\Handler\\RedisPubSubHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php', - 'Monolog\\Handler\\RollbarHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RollbarHandler.php', - 'Monolog\\Handler\\RotatingFileHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php', - 'Monolog\\Handler\\SamplingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SamplingHandler.php', - 'Monolog\\Handler\\SendGridHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SendGridHandler.php', - 'Monolog\\Handler\\SlackHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SlackHandler.php', - 'Monolog\\Handler\\SlackWebhookHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php', - 'Monolog\\Handler\\Slack\\SlackRecord' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php', - 'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php', - 'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php', - 'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php', - 'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php', - 'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php', - 'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php', - 'Monolog\\Handler\\SyslogUdp\\UdpSocket' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php', - 'Monolog\\Handler\\TelegramBotHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php', - 'Monolog\\Handler\\TestHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/TestHandler.php', - 'Monolog\\Handler\\WebRequestRecognizerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php', - 'Monolog\\Handler\\WhatFailureGroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php', - 'Monolog\\Handler\\ZendMonitorHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php', - 'Monolog\\JsonSerializableDateTimeImmutable' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/JsonSerializableDateTimeImmutable.php', - 'Monolog\\Level' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Level.php', - 'Monolog\\LogRecord' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/LogRecord.php', - 'Monolog\\Logger' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Logger.php', - 'Monolog\\Processor\\ClosureContextProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ClosureContextProcessor.php', - 'Monolog\\Processor\\GitProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/GitProcessor.php', - 'Monolog\\Processor\\HostnameProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php', - 'Monolog\\Processor\\IntrospectionProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php', - 'Monolog\\Processor\\LoadAverageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/LoadAverageProcessor.php', - 'Monolog\\Processor\\MemoryPeakUsageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php', - 'Monolog\\Processor\\MemoryProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php', - 'Monolog\\Processor\\MemoryUsageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php', - 'Monolog\\Processor\\MercurialProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php', - 'Monolog\\Processor\\ProcessIdProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php', - 'Monolog\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php', - 'Monolog\\Processor\\PsrLogMessageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php', - 'Monolog\\Processor\\TagProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/TagProcessor.php', - 'Monolog\\Processor\\UidProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/UidProcessor.php', - 'Monolog\\Processor\\WebProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/WebProcessor.php', - 'Monolog\\Registry' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Registry.php', - 'Monolog\\ResettableInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/ResettableInterface.php', - 'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php', - 'Monolog\\Test\\MonologTestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/MonologTestCase.php', - 'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php', - 'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php', 'PDF417' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/pdf417.php', - 'PHPMailer\\PHPMailer\\DSNConfigurator' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/DSNConfigurator.php', - 'PHPMailer\\PHPMailer\\Exception' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/Exception.php', - 'PHPMailer\\PHPMailer\\OAuth' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/OAuth.php', - 'PHPMailer\\PHPMailer\\OAuthTokenProvider' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/OAuthTokenProvider.php', - 'PHPMailer\\PHPMailer\\PHPMailer' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/PHPMailer.php', - 'PHPMailer\\PHPMailer\\POP3' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/POP3.php', - 'PHPMailer\\PHPMailer\\SMTP' => __DIR__ . '/..' . '/phpmailer/phpmailer/src/SMTP.php', - 'PhpOption\\LazyOption' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/LazyOption.php', - 'PhpOption\\None' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/None.php', - 'PhpOption\\Option' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/Option.php', - 'PhpOption\\Some' => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption/Some.php', 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php', - 'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php', - 'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php', - 'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php', - 'Psr\\Http\\Message\\RequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/RequestFactoryInterface.php', - 'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php', - 'Psr\\Http\\Message\\ResponseFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ResponseFactoryInterface.php', - 'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php', - 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ServerRequestFactoryInterface.php', - 'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php', - 'Psr\\Http\\Message\\StreamFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/StreamFactoryInterface.php', - 'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php', - 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UploadedFileFactoryInterface.php', - 'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php', - 'Psr\\Http\\Message\\UriFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UriFactoryInterface.php', - 'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php', - 'Psr\\Http\\Server\\MiddlewareInterface' => __DIR__ . '/..' . '/psr/http-server-middleware/src/MiddlewareInterface.php', - 'Psr\\Http\\Server\\RequestHandlerInterface' => __DIR__ . '/..' . '/psr/http-server-handler/src/RequestHandlerInterface.php', - 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php', - 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php', - 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php', - 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php', - 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php', - 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php', - 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php', - 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php', 'QRcode' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/qrcode.php', - 'ReCaptcha\\ReCaptcha' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', - 'ReCaptcha\\RequestMethod' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', - 'ReCaptcha\\RequestMethod\\Curl' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', - 'ReCaptcha\\RequestMethod\\CurlPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', - 'ReCaptcha\\RequestMethod\\Post' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', - 'ReCaptcha\\RequestMethod\\Socket' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', - 'ReCaptcha\\RequestMethod\\SocketPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', - 'ReCaptcha\\RequestParameters' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', - 'ReCaptcha\\Response' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/Response.php', - 'Slim\\App' => __DIR__ . '/..' . '/slim/slim/Slim/App.php', - 'Slim\\CallableResolver' => __DIR__ . '/..' . '/slim/slim/Slim/CallableResolver.php', - 'Slim\\Csrf\\Guard' => __DIR__ . '/..' . '/slim/csrf/src/Guard.php', - 'Slim\\Error\\AbstractErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/AbstractErrorRenderer.php', - 'Slim\\Error\\Renderers\\HtmlErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/Renderers/HtmlErrorRenderer.php', - 'Slim\\Error\\Renderers\\JsonErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/Renderers/JsonErrorRenderer.php', - 'Slim\\Error\\Renderers\\PlainTextErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/Renderers/PlainTextErrorRenderer.php', - 'Slim\\Error\\Renderers\\XmlErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/Renderers/XmlErrorRenderer.php', - 'Slim\\Exception\\HttpBadRequestException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpBadRequestException.php', - 'Slim\\Exception\\HttpException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpException.php', - 'Slim\\Exception\\HttpForbiddenException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpForbiddenException.php', - 'Slim\\Exception\\HttpGoneException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpGoneException.php', - 'Slim\\Exception\\HttpInternalServerErrorException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpInternalServerErrorException.php', - 'Slim\\Exception\\HttpMethodNotAllowedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpMethodNotAllowedException.php', - 'Slim\\Exception\\HttpNotFoundException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotFoundException.php', - 'Slim\\Exception\\HttpNotImplementedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotImplementedException.php', - 'Slim\\Exception\\HttpSpecializedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpSpecializedException.php', - 'Slim\\Exception\\HttpTooManyRequestsException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpTooManyRequestsException.php', - 'Slim\\Exception\\HttpUnauthorizedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php', - 'Slim\\Factory\\AppFactory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/AppFactory.php', - 'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php', - 'Slim\\Factory\\Psr17\\HttpSoftPsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/HttpSoftPsr17Factory.php', - 'Slim\\Factory\\Psr17\\LaminasDiactorosPsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/LaminasDiactorosPsr17Factory.php', - 'Slim\\Factory\\Psr17\\NyholmPsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/NyholmPsr17Factory.php', - 'Slim\\Factory\\Psr17\\Psr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/Psr17Factory.php', - 'Slim\\Factory\\Psr17\\Psr17FactoryProvider' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/Psr17FactoryProvider.php', - 'Slim\\Factory\\Psr17\\ServerRequestCreator' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/ServerRequestCreator.php', - 'Slim\\Factory\\Psr17\\SlimHttpPsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/SlimHttpPsr17Factory.php', - 'Slim\\Factory\\Psr17\\SlimHttpServerRequestCreator' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/SlimHttpServerRequestCreator.php', - 'Slim\\Factory\\Psr17\\SlimPsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/SlimPsr17Factory.php', - 'Slim\\Factory\\ServerRequestCreatorFactory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/ServerRequestCreatorFactory.php', - 'Slim\\Handlers\\ErrorHandler' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/ErrorHandler.php', - 'Slim\\Handlers\\Strategies\\RequestHandler' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestHandler.php', - 'Slim\\Handlers\\Strategies\\RequestResponse' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestResponse.php', - 'Slim\\Handlers\\Strategies\\RequestResponseArgs' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestResponseArgs.php', - 'Slim\\Handlers\\Strategies\\RequestResponseNamedArgs' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestResponseNamedArgs.php', - 'Slim\\Interfaces\\AdvancedCallableResolverInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/AdvancedCallableResolverInterface.php', - 'Slim\\Interfaces\\CallableResolverInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/CallableResolverInterface.php', - 'Slim\\Interfaces\\DispatcherInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/DispatcherInterface.php', - 'Slim\\Interfaces\\ErrorHandlerInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/ErrorHandlerInterface.php', - 'Slim\\Interfaces\\ErrorRendererInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/ErrorRendererInterface.php', - 'Slim\\Interfaces\\InvocationStrategyInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/InvocationStrategyInterface.php', - 'Slim\\Interfaces\\MiddlewareDispatcherInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/MiddlewareDispatcherInterface.php', - 'Slim\\Interfaces\\Psr17FactoryInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/Psr17FactoryInterface.php', - 'Slim\\Interfaces\\Psr17FactoryProviderInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/Psr17FactoryProviderInterface.php', - 'Slim\\Interfaces\\RequestHandlerInvocationStrategyInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RequestHandlerInvocationStrategyInterface.php', - 'Slim\\Interfaces\\RouteCollectorInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteCollectorInterface.php', - 'Slim\\Interfaces\\RouteCollectorProxyInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteCollectorProxyInterface.php', - 'Slim\\Interfaces\\RouteGroupInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteGroupInterface.php', - 'Slim\\Interfaces\\RouteInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteInterface.php', - 'Slim\\Interfaces\\RouteParserInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteParserInterface.php', - 'Slim\\Interfaces\\RouteResolverInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteResolverInterface.php', - 'Slim\\Interfaces\\ServerRequestCreatorInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/ServerRequestCreatorInterface.php', - 'Slim\\Logger' => __DIR__ . '/..' . '/slim/slim/Slim/Logger.php', - 'Slim\\MiddlewareDispatcher' => __DIR__ . '/..' . '/slim/slim/Slim/MiddlewareDispatcher.php', - 'Slim\\Middleware\\BodyParsingMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/BodyParsingMiddleware.php', - 'Slim\\Middleware\\ContentLengthMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/ContentLengthMiddleware.php', - 'Slim\\Middleware\\ErrorMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/ErrorMiddleware.php', - 'Slim\\Middleware\\MethodOverrideMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/MethodOverrideMiddleware.php', - 'Slim\\Middleware\\OutputBufferingMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/OutputBufferingMiddleware.php', - 'Slim\\Middleware\\RoutingMiddleware' => __DIR__ . '/..' . '/slim/slim/Slim/Middleware/RoutingMiddleware.php', - 'Slim\\Psr7\\Cookies' => __DIR__ . '/..' . '/slim/psr7/src/Cookies.php', - 'Slim\\Psr7\\Environment' => __DIR__ . '/..' . '/slim/psr7/src/Environment.php', - 'Slim\\Psr7\\Factory\\RequestFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/RequestFactory.php', - 'Slim\\Psr7\\Factory\\ResponseFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/ResponseFactory.php', - 'Slim\\Psr7\\Factory\\ServerRequestFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/ServerRequestFactory.php', - 'Slim\\Psr7\\Factory\\StreamFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/StreamFactory.php', - 'Slim\\Psr7\\Factory\\UploadedFileFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/UploadedFileFactory.php', - 'Slim\\Psr7\\Factory\\UriFactory' => __DIR__ . '/..' . '/slim/psr7/src/Factory/UriFactory.php', - 'Slim\\Psr7\\Header' => __DIR__ . '/..' . '/slim/psr7/src/Header.php', - 'Slim\\Psr7\\Headers' => __DIR__ . '/..' . '/slim/psr7/src/Headers.php', - 'Slim\\Psr7\\Interfaces\\HeadersInterface' => __DIR__ . '/..' . '/slim/psr7/src/Interfaces/HeadersInterface.php', - 'Slim\\Psr7\\Message' => __DIR__ . '/..' . '/slim/psr7/src/Message.php', - 'Slim\\Psr7\\NonBufferedBody' => __DIR__ . '/..' . '/slim/psr7/src/NonBufferedBody.php', - 'Slim\\Psr7\\Request' => __DIR__ . '/..' . '/slim/psr7/src/Request.php', - 'Slim\\Psr7\\Response' => __DIR__ . '/..' . '/slim/psr7/src/Response.php', - 'Slim\\Psr7\\Stream' => __DIR__ . '/..' . '/slim/psr7/src/Stream.php', - 'Slim\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/slim/psr7/src/UploadedFile.php', - 'Slim\\Psr7\\Uri' => __DIR__ . '/..' . '/slim/psr7/src/Uri.php', - 'Slim\\ResponseEmitter' => __DIR__ . '/..' . '/slim/slim/Slim/ResponseEmitter.php', - 'Slim\\Routing\\Dispatcher' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/Dispatcher.php', - 'Slim\\Routing\\FastRouteDispatcher' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/FastRouteDispatcher.php', - 'Slim\\Routing\\Route' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/Route.php', - 'Slim\\Routing\\RouteCollector' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteCollector.php', - 'Slim\\Routing\\RouteCollectorProxy' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteCollectorProxy.php', - 'Slim\\Routing\\RouteContext' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteContext.php', - 'Slim\\Routing\\RouteGroup' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteGroup.php', - 'Slim\\Routing\\RouteParser' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteParser.php', - 'Slim\\Routing\\RouteResolver' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteResolver.php', - 'Slim\\Routing\\RouteRunner' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteRunner.php', - 'Slim\\Routing\\RoutingResults' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RoutingResults.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', - 'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', - 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', - 'Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php', - 'Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php', 'TCPDF' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf.php', 'TCPDF2DBarcode' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_barcodes_2d.php', 'TCPDFBarcode' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_barcodes_1d.php', @@ -739,21 +197,6 @@ class ComposerStaticInite58358eec498b7b6927cfe671382554c 'TCPDF_FONT_DATA' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_font_data.php', 'TCPDF_IMAGES' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_images.php', 'TCPDF_STATIC' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_static.php', - 'Thepixeldeveloper\\Sitemap\\ChunkedCollection' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/ChunkedCollection.php', - 'Thepixeldeveloper\\Sitemap\\ChunkedUrlset' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/ChunkedUrlset.php', - 'Thepixeldeveloper\\Sitemap\\Collection' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Collection.php', - 'Thepixeldeveloper\\Sitemap\\Drivers\\XmlWriterDriver' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Drivers/XmlWriterDriver.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Image' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Extensions/Image.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Link' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Extensions/Link.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Mobile' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Extensions/Mobile.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\News' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Extensions/News.php', - 'Thepixeldeveloper\\Sitemap\\Extensions\\Video' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Extensions/Video.php', - 'Thepixeldeveloper\\Sitemap\\Interfaces\\DriverInterface' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Interfaces/DriverInterface.php', - 'Thepixeldeveloper\\Sitemap\\Interfaces\\VisitorInterface' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Interfaces/VisitorInterface.php', - 'Thepixeldeveloper\\Sitemap\\Sitemap' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Sitemap.php', - 'Thepixeldeveloper\\Sitemap\\SitemapIndex' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/SitemapIndex.php', - 'Thepixeldeveloper\\Sitemap\\Url' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Url.php', - 'Thepixeldeveloper\\Sitemap\\Urlset' => __DIR__ . '/..' . '/thepixeldeveloper/sitemap/src/Urlset.php', 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index ae438b30..ae0d98ad 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1871,6 +1871,6 @@ "install-path": "../vlucas/phpdotenv" } ], - "dev": false, + "dev": true, "dev-package-names": [] } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 6e60c67b..a3033520 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,11 +3,11 @@ 'name' => 'pinakes/slim-app', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '3e18155a994a148e24be8633ce7a4f6f0ab35abd', + 'reference' => 'a12f4d4d6db75672ca67d8cdc02713cb497538de', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'dev' => false, + 'dev' => true, ), 'versions' => array( 'emleons/sim-rating' => array( @@ -112,7 +112,7 @@ 'pinakes/slim-app' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '3e18155a994a148e24be8633ce7a4f6f0ab35abd', + 'reference' => 'a12f4d4d6db75672ca67d8cdc02713cb497538de', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/version.json b/version.json index a552f390..c1ce972d 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { "name": "Pinakes", - "version": "0.5.3", + "version": "0.5.4", "description": "Library Management System - Sistema di Gestione Bibliotecaria" }