shithub: hugo

ref: b6673e5309685ae162fdef2dc39c3ce4385c6005
dir: /hugolib/author.go/

View raw version
// Copyright 2015 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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

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{}
}

// Author contains details about the author of a page.
type Author struct {
	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 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
// - github
// - facebook
// - twitter
// - googleplus
// - pinterest
// - instagram
// - youtube
// - 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 }