jekyll-webmention_io 2.8.5 → 2.9.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,29 @@
1
+ # frozen_string_literal: false
2
+
3
+ # (c) Aaron Gustafson
4
+ # https://github.com/aarongustafson/jekyll-webmention_io
5
+ # Licence : MIT
6
+ #
7
+ # this liquid plugin insert a webmentions into your Octopress or Jekill blog
8
+ # using http://webmention.io/ and the following syntax:
9
+ #
10
+ # {% webmention_rsvps post.url %}
11
+ #
12
+ module Jekyll
13
+ module WebmentionIO
14
+ class WebmentionRsvpsTag < Jekyll::WebmentionIO::WebmentionTag
15
+ def initialize(tag_name, text, tokens)
16
+ super
17
+ @text = text
18
+ self.template = "rsvps"
19
+ end
20
+
21
+ def set_data(data, _types)
22
+ webmentions = extract_type "rsvps", data
23
+ @data = { "webmentions" => webmentions.values }
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ Liquid::Template.register_tag("webmention_rsvps", Jekyll::WebmentionIO::WebmentionRsvpsTag)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  # (c) Aaron Gustafson
2
4
  # https://github.com/aarongustafson/jekyll-webmention_io
3
5
  # Licence : MIT
@@ -5,15 +7,15 @@
5
7
  # this liquid plugin insert a webmentions into your Octopress or Jekill blog
6
8
  # using http://webmention.io/ and the following syntax:
7
9
  #
8
- # {% webmentions post.url [ likes | links | posts | replies | reposts ]* %}
10
+ # {% webmentions post.url [ bookmarks | likes | links | posts | replies | reposts | rsvps ]* %}
9
11
  #
10
12
  module Jekyll
11
13
  module WebmentionIO
12
14
  class WebmentionsTag < Jekyll::WebmentionIO::WebmentionTag
13
- def initialize(tagName, text, tokens)
15
+ def initialize(tag_name, text, tokens)
14
16
  super
15
17
  @text = text
16
- set_template "webmentions"
18
+ self.template = "webmentions"
17
19
  end
18
20
  end
19
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  # (c) Aaron Gustafson
2
4
  # https://github.com/aarongustafson/jekyll-webmention_io
3
5
  # Licence : MIT
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  # (c) Aaron Gustafson
2
4
  # https://github.com/aarongustafson/jekyll-webmention_io
3
5
  # Licence : MIT
@@ -31,6 +33,7 @@ module Jekyll
31
33
  js << "<script src=\"#{js_file_path}\" async></script>"
32
34
  end
33
35
 
36
+ Jekyll::WebmentionIO.log "info", "Gathering templates for JavaScript."
34
37
  templates = ""
35
38
  template_files = Jekyll::WebmentionIO.types + %w(count webmentions)
36
39
  template_files.each do |template|
@@ -0,0 +1,26 @@
1
+ <div class="webmentions webmentions--bookmarks">
2
+ {% if webmentions.size > 0 %}
3
+ <ol class="webmentions__list">
4
+ {% for webmention in webmentions %}
5
+ <li id="webmention-{{ webmention.id }}" class="webmentions__item webmention webmention--{{ webmention.type }}">
6
+ <div class="webmention__content p-content">
7
+ {{ webmention.content }}
8
+ </div>
9
+ <div class="webmention__meta">
10
+ {% if webmention.author %}
11
+ <a class="webmention__author h-card u-url" href="{{ webmention.author.url }}">{{ webmention.author.name }}</a>
12
+ {% endif %}
13
+ {% if webmention.pubdate and webmention.url %}on{% endif %}
14
+ {% if webmention.pubdate %}
15
+ <a class="webmention__source u-url" href="{{ webmention.url }}">
16
+ <time class="webmention__pubdate dt-published" datetime="{{ webmention.pubdate | date: '%FT%T%:z' }}">{{ webmention.pubdate | date: '%d %B %Y' }}</time>
17
+ </a>
18
+ {% endif %}
19
+ </div>
20
+ </li>
21
+ {% endfor %}
22
+ </ol>
23
+ {% else %}
24
+ <p class="webmentions__not-found">No bookmarks were found.</p>
25
+ {% endif %}
26
+ </div>
@@ -21,6 +21,6 @@
21
21
  {% endfor %}
