content,internal: add badge generation for package authors

This change creates a badge generation tool page for package authors. A future change will add
documenation of this feature and a link to the about page.

Fixes golang/go#36982

Change-Id: Ia64ba9db73ed92b853f1f955330caf93d996da91
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/241273
Reviewed-by: Andrew Bonventre <andybons@golang.org>
This commit is contained in:
Jamal Carvalho 2020-07-07 13:01:02 -04:00
Родитель 103a6b7425
Коммит d37103e11f
11 изменённых файлов: 292 добавлений и 4 удалений

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright 2019 The Go Authors. All rights reserved.
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
@ -1095,6 +1095,64 @@ table.Directories {
margin-left: 1.1rem;
}
.Badge-formElement {
display: block;
font-size: 1rem;
margin-top: 1rem;
}
.Badge-badgeIcon {
height: 20px;
width: 90px;
}
.Badge-previewLink,
.Badge-formElement > input {
display: block;
margin-top: 1rem;
max-width: 50.25rem;
}
.Badge-formElement > input {
border: 0.0625rem solid var(--gray-8);
border-radius: 0.25rem;
color: var(--gray-4);
font-family: inherit;
height: 3rem;
padding: 0 2rem 0 0.75rem;
text-overflow: ellipsis;
width: 100%;
}
.Badge-formElement > input:focus::placeholder {
color: transparent;
}
.Badge-clickToCopy {
background: var(--gray-10) url('/static/img/copy-click.svg') right no-repeat;
background-position: right 0.75rem center;
}
.Badge-submitButton {
border: none;
border-radius: 0.25rem;
background-color: var(--turq-dark);
color: var(--white);
cursor: pointer;
font-family: inherit;
height: 2.75rem;
width: 7.125rem;
}
.Badge-snippetContainer {
background-color: var(--gray-10);
display: block;
margin-top: 1rem;
max-width: 50.25rem;
padding: 1rem;
}
.Badge-gopherLanding {
height: 12.25rem;
text-align: center;
}
.Badge-gopherLanding img {
height: 125px;
width: auto;
}
/* dialogs, including the jump-to-identifier dialog on documentation pages */
.Dialog {

Просмотреть файл

@ -0,0 +1,57 @@
<!--
Copyright 2020 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "main_content"}}
<div class="Container">
<div class="Content">
<h1 class="Content-header">Create a badge</h1>
<p>Create a badge to link to pkg.go.dev from your project website or README file.</p>
<label class="Badge-formElement">
Badge
<div class="Badge-previewLink">
<a class="js-badgeExampleButton" href="{{.SiteURL}}/{{.Path}}">
<img class="Badge-badgeIcon" src="/static/img/badge.svg" alt="PkgGoDev">
</a>
</div>
</label>
<form action="/badge/">
<label class="Badge-formElement">
URL
<input name="path" class="js-toolsPathInput"
value="{{if .Path}}{{.SiteURL}}/{{.Path}}{{end}}" placeholder="e.g., https://pkg.go.dev/golang.org/x/pkgsite">
</label>
<label class="Badge-formElement">
<button type="submit" class="Badge-submitButton">Create</button>
</label>
</form>
<div class="Badge-snippetContainer">
{{if .Path}}
<label class="Badge-formElement">
HTML
<input title="Click to copy HTML" name="html" class="Badge-clickToCopy js-toolsCopySnippet" type="text"
value='<a href="{{.SiteURL}}/{{.Path}}"><img src="{{.SiteURL}}/badge/{{.Path}}" alt="PkgGoDev"></a>' readonly>
</label>
<label class="Badge-formElement">
Markdown
<input title="Click to copy markdown" name="markdown" class="Badge-clickToCopy js-toolsCopySnippet" type="text"
value="[![PkgGoDev]({{.SiteURL}}/{{.Path}})]({{.SiteURL}}/badge/{{.Path}})" readonly>
</label>
{{else}}
<div class="Badge-gopherLanding">
<img src="/static/img/gopher-airplane.svg" alt="The Go Gopher"/>
<p>Type a pkg.go.dev URL above to create a badge link.</p>
</div>
{{end}}
</div>
</div>
</div>
{{end}}
{{define "post_content"}}
<script>
loadScript("/static/js/badge.min.js");
</script>
{{end}}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 6.3 KiB

Просмотреть файл

@ -0,0 +1 @@
<svg fill="#00add8" width="13" height="15" xmlns="http://www.w3.org/2000/svg"><path d="M8 0H2a2 2 0 00-2 2v8a2 2 0 002 2V2h8a2 2 0 00-2-2zm3 3H5a2 2 0 00-2 2v8a2 2 0 002 2h6a2 2 0 002-2V5a2 2 0 00-2-2zM5 13h6V5H5v8z" fill-rule="evenodd"/></svg>

