jekyll-webmention_io 2.0.1

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,48 @@
1
+ module Jekyll
2
+ module Commands
3
+ class WebmentionCommand < Command
4
+ def self.init_with_program( prog )
5
+ prog.command(:webmention) do |c|
6
+ c.syntax 'webmention'
7
+ c.description 'Sends queued webmentions'
8
+
9
+ c.action { |args, options| process args, options }
10
+ end
11
+ end
12
+
13
+ def self.process( args=[], options={} )
14
+ cached_outgoing = WebmentionIO.get_cache_file_path 'outgoing'
15
+ cached_sent = WebmentionIO.get_cache_file_path 'sent'
16
+ if File.exists?(cached_outgoing)
17
+ if File.exists?(cached_sent)
18
+ sent = open(cached_sent) { |f| YAML.load(f) }
19
+ else
20
+ sent = {}
21
+ end # file exists (sent)
22
+ outgoing = open(cached_outgoing) { |f| YAML.load(f) }
23
+ outgoing.each_pair do |source, targets|
24
+ if ! sent[source] or ! sent[source].kind_of? Array
25
+ sent[source] = Array.new
26
+ end
27
+ targets.each do |target|
28
+ if target and ! sent[source].find_index( target )
29
+ if target.index( "//" ) == 0
30
+ target = "http:#{target}"
31
+ end
32
+ endpoint = WebmentionIO.get_webmention_endpoint( target )
33
+ if endpoint
34
+ endpoint.scan(/href="([^"]+)"/) do |endpoint_url|
35
+ endpoint_url = endpoint_url[0]
36
+ WebmentionIO.webmention( source, target, endpoint )
37
+ end
38
+ sent[source].push( target )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ File.open(cached_sent, 'w') { |f| YAML.dump(sent, f) }
44
+ end # file exists (outgoing)
45
+ end # def process
46
+ end # WebmentionCommand
47
+ end # Commands
48
+ end # Jekyll
@@ -0,0 +1,239 @@
1
+ # (c) Aaron Gustafson
2
+ # https://github.com/aarongustafson/jekyll-webmention_io
3
+ # Licence : MIT
4
+ #
5
+ # This generator gathers webmentions of your pages
6
+ #
7
+
8
+ module Jekyll
9
+ class GatherWebmentions < Generator
10
+
11
+ safe true
12
+ priority :high
13
+
14
+ def generate(site)
15
+ WebmentionIO.log 'info', 'Beginning to gather webmentions of your posts. This may take a while.'
16
+
17
+ WebmentionIO.set_api_endpoint('mentions')
18
+ # add an arbitrarily high perPage to trump pagination
19
+ WebmentionIO.set_api_suffix('&perPage=9999')
20
+
21
+ cache_file = WebmentionIO.get_cache_file_path 'incoming'
22
+ if File.exists?(cache_file)
23
+ @cached_webmentions = open(cache_file) { |f| YAML.load(f) }
24
+ else
25
+ @cached_webmentions = {}
26
+ end
27
+
28
+ if Jekyll::VERSION >= "3.0.0"
29
+ posts = site.posts.docs
30
+ else
31
+ posts = site.posts
32
+ end
33
+
34
+ # post Jekyll commit 0c0aea3
35
+ # https://github.com/jekyll/jekyll/commit/0c0aea3ad7d2605325d420a23d21729c5cf7cf88
36
+ if defined? site.find_converter_instance
37
+ @converter = site.find_converter_instance(::Jekyll::Converters::Markdown)
38
+ # Prior to Jekyll commit 0c0aea3
39
+ else
40
+ @converter = site.getConverterImpl(::Jekyll::Converters::Markdown)
41
+ end
42
+
43
+ posts.each do |post|
44
+ # Gather the URLs
45
+ targets = get_webmention_target_urls(site, post)
46
+
47
+ # execute the API
48
+ api_params = targets.collect { |v| "target[]=#{v}" }.join('&')
49
+ response = WebmentionIO.get_response(api_params)
50
+ # @webmention_io.log 'info', response.inspect
51
+
52
+ process_webmentions( post.url, response )
53
+ end # posts loop
54
+
55
+ File.open(cache_file, 'w') { |f| YAML.dump(@cached_webmentions, f) }
56
+
57
+ WebmentionIO.log 'info', 'Webmentions have been gathered and cached.'
58
+ end # generate
59
+
60
+ def get_webmention_target_urls(site, post)
61
+ targets = []
62
+ uri = "#{site.config['url']}#{post.url}"
63
+ targets.push( uri )
64
+
65
+ # Redirection?
66
+ redirected = false
67
+ if post.data.has_key? 'redirect_from'
68
+ redirected = uri.sub post.url, post.data['redirect_from']
69
+ targets.push( redirected )
70
+ end
71
+
72
+ # Domain changed?
73
+ if WebmentionIO.config.has_key? 'legacy_domains'
74
+ # WebmentionIO.log 'info', 'adding legacy URIs'
75
+ WebmentionIO.config['legacy_domains'].each do |domain|
76
+ legacy = uri.sub site.config['url'], domain
77
+ # WebmentionIO.log 'info', "adding URI #{legacy}"
78
+ targets.push(legacy)
79
+ end
80
+ end
81
+ return targets
82
+ end
83
+
84
+ def markdownify( string )
85
+ string = @converter.convert("#{string}")
86
+ if ! string.start_with?('<p')
87
+ string = string.sub(/^<[^>]+>/, '<p>').sub(/<\/[^>]+>$/, '</p>')
88
+ end
89
+ string.strip
90
+ end
91
+
92
+ def process_webmentions( post_uri, response )
93
+
94
+ # Get cached webmentions
95
+ if @cached_webmentions.has_key? post_uri
96
+ webmentions = @cached_webmentions[post_uri]
97
+ else
98
+ webmentions = {}
99
+ end
100
+
101
+ if response and response['links']
102
+
103
+ response['links'].reverse_each do |link|
104
+
105
+ uri = link['data']['url'] || link['source']
106
+
107
+ # set the source
108
+ source = false
109
+ if uri.include? 'twitter.com/'
110
+ source = 'twitter'
111
+ elsif uri.include? '/googleplus/'
112
+ source = 'googleplus'
113
+ end
114
+
115
+ # set an id
116
+ id = link['id'].to_s
117
+ if source == 'twitter' and ! uri.include? '#favorited-by'
118
+ id = URI(uri).path.split('/').last.to_s
119
+ end
120
+ if ! id
121
+ time = Time.now();
122
+ id = time.strftime('%s').to_s
123
+ end
124
+
125
+ # Do we already have it?
126
+ if webmentions.has_key? id
127
+ next
128
+ end
129
+
130
+ # Get the mentioned URI, stripping fragments and query strings
131
+ #target = URI::parse( link['target'] )
132
+ #target.fragment = target.query = nil
133
+ #target = target.to_s
134
+
135
+ pubdate = link['data']['published_ts']
136
+ if pubdate
137
+ pubdate = Time.at(pubdate)
138
+ elsif link['verified_date']
139
+ pubdate = Time.parse(link['verified_date'])
140
+ end
141
+ #the_date = pubdate.strftime('%s')
142
+
143
+ # Make sure we have the date
144
+ # if ! webmentions.has_key? the_date
145
+ # webmentions[the_date] = {}
146
+ # end
147
+
148
+ # Make sure we have the webmention
149
+ if ! webmentions.has_key? id
150
+
151
+ # Scaffold the webmention
152
+ webmention = {
153
+ 'id' => id,
154
+ 'url' => uri,
155
+ 'source' => source,
156
+ 'pubdate' => pubdate,
157
+ 'raw' => link
158
+ }
159
+
160
+ # Set the author
161
+ if link['data'].has_key? 'author'
162
+ webmention['author'] = link['data']['author']
163
+ end
164
+
165
+ # Set the type
166
+ type = link['activity']['type']
167
+ if ! type
168
+ if source == 'googleplus'
169
+ if uri.include? '/like/'
170
+ type = 'like'
171
+ elsif uri.include? '/repost/'
172
+ type = 'repost'
173
+ elsif uri.include? '/comment/'
174
+ type = 'reply'
175
+ else
176
+ type = 'link'
177
+ end
178
+ else
179
+ type = 'post'
180
+ end
181
+ end # if no type
182
+ webmention['type'] = type
183
+
184
+ # Posts
185
+ title = false
186
+ if type == 'post'
187
+
188
+ html_source = WebmentionIO.get_uri_source( uri )
189
+ if ! html_source
190
+ next
191
+ end
192
+
193
+ if ! html_source.valid_encoding?
194
+ html_source = html_source.encode('UTF-16be', :invalid=>:replace, :replace=>"?").encode('UTF-8')
195
+ end
196
+
197
+ # Check the `title` first
198
+ matches = /<title>(.*)<\/title>/.match( html_source )
199
+ if matches
200
+ title = matches[1].strip
201
+ else
202
+ # Fall back to the first `h1`
203
+ matches = /<h1>(.*)<\/h1>/.match( html_source )
204
+ if matches
205
+ title = matches[1].strip
206
+ else
207
+ # No title found
208
+ title = 'No title available'
209
+ end
210
+ end
211
+
212
+ # cleanup
213
+ title = title.gsub(%r{</?[^>]+?>}, '')
214
+ end # if no title
215
+ webmention['title'] = markdownify( title )
216
+
217
+ # Everything else
218
+ content = link['data']['content']
219
+ if type != 'post' && type != 'reply' && type != 'link'
220
+ content = link['activity']['sentence_html']
221
+ end
222
+ webmention['content'] = markdownify( content )
223
+
224
+ # Add it to the list
225
+ # @webmention_io.log 'info', webmention.inspect
226
+ webmentions[id] = webmention
227
+
228
+ end # if ID does not exist
229
+
230
+ end # each link
231
+
232
+ end # if response
233
+
234
+ @cached_webmentions[post_uri] = webmentions
235
+
236
+ end # process_webmentions
237
+
238
+ end
239
+ end
@@ -0,0 +1,50 @@
1
+ # (c) Aaron Gustafson
2
+ # https://github.com/aarongustafson/jekyll-webmention_io
3
+ # Licence : MIT
4
+ #
5
+ # This generator caches sites you mention so they can be mentioned
6
+ #
7
+
8
+ module Jekyll
9
+ class QueueWebmentions < Generator
10
+
11
+ safe true
12
+ priority :low
13
+
14
+ def generate(site)
15
+ WebmentionIO.log 'info', 'Beginning to gather webmentions you’ve made. This may take a while.'
16
+
17
+ webmentions = {}
18
+
19
+ if Jekyll::VERSION >= "3.0.0"
20
+ posts = site.posts.docs
21
+ else
22
+ posts = site.posts
23
+ end
24
+
25
+ posts.each do |post|
26
+ uri = "#{site.config['url']}#{post.url}"
27
+ webmentions[uri] = get_mentioned_uris(post)
28
+ end
29
+
30
+ cache_file = WebmentionIO.get_cache_file_path 'outgoing'
31
+ File.open(cache_file, 'w') { |f| YAML.dump(webmentions, f) }
32
+
33
+ WebmentionIO.log 'info', 'Webmentions have been gathered and cached.'
34
+ end
35
+
36
+ def get_mentioned_uris(post)
37
+ uris = []
38
+ if post.data['in_reply_to']
39
+ uris.push(post.data['in_reply_to'])
40
+ end
41
+ post.content.scan(/(?:https?:)?\/\/[^\s)#"]+/) do |match|
42
+ if ! uris.find_index( match )
43
+ uris.push(match)
44
+ end
45
+ end
46
+ return uris
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,132 @@
1
+ # (c) Aaron Gustafson
2
+ # https://github.com/aarongustafson/jekyll-webmention_io
3
+ # Licence : MIT
4
+ #
5
+ # Base webmention tag
6
+ #
7
+
8
+ require 'htmlbeautifier'
9
+
10
+ module Jekyll
11
+ using StringInflection
12
+ class WebmentionTag < Liquid::Tag
13
+
14
+ def initialize(tagName, text, tokens)
15
+ super
16
+ cache_file = WebmentionIO.get_cache_file_path 'incoming'
17
+ if File.exists?(cache_file)
18
+ @cached_webmentions = open(cache_file) { |f| YAML.load(f) }
19
+ else
20
+ @cached_webmentions = {}
21
+ end
22
+ end
23
+
24
+ def lookup(context, name)
25
+ lookup = context
26
+ name.split(".").each do |value|
27
+ lookup = lookup[value]
28
+ end
29
+ lookup
30
+ end
31
+
32
+ def set_template( template )
33
+ supported_templates = WebmentionIO.types + ['count', 'webmentions']
34
+
35
+ WebmentionIO.log 'error', "#{template} is not supported" if ! supported_templates.include? template
36
+
37
+ if WebmentionIO.config.has_key? 'templates' and WebmentionIO.config['templates'].has_key? template
38
+ # WebmentionIO.log 'info', "Using custom #{template} template"
39
+ template_file = WebmentionIO.config['templates'][template]
40
+ else
41
+ # WebmentionIO.log 'info', "Using default #{template} template"
42
+ template_file = File.join(File.dirname(File.expand_path(__FILE__)), "../../../templates/#{template}.html")
43
+ end
44
+
45
+ # WebmentionIO.log 'info', "Template file: #{template_file}"
46
+ handler = File.open(template_file, 'rb')
47
+ @template = handler.read
48
+ # WebmentionIO.log 'info', "template: #{@template}"
49
+ end
50
+
51
+ def set_data(data)
52
+ @data = { 'webmentions' => data }
53
+ end
54
+
55
+ def extract_type( type, webmentions )
56
+ # WebmentionIO.log 'info', "Looking for #{type}"
57
+ keep = {}
58
+ if ! WebmentionIO.types.include? type
59
+ WebmentionIO.log 'warn', "#{type} are not extractable"
60
+ else
61
+ type = type.to_singular
62
+ # WebmentionIO.log 'info', "Searching #{webmentions.length} webmentions for type==#{type}"
63
+ if webmentions.is_a? Hash
64
+ webmentions = webmentions.values
65
+ end
66
+ webmentions.each do |webmention|
67
+ keep[webmention['id']] = webmention if webmention['type'] == type
68
+ end
69
+ end
70
+ keep
71
+ end
72
+
73
+ def sort_webmentions( webmentions )
74
+ return webmentions.sort_by { |webmention| webmention['pubdate'].to_i }
75
+ end
76
+
77
+ def render(context)
78
+ output = super
79
+
80
+ # Get the URI
81
+ args = @text.split(/\s+/).map(&:strip)
82
+ uri = args.shift
83
+ uri = lookup(context, uri)
84
+
85
+ if @cached_webmentions.has_key? uri
86
+ all_webmentions = @cached_webmentions[uri].clone
87
+ # WebmentionIO.log 'info', "#{all_webmentions.length} total webmentions for #{uri}"
88
+ if args.length > 0
89
+ # WebmentionIO.log 'info', "Requesting only #{args.inspect}"
90
+ webmentions = {}
91
+ args.each do |type|
92
+ extracted = extract_type( type, all_webmentions )
93
+ # WebmentionIO.log 'info', "Merging in #{extracted.length} #{type}"
94
+ webmentions = webmentions.merge( extracted )
95
+ end
96
+ else
97
+ # WebmentionIO.log 'info', 'Grabbing all webmentions'
98
+ webmentions = all_webmentions
99
+ end
100
+
101
+ if webmentions.is_a? Hash
102
+ webmentions = webmentions.values
103
+ end
104
+
105
+ webmentions = sort_webmentions( webmentions )
106
+
107
+ set_data( webmentions )
108
+ end
109
+
110
+ args = nil
111
+
112
+ if @template and @data
113
+ # WebmentionIO.log 'info', "Preparing to render\n\n#{@data.inspect}\n\ninto\n\n#{@template}"
114
+ template = Liquid::Template.parse(@template, :error_mode => :strict)
115
+ html = template.render(@data, { strict_variables: true, strict_filters: true })
116
+ template.errors.each do |error|
117
+ WebmentionIO.log 'error', error
118
+ end
119
+ # Clean up the output
120
+ HtmlBeautifier.beautify html.each_line.reject{|x| x.strip == ""}.join
121
+ else
122
+ if ! @template
123
+ WebmentionIO.log 'warn', "#{self.class} No template provided"
124
+ end
125
+ if ! @data
126
+ WebmentionIO.log 'warn', "#{self.class} No data provided"
127
+ end
128
+ ""
129
+ end
130
+ end
131
+ end
132
+ end