281 lines
7.3 KiB
Ruby
281 lines
7.3 KiB
Ruby
module Jekyll
|
|
class SeoTag
|
|
class Drop < Jekyll::Drops::Drop
|
|
include Jekyll::SeoTag::JSONLD
|
|
|
|
TITLE_SEPARATOR = " | ".freeze
|
|
FORMAT_STRING_METHODS = %i[
|
|
markdownify strip_html normalize_whitespace escape_once
|
|
].freeze
|
|
HOMEPAGE_OR_ABOUT_REGEX = %r!^/(about/)?(index.html?)?$!
|
|
|
|
def initialize(text, context)
|
|
@obj = {}
|
|
@mutations = {}
|
|
@text = text
|
|
@context = context
|
|
end
|
|
|
|
def version
|
|
Jekyll::SeoTag::VERSION
|
|
end
|
|
|
|
# Should the `<title>` tag be generated for this page?
|
|
def title?
|
|
return false unless title
|
|
return @display_title if defined?(@display_title)
|
|
@display_title = (@text !~ %r!title=false!i)
|
|
end
|
|
|
|
def site_title
|
|
@site_title ||= format_string(site["title"] || site["name"])
|
|
end
|
|
|
|
def site_description
|
|
@site_description ||= format_string site["description"]
|
|
end
|
|
|
|
# Page title without site title or description appended
|
|
def page_title
|
|
@page_title ||= format_string(page["title"]) || site_title
|
|
end
|
|
|
|
# Page title with site title or description appended
|
|
def title
|
|
@title ||= begin
|
|
if site_title && page_title != site_title
|
|
page_title + TITLE_SEPARATOR + site_title
|
|
elsif site_description && site_title
|
|
site_title + TITLE_SEPARATOR + site_description
|
|
else
|
|
page_title || site_title
|
|
end
|
|
end
|
|
end
|
|
|
|
def name
|
|
return @name if defined?(@name)
|
|
@name = if seo_name
|
|
seo_name
|
|
elsif !homepage_or_about?
|
|
nil
|
|
elsif site_social["name"]
|
|
format_string site_social["name"]
|
|
elsif site_title
|
|
site_title
|
|
end
|
|
end
|
|
|
|
def description
|
|
@description ||= begin
|
|
format_string(page["description"] || page["excerpt"]) || site_description
|
|
end
|
|
end
|
|
|
|
# Returns a nil or a hash representing the author
|
|
# Author name will be pulled from:
|
|
#
|
|
# 1. The `author` key, if the key is a string
|
|
# 2. The first author in the `authors` key
|
|
# 3. The `author` key in the site config
|
|
#
|
|
# If the result from the name search is a string, we'll also check
|
|
# to see if the author exists in `site.data.authors`
|
|
def author
|
|
@author ||= begin
|
|
return if author_string_or_hash.to_s.empty?
|
|
|
|
author = if author_string_or_hash.is_a?(String)
|
|
author_hash(author_string_or_hash)
|
|
else
|
|
author_string_or_hash
|
|
end
|
|
|
|
author["twitter"] ||= author["name"]
|
|
author["twitter"].delete! "@" if author["twitter"]
|
|
author.to_liquid
|
|
end
|
|
end
|
|
|
|
def date_modified
|
|
@date_modified ||= begin
|
|
date = if page_seo["date_modified"]
|
|
page_seo["date_modified"]
|
|
elsif page["last_modified_at"]
|
|
page["last_modified_at"].to_liquid
|
|
else
|
|
page["date"]
|
|
end
|
|
filters.date_to_xmlschema(date) if date
|
|
end
|
|
end
|
|
|
|
def date_published
|
|
@date_published ||= filters.date_to_xmlschema(page["date"]) if page["date"]
|
|
end
|
|
|
|
def type
|
|
@type ||= begin
|
|
if page_seo["type"]
|
|
page_seo["type"]
|
|
elsif homepage_or_about?
|
|
"WebSite"
|
|
elsif page["date"]
|
|
"BlogPosting"
|
|
else
|
|
"WebPage"
|
|
end
|
|
end
|
|
end
|
|
|
|
def links
|
|
@links ||= begin
|
|
if page_seo["links"]
|
|
page_seo["links"]
|
|
elsif homepage_or_about? && site_social["links"]
|
|
site_social["links"]
|
|
end
|
|
end
|
|
end
|
|
|
|
def logo
|
|
@logo ||= begin
|
|
return unless site["logo"]
|
|
if absolute_url? site["logo"]
|
|
filters.uri_escape site["logo"]
|
|
else
|
|
filters.uri_escape filters.absolute_url site["logo"]
|
|
end
|
|
end
|
|
end
|
|
|
|
# Returns nil or a hash representing the page image
|
|
# The image hash will always contain a path, pulled from:
|
|
#
|
|
# 1. The `image` key if it's a string
|
|
# 2. The `image.path` key if it's a hash
|
|
# 3. The `image.facebook` key
|
|
# 4. The `image.twitter` key
|
|
#
|
|
# The resulting path is always an absolute URL
|
|
def image
|
|
return @image if defined?(@image)
|
|
|
|
image = page["image"]
|
|
return @image = nil unless image
|
|
|
|
image = { "path" => image } if image.is_a?(String)
|
|
image["path"] ||= image["facebook"] || image["twitter"]
|
|
return @image = nil unless image["path"]
|
|
|
|
unless absolute_url? image["path"]
|
|
image["path"] = filters.absolute_url image["path"]
|
|
end
|
|
|
|
image["path"] = filters.uri_escape image["path"]
|
|
|
|
@image = image.to_liquid
|
|
end
|
|
|
|
def page_lang
|
|
@page_lang ||= page["lang"] || site["lang"] || "en_US"
|
|
end
|
|
|
|
def canonical_url
|
|
@canonical_url ||= begin
|
|
if page["canonical_url"].present?
|
|
page["canonical_url"]
|
|
else
|
|
filters.absolute_url(page["url"]).to_s.gsub(%r!/index\.html$!, "/")
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def filters
|
|
@filters ||= Jekyll::SeoTag::Filters.new(@context)
|
|
end
|
|
|
|
def page
|
|
@page ||= @context.registers[:page].to_liquid
|
|
end
|
|
|
|
def site
|
|
@site ||= @context.registers[:site].site_payload["site"].to_liquid
|
|
end
|
|
|
|
def homepage_or_about?
|
|
page["url"] =~ HOMEPAGE_OR_ABOUT_REGEX
|
|
end
|
|
|
|
attr_reader :context
|
|
|
|
def fallback_data
|
|
@fallback_data ||= {}
|
|
end
|
|
|
|
def absolute_url?(string)
|
|
return unless string
|
|
Addressable::URI.parse(string).absolute?
|
|
rescue Addressable::URI::InvalidURIError
|
|
nil
|
|
end
|
|
|
|
def format_string(string)
|
|
string = FORMAT_STRING_METHODS.reduce(string) do |memo, method|
|
|
filters.public_send(method, memo)
|
|
end
|
|
|
|
string unless string.empty?
|
|
end
|
|
|
|
def author_string_or_hash
|
|
@author_string_or_hash ||= begin
|
|
author = page["author"]
|
|
author = page["authors"][0] if author.to_s.empty? && page["authors"]
|
|
author = site["author"] if author.to_s.empty?
|
|
author
|
|
end
|
|
end
|
|
|
|
def author_hash(author_string)
|
|
if site.data["authors"] && site.data["authors"][author_string]
|
|
hash = site.data["authors"][author_string]
|
|
hash["name"] ||= author_string
|
|
hash["twitter"] ||= author_string
|
|
hash
|
|
else
|
|
{ "name" => author_string }
|
|
end
|
|
end
|
|
|
|
def seo_name
|
|
@seo_name ||= format_string(page_seo["name"]) if page_seo["name"]
|
|
end
|
|
|
|
def page_seo
|
|
@page_seo ||= sub_hash(page, "seo")
|
|
end
|
|
|
|
def site_social
|
|
@site_social ||= sub_hash(site, "social")
|
|
end
|
|
|
|
# Safely returns a sub hash
|
|
#
|
|
# hash - the parent hash
|
|
# key - the key in the parent hash
|
|
#
|
|
# Returns the sub hash or an empty hash, if it does not exist
|
|
def sub_hash(hash, key)
|
|
if hash[key].is_a?(Hash)
|
|
hash[key]
|
|
else
|
|
{}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|