Module: Jekyll::WebmentionIO

Defined in:
lib/jekyll/tags/count.rb,
lib/jekyll/tags/likes.rb,
lib/jekyll/tags/links.rb,
lib/jekyll/tags/posts.rb,
lib/jekyll/tags/rsvps.rb,
lib/jekyll/tags/replies.rb,
lib/jekyll/tags/reposts.rb,
lib/jekyll/webmention_io.rb,
lib/jekyll/tags/bookmarks.rb,
lib/jekyll/tags/webmention.rb,
lib/jekyll/tags/webmentions.rb,
lib/jekyll/tags/webmentions_js.rb,
lib/jekyll/tags/webmention_type.rb,
lib/jekyll/generators/compile_js.rb,
lib/jekyll/tags/webmentions_head.rb,
lib/jekyll/webmention_io/version.rb,
lib/jekyll/webmention_io/js_handler.rb,
lib/jekyll/generators/queue_webmentions.rb,
lib/jekyll/generators/gather_webmentions.rb,
lib/jekyll/webmention_io/webmention_item.rb

Defined Under Namespace

Classes: CompileJS, GatherWebmentions, JSHandler, JavaScriptFile, QueueWebmentions, WebmentionBookmarksTag, WebmentionCountTag, WebmentionHeadTag, WebmentionItem, WebmentionJSTag, WebmentionLikesTag, WebmentionLinksTag, WebmentionPostsTag, WebmentionRepliesTag, WebmentionRepostsTag, WebmentionRsvpsTag, WebmentionTag, WebmentionTypeTag, WebmentionsTag

Constant Summary collapse

EXCEPTIONS =
[
  SocketError, Timeout::Error,
  Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError,
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
  OpenSSL::SSL::SSLError,
].freeze
TIMEFRAMES =
{
  "last_week"  => "weekly",
  "last_month" => "monthly",
  "last_year"  => "yearly",
}.freeze
VERSION =
"3.3.1"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.api_suffix=(value) ⇒ Object (writeonly)

Sets the attribute api_suffix

Parameters:

  • value

    the value to set the attribute api_suffix to.



27
28
29
# File 'lib/jekyll/webmention_io.rb', line 27

def api_suffix=(value)
  @api_suffix = value
end

.cache_filesObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def cache_files
  @cache_files
end

.cache_folderObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def cache_folder
  @cache_folder
end

.configObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def config
  @config
end

.file_prefixObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def file_prefix
  @file_prefix
end

.jekyll_configObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def jekyll_config
  @jekyll_config
end

.js_handlerObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def js_handler
  @js_handler
end

.supported_templatesObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def supported_templates
  @supported_templates
end

.typesObject (readonly)

define simple getters and setters



25
26
27
# File 'lib/jekyll/webmention_io.rb', line 25

def types
  @types
end

Class Method Details

.api_path=(path) ⇒ Object

Setter



74
75
76
# File 'lib/jekyll/webmention_io.rb', line 74

def self.api_path=(path)
  @api_endpoint = "#{@api_url}/#{path}"
end

.bootstrap(site) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/jekyll/webmention_io.rb', line 50

def self.bootstrap(site)
  @site = site
  @jekyll_config = site.config
  @config = @jekyll_config["webmentions"] || {}

  # Set up the cache folder & files
  @cache_folder = site.in_source_dir(@config["cache_folder"] || ".jekyll-cache")
  Dir.mkdir(@cache_folder) unless File.exist?(@cache_folder)
  @file_prefix = ""
  @file_prefix = "webmention_io_" unless @cache_folder.include? "webmention"
  @cache_files = {
    "incoming" => cache_file("received.yml"),
    "outgoing" => cache_file("outgoing.yml"),
    "bad_uris" => cache_file("bad_uris.yml"),
    "lookups"  => cache_file("lookups.yml")
  }
  @cache_files.each_value do |file|
    dump_yaml(file) unless File.exist?(file)
  end

  @js_handler = WebmentionIO::JSHandler.new(site)
end

.cache_file(filename) ⇒ Object

Helpers



79
80
81
# File 'lib/jekyll/webmention_io.rb', line 79

def self.cache_file(filename)
  Jekyll.sanitized_path(@cache_folder, "#{@file_prefix}#{filename}")
end

.cache_lookup_dates(lookups) ⇒ Object



144
145
146
147
148
149
# File 'lib/jekyll/webmention_io.rb', line 144

def self.cache_lookup_dates(lookups)
  cache_file = get_cache_file_path "lookups"
  dump_yaml(cache_file, lookups)

  log "msg", "Lookups have been cached."
