diff --git a/config/_default/hugo.yaml b/config/_default/hugo.yaml index 0909f30ba44..36952d4f2b2 100644 --- a/config/_default/hugo.yaml +++ b/config/_default/hugo.yaml @@ -177,11 +177,16 @@ cascade: target: path: /dev/** +mediaTypes: + text/nginx: + suffixes: nginx + outputs: home: - html - cms_config - rss + - redirects section: - html taxonomy: @@ -193,9 +198,16 @@ outputFormats: cms_config: baseName: cms-config mediaType: application/yaml - isHTML: false isPlainText: true notAlternative: true + redirects: + baseName: .nginx/301 + mediaType: text/nginx + rel: nginx + isPlainText: true + notAlternative: true + root: true + ugly: true # Keep for local dev. Production build uses --minify. minify: @@ -273,4 +285,8 @@ caches: images: dir: :cacheDir/images -disableHugoGeneratorInject: true \ No newline at end of file +disableHugoGeneratorInject: true +# Aliases are served as nginx 301 redirects from the generated .nginx/301.nginx +# map (themes/trigo/layouts/home.redirects.nginx), not as HTML redirect pages. +# Hosts without that nginx config, e.g. GitHub Pages, have no alias redirects. +disableAliases: true \ No newline at end of file diff --git a/content/en/about/conduct/_index.md b/content/en/about/conduct/_index.md index fa588a9d17e..e96ec445a4d 100644 --- a/content/en/about/conduct/_index.md +++ b/content/en/about/conduct/_index.md @@ -1,6 +1,8 @@ --- title: FreeCAD Code of Conduct linkTitle: Conduct -draft: true +build: + render: never +aliases: ["/codeofconduct"] redirect: https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md --- diff --git a/content/en/about/privacy/_index.md b/content/en/about/privacy/_index.md index ff9596cab57..1fecd5210d5 100644 --- a/content/en/about/privacy/_index.md +++ b/content/en/about/privacy/_index.md @@ -1,6 +1,8 @@ --- title: FreeCAD Privacy Policy linkTitle: Privacy -draft: true +build: + render: never +aliases: ["/privacy"] redirect: https://github.com/FreeCAD/FreeCAD/blob/main/PRIVACY_POLICY.md --- diff --git a/content/en/community/_index.md b/content/en/community/_index.md index 54e718a3d0f..ba3a19a25da 100644 --- a/content/en/community/_index.md +++ b/content/en/community/_index.md @@ -1,6 +1,7 @@ --- title: Community description: Get in touch with the community around the world. +aliases: ["/professional-network"] icon: people icon_cms: groups --- diff --git a/content/en/community/calendar/_index.md b/content/en/community/calendar/_index.md index 798489604ff..d799a17e54a 100644 --- a/content/en/community/calendar/_index.md +++ b/content/en/community/calendar/_index.md @@ -1,6 +1,7 @@ --- title: Calendar description: Browse all scheduled happenings. +aliases: ["/events"] weight: 1 icon: date --- diff --git a/content/en/dev/_index.md b/content/en/dev/_index.md index 139ce330fd0..1a59f005a80 100644 --- a/content/en/dev/_index.md +++ b/content/en/dev/_index.md @@ -1,6 +1,7 @@ --- title: Development Handbook description: Start contributing, extending, and shaping the future of the FreeCAD ecosystem. +aliases: ["/contributing"] icon: dev icon_cms: logo_dev --- \ No newline at end of file diff --git a/content/en/donate/_index.md b/content/en/donate/_index.md index 146323a4f97..67775f0cff8 100644 --- a/content/en/donate/_index.md +++ b/content/en/donate/_index.md @@ -2,6 +2,7 @@ title: Support the FreeCAD project linkTitle: Donate description: Donate once or sponsor monthly to make FreeCAD even more awesome! +aliases: ["/sponsor"] icon: heart icon_cms: favorite --- diff --git a/content/en/download/_index.md b/content/en/download/_index.md index 1b518c34996..c53a1f0ec72 100644 --- a/content/en/download/_index.md +++ b/content/en/download/_index.md @@ -2,6 +2,7 @@ title: Launch into design! linkTitle: Download description: Get FreeCAD and start with freedom right away. +aliases: ["/downloads"] icon: download icon_cms: download --- diff --git a/content/en/download/addons/_index.md b/content/en/download/addons/_index.md index c8cc5cb8b56..08ab78fd0c9 100644 --- a/content/en/download/addons/_index.md +++ b/content/en/download/addons/_index.md @@ -1,6 +1,7 @@ --- title: Addons description: Explore FreeCAD ecosystem and take advantage of specialized tools. +aliases: ["/addons"] weight: 3 icon: puzzle --- diff --git a/content/en/features/_index.md b/content/en/features/_index.md index 54c11a5b521..20f9b4eaaa2 100644 --- a/content/en/features/_index.md +++ b/content/en/features/_index.md @@ -2,6 +2,7 @@ title: Innovate without limits linkTitle: Features description: Powerful and open parametric design for everyone. +aliases: ["/features"] icon: stars icon_cms: bolt --- diff --git a/content/en/news/_index.md b/content/en/news/_index.md index b6ba73eac45..abc54beccee 100644 --- a/content/en/news/_index.md +++ b/content/en/news/_index.md @@ -1,6 +1,7 @@ --- title: News description: Browse the latest news +aliases: ["/blog"] icon: news icon_cms: brand_awareness --- diff --git a/themes/trigo/layouts/_partials/utils/link_redirect.html b/themes/trigo/layouts/_partials/utils/link_redirect.html index 3011f4dc854..34093c3a3b2 100644 --- a/themes/trigo/layouts/_partials/utils/link_redirect.html +++ b/themes/trigo/layouts/_partials/utils/link_redirect.html @@ -21,12 +21,12 @@ {{- $link = .RelPermalink -}} {{- end -}} {{- else -}} - {{- $target := site.GetPage . -}} + {{- $target := $page.Site.GetPage . -}} {{- with $target -}} {{- $link = .RelPermalink -}} {{- else -}} - {{- if and (hasPrefix . $wiki_url) (not site.Language.IsDefault) -}} - {{- $link = printf "%s/%s" . site.Language.Name -}} + {{- if and (hasPrefix . $wiki_url) (not $page.Site.Language.IsDefault) -}} + {{- $link = printf "%s/%s" . $page.Site.Language.Name -}} {{- else -}} {{- $link = . -}} {{- end -}} diff --git a/themes/trigo/layouts/home.redirects.nginx b/themes/trigo/layouts/home.redirects.nginx new file mode 100644 index 00000000000..51401c53ae6 --- /dev/null +++ b/themes/trigo/layouts/home.redirects.nginx @@ -0,0 +1,146 @@ +{{- /* +SPDX-License-Identifier: MIT +SPDX-FileCopyrightText: 2026 FreeCAD +SPDX-FileNotice: Part of the Trigo theme for Hugo. +*/ -}} + +{{- /* Nginx redirects: + +- Get all pages with aliases or redirect param. +- Homepage also gets the old /index.php. +- Resolve targets like site links do, so pages with a `redirect` param point to their target. +- Match languages by normalized lang name without locale, e.g. es_CO -> es, zh_TW -> zh. +- Do not include equal old and new paths to avoid circular self-redirecting loop. +- Output is parsed as nginx config, so every line is checked against a safe character set. +- Generate nginx redirects map for each alias or redirect-only page. + + @example /old, /old/, and /old.php -> /new/ + @example /old.php?lang= -> translated //new/ or default /new/ page if translation missing + @example /?lang= -> translated homepage + @example //internal and //internal/ -> https://external.org/ + @example //releases/0-17 and //releases/0-17/ -> https://wiki.freecad.org/Release_notes_0.17/ + +*/ -}} + +{{- if site.IsDefault -}} + + {{- $redirects := dict -}} + {{- $safe_source := `^/[A-Za-z0-9._~/%-]*(\?lang=[A-Za-z_]+)?$` -}} + {{- $safe_target := `^(/|https://)[A-Za-z0-9._~/%:?&=+()-]*$` -}} + {{- $old_locales := slice + "be" + "ca" + "cs" + "de" + "el" + "en" + "es" + "es_CO" + "eu" + "fa" + "fi" + "fr" + "ga" + "hr" + "hu" + "it" + "ja" + "ka" + "ko" + "lo" + "nl" + "pl" + "pt_BR" + "ru" + "sl" + "sr" + "sv" + "ta" + "tr" + "uk" + "zh_CN" + "zh_TW" + -}} + + {{- range $page := hugo.Sites.Default.Pages -}} + {{- $aliases := $page.Aliases -}} + + {{- if $page.IsHome -}} + {{- $aliases = $aliases | append "/index" -}} + {{- end -}} + + {{- if $aliases -}} + {{- $key := partial "utils/cache_key.html" $page -}} + {{- $new_redirect := partialCached "utils/link_redirect.html" $page $key -}} + {{- $new_langs := dict -}} + + {{- range $translation := $page.Translations -}} + {{- $normalized := replace (lower $translation.Language.Name) "_" "-" -}} + {{- $lang_name := index (split $normalized "-") 0 -}} + {{- if not (isset $new_langs $lang_name) -}} + {{- $key := partial "utils/cache_key.html" $translation -}} + {{- $lang_target := partialCached "utils/link_redirect.html" $translation $key .Language.Name -}} + {{- $new_langs = merge $new_langs (dict $lang_name $lang_target) -}} + {{- end -}} + {{- end -}} + + {{- range $alias := $aliases -}} + {{- $alias = trim $alias "/" -}} + {{- $php_alias := printf "/%s.php" $alias -}} + + {{- range $old_source := slice (printf "/%s" $alias) (printf "/%s/" $alias) $php_alias -}} + {{- $redirects = merge $redirects (dict $old_source $new_redirect) -}} + {{- end -}} + + {{- $lang_sources := slice $php_alias -}} + {{- if $page.IsHome -}} + {{- $lang_sources = $lang_sources | append "/" -}} + {{- end -}} + + {{- range $old_locale := $old_locales -}} + {{- $normalized := replace (lower $old_locale) "_" "-" -}} + {{- $lang_name := index (split $normalized "-") 0 -}} + {{- $new_target := default $new_redirect (index $new_langs $lang_name) -}} + + {{- range $lang_source := $lang_sources -}} + {{- $redirects = merge $redirects (dict (printf "%s?lang=%s" $lang_source $old_locale) $new_target) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- else if and $page.Params.redirect (ne $page.Params.redirect "auto") -}} + {{- $key := partial "utils/cache_key.html" $page -}} + {{- $new_redirect := partialCached "utils/link_redirect.html" $page $key -}} + {{- $page_source := trim $page.RelPermalink "/" -}} + + {{- /* Include the canonical trailing-slash path, which would otherwise serve the placeholder page. */ -}} + {{- range $old_source := slice (printf "/%s" $page_source) (printf "/%s/" $page_source) -}} + {{- $redirects = merge $redirects (dict $old_source $new_redirect) -}} + {{- end -}} + + {{- range $translation := $page.Translations -}} + {{- $key := partial "utils/cache_key.html" $translation -}} + {{- $redirect_target := partialCached "utils/link_redirect.html" $translation $key .Language.Name -}} + {{- $translated_source := trim $translation.RelPermalink "/" -}} + {{- range $old_source := slice (printf "/%s" $translated_source) (printf "/%s/" $translated_source) -}} + {{- $redirects = merge $redirects (dict $old_source $redirect_target) -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- range $old, $new := $redirects -}} + {{- if not (findRE $safe_source $old) -}} + {{- errorf "Redirect source %q contains characters not allowed in the nginx map" $old -}} + {{- end -}} + + {{- if not (findRE $safe_target $new) -}} + {{- errorf "Redirect target %q for %q contains characters not allowed in the nginx map" $new $old -}} + {{- end -}} + + {{- /* Skip equal old and new paths, which would redirect to themselves. */ -}} + {{- if ne (trim $old "/") (trim $new "/") -}} + {{- printf "%s %s;\n" $old $new -}} + {{- end -}} + {{- end -}} +{{- end -}}