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
+// - googleplus
+// - youtube
+// - 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_admin
+// twitter_domain
+// googleplus
+// youtube
+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 }}`)
+
}