bridgetown-seo-tag 3.0.0

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