content,internal: add page descriptions to unit main pages
Adds page descriptions to the unit main pages
by using a safehtml escape hatch to build the
HTML meta tag manually. This a workaround for
a safehtml limitation.
See: https://github.com/google/safehtml/issues/6.
Fixes golang/go#40752
Change-Id: I021e77bd2d01bb435751d353b809087ce818f57d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/275874
Trust: Jamal Carvalho <jamal@golang.org>
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/content/static/html/base.tmpl b/content/static/html/base.tmpl
index c7acbfa..f3660ac 100644
--- a/content/static/html/base.tmpl
+++ b/content/static/html/base.tmpl
@@ -11,7 +11,11 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
-<meta name="Description" content="Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.">
+{{if .MetaDescription.String}}
+ {{.MetaDescription}}
+{{else}}
+ <meta name="Description" content="Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.">
+{{end}}
<meta class="js-gtmID" data-gtmid="{{.GoogleTagManagerID}}">
<link href="/static/css/stylesheet.css?version={{.AppVersionLabel}}" rel="stylesheet">
<link href="/third_party/dialog-polyfill/dialog-polyfill.css?version={{.AppVersionLabel}}" rel="stylesheet">
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index 8c5a9b1..baeefa7 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -159,6 +159,7 @@
cmp.AllowUnexported(SearchPage{}, pagination{}),
cmpopts.IgnoreFields(licenses.Metadata{}, "FilePath"),
cmpopts.IgnoreFields(pagination{}, "Approximate"),
+ cmpopts.IgnoreFields(basePage{}, "MetaDescription"),
}
if diff := cmp.Diff(test.wantSearchPage, got, opts...); diff != "" {
t.Errorf("fetchSearchPage(db, %q) mismatch (-want +got):\n%s", test.query, diff)
diff --git a/internal/frontend/server.go b/internal/frontend/server.go
index 219371c..7513ef4 100644
--- a/internal/frontend/server.go
+++ b/internal/frontend/server.go
@@ -17,6 +17,7 @@
"time"
"github.com/go-redis/redis/v8"
+ "github.com/google/safehtml"
"github.com/google/safehtml/template"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
@@ -237,6 +238,9 @@
// HTMLTitle is the value to use in the page’s <title> tag.
HTMLTitle string
+ // MetaDescription is the html used for rendering the <meta name="Description"> tag.
+ MetaDescription safehtml.HTML
+
// Query is the current search query (if applicable).
Query string
diff --git a/internal/frontend/unit.go b/internal/frontend/unit.go
index 6320f4c..9234caa 100644
--- a/internal/frontend/unit.go
+++ b/internal/frontend/unit.go
@@ -12,6 +12,8 @@
"strings"
"time"
+ "github.com/google/safehtml"
+ "github.com/google/safehtml/uncheckedconversions"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/log"
@@ -141,10 +143,28 @@
return err
}
page.Details = d
+ main, ok := d.(*MainDetails)
+ if ok {
+ page.MetaDescription = metaDescription(main.ImportedByCount)
+ }
s.servePage(ctx, w, tabSettings.TemplateName, page)
return nil
}
+// metaDescription uses a safehtml escape hatch to build HTML used
+// to render the <meta name="Description"> for unit pages as a
+// workaround for https://github.com/google/safehtml/issues/6.
+func metaDescription(synopsis string) safehtml.HTML {
+ if synopsis == "" {
+ return safehtml.HTML{}
+ }
+ return safehtml.HTMLConcat(
+ uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(`<meta name="Description" content="`),
+ safehtml.HTMLEscaped(synopsis),
+ uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(`">`),
+ )
+}
+
// isValidTabForUnit reports whether the tab is valid for the given unit.
// It is assumed that tab is a key in unitTabLookup.
func isValidTabForUnit(tab string, um *internal.UnitMeta) bool {
diff --git a/internal/frontend/unit_main.go b/internal/frontend/unit_main.go
index 6b191e9..4ad94b3 100644
--- a/internal/frontend/unit_main.go
+++ b/internal/frontend/unit_main.go
@@ -74,6 +74,10 @@
MobileOutline safehtml.HTML
IsPackage bool
+ // DocSynopsis is used as the content for the <meta name="Description">
+ // tag on the main unit page.
+ DocSynopsis string
+
// SourceFiles contains .go files for the package.
SourceFiles []*File
@@ -135,8 +139,10 @@
docParts = &dochtml.Parts{}
docLinks, modLinks []link
files []*File
+ synopsis string
)
if unit.Documentation != nil {
+ synopsis = unit.Documentation.Synopsis
end := middleware.ElapsedStat(ctx, "DecodePackage")
docPkg, err := godoc.DecodePackage(unit.Documentation.Source)
end()
@@ -192,6 +198,7 @@
ModuleReadmeLinks: modLinks,
DocOutline: docParts.Outline,
DocBody: docParts.Body,
+ DocSynopsis: synopsis,
SourceFiles: files,
RepositoryURL: um.SourceInfo.RepoURL(),
SourceURL: um.SourceInfo.DirectoryURL(internal.Suffix(um.Path, um.ModulePath)),
diff --git a/internal/frontend/unit_test.go b/internal/frontend/unit_test.go
index f18b026..953344e 100644
--- a/internal/frontend/unit_test.go
+++ b/internal/frontend/unit_test.go
@@ -128,3 +128,27 @@
}
}
}
+
+func TestMetaDescription(t *testing.T) {
+ for _, test := range []struct {
+ synopsis, want string
+ }{
+ {
+ synopsis: "",
+ want: "",
+ },
+ {
+ synopsis: "Hello, world.",
+ want: `<meta name="Description" content="Hello, world.">`,
+ },
+ {
+ synopsis: `"><script>alert();</script><br`,
+ want: `<meta name="Description" content=""><script>alert();</script><br">`,
+ },
+ } {
+ got := metaDescription(test.synopsis).String()
+ if got != test.want {
+ t.Errorf("metaDescription(%q) = %q, want %q", test.synopsis, got, test.want)
+ }
+ }
+}