22
22
  </ol>
23
23
  {% else %}
24
- <p class="webmentions__not-found">No webmentions were found.</p>
24
+ <p class="webmentions__not-found">No links were found.</p>
25
25
  {% endif %}
26
26
  </div>
@@ -13,6 +13,6 @@
13
13
  {% endfor %}
14
14
  </ol>
15
15
  {% else %}
16
- <p class="webmentions__not-found">No webmentions were found.</p>
16
+ <p class="webmentions__not-found">No posts were found.</p>
17
17
  {% endif %}
18
18
  </div>
@@ -26,6 +26,6 @@
26
26
  {% endfor %}
27
27
  </ol>
28
28
  {% else %}
29
- <p class="webmentions__not-found">No webmentions were found.</p>
29
+ <p class="webmentions__not-found">No replies were found.</p>
30
30
  {% endif %}
31
31
  </div>
@@ -0,0 +1,19 @@
1
+ <div class="webmentions webmentions--rsvps">
2
+ {% if webmentions.size > 0 %}
3
+ <ol class="webmentions__list">
4
+ {% for webmention in webmentions %}
5
+ <li id="webmention-{{ webmention.id }}" class="webmentions__item webmention webmention--{{ webmention.type }}">
6
+ <div class="webmention__author p-author h-card">
7
+ <a class="u-url" href="{{ webmention.author.url }}">
8
+ <img class="webmention__author__photo u-photo" src="{{ webmention.author.photo }}" alt="Avatar for {{ webmention.author.name }}" title="{{ webmention.author.name }}">
9
+ <b class="p-name">{{ webmention.author.name }}</b>
10
+ {{ webmention.content }}
11
+ </a>
12
+ </div>
13
+ </li>
14
+ {% endfor %}
15
+ </ol>
16
+ {% else %}
17
+ <p class="webmentions__not-found">No RSVPs were found.</p>
18
+ {% endif %}
19
+ </div>
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: false
2
+
1
3
  module Jekyll
2
4
  module WebmentionIO
3
- VERSION = "2.8.5".freeze
5
+ VERSION = "2.9.0".freeze
4
6
  end
5
7
  end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: false