end

.cache_webmentions(which, webmentions) ⇒ Object



94
95
96
97
98
99
100
101
# File 'lib/jekyll/webmention_io.rb', line 94

def self.cache_webmentions(which, webmentions)
  if %w(incoming outgoing).include? which
    cache_file = get_cache_file_path which
    dump_yaml(cache_file, webmentions)

    log "msg", "#{which.capitalize} webmentions have been cached."
  end
end

.dump_yaml(file, data = {}) ⇒ Object

Utility Method Caches given data to memory and then proceeds to write data as YAML string into file path.

Returns nothing.



314
315
316
317
# File 'lib/jekyll/webmention_io.rb', line 314

def self.dump_yaml(file, data = {})
  @webmention_data_cache[file] = data
  File.open(file, "wb") { |f| f.puts YAML.dump(data) }
end

.gather_documents(site) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/jekyll/webmention_io.rb', line 103

def self.gather_documents(site)
  documents = site.posts.docs.clone

  if @config.dig("pages") == true
    log "info", "Including site pages."
    documents.concat site.pages.clone
  end

  collections = @config.dig("collections")
  if collections
    log "info", "Adding collections."
    site.collections.each do |name, collection|
      # skip _posts
      next if name == "posts"

      unless collections.is_a?(Array) && !collections.include?(name)
        documents.concat collection.docs.clone
      end
    end
  end

  return documents
end

.get_cache_file_path(key) ⇒ Object



83
84
85
# File 'lib/jekyll/webmention_io.rb', line 83

def self.get_cache_file_path(key)
  @cache_files[key] || false
end

.get_date_from_string(text) ⇒ Object

supported: daily, weekly, monthly, yearly, every X days|weeks|months|years



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/jekyll/webmention_io.rb', line 185

def self.get_date_from_string(text)
  today = Date.today
  pattern = /every\s(?:(\d+)\s)?(day|week|month|year)s?/
  matches = text.match(pattern)
  unless matches
    text = if text == "daily"
             "every 1 day"
           else
             "every 1 #{text.sub("ly", "")}"
           end
    matches = text.match(pattern)
  end
  n = matches[1] ? matches[1].to_i : 1
  unit = matches[2]
  # weeks aren't natively supported in Ruby
  if unit == "week"
    n *= 7
    unit = "day"
  end
  # dynamic method call
  return today.send "prev_#{unit}", n
end

.get_response(api_params) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/jekyll/webmention_io.rb', line 127

def self.get_response(api_params)
  api_params << @api_suffix
  url = "#{@api_endpoint}?#{api_params}"
  log "info", "Sending request to #{url}."
  source = get_uri_source(url)
  if source
    JSON.parse(source)
  else
    {}
  end
end

.get_template_contents(template) ⇒ Object



251
252
253
254
255
256
257
# File 'lib/jekyll/webmention_io.rb', line 251

def self.get_template_contents(template)
  template_file = template_file(template)
  @template_content_cache[template_file] ||= begin
    log "info", "Template file: #{template_file}"
    File.read(template_file)
  end
end

.get_timeframe_from_date(time) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/jekyll/webmention_io.rb', line 172

def self.get_timeframe_from_date(time)
  date = time.to_date
  TIMEFRAMES.each do |key, value|
    if date.to_date > get_date_from_string(value)
      timeframe = key
      break
    end
  end
  timeframe ||= "older"
  return timeframe
end

.get_uri_source(uri, redirect_limit = 10, original_uri = false) ⇒ Object

Connections



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/jekyll/webmention_io.rb', line 277

def self.get_uri_source(uri, redirect_limit = 10, original_uri = false)
  original_uri ||= uri
  return false unless uri_ok?(uri)

  if redirect_limit.positive?
    response = get_http_response(uri)
    case response
    when Net::HTTPSuccess then
      return response.body.force_encoding("UTF-8")
    when Net::HTTPRedirection then
      redirect_to = URI.parse(URI.encode(response["location"]))
      redirect_to = redirect_to.relative? ? "#{uri.scheme}://#{uri.host}" + redirect_to.to_s : redirect_to.to_s
      return get_uri_source(redirect_to, redirect_limit - 1, original_uri)
    else
      uri_is_not_ok(uri)
      return false
    end
  else
    log("warn", "too many redirects for #{original_uri}") if original_uri
    uri_is_not_ok(uri)
    return false
  end
end

.get_webmention_endpoint(uri) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/jekyll/webmention_io.rb', line 208

