From 1205aa0448dcc4400e39e22e532ab4b62bb8b4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Weber?= Date: Wed, 29 Jan 2025 18:03:23 +0100 Subject: [PATCH] menu: add internal link check #1002 --- .../authoring/frontmatter/linking/index.en.md | 2 +- .../introduction/releasenotes/7/4.en.md | 4 ++- layouts/partials/_relearn/urlExists.gotmpl | 0 layouts/partials/menu.html | 22 ++++++++++--- layouts/partials/menupermalink.gotmpl | 32 +++++++++++++++---- layouts/partials/meta.html | 17 +++++++++- layouts/partials/shortcodes/image.html | 4 ++- layouts/partials/shortcodes/link.html | 1 + layouts/partials/shortcodes/openapi.html | 4 ++- layouts/partials/version.txt | 2 +- 10 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 layouts/partials/_relearn/urlExists.gotmpl diff --git a/exampleSite/content/authoring/frontmatter/linking/index.en.md b/exampleSite/content/authoring/frontmatter/linking/index.en.md index f9cb4a5b33..4aaef08cd2 100644 --- a/exampleSite/content/authoring/frontmatter/linking/index.en.md +++ b/exampleSite/content/authoring/frontmatter/linking/index.en.md @@ -19,7 +19,7 @@ externalLinkTarget = '_self' ## Enabling Link and Image Link Warnings -{{% badge style="cyan" icon="gears" title=" " %}}Option{{% /badge %}} {{% badge style="green" icon="fa-fw fab fa-markdown" title=" " %}}Front Matter{{% /badge %}} You can use `link.errorlevel` and `image.errorlevel` to control what should happen if a local link can not be resolved to a resource. +{{% badge style="cyan" icon="gears" title=" " %}}Option{{% /badge %}} {{% badge style="green" icon="fa-fw fab fa-markdown" title=" " %}}Front Matter{{% /badge %}} You can use `link.errorlevel` and `image.errorlevel` to control what should happen if a local link can not be resolved to a page and/or a resource. If not set or empty, any unresolved link is written as given into the resulting output. If set to `warning` the same happens and an additional warning is printed in the built console. If set to `error` an error message is printed and the build is aborted. diff --git a/exampleSite/content/introduction/releasenotes/7/4.en.md b/exampleSite/content/introduction/releasenotes/7/4.en.md index d4087be543..69a4a16a81 100644 --- a/exampleSite/content/introduction/releasenotes/7/4.en.md +++ b/exampleSite/content/introduction/releasenotes/7/4.en.md @@ -10,8 +10,10 @@ weight = -4 ### New -- {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} Table headers are sticky now. +- {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} If `link.errorlevel` is configured, now also the `pageRef` of a [Hugo menu item](https://gohugo.io/content-management/menus/) and the `menuPageRef` in a page's front matter will be checked for existence. - {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} The theme supports the new [`source` output format](configuration/sitemanagement/outputformats/#source-support) which behaves similar in configuration as the `markdown` output format but allows the original Markdown source including the front matter of a page to be viewed. You can see this in action on the above linked page, accessible by clicking the topbar button. + +- {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} Table headers are sticky now. diff --git a/layouts/partials/_relearn/urlExists.gotmpl b/layouts/partials/_relearn/urlExists.gotmpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/layouts/partials/menu.html b/layouts/partials/menu.html index 6a40722c71..97830d7d6c 100644 --- a/layouts/partials/menu.html +++ b/layouts/partials/menu.html @@ -163,20 +163,34 @@ {{- $url := partial "permalink.gotmpl" (dict "to" .) }} {{- $isCrosslink := false }} {{- $target := "" }} + {{- $errorlevel := or $currentNode.Params.link.errorlevel $currentNode.Site.Params.link.errorlevel }} {{- if .Params.menuPageRef }} {{- with site.Home.GetPage (.Params.menuPageRef) }} {{- $url = partial "permalink.gotmpl" (dict "to" .) }} {{- $isCrosslink = true }} + {{- else }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: menu link '%s' is not a page but linked anyways" $currentNode.File.Filename .Params.menuPageRef }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: menu link '%s' is not a page" $currentNode.File.Filename .Params.menuPageRef }} + {{- end }} {{- end }} {{- else if .Params.menuUrl }} {{- $url = .Params.menuUrl }} {{- $isCrosslink = true }} {{- $u := urls.Parse $url }} {{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" $url "page" $currentNode "type" "menu link") $u.String }} {{- $target = "_blank" }} {{- if isset site.Params "externallinktarget" }} {{- $target = site.Params.externalLinkTarget }} {{- end }} + {{- else }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: local menu link '%s' was given by 'menuURL'; if it points to a page inside your Hugo site use 'menuPageRef' instead; link created anyways" $currentNode.File.Filename .Params.menuUrl }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: local menu link '%s' was given by 'menuURL'; if it points to a page inside your Hugo site use 'menuPageRef' instead" $currentNode.File.Filename .Params.menuUrl }} + {{- end }} {{- end }} {{- end }} {{- $pre := partial "menu-pre.html" . }} @@ -225,7 +239,7 @@ {{- $entries := . }} {{- if eq (len $entries) 1 }} {{- with index $entries 0 }} - {{- if not (partial "menupermalink.gotmpl" .) }} + {{- if not (partial "menupermalink.gotmpl" (dict "page" $currentNode "menu" .)) }} {{- /* because in Hugo menus can not have parameter but menu entries can, we can flag a single top level menu entry as a container; this container entry carrys just meta information and parameter, uses its children @@ -264,7 +278,7 @@ {{- $isSubCollapsible := .Params.collapsibleMenu | default $root.Params.collapsibleMenu | default site.Params.collapsibleMenu }} {{- if or $isSubSelf $isSubAncestor }} {{- partial "partials/inline/menu-walker" (dict "menu" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "alwaysopen" $defaultAlwaysopen "isSelf" $isSubSelf "isAncestor" $isSubAncestor "isHidden" $isSubHidden "root" $root) }} - {{- else if and (not $isSubHidden) (or $isSubCollapsible (not (partial "menupermalink.gotmpl" $entry)) (and $entry (eq $entry.Page $currentNode)) (and $entry ($currentNode.HasMenuCurrent $entry.Menu $entry))) }} + {{- else if and (not $isSubHidden) (or $isSubCollapsible (not (partial "menupermalink.gotmpl" (dict "page" $currentNode "menu" $entry))) (and $entry (eq $entry.Page $currentNode)) (and $entry ($currentNode.HasMenuCurrent $entry.Menu $entry))) }} {{- $id := md5 (print .) }} {{- partialCached "partials/inline/menu-walker" (dict "menu" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "alwaysopen" $defaultAlwaysopen "isSelf" $isSubSelf "isAncestor" $isSubAncestor "isHidden" $isSubHidden "root" $root) $id }} {{- end }} @@ -300,7 +314,7 @@ {{- end }} {{- end }} {{- $title := partial "menutitle.gotmpl" . }} - {{- $url := partial "menupermalink.gotmpl" . }} + {{- $url := partial "menupermalink.gotmpl" (dict "page" $currentNode "menu" .) }} {{- $target := "" }} {{- $u := urls.Parse $url }} {{- if $u.IsAbs }} @@ -336,7 +350,7 @@ {{- $isSubCollapsible := .Params.collapsibleMenu | default $root.Params.collapsibleMenu | default site.Params.collapsibleMenu }} {{- if or $isSubSelf $isSubAncestor }} {{- partial "partials/inline/menu-walker" (dict "menu" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "alwaysopen" $defaultAlwaysopen "isSelf" $isSubSelf "isAncestor" $isSubAncestor "isHidden" $isSubHidden "root" $root) }} - {{- else if and (not $isSubHidden) (or $isSubCollapsible (not (partial "menupermalink.gotmpl" $entry)) (eq $entry.Page $currentNode) ($currentNode.HasMenuCurrent $entry.Menu $entry)) }} + {{- else if and (not $isSubHidden) (or $isSubCollapsible (not (partial "menupermalink.gotmpl" (dict "page" $currentNode "menu" $entry))) (eq $entry.Page $currentNode) ($currentNode.HasMenuCurrent $entry.Menu $entry)) }} {{- $id := md5 (print .) }} {{- partialCached "partials/inline/menu-walker" (dict "menu" . "currentnode" $currentNode "showvisitedlinks" $showvisitedlinks "alwaysopen" $defaultAlwaysopen "isSelf" $isSubSelf "isAncestor" $isSubAncestor "isHidden" $isSubHidden "root" $root) $id }} {{- end }} diff --git a/layouts/partials/menupermalink.gotmpl b/layouts/partials/menupermalink.gotmpl index 6f9414b12f..cdebec5903 100644 --- a/layouts/partials/menupermalink.gotmpl +++ b/layouts/partials/menupermalink.gotmpl @@ -1,10 +1,30 @@ {{- $url := "" }} -{{- with . }} - {{- with .URL }} - {{- $url = . | relLangURL }} - {{- end }} - {{- with .Page }} - {{- $url = partial "permalink.gotmpl" (dict "to" .) }} +{{- with .menu }} + {{- $errorlevel := or $.page.Params.link.errorlevel $.page.Site.Params.link.errorlevel }} + {{- if .PageRef }} + {{- with .Page }} + {{- $url = partial "permalink.gotmpl" (dict "to" .) }} + {{- else }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: menu link '%s' is not a page but linked anyways" $.page.File.Filename .PageRef }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: menu link '%s' is not a page" $.page.File.Filename .PageRef }} + {{- end }} + {{- end }} + {{- else }} + {{- with .URL }} + {{- $url = . | relLangURL }} + {{- $u := urls.Parse $url }} + {{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" $url "page" $.page "type" "menu link") $u.String }} + {{- else }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: local menu link '%s' was given by 'URL'; if it points to a page inside your Hugo site use 'pageRef' instead; link created anyways" $.page.File.Filename . }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: local menu link '%s' was given by 'URL'; if it points to a page inside your Hugo site use 'pageRef' instead" $.page.File.Filename . }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- end }} {{- return $url }} \ No newline at end of file diff --git a/layouts/partials/meta.html b/layouts/partials/meta.html index 1555c19193..d8325b9419 100644 --- a/layouts/partials/meta.html +++ b/layouts/partials/meta.html @@ -15,14 +15,29 @@ {{- end }} {{- $url := "" }} + {{- $errorlevel := or .Params.link.errorlevel .Site.Params.link.errorlevel }} {{- if .Params.menuPageRef }} {{- with site.Home.GetPage (.Params.menuPageRef) }} {{- $url = partial "permalink.gotmpl" (dict "to" .) }} {{- else }} - {{- warnf "%q: WARNING: page '%s' not found for 'menuPageRef' parameter" .File.Filename .Params.menuPageRef }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: menu link '%s' is not a page but linked anyways" .File.Filename .Params.menuPageRef }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: menu link '%s' is not a page" .File.Filename .Params.menuPageRef }} + {{- end }} {{- end }} {{- else if .Params.menuUrl }} {{- $url = .Params.menuUrl }} + {{- $u := urls.Parse $url }} + {{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" $url "page" . "type" "menu link") $u.String }} + {{- else }} + {{- if eq $errorlevel "warning" }} + {{- warnf "%q: local menu link '%s' was given by 'menuURL'; if it points to a page inside your Hugo site use 'menuPageRef' instead; link created anyways" .File.Filename .Params.menuUrl }} + {{- else if eq $errorlevel "error" }} + {{- errorf "%q: local menu link '%s' was given by 'menuURL'; if it points to a page inside your Hugo site use 'menuPageRef' instead" .File.Filename .Params.menuUrl }} + {{- end }} + {{- end }} {{- end }} {{- if $url }} diff --git a/layouts/partials/shortcodes/image.html b/layouts/partials/shortcodes/image.html index 8b1c614d85..a1644c629d 100644 --- a/layouts/partials/shortcodes/image.html +++ b/layouts/partials/shortcodes/image.html @@ -18,7 +18,9 @@ {{- $attributes := .attributes | default dict }} {{- $u := urls.Parse .url }} {{- $src := $u.String }} -{{- if not $u.IsAbs }} +{{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" .url "page" $page "type" "image") $u.String }} +{{- else }} {{- $path := strings.TrimPrefix "./" $u.Path }} {{- with or ($page.Resources.Get $path) diff --git a/layouts/partials/shortcodes/link.html b/layouts/partials/shortcodes/link.html index d6f429ed61..24c2074de1 100644 --- a/layouts/partials/shortcodes/link.html +++ b/layouts/partials/shortcodes/link.html @@ -16,6 +16,7 @@ {{- $u := urls.Parse .url -}} {{- $href := $u.String }} {{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" .url "page" $page "type" "link") $u.String }} {{- $attributes = merge $attributes (dict "rel" "external") }} {{- $target = "_blank" }} {{- if isset $page.Site.Params "externallinktarget" }} diff --git a/layouts/partials/shortcodes/openapi.html b/layouts/partials/shortcodes/openapi.html index 7a91cb9d6d..662a51c16a 100644 --- a/layouts/partials/shortcodes/openapi.html +++ b/layouts/partials/shortcodes/openapi.html @@ -7,7 +7,9 @@ {{- $src := $u.String }} {{- $spec := "" }} {{- $id := cond (or (eq .id nil) (eq .id "")) (partial "_relearn/makeRandomMd5.gotmpl" $page) .id }} -{{- if not $u.IsAbs }} +{{- if $u.IsAbs }} + {{- partialCached "_relearn/urlExists.gotmpl" (dict "url" .src "page" $page "type" "OpenAPI spec link") $u.String }} +{{- else }} {{- $path := strings.TrimPrefix "./" $u.Path }} {{- with or ($page.Resources.Get $path) diff --git a/layouts/partials/version.txt b/layouts/partials/version.txt index 948e64a716..c6f084e0f1 100644 --- a/layouts/partials/version.txt +++ b/layouts/partials/version.txt @@ -1 +1 @@ -7.3.2+44f7ff47eebd1a552b906059400888958f2709ae \ No newline at end of file +7.3.2+ba7e3b9b27ff1459815a786b6c6adb5686723b58 \ No newline at end of file