shithub: hugo

Download patch

ref: 8f6f871f539a24347388dec1927adf955248cb53
parent: fd33e5d20245add7eef49ef15beea0de925304f9
author: Derek Perkins <[email protected]>
date: Tue Dec 9 06:33:55 EST 2014

Added AuthorList, Author, AuthorSocial, SiteSocial, Image and Video structs
Added Page.Author(s) functions
Added schema, opengraph, twitter_cards, google_news metadata templates
Added "" template

--- /dev/null
+++ b/hugolib/author.go
@@ -1,0 +1,49 @@
+// Copyright © 2013 Steve Francia <[email protected]>.
+//
+// Licensed under the Simple Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://opensource.org/licenses/Simple-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+/*
+ *  An author list is a list of all authors and their metadata
+ */
+type AuthorList map[string]Author
+
+/*
+ *  An 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
+}
+
+// AuthorSocial is a place to put social details 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
+// - github
+// - facebook
+// - twitter
+// - googleplus
+// - pinterest
+// - instagram
+// - youtube
+// - linkedin
+// - skype
+type AuthorSocial map[string]string
--- /dev/null
+++ b/hugolib/media.go
@@ -1,0 +1,60 @@
+// Copyright © 2013 Steve Francia <[email protected]>.
+//
+// Licensed under the Simple Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://opensource.org/licenses/Simple-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+// An image contains metadata for images + image sitemaps
+// https://support.google.com/webmasters/answer/178636?hl=en
+type Image struct {
+
+	// The URL of the image. In some cases, the image URL may not be on the
+	// same domain as your main site. This is fine, as long as both domains
+	// are verified in Webmaster Tools. If, for example, you use a
+	// content delivery network (CDN) to host your images, make sure that the
+	// hosting site is verified in Webmaster Tools OR that you submit your
+	// sitemap using robots.txt. In addition, make sure that your robots.txt
+	// file doesn’t disallow the crawling of any content you want indexed.
+	URL     string
+	Title   string
+	Caption string
+	AltText string
+
+	// The geographic location of the image. For example,
+	// <image:geo_location>Limerick, Ireland</image:geo_location>.
+	GeoLocation string
+
+	// A URL to the license of the image.
+	License string
+}
+
+// An video contains metadata for videos + video sitemaps
+// https://support.google.com/webmasters/answer/80471?hl=en
+type Video struct {
+	ThumbnailLoc         string
+	Title                string
+	Description          string
+	ContentLoc           string
+	PlayerLoc            string
+	Duration             string
+	ExpirationDate       string
+	Rating               string
+	ViewCount            string
+	PublicationDate      string
+	FamilyFriendly       string
+	Restriction          string
+	GalleryLoc           string
+	Price                string
+	RequiresSubscription string
+	Uploader             string
+	Live                 string
+}
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -40,7 +40,8 @@
 	Summary         template.HTML
 	Aliases         []string
 	Status          string
-	Images          []string
+	Images          []Image
+	Videos          []Video
 	TableOfContents template.HTML
 	Truncated       bool
 	Draft           bool
@@ -68,7 +69,6 @@
 	Content     []byte
 	source.File
 }
-
 type PageMeta struct {
 	WordCount      int
 	FuzzyWordCount int
@@ -96,6 +96,32 @@
 
 func (p *Page) IsPage() bool {
 	return true
+}
+
+func (p *Page) Author() Author {
+	authors := p.Authors()
+
+	for _, author := range authors {
+		return author
+	}
+	return Author{}
+}
+
+func (p *Page) Authors() AuthorList {
+	authorKeys, ok := p.Params["authors"]
+	authors := authorKeys.([]string)
+	if !ok || 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
+		}
+	}
+	return al
 }
 
 func (p *Page) UniqueId() string {
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -88,6 +88,8 @@
 type SiteInfo struct {
 	BaseUrl         template.URL
 	Taxonomies      TaxonomyList
+	Authors         AuthorList
+	Social          SiteSocial
 	Indexes         *TaxonomyList // legacy, should be identical to Taxonomies
 	Sections        Taxonomy
 	Pages           *Pages
@@ -104,6 +106,21 @@
 	Params          map[string]interface{}
 	BuildDrafts     bool
 }
+
+// SiteSocial is a place to put social details on a site level. These are the
+// standard keys that themes will expect to have available, but can be
+// expanded to any others on a per site basis
+// github
+// facebook
+// facebook_admin
+// twitter
+// twitter_domain
+// googleplus
+// pinterest
+// instagram
+// youtube
+// linkedin
+type SiteSocial map[string]string
 
 func (s *SiteInfo) GetParam(key string) interface{} {
 	v := s.Params[strings.ToLower(key)]
--- a/tpl/template_embedded.go
+++ b/tpl/template_embedded.go
@@ -96,4 +96,83 @@
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
 <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)
 
+	// Add SEO & Social metadata
+	t.AddInternalTemplate("_default", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />
+<meta property="og:description" content="{{ if .Description }}{{ .Description }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}" />
+<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
+<meta property="og:url" content="{{ .Permalink }}" />
+{{ with .Params.images }}{{ range first 6 . }}
+  <meta property="og:image" content="{{ . }}" />
+{{ end }}{{ end }}
+
+<meta property="og:updated_time" content="{{ .Date }}"/>{{ with .Params.audio }}
+<meta property="og:audio" content="{{ . }}" />{{ end }}{{ with .Params.locale }}
+<meta property="og:locale" content="{{ . }}" />{{ end }}{{ with .Site.Params.title }}
+<meta property="og:site_name" content="{{ . }}" />{{ end }}{{ with .Params.videos }}
+{{ range .Params.videos }}
+  <meta property="og:video" content="{{ . }}" />
+{{ end }}
+
+<!-- If it is part of a series, link to related articles -->
+{{ $permalink := .Permalink }}
+{{ $siteSeries := .Site.Taxonomies.series }}{{ with .Params.series }}
+{{ range $name := . }}
+  {{ $series := index $siteSeries $name }}
+  {{ range $page := first 6 $series.Pages }}
+    {{ if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
+  {{ end }}
+{{ end }}{{ end }}
+
+{{ if .IsPage }}
+{{ range .Site.Authors }}{{ with .Social.facebook }}
+<meta property="article:author" content="https://www.facebook.com/{{ . }}" />{{ end }}{{ with .Site.Social.facebook }}
+<meta property="article:publisher" content="https://www.facebook.com/{{ . }}" />{{ end }}
+<meta property="article:published_time" content="{{ .PublishDate }}" />
+<meta property="article:modified_time" content="{{ .Date }}" />
+<meta property="article:section" content="{{ .Section }}" />
+{{ with .Params.tags }}{{ range first 6 . }}
+  <meta property="article:tag" content="{{ . }}" />{{ end }}{{ end }}
+{{ end }}
+
+<!-- Facebook Page Admin ID for Domain Insights -->
+{{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}`)
+
+	t.AddInternalTemplate("_default", "twitter_cards.html", `{{ if .IsPage }}
+{{ with .Params.images }}
+<!-- Twitter summary card with large image must be at least 280x150px -->
+  <meta name="twitter:card" content="summary_large_image"/>
+  <meta name="twitter:image:src" content="{{ index . 0 }}"/>
+{{ else }}
+  <meta name="twitter:card" content="summary"/>
+{{ end }}
+
+<!-- Twitter Card data -->
+<meta name="twitter:title" content="{{ .Title }}"/>
+<meta name="twitter:description" content="{{ if .Description }}{{ .Description }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}"/>
+{{ with .Site.Social.twitter }}<meta name="twitter:site" content="@{{ . }}"/>{{ end }}
+{{ with .Site.Social.twitter_domain }}<meta name="twitter:domain" content="{{ . }}"/>{{ end }}
+{{ range .Site.Authors }}
+  {{ with .twitter }}<meta name="twitter:creator" content="@{{ . }}"/>{{ end }}
+{{ end }}{{ end }}`)
+
+	t.AddInternalTemplate("_default", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}
+  <meta name="news_keywords" content="{{ range $i, $kw := first 10 . }}{{ if $i }},{{ end }}{{ $kw }}{{ end }}" />
+{{ end }}{{ end }}`)
+
+	t.AddInternalTemplate("_default", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}
+<meta itemprop="name" content="{{ .Title }}">
+<meta itemprop="description" content="{{ if .Description }}{{ .Description }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}">
+
+{{if .IsPage}}
+<meta itemprop="datePublished" content="{{ .PublishDate }}" />
+<meta itemprop="dateModified" content="{{ .Date }}" />
+<meta itemprop="wordCount" content="{{ .WordCount }}">
+{{ with .Params.images }}{{ range first 6 . }}
+  <meta itemprop="image" content="{{ . }}">
+{{ end }}{{ end }}
+
+<!-- Output all taxonomies as schema.org keywords -->
+<meta itemprop="keywords" content="{{ range $plural, $terms := .Site.Taxonomies }}{{ range $term, $val := $terms }}{{ printf "%s," $term }}{{ end }}{{ end }}" />
+{{ end }}{{ end }}`)
+
 }