После

Ширина:  |  Высота:  |  Размер: 244 B

Просмотреть файл

@ -0,0 +1,30 @@
/**
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
const snippetEls = document.querySelectorAll('.js-toolsCopySnippet');
snippetEls.forEach(inputEl => {
inputEl.addEventListener('click', e => {
e.preventDefault();
e.currentTarget.select();
document.execCommand('copy');
});
});
const pathEl = document.querySelector('.js-toolsPathInput');
const htmlEl = document.querySelector('input[name="html"].js-toolsCopySnippet');
const markdownEl = document.querySelector('input[name="markdown"].js-toolsCopySnippet');
const badgeEl = document.querySelector('.js-badgeExampleButton');
if (pathEl && htmlEl && markdownEl && badgeEl) {
pathEl.addEventListener('input', e => {
const origin = window.location.origin;
const href = `${origin}/${e.target.value}`;
const imgSrc = `${origin}/badge/${e.target.value}`;
htmlEl.value = `<a href="${href}"><img src="${imgSrc}" alt="PkgGoDev"></a>`;
markdownEl.value = `[![PkgGoDev](${href})](${imgSrc})`;
badgeEl.href = href;
});
}

8
content/static/js/badge.min.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
/*
Copyright 2019-2020 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
*/
var snippetEls=document.querySelectorAll(".js-toolsCopySnippet");snippetEls.forEach(function(a){a.addEventListener("click",function(a){a.preventDefault();a.currentTarget.select();document.execCommand("copy")})});var pathEl=document.querySelector(".js-toolsPathInput"),htmlEl=document.querySelector('input[name="html"].js-toolsCopySnippet'),markdownEl=document.querySelector('input[name="markdown"].js-toolsCopySnippet'),badgeEl=document.querySelector(".js-badgeExampleButton");
pathEl&&htmlEl&&markdownEl&&badgeEl&&pathEl.addEventListener("input",function(a){var c=window.location.origin,b=c+"/"+a.target.value;a=c+"/badge/"+a.target.value;htmlEl.value='<a href="'+b+'"><img src="'+a+'" alt="PkgGoDev"></a>';markdownEl.value="[![PkgGoDev]("+b+")]("+a+")";badgeEl.href=b});

Просмотреть файл

@ -43,6 +43,7 @@ main() {
# TODO: once this is not an experiment, add it to the line above.
$cmd $JSDIR/completion.min.js $JSDIR/completion.js
$cmd $JSDIR/fetch.min.js $JSDIR/fetch.js
$cmd $JSDIR/badge.min.js $JSDIR/badge.js
$cmd $JSDIR/jump.min.js third_party/dialog-polyfill/dialog-polyfill.js $JSDIR/jump.js
}

Просмотреть файл

@ -0,0 +1,43 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package frontend
import (
"fmt"
"net/http"
"net/url"
"strings"
)
type badgePage struct {
basePage
SiteURL string
Path string
}
// badgeHandler serves a Go SVG badge image for requests to /badge/<path>
// and a badge generation tool page for requests to /badge/[?path=<path>].
func (s *Server) badgeHandler(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/badge/")
if path != "" {
http.ServeFile(w, r, fmt.Sprintf("%s/img/badge.svg", s.staticPath))
return
}
// The user may input a fully qualified URL (https://pkg.go.dev/net/http?tab=doc)
// or just a pathname (net/http). Using url.Parse we handle both cases.
inputURL := r.URL.Query().Get("path")
parsedURL, _ := url.Parse(inputURL)
if parsedURL != nil {
path = strings.TrimPrefix(parsedURL.RequestURI(), "/")
}
page := badgePage{
basePage: s.newBasePage(r, "Badge generation tool"),
SiteURL: "https://" + r.Host,
Path: path,
}
s.servePage(r.Context(), w, "badge.tmpl", page)
}

Просмотреть файл