2
+
3
+ # (c) Aaron Gustafson
4
+ # https://github.com/aarongustafson/jekyll-webmention_io
5
+ # Licence : MIT
6
+ #
7
+ # this liquid plugin insert a webmentions into your Octopress or Jekill blog
8
+ # using http://webmention.io/ and the following syntax:
9
+ #
10
+ module Jekyll
11
+ module WebmentionIO
12
+ class Webmention
13
+ attr_reader :id, :hash
14
+
15
+ def initialize(mention, site)
16
+ @raw = mention
17
+ @site = site
18
+
19
+ @uri = determine_uri
20
+ @source = determine_source
21
+ @id = determine_id
22
+ @type = determine_type
23
+ end
24
+
25
+ def to_hash
26
+ gather_content
27
+
28
+ the_hash = {
29
+ "id" => @id,
30
+ "uri" => @uri,
31
+ "source" => @source,
32
+ "pubdate" => @pubdate,
33
+ "raw" => @raw,
34
+ "author" => @author,
35
+ "type" => @type,
36
+ }
37
+
38
+ the_hash["title"] = @title if @title
39
+ the_hash["content"] = @content || ""
40
+
41
+ the_hash
42
+ end
43
+
44
+ private
45
+
46
+ def gather_content
47
+ @pubdate = determine_pubdate
48
+ @author = determine_author
49
+ @title = determine_title
50
+ @content = determine_content
51
+ end
52
+
53
+ def markdownify(string)
54
+ unless @converter
55
+ @converter = if defined? @site.find_converter_instance
56
+ @site.find_converter_instance(::Jekyll::Converters::Markdown)
57
+ else
58
+ @site.getConverterImpl(::Jekyll::Converters::Markdown)
59
+ end
60
+ end
61
+
62
+ if string
63
+ string = @converter.convert(string.to_s)
64
+ unless string.start_with?("<p")
65
+ string = string.sub(/^<[^>]+>/, "<p>").sub(/<\/[^>]+>$/, "</p>")
66
+ end
67
+ string.strip
68
+ else
69
+ string
70
+ end
71
+ end
72
+
73
+ def determine_uri
74
+ @raw["data"]["url"] || @raw["source"]
75
+ end
76
+
77
+ def determine_source
78
+ if @uri.include? "twitter.com/"
79
+ "twitter"
80
+ elsif @uri.include? "/googleplus/"
81
+ "googleplus"
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ def determine_id
88
+ id = @raw["id"].to_s
89
+ if @source == "twitter" && !@uri.include?("#favorited-by")
90
+ id = URI(@uri).path.split("/").last.to_s
91
+ end
92
+ unless id
93
+ time = Time.now
94
+ id = time.strftime("%s").to_s
95
+ end
96
+ id
97
+ end
98
+
99
+ def determine_type
100
+ type = @raw.dig("activity", "type")
101
+ unless type
102
+ type = "post"
103
+ if @source == "googleplus"
104
+ type = if @uri.include? "/like/"
105
+ "like"
106
+ elsif @uri.include? "/repost/"
107
+ "repost"
108
+ elsif @uri.include? "/comment/"
109
+ "reply"
110
+ else
111
+ "link"
112
+ end
113
+ end
114
+ end
115
+ type
116
+ end
117
+
118
+ def determine_pubdate
119
+ pubdate = @raw.dig("data", "published_ts")
120
+ if pubdate
121
+ pubdate = Time.at(pubdate)
122
+ elsif @raw["verified_date"]
123
+ pubdate = Time.parse(@raw["verified_date"])
124
+ end
125
+ pubdate
126
+ end
127
+
128
+ def determine_author
129
+ @raw.dig("data", "author")
130
+ end
131
+
132
+ def determine_title
133
+ title = false
134
+
135
+ if @type == "post"
136
+
137
+ html_source = Jekyll::WebmentionIO.get_uri_source(@uri)
138
+ unless html_source
139
+ return title
140
+ end
141
+
142
+ unless html_source.valid_encoding?
143
+ html_source = html_source.encode("UTF-16be", :invalid => :replace, :replace => "?").encode("UTF-8")
144
+ end
145
+
146
+ # Check the `title` first
147
+ matches = /<title>(.*)<\/title>/.match(html_source)
148
+ if matches
149
+ title = matches[1].strip
150
+ else
151
+ # Fall back to the first `h1`
152
+ matches = /<h1>(.*)<\/h1>/.match(html_source)
153
+ title = if matches
154
+ matches[1].strip
155
+ else
156
+ title = "No title available"
157
+ end
158
+ end
159
+
160
+ # cleanup
161
+ title = title.gsub(/<\/?[^>]+?>/, "")
162
+ end # if post
163
+
164
+ markdownify(title)
165
+ end
166
+
167
+ def determine_content
168
+ content = if %w(post reply link).include? @type
169
+ @raw.dig("data", "content")
170
+ else
171
+ @raw.dig("activity", "sentence_html")
172
+ end
173
+
174
+ markdownify(content)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  # (c) Aaron Gustafson
2
4
  # https://github.com/aarongustafson/jekyll-webmention_io
3
5
  # Licence : MIT
@@ -6,6 +8,7 @@
6
8
  # using http://webmention.io/ and the following syntax:
