From b03d9a9bc3d7699aed282292410f94119892d1a2 Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Sat, 14 Feb 2026 00:18:18 +0100 Subject: [PATCH 01/55] feat: add full subfolder installation support Add base path detection and rewriting so the app works correctly when installed in a subdirectory (e.g. http://host/pinakes/) instead of the domain root. - Add HtmlHelper::getBasePath() with APP_CANONICAL_URL and SCRIPT_NAME auto-detection - Add url() helper for views, update route_path() and book_url() - Add BasePathMiddleware to auto-rewrite Location headers on all controller redirects (no controller changes needed) - Set Slim's basePath in public/index.php - Inject window.BASE_PATH in all 3 layouts for inline JS - Replace ~500 hardcoded href/action/fetch paths across 75+ view files - Fix installer base path detection Closes #44 --- .gitignore | 1 + app/Middleware/BasePathMiddleware.php | 38 ++++++++ app/Support/HtmlHelper.php | 38 +++++++- app/Views/admin/cms-edit.php | 10 +- app/Views/admin/csv_import.php | 14 +-- app/Views/admin/imports_history.php | 4 +- app/Views/admin/integrity_report.php | 2 +- app/Views/admin/languages/create.php | 6 +- app/Views/admin/languages/edit-routes.php | 6 +- app/Views/admin/languages/edit.php | 8 +- app/Views/admin/languages/index.php | 18 ++-- app/Views/admin/pending_loans.php | 12 +-- app/Views/admin/plugins.php | 16 ++-- app/Views/admin/security_logs.php | 2 +- app/Views/admin/settings.php | 8 +- app/Views/admin/stats.php | 6 +- app/Views/admin/theme-customize.php | 8 +- app/Views/admin/themes.php | 4 +- app/Views/admin/updates.php | 18 ++-- app/Views/auth/forgot-password.php | 7 +- app/Views/auth/login.php | 7 +- app/Views/auth/register.php | 9 +- app/Views/auth/register_success.php | 9 +- app/Views/auth/reset-password.php | 7 +- app/Views/autori/crea_autore.php | 10 +- app/Views/autori/index.php | 22 ++--- app/Views/autori/modifica_autore.php | 8 +- app/Views/autori/scheda_autore.php | 18 ++-- app/Views/cms/edit-home.php | 14 +-- app/Views/collocazione/index.php | 22 ++--- app/Views/dashboard/index.php | 28 +++--- app/Views/editori/crea_editore.php | 10 +- app/Views/editori/index.php | 22 ++--- app/Views/editori/modifica_editore.php | 8 +- app/Views/editori/scheda_editore.php | 20 ++-- app/Views/errors/session-expired.php | 7 +- app/Views/events/form.php | 6 +- app/Views/events/index.php | 14 +-- app/Views/frontend/book-detail.php | 14 +-- app/Views/frontend/event-detail.php | 12 +-- app/Views/frontend/events.php | 8 +- app/Views/frontend/home-sections/events.php | 8 +- app/Views/frontend/home-sections/hero.php | 2 +- app/Views/frontend/home.php | 2 +- app/Views/frontend/layout.php | 23 +---- app/Views/generi/crea_genere.php | 6 +- app/Views/generi/dettaglio_genere.php | 8 +- app/Views/generi/index.php | 14 +-- app/Views/layout.php | 91 ++++++++++--------- app/Views/libri/crea_libro.php | 4 +- app/Views/libri/import_librarything.php | 18 ++-- app/Views/libri/index.php | 56 ++++++------ app/Views/libri/modifica_libro.php | 6 +- app/Views/libri/partials/book_form.php | 54 +++++------ app/Views/libri/scheda_libro.php | 38 ++++---- app/Views/partials/language-switcher.php | 2 +- app/Views/plugins/librarything_admin.php | 12 +-- app/Views/prenotazioni/crea_prenotazione.php | 10 +- app/Views/prenotazioni/index.php | 10 +- .../prenotazioni/modifica_prenotazione.php | 8 +- app/Views/prestiti/crea_prestito.php | 14 +-- app/Views/prestiti/dettagli_prestito.php | 22 ++--- app/Views/prestiti/index.php | 40 ++++---- app/Views/prestiti/modifica_prestito.php | 14 +-- app/Views/prestiti/restituito_prestito.php | 10 +- app/Views/profile/index.php | 6 +- app/Views/profile/reservations.php | 8 +- app/Views/profile/wishlist.php | 4 +- app/Views/settings/advanced-tab.php | 16 ++-- app/Views/settings/contacts-tab.php | 2 +- app/Views/settings/index.php | 20 ++-- app/Views/settings/messages-tab.php | 8 +- app/Views/settings/privacy-tab.php | 6 +- app/Views/user_dashboard/prenotazioni.php | 8 +- app/Views/user_layout.php | 29 +++--- app/Views/utenti/crea_utente.php | 4 +- app/Views/utenti/dettagli_utente.php | 12 +-- app/Views/utenti/index.php | 34 +++---- app/Views/utenti/modifica_utente.php | 4 +- app/helpers.php | 53 ++++++++++- installer/index.php | 36 +++++--- installer/steps/step7.php | 2 +- public/index.php | 18 +++- 83 files changed, 687 insertions(+), 556 deletions(-) create mode 100644 app/Middleware/BasePathMiddleware.php diff --git a/.gitignore b/.gitignore index 30592a53..f355e6cf 100644 --- a/.gitignore +++ b/.gitignore @@ -408,5 +408,6 @@ docs/reference/agents.md # Internal development folder (completely untracked) internal/ updater.md +updater_new.md scraping-pro-*.zip fix-autoloader.php diff --git a/app/Middleware/BasePathMiddleware.php b/app/Middleware/BasePathMiddleware.php new file mode 100644 index 00000000..13f5ef89 --- /dev/null +++ b/app/Middleware/BasePathMiddleware.php @@ -0,0 +1,38 @@ +handle($request); + + $basePath = HtmlHelper::getBasePath(); + if ($basePath === '') { + return $response; + } + + if ($response->hasHeader('Location')) { + $location = $response->getHeaderLine('Location'); + // Only rewrite absolute paths (starting with /) that aren't already prefixed + if ( + str_starts_with($location, '/') + && !str_starts_with($location, '//') + && !str_starts_with($location, $basePath . '/') + && $location !== $basePath + ) { + $response = $response->withHeader('Location', $basePath . $location); + } + } + + return $response; + } +} diff --git a/app/Support/HtmlHelper.php b/app/Support/HtmlHelper.php index da63a3a3..cf8864c5 100644 --- a/app/Support/HtmlHelper.php +++ b/app/Support/HtmlHelper.php @@ -199,6 +199,42 @@ public static function sanitizeHtml(?string $html): string return $html; } + /** + * Ottiene il base path dell'applicazione (es. '/pinakes' se in sottocartella) + * Restituisce stringa vuota se l'app è alla root del dominio. + * + * @return string Base path senza trailing slash, o stringa vuota + */ + public static function getBasePath(): string + { + static $basePath = null; + if ($basePath !== null) { + return $basePath; + } + + // Priorità 1: Estrai path da APP_CANONICAL_URL + $canonicalUrl = $_ENV['APP_CANONICAL_URL'] ?? getenv('APP_CANONICAL_URL') ?: ''; + if ($canonicalUrl !== '') { + $path = parse_url($canonicalUrl, PHP_URL_PATH); + if ($path !== null && $path !== '' && $path !== '/') { + $basePath = rtrim($path, '/'); + return $basePath; + } + } + + // Priorità 2: Auto-detect da SCRIPT_NAME + if (isset($_SERVER['SCRIPT_NAME'])) { + $scriptDir = dirname(dirname($_SERVER['SCRIPT_NAME'])); + if ($scriptDir !== '/' && $scriptDir !== '\\' && $scriptDir !== '.') { + $basePath = rtrim($scriptDir, '/'); + return $basePath; + } + } + + $basePath = ''; + return $basePath; + } + /** * Ottiene l'URL base sicuro dell'applicazione * Usa APP_CANONICAL_URL dalla configurazione per evitare Host header injection @@ -248,7 +284,7 @@ public static function getBaseUrl(): string } } - return $baseUrl; + return $baseUrl . self::getBasePath(); } /** diff --git a/app/Views/admin/cms-edit.php b/app/Views/admin/cms-edit.php index ffd7a34b..1e80208d 100644 --- a/app/Views/admin/cms-edit.php +++ b/app/Views/admin/cms-edit.php @@ -10,7 +10,7 @@

- + @@ -32,7 +32,7 @@ -
+ @@ -156,7 +156,7 @@ class="rounded border-gray-300 text-gray-900 focus:ring-gray-500"
- +
- + + + + + diff --git a/app/Views/auth/login.php b/app/Views/auth/login.php index f8f5d47c..cd86d625 100644 --- a/app/Views/auth/login.php +++ b/app/Views/auth/login.php @@ -15,10 +15,11 @@ <?= __('Accesso') ?> - <?= htmlspecialchars($appName, ENT_QUOTES, 'UTF-8') ?> - + + - - + + diff --git a/app/Views/auth/register.php b/app/Views/auth/register.php index f8be0545..a6affc6e 100644 --- a/app/Views/auth/register.php +++ b/app/Views/auth/register.php @@ -13,11 +13,12 @@ <?= __('Registrazione') ?> - <?= htmlspecialchars($appName, ENT_QUOTES, 'UTF-8') ?> + + + - - - - + + diff --git a/app/Views/auth/register_success.php b/app/Views/auth/register_success.php index 3cea2dd3..f7a2d9bc 100644 --- a/app/Views/auth/register_success.php +++ b/app/Views/auth/register_success.php @@ -12,11 +12,12 @@ <?= __('Registrazione Completata') ?> - <?= htmlspecialchars($appName, ENT_QUOTES, 'UTF-8') ?> + + + - - - - + + diff --git a/app/Views/auth/reset-password.php b/app/Views/auth/reset-password.php index 044e8586..5211a0af 100644 --- a/app/Views/auth/reset-password.php +++ b/app/Views/auth/reset-password.php @@ -11,9 +11,10 @@ <?= __('Resetta Password') ?> - <?= htmlspecialchars($appName, ENT_QUOTES, 'UTF-8') ?> - - - + + + + diff --git a/app/Views/autori/crea_autore.php b/app/Views/autori/crea_autore.php index 7f5e7927..de58c18f 100644 --- a/app/Views/autori/crea_autore.php +++ b/app/Views/autori/crea_autore.php @@ -5,7 +5,7 @@