diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 608de27..e09b0ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,25 +68,25 @@ deploy:staging: echo "🚀 Deploy ${CI_ENVIRONMENT_NAME} → ${FTP_HOST}:${TARGET_PATH}" # ------------------------------------------- - # 🔢 Versionierung: versions.php laden/bumpen + # 🔢 Versionierung aus Repo-Datei ableiten # ------------------------------------------- VERSION_WORK_FILE=".ci_versions_${CI_ENVIRONMENT_NAME}.php" - REMOTE_VERSION_PATH="${TARGET_PATH}${CONFIG_BASE_DIR}/versions.php" + VERSION_SOURCE="${CONFIG_ENV_DIR}/versions.php" + if [ ! -f "${VERSION_SOURCE}" ]; then + VERSION_SOURCE="${CONFIG_BASE_DIR}/versions.php" + fi - echo "🔍 Prüfe versions.php auf dem Server: ${REMOTE_VERSION_PATH}" - - if lftp -u "${FTP_USER}","${FTP_PASSWORD}" "${FTP_HOST}" -e " - set ftp:ssl-force true; - set ftp:passive-mode true; - set ftp:ssl-protect-data true; - set ssl:verify-certificate no; - get ${REMOTE_VERSION_PATH} -o ${VERSION_WORK_FILE}; - bye - "; then - echo "✅ Remote versions.php geladen." + if [ -f "${VERSION_SOURCE}" ]; then + echo "📄 Verwende lokale versions.php: ${VERSION_SOURCE}" + cp "${VERSION_SOURCE}" "${VERSION_WORK_FILE}" else - echo "⚠️ Remote versions.php nicht gefunden, verwende lokale ${CONFIG_ENV_DIR}/versions.php" - cp "${CONFIG_ENV_DIR}/versions.php" "${VERSION_WORK_FILE}" + echo "⚠️ Keine versions.php gefunden – nutze Defaults 1.0.0" + cat <<'PHP' > "${VERSION_WORK_FILE}" +/dev/null | tr -cd '0-9') @@ -97,15 +97,52 @@ deploy:staging: [ -z "$SUB" ] && SUB=0 [ -z "$PATCH" ] && PATCH=0 - PATCH=$((PATCH + 1)) + FORCED=0 + if [ -n "${CI_VERSION_FORCE:-}" ]; then + if expr "${CI_VERSION_FORCE}" : '^[0-9]\+\.[0-9]\+\.[0-9]\+$' >/dev/null; then + IFS='.' read -r FORCE_MAIN FORCE_SUB FORCE_PATCH < "${VERSION_WORK_FILE}" - echo "\$mainversion = ${MAIN};" >> "${VERSION_WORK_FILE}" - echo "\$subversion = ${SUB};" >> "${VERSION_WORK_FILE}" - echo "\$patchversion = ${PATCH};" >> "${VERSION_WORK_FILE}" + { + echo ' "${VERSION_WORK_FILE}" # Textdatei für die Webseite echo "${MAIN}.${SUB}.${PATCH}" > public/build_version.txt @@ -225,25 +262,25 @@ deploy:production: echo "🚀 Deploy ${CI_ENVIRONMENT_NAME} → ${FTP_HOST}:${TARGET_PATH}" # ------------------------------------------- - # 🔢 Versionierung: versions.php laden/bumpen + # 🔢 Versionierung aus Repo-Datei ableiten # ------------------------------------------- VERSION_WORK_FILE=".ci_versions_${CI_ENVIRONMENT_NAME}.php" - REMOTE_VERSION_PATH="${TARGET_PATH}${CONFIG_BASE_DIR}/versions.php" + VERSION_SOURCE="${CONFIG_ENV_DIR}/versions.php" + if [ ! -f "${VERSION_SOURCE}" ]; then + VERSION_SOURCE="${CONFIG_BASE_DIR}/versions.php" + fi - echo "🔍 Prüfe versions.php auf dem Server: ${REMOTE_VERSION_PATH}" - - if lftp -u "${FTP_USER}","${FTP_PASSWORD}" "${FTP_HOST}" -e " - set ftp:ssl-force true; - set ftp:passive-mode true; - set ftp:ssl-protect-data true; - set ssl:verify-certificate no; - get ${REMOTE_VERSION_PATH} -o ${VERSION_WORK_FILE}; - bye - "; then - echo "✅ Remote versions.php geladen." + if [ -f "${VERSION_SOURCE}" ]; then + echo "📄 Verwende lokale versions.php: ${VERSION_SOURCE}" + cp "${VERSION_SOURCE}" "${VERSION_WORK_FILE}" else - echo "⚠️ Remote versions.php nicht gefunden, verwende lokale ${CONFIG_ENV_DIR}/versions.php" - cp "${CONFIG_ENV_DIR}/versions.php" "${VERSION_WORK_FILE}" + echo "⚠️ Keine versions.php gefunden – nutze Defaults 1.0.0" + cat <<'PHP' > "${VERSION_WORK_FILE}" +/dev/null | tr -cd '0-9') @@ -254,15 +291,52 @@ deploy:production: [ -z "$SUB" ] && SUB=0 [ -z "$PATCH" ] && PATCH=0 - PATCH=$((PATCH + 1)) + FORCED=0 + if [ -n "${CI_VERSION_FORCE:-}" ]; then + if expr "${CI_VERSION_FORCE}" : '^[0-9]\+\.[0-9]\+\.[0-9]\+$' >/dev/null; then + IFS='.' read -r FORCE_MAIN FORCE_SUB FORCE_PATCH < "${VERSION_WORK_FILE}" - echo "\$mainversion = ${MAIN};" >> "${VERSION_WORK_FILE}" - echo "\$subversion = ${SUB};" >> "${VERSION_WORK_FILE}" - echo "\$patchversion = ${PATCH};" >> "${VERSION_WORK_FILE}" + { + echo ' "${VERSION_WORK_FILE}" # Textdatei für die Webseite echo "${MAIN}.${SUB}.${PATCH}" > public/build_version.txt diff --git a/config/fileload.php b/config/fileload.php index 35854c4..d5920dd 100644 --- a/config/fileload.php +++ b/config/fileload.php @@ -6,10 +6,19 @@ // APP_COOKIE_PREFIX, APP_COOKIE_DOMAIN, APP_ENV etc. // ----------------------------------------------------------- // Try to load primary environment bootstrap. -$bootstrapCandidates = [__DIR__ . '/config.php']; -$bootstrapCandidates = [__DIR__ . '/versions.php']; +$envHint = getenv('APP_ENV_FILE') ?: (getenv('APP_ENV') ?: null); +if ($envHint && !preg_match('/^[A-Za-z0-9_\-]+$/', $envHint)) { + $envHint = null; +} +if ($envHint === null) { + if (is_dir(__DIR__ . '/staging')) { + $envHint = 'staging'; + } elseif (is_dir(__DIR__ . '/prod')) { + $envHint = 'prod'; + } +} -$envHint = getenv('APP_ENV') ?: (getenv('APP_ENV_FILE') ?: null); +$bootstrapCandidates = [__DIR__ . '/config.php']; if ($envHint) { $bootstrapCandidates[] = __DIR__ . '/' . $envHint . '/config.php'; } @@ -18,13 +27,32 @@ foreach ($bootstrapCandidates as $bootstrap) { if ($bootstrap && is_file($bootstrap)) { require_once $bootstrap; $bootstrapLoaded = true; - break; } } if (!$bootstrapLoaded) { throw new RuntimeException('No environment config found under config/.'); } +$versionFiles = [__DIR__ . '/versions.php']; +if ($envHint) { + $versionFiles[] = __DIR__ . '/' . $envHint . '/versions.php'; +} +$versionLoaded = false; +foreach ($versionFiles as $versionFile) { + if ($versionFile && is_file($versionFile)) { + require_once $versionFile; + $versionLoaded = true; + } +} +if (!$versionLoaded) { + $mainversion = $mainversion ?? 1; + $subversion = $subversion ?? 0; + $patchversion = $patchversion ?? 0; +} +$mainversion = (int)($mainversion ?? 1); +$subversion = (int)($subversion ?? 0); +$patchversion = (int)($patchversion ?? 0); + $emailtemplateConfigPath = __DIR__ . '/emailtemplate.conf.php'; if (is_file($emailtemplateConfigPath)) { $GLOBALS['emailtemplate_config'] = require $emailtemplateConfigPath; @@ -173,4 +201,4 @@ require_once __DIR__ . '/i18n.php'; // 4) Rest des Systems laden (DB, Funktionen, Hilfs-Libs) // ----------------------------------------------------------- require_once __DIR__ . "/db.php"; -//require_once __DIR__ . '/../src/functions.php'; +require_once __DIR__ . '/../inc/helpers.php'; diff --git a/inc/helpers.php b/inc/helpers.php new file mode 100644 index 0000000..37a0d8a --- /dev/null +++ b/inc/helpers.php @@ -0,0 +1,165 @@ + helper_append_version($src, $version), + 'defer' => $defer, + 'async' => $async, + 'type' => $type, + 'module' => $module, + ]; + + if ($pos === 'header') { + $GLOBALS['page_header_scripts'][] = $data; + } else { + $GLOBALS['page_footer_scripts'][] = $data; + } +} + +function tpl_add_style( + string $href, + string $pos = 'header', + string $priority = 'normal', + ?string $version = null, + string $media = 'all' +): void { + $GLOBALS['page_styles'][] = [ + 'href' => helper_append_version($href, $version), + 'pos' => $pos, + 'priority' => $priority, + 'media' => $media, + ]; +} + +function tpl(string $file, string $type = 'structure', string $site = 'admin'): void +{ + $base = __DIR__ . '/../partials/'; + + $safe = static function ($value) { + return preg_match('/^[a-zA-Z0-9_\-]+$/', $value) === 1; + }; + + if (!$safe($file) || !$safe($type) || !$safe($site)) { + echo ""; + return; + } + + $path = $type === 'landing' + ? $base . "landingpage/{$site}/{$file}.php" + : $base . "structure/{$file}.php"; + + extract($GLOBALS, EXTR_SKIP); + + if (is_file($path)) { + include $path; + } else { + echo ""; + } +} + +function tpl_render_scripts(?array $scripts = null, string $pos = 'footer'): void +{ + if ($scripts === null) { + $scripts = $pos === 'header' + ? ($GLOBALS['page_header_scripts'] ?? []) + : ($GLOBALS['page_footer_scripts'] ?? []); + } + + foreach ($scripts as $script) { + if (empty($script['src'])) { + continue; + } + $attrs = []; + if (!empty($script['module'])) { + $attrs[] = 'type="module"'; + } elseif (!empty($script['type'])) { + $attrs[] = 'type="' . htmlspecialchars((string)$script['type'], ENT_QUOTES) . '"'; + } + if (!empty($script['defer'])) { + $attrs[] = 'defer'; + } + if (!empty($script['async'])) { + $attrs[] = 'async'; + } + $attrString = ''; + if (!empty($attrs)) { + $attrString = ' ' . implode(' ', $attrs); + } + echo '' . PHP_EOL; + } +} + +function tpl_render_styles(?array $styles = null, string $pos = 'header'): void +{ + if ($styles === null) { + $styles = array_filter( + $GLOBALS['page_styles'] ?? [], + static fn ($style) => ($style['pos'] ?? 'header') === $pos + ); + } + + if (!$styles) { + return; + } + + $priorityOrder = ['critical' => 0, 'high' => 1, 'normal' => 2, 'low' => 3]; + usort($styles, static function ($a, $b) use ($priorityOrder) { + $aPriority = strtolower($a['priority'] ?? 'normal'); + $bPriority = strtolower($b['priority'] ?? 'normal'); + $aScore = $priorityOrder[$aPriority] ?? $priorityOrder['normal']; + $bScore = $priorityOrder[$bPriority] ?? $priorityOrder['normal']; + return $aScore <=> $bScore; + }); + + foreach ($styles as $style) { + if (empty($style['href'])) { + continue; + } + $media = trim((string)($style['media'] ?? '')); + $mediaAttr = $media && strtolower($media) !== 'all' ? ' media="' . htmlspecialchars($media, ENT_QUOTES) . '"' : ''; + echo '' . PHP_EOL; + } +} diff --git a/partials/landingpage/admin/bridge.php b/partials/landingpage/admin/bridge.php index c0bccba..c904a3d 100644 --- a/partials/landingpage/admin/bridge.php +++ b/partials/landingpage/admin/bridge.php @@ -144,8 +144,6 @@ PHP, ENT_QUOTES); ?> app_asset_url('/assets/js/toast.js')], - ['src' => app_asset_url('/assets/js/bridge-setup.js'), 'module' => true], -]; +tpl_add_script(app_asset_url('/assets/js/toast.js')); +tpl_add_script(app_asset_url('/assets/js/bridge-setup.js'), 'footer', false, false, '', null, true); require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/dashboard.php b/partials/landingpage/admin/dashboard.php index 8cb92ee..744e683 100644 --- a/partials/landingpage/admin/dashboard.php +++ b/partials/landingpage/admin/dashboard.php @@ -67,8 +67,6 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
app_asset_url('/assets/js/toast.js')], - ['src' => app_asset_url('/assets/js/dashboard.js'), 'module' => true], -]; +tpl_add_script(app_asset_url('/assets/js/toast.js')); +tpl_add_script(app_asset_url('/assets/js/dashboard.js'), 'footer', false, false, '', null, true); require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/profile.php b/partials/landingpage/admin/profile.php index 7111dee..3faecba 100644 --- a/partials/landingpage/admin/profile.php +++ b/partials/landingpage/admin/profile.php @@ -56,8 +56,6 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
app_asset_url('/assets/js/toast.js')], - ['src' => app_asset_url('/assets/js/account.js'), 'module' => true], -]; +tpl_add_script(app_asset_url('/assets/js/toast.js')); +tpl_add_script(app_asset_url('/assets/js/account.js'), 'footer', false, false, '', null, true); require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/landingpage/admin/settings.php b/partials/landingpage/admin/settings.php index cefbdb8..4cb7e71 100644 --- a/partials/landingpage/admin/settings.php +++ b/partials/landingpage/admin/settings.php @@ -138,8 +138,6 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
app_asset_url('/assets/js/toast.js')], - ['src' => app_asset_url('/assets/js/account.js'), 'module' => true], -]; +tpl_add_script(app_asset_url('/assets/js/toast.js')); +tpl_add_script(app_asset_url('/assets/js/account.js'), 'footer', false, false, '', null, true); require dirname(__DIR__) . '/../structure/layout_end.php'; diff --git a/partials/structure/layout_end.php b/partials/structure/layout_end.php index d3daa87..fe33612 100644 --- a/partials/structure/layout_end.php +++ b/partials/structure/layout_end.php @@ -1,27 +1,38 @@ - - - - - - - - + + diff --git a/partials/structure/layout_start.php b/partials/structure/layout_start.php index 0ec0e74..697ee21 100644 --- a/partials/structure/layout_start.php +++ b/partials/structure/layout_start.php @@ -1,5 +1,6 @@ @@ -31,12 +39,7 @@ $sharedCss = [ - - - - - - +