7
9
  #
8
10
  require_relative "webmention_io/version"
11
+ require_relative "webmention_io/webmention"
9
12
 
10
13
  require "json"
11
14
  require "net/http"
@@ -23,7 +26,12 @@ module Jekyll
23
26
  @api_endpoint = @api_url
24
27
  @api_suffix = ""
25
28
 
26
- @types = %w(likes links posts replies reposts)
29
+ @types = %w(bookmarks likes links posts replies reposts rsvps)
30
+
31
+ EXCEPTIONS = [ SocketError, Timeout::Error, Errno::EINVAL,
32
+ Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError,
33
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
34
+ Net::ProtocolError, OpenSSL::SSL::SSLError, ].freeze
27
35
 
28
36
  def self.bootstrap
29
37
  # @jekyll_config = Jekyll.configuration({ 'quiet' => true })
@@ -49,7 +57,7 @@ module Jekyll
49
57
  end
50
58
  end
51
59
 
52
- # Attributes
60
+ # Getters
53
61
  def self.config
54
62
  @config
55
63
  end
@@ -74,6 +82,16 @@ module Jekyll
74
82
  @types
75
83
  end
76
84
 
85
+ # Setters
86
+ def self.api_path=(path)
87
+ @api_endpoint = "#{@api_url}/#{path}"
88
+ end
89
+
90
+ def self.api_suffix=(suffix)
91
+ @api_suffix = suffix
92
+ end
93
+
94
+ # Heplers
77
95
  def self.get_cache_file_path(key)
78
96
  path = false
79
97
  if @cache_files.key? key
@@ -82,24 +100,59 @@ module Jekyll
82
100
  return path
83
101
  end
84
102
 
85
- # API helpers
86
- # def uri_params_for(api_params)
87
- # api_params.keys.sort.map do |k|
88
- # "#{CGI::escape(k)}=#{CGI::escape(api_params[k])}"
89
- # end.join('&')
90
- # end
103
+ def self.read_cached_webmentions(which)
104
+ unless %w(incoming outgoing).include? which
105
+ return {}
106
+ end
91
107
 
92
- def self.set_api_endpoint(path)
93
- @api_endpoint = "#{@api_url}/#{path}"
108
+ cache_file = get_cache_file_path which
109
+ cached_webmentions = open(cache_file) { |f| YAML.load(f) }
110
+
111
+ cached_webmentions
94
112
  end
95
113
 
96
- def self.set_api_suffix(suffix)
97
- @api_suffix = suffix
114
+ def self.cache_webmentions(which, webmentions)
115
+ if %w(incoming outgoing).include? which
116
+ cache_file = get_cache_file_path which
117
+ File.open(cache_file, "w") { |f| YAML.dump(webmentions, f) }
118
+
119
+ Jekyll::WebmentionIO.log "msg", "#{which.capitalize} webmentions have been cached."
120
+ end
121
+ end
122
+
123
+ def self.gather_documents(site)
124
+ documents = if Jekyll::VERSION >= "3.0.0"
125
+ site.posts.docs.clone
126
+ else
127
+ site.posts.clone
128
+ end
129
+
130
+ if @config.dig("pages") == true
131
+ Jekyll::WebmentionIO.log "info", "Including site pages."
132
+ documents.concat site.pages.clone
133
+ end
134
+
135
+ collections = @config.dig("collections")
136
+ if collections
137
+ Jekyll::WebmentionIO.log "info", "Adding collections."
138
+ site.collections.each do |name, collection|
139
+ # skip _posts
140
+ next if name == "posts"
141
+
142
+ unless collections.is_a?(Array) && !collections.include?(name)
143
+ documents.concat collection.docs.clone
144
+ end
145
+ end
146
+ end
147
+
148
+ return documents
98
149
  end
99
150
 
100
151
  def self.get_response(api_params)
101
152
  api_params << @api_suffix
