package plugins import ( "fmt" "regexp" "github.com/osteele/gojekyll/utils" "github.com/osteele/liquid" "github.com/osteele/liquid/render" ) type jekyllSEOTagPlugin struct { plugin site Site tpl *liquid.Template } func init() { register("jekyll-seo-tag", &jekyllSEOTagPlugin{}) } func (p *jekyllSEOTagPlugin) Initialize(s Site) error { p.site = s return nil } func (p *jekyllSEOTagPlugin) ConfigureTemplateEngine(e *liquid.Engine) error { e.RegisterTag("seo", p.seoTag) tpl, err := e.ParseTemplate([]byte(seoTagTemplateSource)) if err != nil { panic(err) } p.tpl = tpl return nil } var seoSiteFields = []string{"url", "twitter", "facebook", "logo", "social", "google_site_verification", "lang"} var seoPageOrSiteFields = []string{"author", "description", "image", "author", "lang"} var seoTagMultipleLinesPattern = regexp.MustCompile(`( *\n)+`) func (p *jekyllSEOTagPlugin) seoTag(ctx render.Context) (string, error) { var ( site = liquid.FromDrop(ctx.Get("site")).(map[string]interface{}) page = liquid.FromDrop(ctx.Get("page")).(map[string]interface{}) pageTitle = page["title"] siteTitle = site["title"] canonicalURL = fmt.Sprintf("%s%s", site["url"], page["url"]) ) if siteTitle == nil { siteTitle = site["name"] } seoTag := map[string]interface{}{ "title?": true, "title": siteTitle, // the following are not doc'ed, but evident from inspection: // FIXME canonical w|w/out site.url and site.prefix "canonical_url": canonicalURL, "page_lang": "en_US", "page_title": pageTitle, "site_title": siteTitle, } copyFields(seoTag, site, append(seoSiteFields, seoPageOrSiteFields...)) copyFields(seoTag, page, seoPageOrSiteFields) // seoTag["description"] = page["excerpt"] FIXME appears to behave something like following, but excerpt should not be processed if pageTitle != nil && siteTitle != nil && pageTitle != siteTitle { seoTag["title"] = fmt.Sprintf("%s | %s", pageTitle, siteTitle) } if author, ok := seoTag["author"].(string); ok { if data, _ := utils.FollowDots(site, []string{"data", "authors", author}); data != nil { seoTag["author"] = data } } seoTag["json_ld"] = makeJSONLD(seoTag) bindings := map[string]interface{}{ "page": page, "site": site, "seo_tag": seoTag, } b, err := p.tpl.Render(bindings) if err != nil { return "", err } return string(seoTagMultipleLinesPattern.ReplaceAll(b, []byte{'\n'})), nil } func copyFields(to, from map[string]interface{}, fields []string) { for _, name := range fields { if value := from[name]; value != nil { to[name] = value } } } func makeJSONLD(seoTag map[string]interface{}) interface{} { jsonLD := map[string]interface{}{ // TODO dateModified, datePublished, publisher, mainEntityOfPage "@context": "http://schema.org", "@type": "WebPage", // FIXME this is BlogPosting for a blog page "headline": seoTag["page_title"], "description": seoTag["description"], "url": seoTag["canonical_url"], } if author := seoTag["author"]; author != nil { if m, ok := author.(map[string]interface{}); ok { author = m["name"] } jsonLD["author"] = map[string]interface{}{ "@type": "Person", "name": author, } } return jsonLD } // Taken verbatim from https://github.com/jekyll/jekyll-seo-tag/ const seoTagTemplateSource = ` {% if seo_tag.title? %}