jekyll-soopr-seo-tag 2.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 -->