def self.get_webmention_endpoint(uri)
  # log "info", "Looking for webmention endpoint at #{uri}"
  begin
    endpoint = Webmention::Client.supports_webmention?(uri)
    unless endpoint
      log("info", "Could not find a webmention endpoint at #{uri}")
      uri_is_not_ok(uri)
    end
  rescue StandardError => e
    log "info", "Endpoint lookup failed for #{uri}: #{e.message}"
    uri_is_not_ok(uri)
    endpoint = false
  end
  endpoint
end

.html_templatesObject



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/jekyll/webmention_io.rb', line 259

def self.html_templates
  proofer = if @config['html_proofer'] == true
              ' data-proofer-ignore'
            else
              ''
            end
  @html_templates ||= begin
    templates = +"" # unfrozen String
    supported_templates.each do |template|
      templates << "<template style=\"display:none\" id=\"webmention-#{template}\"#{proofer}>"
      templates << get_template_contents(template)
      templates << "</template>"
    end
    templates
  end
end

.load_yaml(file) ⇒ Object

Utility Method Attempts to first load data cached in memory and then proceeds to safely parse given YAML file path and return data.

Returns empty hash if parsing fails to return data



324
325
326
# File 'lib/jekyll/webmention_io.rb', line 324

def self.load_yaml(file)
  @webmention_data_cache[file] || SafeYAML.load_file(file) || {}
end

.log(type, message) ⇒ Object



301
302
303
304
305
306
307
# File 'lib/jekyll/webmention_io.rb', line 301

def self.log(type, message)
  debug = !!@config.dig("debug")
  if debug || %w(error msg).include?(type)
    type = "info" if type == "msg"
    Jekyll.logger.method(type).call("#{@logger_prefix} #{message}")
  end
end

.post_should_be_throttled?(post, item_date, last_lookup) ⇒ Boolean

allowed throttles: last_week, last_month, last_year, older allowed values: daily, weekly, monthly, yearly, every X days|weeks|months|years

Returns:

  • (Boolean)


153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/jekyll/webmention_io.rb', line 153

def self.post_should_be_throttled?(post, item_date, last_lookup)
  throttles = @config.dig("throttle_lookups")
  if throttles && item_date && last_lookup
    age = get_timeframe_from_date(item_date)
    throttle = throttles.dig(age)
    if throttle && last_lookup >= get_date_from_string(throttle)
      log "info", "Throttling #{post.data["title"]} (Only checking it #{throttle})"
      return true
    end
  end
  return false
end

.read_cached_webmentions(which) ⇒ Object



87
88
89
90
91
92
# File 'lib/jekyll/webmention_io.rb', line 87

def self.read_cached_webmentions(which)
  return {} unless %w(incoming outgoing).include?(which)

  cache_file = get_cache_file_path which
  load_yaml(cache_file)
end

.read_lookup_datesObject



139
140
141
142
# File 'lib/jekyll/webmention_io.rb', line 139

def self.read_lookup_dates
  cache_file = get_cache_file_path "lookups"
  load_yaml(cache_file)
end

.template_file(template) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
# File 'lib/jekyll/webmention_io.rb', line 239

def self.template_file(template)
  @template_file_cache[template] ||= begin
    configured_template = @config.dig("templates", template)
    if configured_template
      log "info", "Using custom #{template} template from site source"
      @site.in_source_dir configured_template
    else
      File.expand_path("templates/#{template}.html", __dir__)
    end
  end
end

.uri_ok?(uri) ⇒ Boolean

Returns:

  • (Boolean)


364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/jekyll/webmention_io.rb', line 364

def self.uri_ok?(uri)
  uri = URI.parse(URI.encode(uri))
  now = Time.now.to_s
  bad_uris = load_yaml(@cache_files["bad_uris"])
  if bad_uris.key? uri.host
    last_checked = DateTime.parse(bad_uris[uri.host])
    cache_bad_uris_for = @config["cache_bad_uris_for"] || 1 # in days
    recheck_at = last_checked.next_day(cache_bad_uris_for).to_s
    return false if recheck_at > now
  end
  return true
end

.webmention(source, target, endpoint) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/jekyll/webmention_io.rb', line 224

def self.webmention(source, target, endpoint)
  log "info", "Sending webmention of #{target} in #{source}"
  # return `curl -s -i -d \"source=#{source}&target=#{target}\" -o /dev/null #{endpoint}`
  mention = Webmention::Client.send_mention(endpoint, source, target, true)
  case mention.response
  when Net::HTTPOK, Net::HTTPCreated, Net::HTTPAccepted
    log "info", "Webmention successful!"
    mention.response.body
  else
    log "info", mention.inspect
    log "info", "Webmention failed, but will remain queued for next time"
    false
  end
end