102
- source = get_uri_source(@api_endpoint + "?#{api_params}")
153
+ url = "#{@api_endpoint}?#{api_params}"
154
+ Jekyll::WebmentionIO.log "info", "Sending request to #{url}."
155
+ source = get_uri_source(url)
103
156
  if source
104
157
  JSON.parse(source)
105
158
  else
@@ -145,7 +198,7 @@ module Jekyll
145
198
  # supported: daily, weekly, monthly, yearly, every X days|weeks|months|years
146
199
  def self.get_date_from_string(text)
147
200
  today = Date.today
148
- pattern = %r!every\s(?:(\d+)\s)?(day|week|month|year)s?!
201
+ pattern = /every\s(?:(\d+)\s)?(day|week|month|year)s?/
149
202
  matches = text.match(pattern)
150
203
  unless matches
151
204
  text = if text == "daily"
@@ -167,7 +220,7 @@ module Jekyll
167
220
  end
168
221
 
169
222
  def self.get_webmention_endpoint(uri)
170
- # log 'info', "Looking for webmention endpoint at #{uri}"
223
+ # log "info", "Looking for webmention endpoint at #{uri}"
171
224
  begin
172
225
  endpoint = Webmention::Client.supports_webmention?(uri)
173
226
  unless endpoint
@@ -184,8 +237,8 @@ module Jekyll
184
237
  log "info", "Sending webmention of #{target} in #{source}"
185
238
  # return `curl -s -i -d \"source=#{source}&target=#{target}\" -o /dev/null #{endpoint}`
186
239
  response = Webmention::Client.send_mention(endpoint, source, target, true)
187
- status = response.dig('parsed_response', 'data', 'status').to_s
188
- if status == '200'
240
+ status = response.dig("parsed_response", "data", "status").to_s
241
+ if status == "200"
189
242
  log "info", "Webmention successful!"
190
243
  return response.response.body
191
244
  else
@@ -195,68 +248,25 @@ module Jekyll
195
248
  end
196
249
 
197
250
  def self.get_template_contents(template)
198
- if Jekyll::WebmentionIO.config.dig("templates", template)
199
- # Jekyll::WebmentionIO::log 'info', "Using custom #{template} template"
200
- template_file = Jekyll::WebmentionIO.config["templates"][template]
201
- else
202
- # Jekyll::WebmentionIO::log 'info', "Using default #{template} template"
203
- template_file = File.expand_path("templates/#{template}.html", __dir__)
204
- end
205
- # Jekyll::WebmentionIO::log 'info', "Template file: #{template_file}"
251
+ template_file = if Jekyll::WebmentionIO.config.dig("templates", template)
252
+ Jekyll::WebmentionIO.log "info", "Using custom #{template} template"
253
+ Jekyll::WebmentionIO.config["templates"][template]
254
+ else
255
+ File.expand_path("templates/#{template}.html", __dir__)
256
+ end
257
+ Jekyll::WebmentionIO.log "info", "Template file: #{template_file}"
206
258
  handler = File.open(template_file, "rb")
207
259
  handler.read
208
260
  end
209
261
 
210
262
  # Connections
211
- def self.uri_ok?(uri)
212
- uri = URI.parse(URI.encode(uri))
213
- now = Time.now.to_s
214
- bad_uris = open(@cache_files["bad_uris"]) { |f| YAML.safe_load(f) }
215
- if bad_uris.key? uri.host
216
- last_checked = DateTime.parse(bad_uris[uri.host])
217
- cache_bad_uris_for = @config["cache_bad_uris_for"] || 1 # in days
218
- recheck_at = last_checked.next_day(cache_bad_uris_for).to_s
219
- if recheck_at > now
220
- return false
221
- end
222
- end
223
- return true
224
- end
225
-
226
- # Cache bad URLs for a bit
227
- def self.uri_is_not_ok(uri)
228
- # Never cache webmention.io in here
229
- if uri.host == 'webmention.io'
230
- return
231
- end
232
- cache_file = @cache_files["bad_uris"]
233
- bad_uris = open(cache_file) { |f| YAML.safe_load(f) }
234
- bad_uris[uri.host] = Time.now.to_s
235
- File.open(cache_file, "w") { |f| YAML.dump(bad_uris, f) }
236
- end
237
-
238
263
  def self.get_uri_source(uri, redirect_limit = 10, original_uri = false)
