bridgetown-seo-tag 3.0.0

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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 32c047b2734dc57d7a2149b9538ae9513567c3a6efc0fafdcb0ea7dca9c9bdee
4
+ data.tar.gz: f92049046ce16aaf68798c51abccbbbb13fc95bf83c869931a0aee97d9cc656f
5
+ SHA512:
6
+ metadata.gz: 6e4ea4bb79bbbf524a36a62d807f4f3cc1ee08fd8b5c01ccfd7be49f960884c385418b799fc35df69a4cf278ae94dc82472bdfb287f37b55f8de56a7a8494dd1
7
+ data.tar.gz: 61573b8b0a2a673594379704ac803d548ecfad0cfba649814e27799dbcdb837f8882560dd1cd1afef300bcd2f17a4810371178ccbca0955fed2d948172ec9b04
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ pkg
6
+ spec/reports/
7
+ spec/fixtures/.bridgetown-cache
8
+ tmp/
9
+ vendor/bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,19 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require: rubocop-jekyll
4
+ inherit_gem:
5
+ rubocop-jekyll: .rubocop.yml
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 2.4
9
+ Exclude:
10
+ - vendor/**/*
11
+
12
+ Layout/LineLength:
13
+ Exclude:
14
+ - spec/**/*
15
+ - bridgetown-seo-tag.gemspec
16
+
17
+ Layout/BlockLength:
18
+ Exclude:
19
+ - spec/**/*
@@ -0,0 +1,13 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
+ # on 2020-03-20 11:41:46 +0100 using RuboCop version 0.80.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Cop supports --auto-correct.
11
+ Lint/ToJSON:
12
+ Exclude:
13
+ - 'lib/bridgetown-seo-tag/json_ld_drop.rb'
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "bridgetown", ENV["BRIDGETOWN_VERSION"] if ENV["BRIDGETOWN_VERSION"]
@@ -0,0 +1,5 @@
1
+ # Change Log
2
+
3
+ ## 3.0.0 / 2020-04-14
4
+
5
+ Use Bridgetown gem and rename to bridgetown-seo-tag.
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020-present Jared White and Bridgetown contributors
4
+ Copyright (c) 2015-2020 Ben Balter and the jekyll-seo-tag contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ ## About Bridgetown SEO Tag
2
+
3
+ A Bridgetown plugin to add metadata tags for search engines and social networks to better index and display your site's content.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/bridgetown-seo-tag.svg)](https://badge.fury.io/rb/bridgetown-seo-tag)
6
+
7
+ ## What it does
8
+
9
+ Bridgetown SEO Tag adds the following meta tags to your site:
10
+
11
+ * Page title, with site title or description appended
12
+ * Page description
13
+ * Canonical URL
14
+ * Next and previous URLs on paginated pages
15
+ * [Open Graph](https://ogp.me/) title, description, site title, and URL (for Facebook, LinkedIn, etc.)
16
+ * [Twitter Summary Card](https://dev.twitter.com/cards/overview) metadata
17
+
18
+ While you could theoretically add the necessary metadata tags yourself, Bridgetown SEO Tag provides a battle-tested template of crowdsourced best-practices.
19
+
20
+ **NOTE:** make sure you add your site-wide SEO Tag metadata to `src/_data/site_metadata.yml`, not `bridgetown.config.yml`
21
+
22
+ ## What it doesn't do
23
+
24
+ Bridgetown SEO tag is designed to output machine-readable metadata for search engines and social networks to index and display.
25
+
26
+ Bridgetown SEO tag isn't designed to accommodate every possible use case. It should work for most site out of the box and without a laundry list of configuration options that serve only to confuse most users.
27
+
28
+ ## Documentation
29
+
30
+ More detailed documentation forthcoming.
31
+
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it (https://github.com/bridgetownrb/bridgetown-seo-tag/fork)
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create a new Pull Request
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bridgetown-seo-tag/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bridgetown-seo-tag"
7
+ spec.version = Bridgetown::SeoTag::VERSION
8
+ spec.authors = ["Ben Balter"]
9
+ spec.email = ["ben.balter@github.com"]
10
+ spec.summary = "A Bridgetown plugin to add metadata tags for search engines and social networks to better index and display your site's content."
11
+ spec.homepage = "https://github.com/bridgetownrb/bridgetown-seo-tag"
12
+ spec.license = "MIT"
13
+
14
+ spec.required_ruby_version = ">= 2.4.0"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r!^(test|spec|features)/!) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r!^exe/!) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "bridgetown", ">= 0.6", "< 2.0"
22
+
23
+ spec.add_development_dependency "bundler", ">= 1.15"
24
+ spec.add_development_dependency "rake", "~> 12.0"
25
+ spec.add_development_dependency "html-proofer", "~> 3.7"
26
+ spec.add_development_dependency "rspec", "~> 3.5"
27
+ spec.add_development_dependency "rubocop-jekyll", "~> 0.5"
28
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bridgetown"
4
+ require "bridgetown-seo-tag/version"
5
+
6
+ module Bridgetown
7
+ class SeoTag < Liquid::Tag
8
+ autoload :AuthorDrop, "bridgetown-seo-tag/author_drop"
9
+ autoload :ImageDrop, "bridgetown-seo-tag/image_drop"
10
+ autoload :UrlHelper, "bridgetown-seo-tag/url_helper"
11
+ autoload :Drop, "bridgetown-seo-tag/drop"
12
+ autoload :Filters, "bridgetown-seo-tag/filters"
13
+
14
+ attr_accessor :context
15
+
16
+ # Matches all whitespace that follows either
17
+ # 1. A '}', which closes a Liquid tag
18
+ # 2. A '{', which opens a JSON block
19
+ # 3. A '>' followed by a newline, which closes an XML tag or
20
+ # 4. A ',' followed by a newline, which ends a JSON line
21
+ # We will strip all of this whitespace to minify the template
22
+ # We will not strip any whitespace if the next character is a '-'
23
+ # so that we do not interfere with the HTML comment at the
24
+ # very begining
25
+ MINIFY_REGEX = %r!(?<=[{}]|[>,]\n)\s+(?\!-)!.freeze
26
+
27
+ def initialize(_tag_name, text, _tokens)
28
+ super
29
+ @text = text
30
+ end
31
+
32
+ def render(context)
33
+ @context = context
34
+ SeoTag.template.render!(payload, info)
35
+ end
36
+
37
+ private
38
+
39
+ def options
40
+ {
41
+ "version" => Bridgetown::SeoTag::VERSION,
42
+ "title" => title?,
43
+ }
44
+ end
45
+
46
+ def payload
47
+ # site_payload is an instance of UnifiedPayloadDrop
48
+ Bridgetown::Utils.deep_merge_hashes(
49
+ context.registers[:site].site_payload,
50
+ "page" => context.registers[:page],
51
+ "paginator" => context["paginator"],
52
+ "seo_tag" => drop
53
+ )
54
+ end
55
+
56
+ def drop
57
+ if context.registers[:site].liquid_renderer.respond_to?(:cache)
58
+ Bridgetown::SeoTag::Drop.new(@text, @context)
59
+ else
60
+ @drop ||= Bridgetown::SeoTag::Drop.new(@text, @context)
61
+ end
62
+ end
63
+
64
+ def info
65
+ {
66
+ :registers => context.registers,
67
+ :filters => [Bridgetown::Filters],
68
+ }
69
+ end
70
+
71
+ class << self
72
+ def template
73
+ @template ||= Liquid::Template.parse template_contents
74
+ end
75
+
76
+ private
77
+
78
+ def template_contents
79
+ @template_contents ||= begin
80
+ File.read(template_path).gsub(MINIFY_REGEX, "")
81
+ end
82
+ end
83
+
84
+ def template_path
85
+ @template_path ||= begin
86
+ File.expand_path "./template.html", File.dirname(__FILE__)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ Liquid::Template.register_tag("seo", Bridgetown::SeoTag)
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class SeoTag
5
+ # A drop representing the current page's author
6
+ #
7
+ # Author name will be pulled from:
8
+ #
9
+ # 1. The page's `author` key
10
+ # 2. The first author in the page's `authors` key
11
+ # 3. The `author` key in the site config
12
+ #
13
+ # If the result from the name search is a string, we'll also check
14
+ # for additional author metadata in `site.data.authors`
15
+ class AuthorDrop < Bridgetown::Drops::Drop
16
+ # Initialize a new AuthorDrop
17
+ #
18
+ # page - The page hash (e.g., Page#to_liquid)
19
+ # site - The Bridgetown::Drops::SiteDrop
20
+ def initialize(page: nil, site: nil)
21
+ raise ArgumentError unless page && site
22
+
23
+ @mutations = {}
24
+ @page = page
25
+ @site = site
26
+ end
27
+
28
+ # AuthorDrop#to_s should return name, allowing the author drop to safely
29
+ # replace `page.author`, if necessary, and remain backwards compatible
30
+ def name
31
+ author_hash["name"]
32
+ end
33
+ alias_method :to_s, :name
34
+
35
+ def twitter
36
+ return @twitter if defined? @twitter
37
+
38
+ twitter = author_hash["twitter"] || author_hash["name"]
39
+ @twitter = twitter.is_a?(String) ? twitter.sub(%r!^@!, "") : nil
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :page
45
+ attr_reader :site
46
+
47
+ # Finds the page author in the page.author, page.authors, or site.author
48
+ #
49
+ # Returns a string or hash representing the author
50
+ def resolved_author
51
+ return @resolved_author if defined? @resolved_author
52
+
53
+ sources = [page["author"]]
54
+ sources << page["authors"].first if page["authors"].is_a?(Array)
55
+ sources << site.data.dig("site_metadata", "author")
56
+ @resolved_author = sources.find { |s| !s.to_s.empty? }
57
+ end
58
+
59
+ # If resolved_author is a string, attempts to find coresponding author
60
+ # metadata in `site.data.authors`
61
+ #
62
+ # Returns a hash representing additional metadata or an empty hash
63
+ def site_data_hash
64
+ @site_data_hash ||= begin
65
+ return {} unless resolved_author.is_a?(String)
66
+ return {} unless site.data["authors"].is_a?(Hash)
67
+
68
+ author_hash = site.data["authors"][resolved_author]
69
+ author_hash.is_a?(Hash) ? author_hash : {}
70
+ end
71
+ end
72
+
73
+ # Returns the normalized author hash representing the page author,
74
+ # including site-wide metadata if the author is provided as a string,
75
+ # or an empty hash, if the author cannot be resolved
76
+ def author_hash
77
+ @author_hash ||= begin
78
+ if resolved_author.is_a? Hash
79
+ resolved_author
80
+ elsif resolved_author.is_a? String
81
+ { "name" => resolved_author }.merge(site_data_hash)
82
+ else
83
+ {}
84
+ end
85
+ end
86
+ end
87
+
88
+ # Since author_hash is aliased to fallback_data, any values in the hash
89
+ # will be exposed via the drop, allowing support for arbitrary metadata
90
+ alias_method :fallback_data, :author_hash
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class SeoTag
5
+ class Drop < Bridgetown::Drops::Drop
6
+ include Bridgetown::SeoTag::UrlHelper
7
+
8
+ TITLE_SEPARATOR = " | "
9
+ FORMAT_STRING_METHODS = [
10
+ :markdownify, :strip_html, :normalize_whitespace, :escape_once,
11
+ ].freeze
12
+ HOMEPAGE_OR_ABOUT_REGEX = %r!^/(about/)?(index.html?)?$!.freeze
13
+
14
+ def initialize(text, context)
15
+ @obj = {}
16
+ @mutations = {}
17
+ @text = text
18
+ @context = context
19
+ end
20
+
21
+ def version
22
+ Bridgetown::SeoTag::VERSION
23
+ end
24
+
25
+ # Should the `<title>` tag be generated for this page?
26
+ def title?
27
+ return false unless title
28
+ return @display_title if defined?(@display_title)
29
+
30
+ @display_title = (@text !~ %r!title=false!i)
31
+ end
32
+
33
+ def site_title
34
+ @site_title ||= format_string(site.data.dig("site_metadata", "title") || site.data.dig("site_metadata", "name"))
35
+ end
36
+
37
+ def site_tagline
38
+ @site_tagline ||= format_string site.data.dig("site_metadata", "tagline")
39
+ end
40
+
41
+ def site_description
42
+ @site_description ||= format_string site.data.dig("site_metadata", "description")
43
+ end
44
+
45
+ # Page title without site title or description appended
46
+ def page_title
47
+ @page_title ||= format_string(page["title"]) || site_title
48
+ end
49
+
50
+ def site_tagline_or_description
51
+ site_tagline || site_description
52
+ end
53
+
54
+ # Page title with site title or description appended
55
+ # rubocop:disable Metrics/CyclomaticComplexity
56
+ def title
57
+ @title ||= begin
58
+ if site_title && page_title != site_title
59
+ page_title + TITLE_SEPARATOR + site_title
60
+ elsif site_description && site_title
61
+ site_title + TITLE_SEPARATOR + site_tagline_or_description
62
+ else
63
+ page_title || site_title
64
+ end
65
+ end
66
+
67
+ return page_number + @title if page_number
68
+
69
+ @title
70
+ end
71
+ # rubocop:enable Metrics/CyclomaticComplexity
72
+
73
+ def name
74
+ return @name if defined?(@name)
75
+
76
+ @name = if seo_name
77
+ seo_name
78
+ elsif !homepage_or_about?
79
+ nil
80
+ elsif site_social["name"]
81
+ format_string site_social["name"]
82
+ elsif site_title
83
+ site_title
84
+ end
85
+ end
86
+
87
+ def description
88
+ @description ||= begin
89
+ format_string(page["description"] || page["excerpt"]) || site_description
90
+ end
91
+ end
92
+
93
+ # A drop representing the page author
94
+ def author
95
+ @author ||= AuthorDrop.new(:page => page, :site => site)
96
+ end
97
+
98
+ # Returns a Drop representing the page's image
99
+ # Returns nil if the image has no path, to preserve backwards compatability
100
+ def image
101
+ @image ||= ImageDrop.new(:page => page, :context => @context)
102
+ @image if @image.path
103
+ end
104
+
105
+ def date_modified
106
+ @date_modified ||= begin
107
+ date = if page_seo["date_modified"]
108
+ page_seo["date_modified"]
109
+ elsif page["last_modified_at"]
110
+ page["last_modified_at"].to_liquid
111
+ else
112
+ page["date"]
113
+ end
114
+ filters.date_to_xmlschema(date) if date
115
+ end
116
+ end
117
+
118
+ def date_published
119
+ @date_published ||= filters.date_to_xmlschema(page["date"]) if page["date"]
120
+ end
121
+
122
+ def type
123
+ @type ||= begin
124
+ if page_seo["type"]
125
+ page_seo["type"]
126
+ elsif homepage_or_about?
127
+ "WebSite"
128
+ elsif page["date"]
129
+ "BlogPosting"
130
+ else
131
+ "WebPage"
132
+ end
133
+ end
134
+ end
135
+
136
+ def links
137
+ @links ||= begin
138
+ if page_seo["links"]
139
+ page_seo["links"]
140
+ elsif homepage_or_about? && site_social["links"]
141
+ site_social["links"]
142
+ end
143
+ end
144
+ end
145
+
146
+ def logo
147
+ @logo ||= begin
148
+ return unless site.data.dig("site_metadata", "logo")
149
+
150
+ if absolute_url? site.data.dig("site_metadata", "logo")
151
+ filters.uri_escape site.data.dig("site_metadata", "logo")
152
+ else
153
+ filters.uri_escape filters.absolute_url site.data.dig("site_metadata", "logo")
154
+ end
155
+ end
156
+ end
157
+
158
+ def page_lang
159
+ @page_lang ||= page["lang"] || site["lang"] || "en_US"
160
+ end
161
+
162
+ def canonical_url
163
+ @canonical_url ||= begin
164
+ if page["canonical_url"].to_s.empty?
165
+ filters.absolute_url(page["url"]).to_s.gsub(%r!/index\.html$!, "/")
166
+ else
167
+ page["canonical_url"]
168
+ end
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def filters
175
+ @filters ||= Bridgetown::SeoTag::Filters.new(@context)
176
+ end
177
+
178
+ def page
179
+ @page ||= @context.registers[:page].to_liquid
180
+ end
181
+
182
+ def site
183
+ @site ||= @context.registers[:site].site_payload["site"].to_liquid
184
+ end
185
+
186
+ def homepage_or_about?
187
+ page["url"] =~ HOMEPAGE_OR_ABOUT_REGEX
188
+ end
189
+
190
+ def page_number
191
+ return unless @context["paginator"] && @context["paginator"]["page"]
192
+
193
+ current = @context["paginator"]["page"]
194
+ total = @context["paginator"]["total_pages"]
195
+ paginator_message = site["seo_paginator_message"] || "Page %<current>s of %<total>s for "
196
+
197
+ format(paginator_message, :current => current, :total => total) if current > 1
198
+ end
199
+
200
+ attr_reader :context
201
+
202
+ def fallback_data
203
+ @fallback_data ||= {}
204
+ end
205
+
206
+ def format_string(string)
207
+ string = FORMAT_STRING_METHODS.reduce(string) do |memo, method|
208
+ filters.public_send(method, memo)
209
+ end
210
+
211
+ string unless string.empty?
212
+ end
213
+
214
+ def seo_name
215
+ @seo_name ||= format_string(page_seo["name"]) if page_seo["name"]
216
+ end
217
+
218
+ def page_seo
219
+ @page_seo ||= sub_hash(page, "seo")
220
+ end
221
+
222
+ def site_social
223
+ @site_social ||= sub_hash(site.data.dig("site_metadata"), "social")
224
+ end
225
+
226
+ # Safely returns a sub hash
227
+ #
228
+ # hash - the parent hash
229
+ # key - the key in the parent hash
230
+ #
231
+ # Returns the sub hash or an empty hash, if it does not exist
232
+ def sub_hash(hash, key)
233
+ if hash && hash[key].is_a?(Hash)
234
+ hash[key]
235
+ else
236
+ {}
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class SeoTag
5
+ class Filters
6
+ include Bridgetown::Filters
7
+ include Liquid::StandardFilters
8
+
9
+ def initialize(context)
10
+ @context = context
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
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 < Bridgetown::Drops::Drop
13
+ include Bridgetown::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 ||= if page["image"].is_a?(Hash)
43
+ { "path" => nil }.merge(page["image"])
44
+ elsif page["image"].is_a?(String)
45
+ { "path" => page["image"] }
46
+ else
47
+ { "path" => nil }
48
+ end
49
+ end
50
+ alias_method :fallback_data, :image_hash
51
+
52
+ def raw_path
53
+ @raw_path ||= begin
54
+ image_hash["path"] || image_hash["facebook"] || image_hash["twitter"]
55
+ end
56
+ end
57
+
58
+ def absolute_url
59
+ return unless raw_path
60
+ return @absolute_url if defined? @absolute_url
61
+
62
+ @absolute_url = if raw_path.is_a?(String) && absolute_url?(raw_path) == false
63
+ filters.absolute_url raw_path
64
+ else
65
+ raw_path
66
+ end
67
+ end
68
+
69
+ def filters
70
+ @filters ||= Bridgetown::SeoTag::Filters.new(context)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
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 Bridgetown
7
+ class SeoTag < Liquid::Tag
8
+ VERSION = "3.0.0"
9
+ end
10
+ end
@@ -0,0 +1,110 @@
1
+ <!-- Begin Bridgetown SEO tag v{{ seo_tag.version }} -->
2
+ {% if seo_tag.title? %}
3
+ <title>{{ seo_tag.title }}</title>
4
+ {% endif %}
5
+
6
+ {% if seo_tag.page_title %}
7
+ <meta property="og:title" content="{{ seo_tag.page_title }}" />
8
+ {% endif %}
9
+
10
+ {% if seo_tag.author.name %}
11
+ <meta name="author" content="{{ seo_tag.author.name }}" />
12
+ {% endif %}
13
+
14
+ <meta property="og:locale" content="{{ seo_tag.page_lang | replace:'-','_' }}" />
15
+
16
+ {% if seo_tag.description %}
17
+ <meta name="description" content="{{ seo_tag.description }}" />
18
+ <meta property="og:description" content="{{ seo_tag.description }}" />
19
+ {% endif %}
20
+
21
+ {% if site.url %}
22
+ <link rel="canonical" href="{{ seo_tag.canonical_url }}" />
23
+ <meta property="og:url" content="{{ seo_tag.canonical_url }}" />
24
+ {% endif %}
25
+
26
+ {% if seo_tag.site_title %}
27
+ <meta property="og:site_name" content="{{ seo_tag.site_title }}" />
28
+ {% endif %}
29
+
30
+ {% if seo_tag.image %}
31
+ <meta property="og:image" content="{{ seo_tag.image.path }}" />
32
+ {% if seo_tag.image.height %}
33
+ <meta property="og:image:height" content="{{ seo_tag.image.height }}" />
34
+ {% endif %}
35
+ {% if seo_tag.image.width %}
36
+ <meta property="og:image:width" content="{{ seo_tag.image.width }}" />
37
+ {% endif %}
38
+ {% endif %}
39
+
40
+ {% if page.date %}
41
+ <meta property="og:type" content="article" />
42
+ <meta property="article:published_time" content="{{ page.date | date_to_xmlschema }}" />
43
+ {% endif %}
44
+
45
+ {% if paginator.previous_page %}
46
+ <link rel="prev" href="{{ paginator.previous_page_path | absolute_url }}" />
47
+ {% endif %}
48
+ {% if paginator.next_page %}
49
+ <link rel="next" href="{{ paginator.next_page_path | absolute_url }}" />
50
+ {% endif %}
51
+
52
+
53
+ {% if seo_tag.image %}
54
+ <meta name="twitter:card" content="{{ page.twitter.card | default: site.metadata.twitter.card | default: "summary_large_image" }}" />
55
+ <meta property="twitter:image" content="{{ seo_tag.image.path }}" />
56
+ {% else %}
57
+ <meta name="twitter:card" content="summary" />
58
+ {% endif %}
59
+
60
+ {% if seo_tag.page_title %}
61
+ <meta property="twitter:title" content="{{ seo_tag.page_title }}" />
62
+ {% endif %}
63
+
64
+ {% if site.metadata.twitter %}
65
+ <meta name="twitter:site" content="@{{ site.metadata.twitter.username | remove:'@' }}" />
66
+
67
+ {% if seo_tag.author.twitter %}
68
+ <meta name="twitter:creator" content="@{{ seo_tag.author.twitter | remove:'@' }}" />
69
+ {% endif %}
70
+ {% endif %}
71
+
72
+ {% if site.metadata.facebook %}
73
+ {% if site.metadata.facebook.admins %}
74
+ <meta property="fb:admins" content="{{ site.metadata.facebook.admins }}" />
75
+ {% endif %}
76
+
77
+ {% if site.metadata.facebook.publisher %}
78
+ <meta property="article:publisher" content="{{ site.metadata.facebook.publisher }}" />
79
+ {% endif %}
80
+
81
+ {% if site.metadata.facebook.app_id %}
82
+ <meta property="fb:app_id" content="{{ site.metadata.facebook.app_id }}" />
83
+ {% endif %}
84
+ {% endif %}
85
+
86
+ {% if site.metadata.webmaster_verifications %}
87
+ {% if site.metadata.webmaster_verifications.google %}
88
+ <meta name="google-site-verification" content="{{ site.metadata.webmaster_verifications.google }}" />
89
+ {% endif %}
90
+
91
+ {% if site.metadata.webmaster_verifications.bing %}
92
+ <meta name="msvalidate.01" content="{{ site.metadata.webmaster_verifications.bing }}" />
93
+ {% endif %}
94
+
95
+ {% if site.metadata.webmaster_verifications.alexa %}
96
+ <meta name="alexaVerifyID" content="{{ site.metadata.webmaster_verifications.alexa }}" />
97
+ {% endif %}
98
+
99
+ {% if site.metadata.webmaster_verifications.yandex %}
100
+ <meta name="yandex-verification" content="{{ site.metadata.webmaster_verifications.yandex }}" />
101
+ {% endif %}
102
+
103
+ {% if site.metadata.webmaster_verifications.baidu %}
104
+ <meta name="baidu-site-verification" content="{{ site.metadata.webmaster_verifications.baidu }}" />
105
+ {% endif %}
106
+ {% elsif site.metadata.google_site_verification %}
107
+ <meta name="google-site-verification" content="{{ site.metadata.google_site_verification }}" />
108
+ {% endif %}
109
+
110
+ <!-- End Bridgetown SEO tag -->
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ set -ex
4
+
5
+ bundle install
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ set -ex
4
+
5
+ script/fmt
6
+ script/test
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ echo "Rubocop $(bundle exec rubocop --version)"
5
+ bundle exec rubocop -D -E $@
6
+ success=$?
7
+ if ((success != 0)); then
8
+ echo -e "\nTry running \`script/fmt -a\` to automatically fix errors"
9
+ fi
10
+ exit $success
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ # Tag and push a release.
3
+
4
+ set -e
5
+
6
+ script/cibuild
7
+ bundle exec rake release
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ set -ex
3
+
4
+ bundle exec rspec "$@"
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bridgetown-seo-tag
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ben Balter
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-04-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bridgetown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0.6'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.15'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.15'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '12.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '12.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: html-proofer
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.7'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.7'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.5'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.5'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop-jekyll
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.5'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.5'
103
+ description:
104
+ email:
105
+ - ben.balter@github.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".gitignore"
111
+ - ".rspec"
112
+ - ".rubocop.yml"
113
+ - ".rubocop_todo.yml"
114
+ - Gemfile
115
+ - History.markdown
116
+ - LICENSE.txt
117
+ - README.md
118
+ - Rakefile
119
+ - bridgetown-seo-tag.gemspec
120
+ - lib/bridgetown-seo-tag.rb
121
+ - lib/bridgetown-seo-tag/author_drop.rb
122
+ - lib/bridgetown-seo-tag/drop.rb
123
+ - lib/bridgetown-seo-tag/filters.rb
124
+ - lib/bridgetown-seo-tag/image_drop.rb
125
+ - lib/bridgetown-seo-tag/url_helper.rb
126
+ - lib/bridgetown-seo-tag/version.rb
127
+ - lib/template.html
128
+ - script/bootstrap
129
+ - script/cibuild
130
+ - script/fmt
131
+ - script/release
132
+ - script/test
133
+ homepage: https://github.com/bridgetownrb/bridgetown-seo-tag
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.4.0
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.0.6
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: A Bridgetown plugin to add metadata tags for search engines and social networks
156
+ to better index and display your site's content.
157
+ test_files: []