feat(link): add link_guard front matter and support for friend links

This commit is contained in:
Cell
2026-04-11 23:15:30 +08:00
parent 75ee1624bd
commit 5f100a6cca
10 changed files with 88 additions and 47 deletions
+5
View File
@@ -7,6 +7,11 @@ categories:
- Markdown
tags:
- Link
link_guard:
enable: true
# mode: redirect
allow_domains:
- demo.fixit.lruihao.cn
---
+13 -1
View File
@@ -34,7 +34,10 @@
font-family: fixit-var(code-font-family);
font-size: 0.875rem;
line-height: 1rem;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@include user-select(text);
@include border-radius;
}
@@ -80,4 +83,13 @@
background-color: fixit-var(primary);
border-color: fixit-var(primary);
}
@include media('xs') {
padding: 1rem;
.cancel-btn,
.confirm-btn {
flex: 1 1 50%;
}
}
}
@@ -47,24 +47,3 @@
}
}
}
@include media('xs') {
#link-guard-dialog {
width: calc(100% - 2rem);
.guard-content {
padding: 1rem;
.actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.cancel-btn,
.confirm-btn {
width: 100%;
min-width: 0;
}
}
}
}
+2 -3
View File
@@ -1,5 +1,5 @@
#content-link {
margin: auto 0;
align-self: center;
.card {
width: MIN(600px, calc(100% - 2rem));
@@ -8,8 +8,7 @@
@include link-guard-content;
@include media('xs') {
margin-inline: 0;
padding: 1rem;
width: MIN(600px, 100%);
}
}
+4 -1
View File
@@ -1143,7 +1143,8 @@ blackList = []
# whether to add external Icon for external links automatically
external_icon = true
# external link guard config
# External link guard config
# per-page override via front matter: link_guard (bool/object)
[params.link.guard]
enable = false
# external link guard mode, available modes: ["modal" "redirect"]
@@ -1282,6 +1283,8 @@ linkToEdit = true
# FixIt 0.3.0 | NEW whether to show link to report issue for the post
linkToReport = true
# FixIt 0.3.20 | NEW whether to show link to view the post in VSCode
# [todo] (string) migrate to link_to_editor config in the future
# to support more editors (vscode://, trae://, etc.)
linkToVscode = true
# FixIt 0.3.10 | CHANGED Page style ["narrow", "normal", "wide", ...]
pageStyle = "normal"
+1 -1
View File
@@ -2,5 +2,5 @@
{{- with dict "Path" $destination | partial "function/resource.html" -}}
{{- $destination = .RelPermalink -}}
{{- end -}}
{{- $options := dict "Destination" $destination "Title" .Title "Content" .Text -}}
{{- $options := dict "Destination" $destination "Title" .Title "Content" .Text "Page" .Page -}}
{{- partial "plugin/link.html" $options -}}
+5 -1
View File
@@ -66,7 +66,11 @@
{{- end -}}
{{- /* Link Guard Dialog */ -}}
{{- if .Site.Params.link.guard.enable -}}
{{- $linkGuard := .Site.Params.link.guard -}}
{{- with .Params.link_guard -}}
{{- $linkGuard = (cond (reflect.IsMap .) . (dict "enable" .)) | merge $linkGuard -}}
{{- end -}}
{{- if $linkGuard.enable | and (eq $linkGuard.mode "modal") -}}
<dialog id="link-guard-dialog" aria-labelledby="link-guard-dialog-title" role="dialog">
<div class="guard-content">
<h2 class="title">{{ T "linkRedirection.title" }}</h2>
+16 -5
View File
@@ -7,6 +7,7 @@
@param {String} [.Content] link label HTML
@param {String} [.Title] title attribute
@param {String} [.Class] custom CSS classes
@param {String} [.Rel] rel attribute value
@param {Boolean} [.Newtab] force external behavior
@param {Boolean} [.Noreferrer] whether to include noreferrer (default: true)
@param {Boolean} [.ExternalIcon] whether to show external-link icon
@@ -14,6 +15,8 @@
@param {Boolean} [.Card] whether to render card link layout
@param {String} [.CardIcon] card icon URL or icon class
@param {String} [.Download] download attribute value
@param {Boolean} [.IsGuarded] explicitly enable or disable link guard
@param {Object} [.Page] current page context
*/ -}}
{{- /* Regular link rendering */ -}}
@@ -32,6 +35,7 @@
$externalIcon
| and $isExternal
| and (not (hasPrefix $ctx.Content `<img`))
| and (not (hasPrefix $ctx.Content `<svg`))
| and (not (strings.HasSuffix $ctx.Content `</i>`))
| and (not (strings.HasSuffix $ctx.Content `):`))
-}}
@@ -101,7 +105,6 @@
{{- $noreferrer = false -}}
{{- end -}}
{{- $url := urls.Parse .Destination -}}
{{/* {{ partial "_debug/dump" $url }} */}}
{{- if $url.IsAbs | and (not (in $blockSchemes $url.Scheme)) -}}
{{- $isExternal = not (strings.HasPrefix .Destination site.BaseURL) -}}
{{- end -}}
@@ -118,12 +121,20 @@
{{- /* External link guard */ -}}
{{- $isGuarded := false -}}
{{- $guardConfig := $config.guard -}}
{{- with .Page.Params.link_guard -}}
{{- if reflect.IsMap . -}}
{{- $guardConfig = (partial "function/snake2camel.html" .) | merge $guardConfig -}}
{{- else -}}
{{- $guardConfig = (dict "enable" .) | merge $guardConfig -}}
{{- end -}}
{{- end -}}
{{- if
$config.guard.enable
$guardConfig.enable
| and $isExternal
| and (not (in $config.guard.allowDomains $url.Host))
| and (not (in $guardConfig.allowDomains $url.Host))
-}}
{{- $isGuarded = true -}}
{{- $isGuarded = .IsGuarded | default true -}}
{{- end -}}
{{- $destination := cond $isGuarded (add (relLangURL "link") "?target=" (urlquery .Destination)) .Destination -}}
@@ -143,7 +154,7 @@
{{- with .Download -}}
{{- $attrs = printf ` download="%s"` . | add $attrs -}}
{{- end -}}
{{- if $isGuarded | and (eq $config.guard.mode "modal") -}}
{{- if $isGuarded | and (eq $guardConfig.mode "modal") -}}
{{- $attrs = add $attrs ` data-guard="modal"` -}}
{{- end -}}
+3 -2
View File
@@ -2,7 +2,7 @@
{{- with dict "Path" $destination | partial "function/resource.html" -}}
{{- $destination = .RelPermalink -}}
{{- end -}}
{{- $options := dict "Destination" $destination -}}
{{- $options := dict "Destination" $destination "Page" .Page -}}
{{- if .IsNamedParams -}}
{{- $options = dict "Content" (.Get "content") | merge $options -}}
{{- $options = dict "Title" (.Get "title") | merge $options -}}
@@ -12,11 +12,12 @@
{{- $options = dict "Card" (.Get "card") | merge $options -}}
{{- $options = dict "CardIcon" (.Get "card-icon") | merge $options -}}
{{- $options = dict "Noreferrer" (.Get "noreferrer") | merge $options -}}
{{- $options = dict "ExternalIcon" (.Get "external-icon") | merge $options -}}
{{- $options = dict "IsGuarded" (.Get "isGuarded") | merge $options -}}
{{- else -}}
{{- $options = dict "Content" (.Get 1 | default (.Get 0)) | merge $options -}}
{{- $options = dict "Title" (.Get 2) | merge $options -}}
{{- $options = dict "Card" (.Get 3) | merge $options -}}
{{- $options = dict "CardIcon" (.Get 4) | merge $options -}}
{{- end -}}
{{- $options = dict "ExternalIcon" (.Get "external-icon") | merge $options -}}
{{- partial "plugin/link.html" $options -}}
+39 -12
View File
@@ -1,3 +1,23 @@
{{- /*
[todo] refactor #206
[todo] link_guard usage in data file
Friends page template.
Usage:
1. Friend link items are loaded from data/friends.yml.
2. External link guard is disabled by default on this page.
3. To enable guard for all friend links on the current page, set `link_guard` to `true` in front matter.
@example
```md
---
title: friends
layout: friends
link_guard: true
---
```
*/ -}}
{{- define "title" -}}
{{- cond (.Param "capitalizeTitles") (title .Title) .Title -}}
{{- if .Site.Params.withSiteTitle }} {{ .Site.Params.titleDelimiter }} {{ .Site.Title }}{{- end -}}
@@ -5,6 +25,7 @@
{{- define "content" -}}
{{- $params := partial "function/params.html" -}}
{{- $isGuarded := .Params.link_guard | default false -}}
<article class="page single special friends">
<div class="header">
{{- /* Title */ -}}
@@ -16,18 +37,24 @@
{{- /* Friend links */ -}}
<script src="//at.alicdn.com/t/font_578712_g26jo2kbzd5qm2t9.js" async defer></script>
<div class="friend-links">
{{ range $index, $friend := hugo.Data.friends }}
<a class="friend-link" title="{{ $friend.description | safeHTML }}" href="{{ $friend.url | safeURL }}" rel="external noopener" target="_blank">
{{ if $friend.avatar }}
{{- dict "Src" $friend.avatar "Alt" $friend.nickname "Title" $friend.description "Class" "friend-avatar" | partial "plugin/image.html" -}}
{{ else }}
<svg class="friend-avatar" aria-hidden="true">
<use xlink:href="#icon-{{ add 1 $index }}"></use>
</svg>
{{ end }}
<span class="friend-nickname" title="{{ $friend.nickname }}">@{{ $friend.nickname }}</span>
</a>
{{ end }}
{{- range $index, $friend := hugo.Data.friends -}}
{{- $avatar := "" -}}
{{- if $friend.avatar -}}
{{- $avatar = dict "Src" $friend.avatar "Alt" $friend.nickname "Title" $friend.description "Class" "friend-avatar" | partial "plugin/image.html" -}}
{{- else -}}
{{- $avatar = printf `<svg class="friend-avatar" aria-hidden="true"><use xlink:href="#icon-%d"></use></svg>` (add 1 $index) -}}
{{- end -}}
{{- $nickname := printf `<span class="friend-nickname" title="%s">@%s</span>` $friend.nickname $friend.nickname -}}
{{- $options := dict
"Destination" $friend.url
"Title" $friend.description
"Content" (printf `%s%s` $avatar $nickname)
"Class" "friend-link"
"ExternalIcon" false
"IsGuarded" $isGuarded
-}}
{{- partial "plugin/link.html" $options -}}
{{- end -}}
</div>
{{- /* Content */ -}}