mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-24 03:23:37 +00:00
parent
44bf76d0f2
commit
cf978c0649
7 changed files with 198 additions and 49 deletions
|
@ -69,9 +69,7 @@ This is the default RSS template that ships with Hugo. It adheres to the [RSS 2.
|
|||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
<atom:link href="{{.URL}}" rel="self" type="application/rss+xml" />
|
||||
|
@ -80,7 +78,6 @@ This is the default RSS template that ships with Hugo. It adheres to the [RSS 2.
|
|||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Content | html }}</description>
|
||||
</item>
|
||||
|
|
|
@ -168,7 +168,7 @@ Also available is `.Site` which has the following:
|
|||
**.Site.Files** All of the source files of the site.<br>
|
||||
**.Site.Menus** All of the menus in the site.<br>
|
||||
**.Site.Title** A string representing the title of the site.<br>
|
||||
**.Site.Author** A map of the authors as defined in the site configuration.<br>
|
||||
**.Site.Authors** An ordered list (ordered by defined weight) of the authors as defined in the site configuration.<br>
|
||||
**.Site.LanguageCode** A string representing the language as defined in the site configuration. This is mostly used to populate the RSS feeds with the right language code.<br>
|
||||
**.Site.DisqusShortname** A string representing the shortname of the Disqus shortcode as defined in the site configuration.<br>
|
||||
**.Site.GoogleAnalytics** A string representing your tracking code for Google Analytics as defined in the site configuration.<br>
|
||||
|
|
|
@ -13,23 +13,57 @@
|
|||
|
||||
package hugolib
|
||||
|
||||
// AuthorList is a list of all authors and their metadata.
|
||||
type AuthorList map[string]Author
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
var (
|
||||
onlyNumbersRegExp = regexp.MustCompile("^[0-9]*$")
|
||||
)
|
||||
|
||||
// Authors is a list of all authors and their metadata.
|
||||
type Authors []Author
|
||||
|
||||
// Get returns an author from an ID
|
||||
func (a Authors) Get(id string) Author {
|
||||
for _, author := range a {
|
||||
if author.ID == id {
|
||||
return author
|
||||
}
|
||||
}
|
||||
return Author{}
|
||||
}
|
||||
|
||||
// Sort sorts the authors by weight
|
||||
func (a Authors) Sort() Authors {
|
||||
sort.Stable(a)
|
||||
return a
|
||||
}
|
||||
|
||||
// Author contains details about the author of a page.
|
||||
type Author struct {
|
||||
GivenName string
|
||||
FamilyName string
|
||||
DisplayName string
|
||||
Thumbnail string
|
||||
Image string
|
||||
ShortBio string
|
||||
LongBio string
|
||||
Email string
|
||||
Social AuthorSocial
|
||||
ID string
|
||||
GivenName string // givenName OR firstName
|
||||
FirstName string // alias for GivenName
|
||||
FamilyName string // familyName OR lastName
|
||||
LastName string // alias for FamilyName
|
||||
DisplayName string // displayName
|
||||
Thumbnail string // thumbnail
|
||||
Image string // image
|
||||
ShortBio string // shortBio
|
||||
Bio string // bio
|
||||
Email string // email
|
||||
Social AuthorSocial // social
|
||||
Params map[string]string // params
|
||||
Weight int
|
||||
}
|
||||
|
||||
// AuthorSocial is a place to put social details per author. These are the
|
||||
// AuthorSocial is a place to put social usernames per author. These are the
|
||||
// standard keys that themes will expect to have available, but can be
|
||||
// expanded to any others on a per site basis
|
||||
// - website
|
||||
|
@ -43,3 +77,102 @@ type Author struct {
|
|||
// - linkedin
|
||||
// - skype
|
||||
type AuthorSocial map[string]string
|
||||
|
||||
// URL is a convenience function that provides the correct canonical URL
|
||||
// for a specific social network given a username. If an unsupported network
|
||||
// is requested, only the username is returned
|
||||
func (as AuthorSocial) URL(key string) string {
|
||||
switch key {
|
||||
case "github":
|
||||
return fmt.Sprintf("https://github.com/%s", as[key])
|
||||
case "facebook":
|
||||
return fmt.Sprintf("https://www.facebook.com/%s", as[key])
|
||||
case "twitter":
|
||||
return fmt.Sprintf("https://twitter.com/%s", as[key])
|
||||
case "googleplus":
|
||||
isNumeric := onlyNumbersRegExp.Match([]byte(as[key]))
|
||||
if isNumeric {
|
||||
return fmt.Sprintf("https://plus.google.com/%s", as[key])
|
||||
}
|
||||
return fmt.Sprintf("https://plus.google.com/+%s", as[key])
|
||||
case "pinterest":
|
||||
return fmt.Sprintf("https://www.pinterest.com/%s/", as[key])
|
||||
case "instagram":
|
||||
return fmt.Sprintf("https://www.instagram.com/%s/", as[key])
|
||||
case "youtube":
|
||||
return fmt.Sprintf("https://www.youtube.com/user/%s", as[key])
|
||||
case "linkedin":
|
||||
return fmt.Sprintf("https://www.linkedin.com/in/%s", as[key])
|
||||
default:
|
||||
return as[key]
|
||||
}
|
||||
}
|
||||
|
||||
func mapToAuthors(m map[string]interface{}) Authors {
|
||||
authors := make(Authors, len(m))
|
||||
for authorID, data := range m {
|
||||
authorMap, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
authors = append(authors, mapToAuthor(authorID, authorMap))
|
||||
}
|
||||
sort.Stable(authors)
|
||||
return authors
|
||||
}
|
||||
|
||||
func mapToAuthor(id string, m map[string]interface{}) Author {
|
||||
author := Author{ID: id}
|
||||
for k, data := range m {
|
||||
switch k {
|
||||
case "givenName", "firstName":
|
||||
author.GivenName = cast.ToString(data)
|
||||
author.FirstName = author.GivenName
|
||||
case "familyName", "lastName":
|
||||
author.FamilyName = cast.ToString(data)
|
||||
author.LastName = author.FamilyName
|
||||
case "displayName":
|
||||
author.DisplayName = cast.ToString(data)
|
||||
case "thumbnail":
|
||||
author.Thumbnail = cast.ToString(data)
|
||||
case "image":
|
||||
author.Image = cast.ToString(data)
|
||||
case "shortBio":
|
||||
author.ShortBio = cast.ToString(data)
|
||||
case "bio":
|
||||
author.Bio = cast.ToString(data)
|
||||
case "email":
|
||||
author.Email = cast.ToString(data)
|
||||
case "social":
|
||||
author.Social = normalizeSocial(cast.ToStringMapString(data))
|
||||
case "params":
|
||||
author.Params = cast.ToStringMapString(data)
|
||||
}
|
||||
}
|
||||
|
||||
// set a reasonable default for DisplayName
|
||||
if author.DisplayName == "" {
|
||||
author.DisplayName = author.GivenName + " " + author.FamilyName
|
||||
}
|
||||
|
||||
return author
|
||||
}
|
||||
|
||||
// normalizeSocial makes a naive attempt to normalize social media usernames
|
||||
// and strips out extraneous characters or url info
|
||||
func normalizeSocial(m map[string]string) map[string]string {
|
||||
for network, username := range m {
|
||||
username = strings.TrimSpace(username)
|
||||
username = strings.TrimSuffix(username, "/")
|
||||
strs := strings.Split(username, "/")
|
||||
username = strs[len(strs)-1]
|
||||
username = strings.TrimPrefix(username, "@")
|
||||
username = strings.TrimPrefix(username, "+")
|
||||
m[network] = username
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (a Authors) Len() int { return len(a) }
|
||||
func (a Authors) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a Authors) Less(i, j int) bool { return a[i].Weight < a[j].Weight }
|
||||
|
|
|
@ -21,11 +21,9 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"github.com/spf13/hugo/helpers"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
|
@ -322,3 +320,16 @@ func (n *Node) addLangFilepathPrefix(outfile string) string {
|
|||
}
|
||||
return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
|
||||
}
|
||||
|
||||
// Author returns the first defined author, sorted by Weight
|
||||
func (n *Node) Author() Author {
|
||||
if len(n.Site.Authors) == 0 {
|
||||
return Author{}
|
||||
}
|
||||
return n.Site.Authors[0]
|
||||
}
|
||||
|
||||
// Authors returns all defined authors, sorted by Weight
|
||||
func (n *Node) Authors() Authors {
|
||||
return n.Site.Authors
|
||||
}
|
||||
|
|
|
@ -190,33 +190,41 @@ func (p *Page) Param(key interface{}) (interface{}, error) {
|
|||
return p.Site.Params[keyStr], nil
|
||||
}
|
||||
|
||||
// Author returns the first listed author for a page
|
||||
func (p *Page) Author() Author {
|
||||
authors := p.Authors()
|
||||
|
||||
for _, author := range authors {
|
||||
return author
|
||||
if len(authors) == 0 {
|
||||
return Author{}
|
||||
}
|
||||
return Author{}
|
||||
return authors[0]
|
||||
}
|
||||
|
||||
func (p *Page) Authors() AuthorList {
|
||||
authorKeys, ok := p.Params["authors"]
|
||||
if !ok {
|
||||
return AuthorList{}
|
||||
}
|
||||
authors := authorKeys.([]string)
|
||||
if len(authors) < 1 || len(p.Site.Authors) < 1 {
|
||||
return AuthorList{}
|
||||
}
|
||||
|
||||
al := make(AuthorList)
|
||||
for _, author := range authors {
|
||||
a, ok := p.Site.Authors[author]
|
||||
if ok {
|
||||
al[author] = a
|
||||
// Authors returns all listed authors for a page in the order they
|
||||
// are defined in the front matter. It first checks for a single author
|
||||
// since that it the most common use case, then checks for multiple authors.
|
||||
func (p *Page) Authors() Authors {
|
||||
authorID, ok := p.Params["author"].(string)
|
||||
if ok {
|
||||
a := p.Site.Authors.Get(authorID)
|
||||
if a.ID == authorID {
|
||||
return Authors{a}
|
||||
}
|
||||
}
|
||||
return al
|
||||
|
||||
authorIDs, ok := p.Params["authors"].([]string)
|
||||
if !ok || len(authorIDs) == 0 || len(p.Site.Authors) == 0 {
|
||||
return Authors{}
|
||||
}
|
||||
|
||||
authors := make([]Author, 0, len(authorIDs))
|
||||
for _, authorID := range authorIDs {
|
||||
a := p.Site.Authors.Get(authorID)
|
||||
if a.ID == authorID {
|
||||
authors = append(authors, a)
|
||||
}
|
||||
}
|
||||
|
||||
return authors
|
||||
}
|
||||
|
||||
func (p *Page) UniqueID() string {
|
||||
|
|
|
@ -165,7 +165,7 @@ type SiteInfo struct {
|
|||
|
||||
BaseURL template.URL
|
||||
Taxonomies TaxonomyList
|
||||
Authors AuthorList
|
||||
Authors Authors
|
||||
Social SiteSocial
|
||||
Sections Taxonomy
|
||||
Pages *Pages // Includes only pages in this language
|
||||
|
@ -176,7 +176,6 @@ type SiteInfo struct {
|
|||
Hugo *HugoInfo
|
||||
Title string
|
||||
RSSLink string
|
||||
Author map[string]interface{}
|
||||
LanguageCode string
|
||||
DisqusShortname string
|
||||
GoogleAnalytics string
|
||||
|
@ -733,6 +732,11 @@ func (s *Site) readDataFromSourceFS() error {
|
|||
}
|
||||
|
||||
err = s.loadData(dataSources)
|
||||
|
||||
// extract author data from /data/_authors then delete it from .Data
|
||||
s.Info.Authors = mapToAuthors(cast.ToStringMap(s.Data["_authors"]))
|
||||
delete(s.Data, "_authors")
|
||||
|
||||
s.timerStep("load data")
|
||||
return err
|
||||
}
|
||||
|
@ -908,7 +912,6 @@ func (s *Site) initializeSiteInfo() {
|
|||
s.Info = SiteInfo{
|
||||
BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(viper.GetString("BaseURL"))),
|
||||
Title: lang.GetString("Title"),
|
||||
Author: lang.GetStringMap("author"),
|
||||
Social: lang.GetStringMapString("social"),
|
||||
LanguageCode: lang.GetString("languagecode"),
|
||||
Copyright: lang.GetString("copyright"),
|
||||
|
|
|
@ -44,7 +44,7 @@ func (t *GoHTMLTemplate) EmbedShortcodes() {
|
|||
t.AddInternalShortcode("speakerdeck.html", "<script async class='speakerdeck-embed' data-id='{{ index .Params 0 }}' data-ratio='1.33333333333333' src='//speakerdeck.com/assets/embed.js'></script>")
|
||||
t.AddInternalShortcode("youtube.html", `{{ if .IsNamedParams }}
|
||||
<div {{ if .Get "class" }}class="{{ .Get "class" }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
|
||||
<iframe src="//www.youtube.com/embed/{{ .Get "id" }}?{{ with .Get "autoplay" }}{{ if eq . "true" }}autoplay=1{{ end }}{{ end }}"
|
||||
<iframe src="//www.youtube.com/embed/{{ .Get "id" }}?{{ with .Get "autoplay" }}{{ if eq . "true" }}autoplay=1{{ end }}{{ end }}"
|
||||
{{ if not (.Get "class") }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}allowfullscreen frameborder="0"></iframe>
|
||||
</div>{{ else }}
|
||||
<div {{ if len .Params | eq 2 }}class="{{ .Get 1 }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
|
||||
|
@ -70,9 +70,7 @@ func (t *GoHTMLTemplate) EmbedTemplates() {
|
|||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
<atom:link href="{{.Permalink}}" rel="self" type="application/rss+xml" />
|
||||
|
@ -81,7 +79,6 @@ func (t *GoHTMLTemplate) EmbedTemplates() {
|
|||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Content | html }}</description>
|
||||
</item>
|
||||
|
|
Loading…
Reference in a new issue