239
264
  original_uri ||= uri
240
265
  unless uri_ok?(uri)
241
266
  return false
242
267
  end
243
- if redirect_limit > 0
244
- uri = URI.parse(URI.encode(uri))
245
- http = Net::HTTP.new(uri.host, uri.port)
246
- http.read_timeout = 10
247
- if uri.scheme == "https"
248
- http.use_ssl = true
249
- http.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:-LOW"
250
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
251
- end
252
- begin
253
- request = Net::HTTP::Get.new(uri.request_uri)
254
- response = http.request(request)
255
- rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
256
- log "warn", "Got an error checking #{original_uri}: #{e}"
257
- uri_is_not_ok(uri)
258
- return false
259
- end
268
+ if redirect_limit.positive?
269
+ response = get_http_response(uri)
260
270
  case response
261
271
  when Net::HTTPSuccess then
262
272
  return response.body.force_encoding("UTF-8")
@@ -278,9 +288,63 @@ module Jekyll
278
288
  end
279
289
 
280
290
  def self.log(type, message)
281
- Jekyll.logger.method(type).call("#{@logger_prefix} #{message}")
291
+ debug = !!@config.dig("debug")
292
+ if debug || %w(error msg).include?(type)
293
+ if type == "msg"
294
+ type = "info"
295
+ end
296
+ Jekyll.logger.method(type).call("#{@logger_prefix} #{message}")
297
+ end
282
298
  end
283
299
 
300
+ private
301
+
302
+ def self.get_http_response(uri)
303
+ uri = URI.parse(URI.encode(uri))
304
+ http = Net::HTTP.new(uri.host, uri.port)
305
+ http.read_timeout = 10
306
+ if uri.scheme == "https"
307
+ http.use_ssl = true
308
+ http.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:-LOW"
309
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
310
+ end
311
+ begin
312
+ request = Net::HTTP::Get.new(uri.request_uri)
313
+ response = http.request(request)
314
+ return response
315
+ rescue *EXCEPTIONS => e
316
+ log "warn", "Got an error checking #{original_uri}: #{e}"
317
+ uri_is_not_ok(uri)
318
+ return false
319
+ end
320
+ end
321
+
322
+ # Cache bad URLs for a bit
323
+ def self.uri_is_not_ok(uri)
324
+ # Never cache webmention.io in here
325
+ if uri.host == "webmention.io"
326
+ return
327
+ end
328
+ cache_file = @cache_files["bad_uris"]
329
+ bad_uris = open(cache_file) { |f| YAML.load(f) }
330
+ bad_uris[uri.host] = Time.now.to_s
331
+ File.open(cache_file, "w") { |f| YAML.dump(bad_uris, f) }
332
+ end
333
+
334
+ def self.uri_ok?(uri)
335
+ uri = URI.parse(URI.encode(uri))
336
+ now = Time.now.to_s
337
+ bad_uris = open(@cache_files["bad_uris"]) { |f| YAML.load(f) }
338
+ if bad_uris.key? uri.host
339
+ last_checked = DateTime.parse(bad_uris[uri.host])
340
+ cache_bad_uris_for = @config["cache_bad_uris_for"] || 1 # in days
341
+ recheck_at = last_checked.next_day(cache_bad_uris_for).to_s
342
+ if recheck_at > now
343
+ return false
344
+ end
345
+ end
346
+ return true
347
+ end
284
348
  end
285
349
  end
286
350