jekyll-soopr-seo-tag 2.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ class Filters
6
+ include Jekyll::Filters
7
+ include Liquid::StandardFilters
8
+
9
+ def initialize(context)
10
+ @context = context
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ # A drop representing the page image
6
+ # The image path will be pulled from:
7
+ #
8
+ # 1. The `image` key if it's a string
9
+ # 2. The `image.path` key if it's a hash
10
+ # 3. The `image.facebook` key
11
+ # 4. The `image.twitter` key
12
+ class ImageDrop < Jekyll::Drops::Drop
13
+ include Jekyll::SeoTag::UrlHelper
14
+
15
+ # Initialize a new ImageDrop
16
+ #
17
+ # page - The page hash (e.g., Page#to_liquid)
18
+ # context - the Liquid::Context
19
+ def initialize(page: nil, context: nil)
20
+ raise ArgumentError unless page && context
21
+
22
+ @mutations = {}
23
+ @page = page
24
+ @context = context
25
+ end
26
+
27
+ # Called path for backwards compatability, this is really
28
+ # the escaped, absolute URL representing the page's image
29
+ # Returns nil if no image path can be determined
30
+ def path
31
+ @path ||= filters.uri_escape(absolute_url) if absolute_url
32
+ end
33
+ alias_method :to_s, :path
34
+
35
+ private
36
+
37
+ attr_accessor :page
38
+ attr_accessor :context
39
+
40
+ # The normalized image hash with a `path` key (which may be nil)
41
+ def image_hash
42
+ @image_hash ||= begin
43
+ image_meta = page["image"]
44
+
45
+ if image_meta.is_a?(Hash)
46
+ { "path" => nil }.merge!(image_meta)
47
+ elsif image_meta.is_a?(String)
48
+ { "path" => image_meta }
49
+ else
50
+ { "path" => nil }
51
+ end
52
+ end
53
+ end
54
+ alias_method :fallback_data, :image_hash
55
+
56
+ def raw_path
57
+ @raw_path ||= begin
58
+ image_hash["path"] || image_hash["facebook"] || image_hash["twitter"]
59
+ end
60
+ end
61
+
62
+ def absolute_url
63
+ return unless raw_path
64
+ return @absolute_url if defined? @absolute_url
65
+
66
+ @absolute_url = if raw_path.is_a?(String) && absolute_url?(raw_path) == false
67
+ filters.absolute_url raw_path
68
+ else
69
+ raw_path
70
+ end
71
+ end
72
+
73
+ def filters
74
+ @filters ||= Jekyll::SeoTag::Filters.new(context)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ # This module is deprecated, but is included in the Gem to avoid a breaking
6
+ # change and should be removed at the next major version bump
7
+ module JSONLD
8
+ METHODS_KEYS = {
9
+ :json_context => "@context",
10
+ :type => "@type",
11
+ :name => "name",
12
+ :page_title => "headline",
13
+ :json_author => "author",
14
+ :json_image => "image",
15
+ :date_published => "datePublished",
16
+ :date_modified => "dateModified",
17
+ :description => "description",
18
+ :publisher => "publisher",
19
+ :main_entity => "mainEntityOfPage",
20
+ :links => "sameAs",
21
+ :canonical_url => "url",
22
+ }.freeze
23
+
24
+ # Self should be a Jekyll::SeoTag::Drop instance (when extending the module)
25
+ def json_ld
26
+ Jekyll.logger.warn "Jekyll::SeoTag::JSONLD is deprecated"
27
+ @json_ld ||= JSONLDDrop.new(self)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ class JSONLDDrop < Jekyll::Drops::Drop
6
+ extend Forwardable
7
+
8
+ def_delegator :page_drop, :name, :name
9
+ def_delegator :page_drop, :description, :description
10
+ def_delegator :page_drop, :canonical_url, :url
11
+ def_delegator :page_drop, :page_title, :headline
12
+ def_delegator :page_drop, :date_modified, :dateModified
13
+ def_delegator :page_drop, :date_published, :datePublished
14
+ def_delegator :page_drop, :links, :sameAs
15
+ def_delegator :page_drop, :logo, :logo
16
+ def_delegator :page_drop, :type, :type
17
+
18
+ # Expose #type and #logo as private methods and #@type as a public method
19
+ alias_method :"@type", :type
20
+ private :type
21
+ private :logo
22
+
23
+ VALID_ENTITY_TYPES = %w(BlogPosting CreativeWork).freeze
24
+ VALID_AUTHOR_TYPES = %w(Organization Person).freeze
25
+ private_constant :VALID_ENTITY_TYPES, :VALID_AUTHOR_TYPES
26
+
27
+ # page_drop should be an instance of Jekyll::SeoTag::Drop
28
+ def initialize(page_drop)
29
+ @mutations = {}
30
+ @page_drop = page_drop
31
+ end
32
+
33
+ def fallback_data
34
+ @fallback_data ||= {
35
+ "@context" => "https://schema.org",
36
+ }
37
+ end
38
+
39
+ def author
40
+ return unless page_drop.author["name"]
41
+
42
+ author_type = page_drop.author["type"]
43
+ return if author_type && !VALID_AUTHOR_TYPES.include?(author_type)
44
+
45
+ {
46
+ "@type" => author_type || "Person",
47
+ "name" => page_drop.author["name"],
48
+ }
49
+ end
50
+
51
+ def image
52
+ return unless page_drop.image
53
+ return page_drop.image.path if page_drop.image.keys.length == 1
54
+
55
+ hash = page_drop.image.to_h
56
+ hash["url"] = hash.delete("path")
57
+ hash["@type"] = "imageObject"
58
+ hash
59
+ end
60
+
61
+ def publisher
62
+ return unless logo
63
+
64
+ output = {
65
+ "@type" => "Organization",
66
+ "logo" => {
67
+ "@type" => "ImageObject",
68
+ "url" => logo,
69
+ },
70
+ }
71
+ output["name"] = page_drop.author.name if page_drop.author.name
72
+ output
73
+ end
74
+
75
+ def main_entity
76
+ return unless VALID_ENTITY_TYPES.include?(type)
77
+
78
+ {
79
+ "@type" => "WebPage",
80
+ "@id" => page_drop.canonical_url,
81
+ }
82
+ end
83
+ alias_method :mainEntityOfPage, :main_entity
84
+ private :main_entity
85
+
86
+ def to_json
87
+ to_h.reject { |_k, v| v.nil? }.to_json
88
+ end
89
+
90
+ private
91
+
92
+ attr_reader :page_drop
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ # A drop representing soopr and soopr's publish token
6
+ # The publish key will be pulled from:
7
+ #
8
+ # 1. The `soopr` key if it's a string
9
+ # 2. The `soopr.publish_token` if it's a hash
10
+ # 3. The `soopr.publish_key` if it's a hash
11
+ class SooprDrop < Jekyll::Drops::Drop
12
+
13
+ # Initialize a new SooprDrop
14
+ #
15
+ # page - The page hash (e.g., Page#to_liquid)
16
+ # context - the Liquid::Context
17
+ def initialize(page: nil, site: nil)
18
+ raise ArgumentError unless page && site
19
+
20
+ @mutations = {}
21
+ @page = page
22
+ @site = site
23
+ end
24
+
25
+ def publish_token
26
+ soopr_hash["publish_token"] || soopr_hash["publish_key"]
27
+ end
28
+ alias_method :to_s, :publish_token
29
+
30
+ private
31
+
32
+ attr_reader :page
33
+ attr_reader :site
34
+
35
+
36
+ def soopr_hash
37
+ @soopr_hash ||= begin
38
+ return {} unless site["soopr"].is_a?(Hash)
39
+
40
+ soopr_hash = site["soopr"]
41
+ soopr_hash.is_a?(Hash) ? soopr_hash : {}
42
+ end
43
+ end
44
+
45
+ # Since author_hash is aliased to fallback_data, any values in the hash
46
+ # will be exposed via the drop, allowing support for arbitrary metadata
47
+ alias_method :fallback_data, :soopr_hash
48
+ end
49
+
50
+ def filters
51
+ @filters ||= Jekyll::SeoTag::Filters.new(context)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class SeoTag
5
+ # Mixin to share common URL-related methods between class
6
+ module UrlHelper
7
+ private
8
+
9
+ # Determines if the given string is an absolute URL
10
+ #
11
+ # Returns true if an absolute URL.
12
+ # Retruns false if it's a relative URL
13
+ # Returns nil if it is not a string or can't be parsed as a URL
14
+ def absolute_url?(string)
15
+ return unless string
16
+
17
+ Addressable::URI.parse(string).absolute?
18
+ rescue Addressable::URI::InvalidURIError
19
+ nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Prevent bundler errors
4
+ module Liquid; class Tag; end; end
5
+
6
+ module Jekyll
7
+ class SeoTag < Liquid::Tag
8
+ VERSION = "2.7.3"
9
+ end
10
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+ require "jekyll-seo-tag/version"
5
+
6
+ module Jekyll
7
+ class SeoTag < Liquid::Tag
8
+ autoload :JSONLD, "jekyll-seo-tag/json_ld"
9
+ autoload :AuthorDrop, "jekyll-seo-tag/author_drop"
10
+ autoload :ImageDrop, "jekyll-seo-tag/image_drop"
11
+ autoload :JSONLDDrop, "jekyll-seo-tag/json_ld_drop"
12
+ autoload :UrlHelper, "jekyll-seo-tag/url_helper"
13
+ autoload :Drop, "jekyll-seo-tag/drop"
14
+ autoload :Filters, "jekyll-seo-tag/filters"
15
+ autoload :SooprDrop, "jekyll-seo-tag/soopr_drop"
16
+
17
+
18
+ attr_accessor :context
19
+
20
+ # Matches all whitespace that follows either
21
+ # 1. A '}', which closes a Liquid tag
22
+ # 2. A '{', which opens a JSON block
23
+ # 3. A '>' followed by a newline, which closes an XML tag or
24
+ # 4. A ',' followed by a newline, which ends a JSON line
25
+ # We will strip all of this whitespace to minify the template
26
+ # We will not strip any whitespace if the next character is a '-'
27
+ # so that we do not interfere with the HTML comment at the
28
+ # very begining
29
+ MINIFY_REGEX = %r!(?<=[{}]|[>,]\n)\s+(?\!-)!.freeze
30
+
31
+ def initialize(_tag_name, text, _tokens)
32
+ super
33
+ @text = text
34
+ end
35
+
36
+ def render(context)
37
+ @context = context
38
+ SeoTag.template.render!(payload, info)
39
+ end
40
+
41
+ private
42
+
43
+ def options
44
+ {
45
+ "version" => Jekyll::SeoTag::VERSION,
46
+ "title" => title?,
47
+ }
48
+ end
49
+
50
+ def payload
51
+ # site_payload is an instance of UnifiedPayloadDrop. See https://git.io/v5ajm
52
+ context.registers[:site].site_payload.tap do |site_payload|
53
+ site_payload["page"] = context.registers[:page]
54
+ site_payload["paginator"] = context["paginator"]
55
+ site_payload["seo_tag"] = drop
56
+ end
57
+ end
58
+
59
+ def drop
60
+ if context.registers[:site].liquid_renderer.respond_to?(:cache)
61
+ Jekyll::SeoTag::Drop.new(@text, @context)
62
+ else
63
+ @drop ||= Jekyll::SeoTag::Drop.new(@text, @context)
64
+ end
65
+ end
66
+
67
+ def info
68
+ {
69
+ :registers => context.registers,
70
+ :filters => [Jekyll::Filters],
71
+ }
72
+ end
73
+
74
+ class << self
75
+ def template
76
+ @template ||= Liquid::Template.parse template_contents
77
+ end
78
+
79
+ private
80
+
81
+ def template_contents
82
+ @template_contents ||= begin
83
+ File.read(template_path).gsub(MINIFY_REGEX, "")
84
+ end
85
+ end
86
+
87
+ def template_path
88
+ @template_path ||= begin
89
+ File.expand_path "./template.html", File.dirname(__FILE__)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ Liquid::Template.register_tag("seo", Jekyll::SeoTag)
data/lib/template.html ADDED
@@ -0,0 +1,125 @@
1
+ <!-- Begin Jekyll SEO tag v{{ seo_tag.version }} -->
2
+ {% if seo_tag.title? %}
3
+ <title>{{ seo_tag.title }}</title>
4
+ {% endif %}
5
+
6
+ <meta name="generator" content="Jekyll v{{ jekyll.version }}" />
7
+
8
+ {% if seo_tag.page_title %}
9
+ <meta property="og:title" content="{{ seo_tag.page_title }}" />
10
+ {% endif %}
11
+
12
+ {% if seo_tag.author.name %}
13
+ <meta name="author" content="{{ seo_tag.author.name }}" />
14
+ {% endif %}
15
+
16
+ <meta property="og:locale" content="{{ seo_tag.page_locale }}" />
17
+
18
+ {% if seo_tag.description %}
19
+ <meta name="description" content="{{ seo_tag.description }}" />
20
+ <meta property="og:description" content="{{ seo_tag.description }}" />
21
+ {% endif %}
22
+
23
+ {% if site.url %}
24
+ <link rel="canonical" href="{{ seo_tag.canonical_url }}" />
25
+ <meta property="og:url" content="{{ seo_tag.canonical_url }}" />
26
+ {% endif %}
27
+
28
+ {% if seo_tag.site_title %}
29
+ <meta property="og:site_name" content="{{ seo_tag.site_title }}" />
30
+ {% endif %}
31
+
32
+ {% if seo_tag.image && seo_tag.image.path %}
33
+ <meta property="og:image" content="{{ seo_tag.image.path }}" />
34
+ {% if seo_tag.image.height %}
35
+ <meta property="og:image:height" content="{{ seo_tag.image.height }}" />
36
+ {% endif %}
37
+ {% if seo_tag.image.width %}
38
+ <meta property="og:image:width" content="{{ seo_tag.image.width }}" />
39
+ {% endif %}
40
+ {% elsif seo_tag.soopr %}
41
+ <meta property="og:image" content="https://soopr.xyz/images/card?url={{ seo_tag.canonical_url }}" />
42
+ {% endif %}
43
+
44
+ {% if page.date %}
45
+ <meta property="og:type" content="article" />
46
+ <meta property="article:published_time" content="{{ page.date | date_to_xmlschema }}" />
47
+ {% endif %}
48
+
49
+ {% if paginator.previous_page %}
50
+ <link rel="prev" href="{{ paginator.previous_page_path | absolute_url }}" />
51
+ {% endif %}
52
+ {% if paginator.next_page %}
53
+ <link rel="next" href="{{ paginator.next_page_path | absolute_url }}" />
54
+ {% endif %}
55
+
56
+
57
+ {% if seo_tag.image %}
58
+ <meta name="twitter:card" content="{{ page.twitter.card | default: site.twitter.card | default: "summary_large_image" }}" />
59
+ <meta property="twitter:image" content="{{ seo_tag.image.path }}" />
60
+ {% elsif seo_tag.soopr %}
61
+ <meta name="twitter:card" content="summary_large_image" />
62
+ <meta property="og:image" content="https://soopr.xyz/images/card?url={{ seo_tag.canonical_url }}" />
63
+ {% else %}
64
+ <meta name="twitter:card" content="summary" />
65
+ {% endif %}
66
+
67
+ {% if seo_tag.page_title %}
68
+ <meta property="twitter:title" content="{{ seo_tag.page_title }}" />
69
+ {% endif %}
70
+
71
+ {% if site.twitter %}
72
+ <meta name="twitter:site" content="@{{ site.twitter.username | remove:'@' }}" />
73
+
74
+ {% if seo_tag.author.twitter %}
75
+ <meta name="twitter:creator" content="@{{ seo_tag.author.twitter | remove:'@' }}" />
76
+ {% endif %}
77
+ {% endif %}
78
+
79
+ {% if site.facebook %}
80
+ {% if site.facebook.admins %}
81
+ <meta property="fb:admins" content="{{ site.facebook.admins }}" />
82
+ {% endif %}
83
+
84
+ {% if site.facebook.publisher %}
85
+ <meta property="article:publisher" content="{{ site.facebook.publisher }}" />
86
+ {% endif %}
87
+
88
+ {% if site.facebook.app_id %}
89
+ <meta property="fb:app_id" content="{{ site.facebook.app_id }}" />
90
+ {% endif %}
91
+ {% endif %}
92
+
93
+ {% if site.webmaster_verifications %}
94
+ {% if site.webmaster_verifications.google %}
95
+ <meta name="google-site-verification" content="{{ site.webmaster_verifications.google }}" />
96
+ {% endif %}
97
+
98
+ {% if site.webmaster_verifications.bing %}
99
+ <meta name="msvalidate.01" content="{{ site.webmaster_verifications.bing }}" />
100
+ {% endif %}
101
+
102
+ {% if site.webmaster_verifications.alexa %}
103
+ <meta name="alexaVerifyID" content="{{ site.webmaster_verifications.alexa }}" />
104
+ {% endif %}
105
+
106
+ {% if site.webmaster_verifications.yandex %}
107
+ <meta name="yandex-verification" content="{{ site.webmaster_verifications.yandex }}" />
108
+ {% endif %}
109
+
110
+ {% if site.webmaster_verifications.baidu %}
111
+ <meta name="baidu-site-verification" content="{{ site.webmaster_verifications.baidu }}" />
112
+ {% endif %}
113
+ {% elsif site.google_site_verification %}
114
+ <meta name="google-site-verification" content="{{ site.google_site_verification }}" />
115
+ {% endif %}
116
+
117
+ <script type="application/ld+json">
118
+ {{ seo_tag.json_ld | jsonify }}
119
+ </script>
120
+
121
+ {% if seo_tag.soopr %}
122
+ <script async defer data-soopr-token="{{seo_tag.soopr.publish_token}}" src="https://sdk.soopr.co/soopr.js" ></script>
123
+ {% endif %}
124
+
125
+ <!-- End Jekyll SEO tag -->