@ -0,0 +1,85 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package frontend
import (
"net/http/httptest"
"strings"
"testing"
)
func TestBadgeHandler_ServeSVG(t *testing.T) {
_, handler, _ := newTestServer(t, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, httptest.NewRequest("GET", "/badge/net/http", nil))
if got, want := w.Result().Header.Get("Content-Type"), "image/svg+xml"; got != want {
t.Errorf("Content-Type = %q, want %q", got, want)
}
}
func TestBadgeHandler_ServeBadgeTool(t *testing.T) {
_, handler, _ := newTestServer(t, nil)
tests := []struct {
url string
want string
}{
{
"/badge/",
"<p>Type a pkg.go.dev URL above to create a badge link.</p>",
},
{
"/badge/?path=net/http",
`<a href="https://example.com/net/http"><img src="https://example.com/badge/net/http" alt="PkgGoDev"></a>`,
},
{
"/badge/?path=net/http?tab=imports",
`<a href="https://example.com/net/http?tab=imports"><img src="https://example.com/badge/net/http?tab=imports" alt="PkgGoDev"></a>`,
},
{
"/badge/?path=https://pkg.go.dev/net/http",
`<a href="https://example.com/net/http"><img src="https://example.com/badge/net/http" alt="PkgGoDev"></a>`,
},
{
"/badge/?path=https://pkg.go.dev/net/http?tab=imports",
`<a href="https://example.com/net/http?tab=imports"><img src="https://example.com/badge/net/http?tab=imports" alt="PkgGoDev"></a>`,
},
{
"/badge/?path=github.com/google/uuid",
"[![PkgGoDev](https://example.com/github.com/google/uuid)](https://example.com/badge/github.com/google/uuid)",
},
{
"/badge/?path=github.com/google/uuid?tab=imports",
"[![PkgGoDev](https://example.com/github.com/google/uuid?tab=imports)](https://example.com/badge/github.com/google/uuid?tab=imports)",
},
{
"/badge/?path=https://pkg.go.dev/github.com/google/uuid",
"[![PkgGoDev](https://example.com/github.com/google/uuid)](https://example.com/badge/github.com/google/uuid)",
},
{
"/badge/?path=https://pkg.go.dev/github.com/google/uuid?tab=imports",
"[![PkgGoDev](https://example.com/github.com/google/uuid?tab=imports)](https://example.com/badge/github.com/google/uuid?tab=imports)",
},
{
"/badge/?path=https://google.com",
"<p>Type a pkg.go.dev URL above to create a badge link.</p>",
},
{
"/badge/?path=https://google.com/github.com/google/uuid",
"[![PkgGoDev](https://example.com/github.com/google/uuid)](https://example.com/badge/github.com/google/uuid)",
},
}
for _, test := range tests {
t.Run(test.url, func(t *testing.T) {
w := httptest.NewRecorder()
handler.ServeHTTP(w, httptest.NewRequest("GET", test.url, nil))
got := w.Body.String()
if !strings.Contains(w.Body.String(), test.want) {
t.Errorf("Expected html substring not found, want %s, got %s", test.want, got)
}
})
}
}

Просмотреть файл

@ -111,6 +111,7 @@ func (s *Server) Install(handle func(string, http.Handler), redisClient *redis.C
handle("/search-help", s.staticPageHandler("search_help.tmpl", "Search Help - go.dev"))
handle("/license-policy", s.licensePolicyHandler())
handle("/about", http.RedirectHandler("https://go.dev/about", http.StatusFound))
handle("/badge/", http.HandlerFunc(s.badgeHandler))
handle("/", detailHandler)
handle("/autocomplete", http.HandlerFunc(s.handleAutoCompletion))
handle("/robots.txt", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -415,12 +416,13 @@ func parsePageTemplates(base template.TrustedSource) (map[string]*template.Templ
join := template.TrustedSourceJoin
htmlSets := [][]template.TrustedSource{
{tsc("index.tmpl")},
{tsc("badge.tmpl")},
{tsc("error.tmpl")},
{tsc("fetch.tmpl")},
{tsc("index.tmpl")},
{tsc("license_policy.tmpl")},
{tsc("search.tmpl")},
{tsc("search_help.tmpl")},
{tsc("license_policy.tmpl")},
{tsc("overview.tmpl"), tsc("details.tmpl")},
{tsc("subdirectories.tmpl"), tsc("details.tmpl")},
{tsc("pkg_doc.tmpl"), tsc("details.tmpl")},

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Copyright 2019-2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@ -15,6 +15,8 @@ var scriptHashes = []string{
"'sha256-Ajmr6RIM6VV5w/AraBIyO4/XRyuqJlFVukc7TL6feog='",
"'sha256-d6W7MwuGWbguTHRzQhf5QN1jXmNo9Ao218saZkWLWZI='",
"'sha256-qPGTOKPn+niRiNKQIEX0Ktwuj+D+iPQWIxnlhPicw58='",
// From content/static/html/pages/badge.tmpl
"'sha256-T7xOt6cgLji3rhOWyKK7t5XKv8+LASQwOnHiHHy8Kwk='",
// From content/static/html/pages/details.tmpl
"'sha256-s16e7aT7Gsajq5UH1DbaEFEnNx2VjvS5Xixcxwm4+F8='",
// From content/static/html/pages/fetch.tmpl