jekyll-activity-pub 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b01de6dccd0e03a7936d3c107f755e7f5d663e8d8e9622007f57a0a2ff0228d0
4
- data.tar.gz: 4477807387c5b864b337299662f5c564f1c46407d47c213e27b264971cd3b823
3
+ metadata.gz: 9bbfcc9584215764c7097b8dbd857fba16fb9638676ef80a646dd77021f9b72c
4
+ data.tar.gz: a7d019c0613766a3c107e9aa24775032e1721348a4057d62424da9aa48e8340a
5
5
  SHA512:
6
- metadata.gz: cda61088586cd05fa26155db048fd66845a2c32e8a66c05c51d7e8624a49048e0528cb1ccb24ea1ad21ca2d71002be6b3696a12e2ad013d2d3889328ca73fd58
7
- data.tar.gz: 7fa8b2e1c900c0e65a82b2cf96bf408574a4e40167cf9e0d0317aadcfabf7f17c2f3e05d609e4dfa3a704740b176c10adb91c97541ba4160228e09afac2583bd
6
+ metadata.gz: b9556ee3d0b03bb45c9dd051c24edb59915056d44886d56b15b7c6a4cdfed1520e28f3aa955769e8b5d3592f71dd704ab9328b148f22ddd77f6f9be577e666c0
7
+ data.tar.gz: 89bc2152540aeaeec52c5363afa646d9318a1cf9c34f2d732fcc705e785b359dce620ef98e2175b7f0c104ba86d74600b0a22fb4a8990be900d9d2c2fa74a077
@@ -46,7 +46,8 @@ module Jekyll
46
46
  }
47
47
  ],
48
48
  'type' => 'Note',
49
- 'id' => absolute_url(doc.url),
49
+ 'id' => absolute_url(url),
50
+ 'url' => absolute_url(doc.url),
50
51
  'summary' => summary,
51
52
  'published' => (doc.data['created_at'] || doc.date).xmlschema,
52
53
  'updated' => doc.data['last_modified_at']&.xmlschema,
@@ -66,6 +66,18 @@ module Jekyll
66
66
 
67
67
  alias type hook_owner
68
68
 
69
+ # Detects locale
70
+ #
71
+ # @return [String]
72
+ def locale
73
+ return @locale if defined? @locale
74
+
75
+ @locale = site.config['locale']
76
+ @locale ||= ENV['LANG']&.split('.', 2)&.first
77
+
78
+ @locale = @locale&.tr('_', '-')
79
+ end
80
+
69
81
  private
70
82
 
71
83
  # Is Liquid on strict mode?
@@ -137,18 +149,6 @@ module Jekyll
137
149
  File.exist?(cname_file)
138
150
  end
139
151
 
140
- # Detects locale
141
- #
142
- # @return [String]
143
- def locale
144
- return @locale if defined? @locale
145
-
146
- @locale = site.config['locale']
147
- @locale ||= ENV['LANG']&.split('.', 2)&.first
148
-
149
- @locale = @locale&.tr('_', '-')
150
- end
151
-
152
152
  # Finds the value of a text field amongst options
153
153
  #
154
154
  # @params :hash [Hash] The haystack
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ module Jekyll
6
+ module ActivityPub
7
+ # Represents a Link
8
+ class Link
9
+ include Helper
10
+
11
+ attr_reader :data
12
+
13
+ # Initialize with default data
14
+ #
15
+ # @param :site [Jekyll::Site]
16
+ # @param :href [String]
17
+ # @param :rel [String]
18
+ # @param :media_type [String]
19
+ def initialize(site, href, rel = nil, media_type = nil)
20
+ @site = site
21
+ @data = {
22
+ 'type' => 'Link',
23
+ 'mediaType' => media_type,
24
+ 'href' => href,
25
+ 'rel' => rel
26
+ }
27
+
28
+ trigger_hooks :post_init
29
+ end
30
+ end
31
+ end
32
+ end
@@ -4,6 +4,8 @@ require 'pathname'
4
4
  require 'httparty'
5
5
  require 'distributed_press/version'
6
6
  require 'distributed_press/v1/social/client'
7
+ require 'distributed_press/v1/social/inbox'
8
+ require 'distributed_press/v1/social/outbox'
7
9
  require_relative 'errors'
8
10
  require_relative 'create'
9
11
  require_relative 'update'
@@ -118,21 +120,10 @@ module Jekyll
118
120
  raise NotificationError, "Public key at #{public_key_url} differs from local version"
119
121
  end
120
122
 
121
- base_endpoint = "/v1/#{actor}"
122
- outbox_endpoint = "#{base_endpoint}/outbox"
123
123
  actor_object = object_for(site.in_dest_dir(actor_url.sub(site.config['url'], '')))
124
- # TODO: Move to API client
125
- inbox_body = {
126
- 'actorUrl' => actor_url,
127
- 'publicKeyId' => public_key_url,
128
- 'keypair' => {
129
- 'publicKeyPem' => client.public_key.public_to_pem,
130
- 'privateKeyPem' => client.private_key.export
131
- }
132
- }
133
124
 
134
125
  # Create inbox
135
- unless (response = client.post(endpoint: base_endpoint, body: inbox_body)).ok?
126
+ unless (response = inbox.create(actor_url)).ok?
136
127
  raise NotificationError, "Couldn't create inbox (#{response.code}: #{response.message})"
137
128
  end
138
129
 
@@ -140,7 +131,7 @@ module Jekyll
140
131
  data['notifications'].reject do |object_url, _|
141
132
  done? object_url
142
133
  end.each do |object_url, status|
143
- process_object(outbox_endpoint, actor_object, object_for(object_url), status)
134
+ process_object(actor_object, object_for(object_url), status)
144
135
  end
145
136
 
146
137
  # Update actor profile
@@ -148,7 +139,7 @@ module Jekyll
148
139
  Jekyll.logger.debug 'ActivityPub:', 'Updating Actor profile'
149
140
  actor_update = Jekyll::ActivityPub::Update.new(site, actor_object, actor_object)
150
141
 
151
- unless (response = client.post(endpoint: outbox_endpoint, body: actor_update)).ok?
142
+ unless (response = outbox.post(activity: actor_update)).ok?
152
143
  raise NotificationError, "Couldn't update actor (#{response.code}: #{response.message})"
153
144
  end
154
145
  end
@@ -311,6 +302,14 @@ module Jekyll
311
302
  )
312
303
  end
313
304
 
305
+ def inbox
306
+ @@inbox ||= DistributedPress::V1::Social::Inbox.new(client: client, actor: actor)
307
+ end
308
+
309
+ def outbox
310
+ @@outbox ||= DistributedPress::V1::Social::Outbox.new(client: client, actor: actor)
311
+ end
312
+
314
313
  # Run action
315
314
  #
316
315
  # @param :path [String]
@@ -337,16 +336,15 @@ module Jekyll
337
336
 
338
337
  # Turns an object into an activity and notifies outbox
339
338
  #
340
- # @param :endpoint [String]
341
339
  # @param :actor [PseudoObject]
342
340
  # @param :object [PseudoObject]
343
341
  # @param :status [Hash]
344
342
  # @return [nil]
345
- def process_object(endpoint, actor, object, status)
343
+ def process_object(actor, object, status)
346
344
  action = status['action']
347
345
  activity = Object.const_get("Jekyll::ActivityPub::#{action.capitalize}").new(site, actor, object)
348
346
 
349
- if (response = client.post(endpoint: endpoint, body: activity)).ok?
347
+ if (response = outbox.post(activity: activity)).ok?
350
348
  status['action'] = 'done'
351
349
  status["#{action}d_at"] = Time.now.to_i
352
350
  else
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jekyll-activity-pub-nokogiri'
4
+
5
+ # Modifies the Activity contents so any element with a src attribute
6
+ # uses absolute URLs
7
+ Jekyll::Hooks.register :activity, :post_init, priority: 35 do |activity|
8
+ case activity
9
+ when Jekyll::ActivityPub::Activity
10
+ url = Addressable::URI.parse(activity.data['id'])
11
+
12
+ activity.data['content'].css('[src]').each do |img|
13
+ uri = Addressable::URI.parse(img['src'])
14
+
15
+ next unless uri.relative?
16
+
17
+ raise "Can't convert relative paths yet" if uri.path.start_with? '..'
18
+
19
+ uri.path = "/#{uri.path}" unless uri.path.start_with? '/'
20
+
21
+ uri.hostname = url.hostname
22
+ uri.scheme = url.scheme
23
+
24
+ img['src'] = uri.to_s
25
+ rescue Exception => e
26
+ Jekyll.logger.error 'ActivityPub:', "#{img['src']}: #{e}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jekyll-activity-pub-absolute-assets'
4
+
5
+ # Convert all embedded images, videos and audios into attachments. Note
6
+ # Mastodon only supports four attachments per post.
7
+ #
8
+ # The hook run after absolute assets.
9
+ Jekyll::Hooks.register :activity, :post_init, priority: 34 do |activity|
10
+ activity.data['content'].css('img[src],video[src],audio[src]').each do |element|
11
+ src = Addressable::URI.parse(element['src'])
12
+
13
+ activity.data['attachment'] << Jekyll::ActivityPub::Document.new(activity.site, src.path, element['alt'].to_s)
14
+ end
15
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Implement FEP-fffd Proxy Objects for Distributed Press by creating new
4
+ # activities and actors under supported protocols/schemes.
5
+ #
6
+ # Priority is low so it's the last thing we do. If you need to modify
7
+ # the original activities after this plugins runs, use priority 0.
8
+ #
9
+ # The hook runs on pre render so we can add the extra pages.
10
+ Jekyll::Hooks.register :site, :pre_render, priority: 1 do |site|
11
+ require 'jekyll/activity_pub/link'
12
+
13
+ # Changes the URLs from a base URL to have a different scheme
14
+ #
15
+ # @param object [Any]
16
+ # @param base_url [String]
17
+ # @param scheme [String]
18
+ def convert_uris(object, base_url, scheme)
19
+ case object
20
+ when Hash
21
+ object.transform_values do |value|
22
+ convert_uris(value, base_url, scheme)
23
+ end
24
+ when Array
25
+ object.map do |value|
26
+ convert_uris(value, base_url, scheme)
27
+ end
28
+ when String
29
+ if object.start_with? base_url
30
+ Addressable::URI.parse(object).tap do |uri|
31
+ uri.scheme = scheme
32
+ end.to_s
33
+ else
34
+ object
35
+ end
36
+ when Nokogiri::HTML5::DocumentFragment
37
+ object.dup.tap do |html|
38
+ html.css('[src]').each do |element|
39
+ element['src'] = convert_uris(element['src'], base_url, scheme)
40
+ end
41
+ end
42
+ when Jekyll::ActivityPub::Helper
43
+ convert_uris(object.data, base_url, scheme)
44
+ else
45
+ object
46
+ end
47
+ end
48
+
49
+ site.pages.each do |page|
50
+ case page
51
+ when Jekyll::ActivityPub::Activity, Jekyll::ActivityPub::Actor
52
+ site = page.site
53
+ uri = Addressable::URI.parse page.data['id']
54
+ url = [ Jekyll::ActivityPub::Link.new(site, page.data['url'], 'canonical', 'text/html') ]
55
+
56
+ %w[https ipns hyper bittorrent].each do |protocol|
57
+ uri.scheme = protocol
58
+ url <<
59
+ Jekyll::ActivityPub::Link.new(site, uri.to_s, 'alternate', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"')
60
+ url <<
61
+ Jekyll::ActivityPub::Link.new(site, uri.to_s, 'alternate', 'application/activity+json')
62
+ end
63
+
64
+ %w[ipns hyper bittorrent].each do |protocol|
65
+ json = convert_uris(page.data, site.config['url'], protocol).reject do |_, v|
66
+ v.nil? || (v.respond_to?(:empty?) && v.empty?)
67
+ end
68
+
69
+ uri = Addressable::URI.parse(json['id'])
70
+ uri.path = uri.path.sub('.jsonld', ".#{protocol}.jsonld")
71
+ json['id'] = uri.to_s
72
+ path = uri.path.sub(%r{\A/}, '')
73
+
74
+ Jekyll::PageWithoutAFile.new(site, site.source, File.dirname(path), File.basename(path)).tap do |alternate|
75
+ site.pages << alternate
76
+
77
+ alternate.content = json.to_json
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Converts iframes to links. Priority is higher than high.
4
+ Jekyll::Hooks.register :activity, :post_init, priority: 31 do |activity|
5
+ activity.data['content'].tap do |html|
6
+ html.css('iframe[src]').each do |iframe|
7
+ ::Nokogiri::XML::Node.new('p', html) do |p|
8
+ ::Nokogiri::XML::Node.new('a', p) do |a|
9
+ a['href'] = iframe['src']
10
+ iframe.replace p
11
+ p << a
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ # Parses the Activity contents as HTML5 so we can modify it later.
6
+ # Priority is highest so we always have the parsed contents.
7
+ Jekyll::Hooks.register :activity, :post_init, priority: 100 do |activity|
8
+ case activity.data['content']
9
+ when String
10
+ Jekyll.logger.debug 'ActivityPub:', "Parsing content for #{activity.data['id']}"
11
+
12
+ activity.data['content'] =
13
+ activity.data['contentMap'][activity.locale] =
14
+ ::Nokogiri::HTML5.fragment(activity.data['content'])
15
+ end
16
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-activity-pub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sutty
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-05 00:00:00.000000000 Z
11
+ date: 2024-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: distributed-press-api-client
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.0rc4
19
+ version: 0.4.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.0rc4
26
+ version: 0.4.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jekyll
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -139,11 +139,15 @@ extra_rdoc_files:
139
139
  files:
140
140
  - LICENSE.txt
141
141
  - README.md
142
+ - lib/jekyll-activity-pub-absolute-assets.rb
143
+ - lib/jekyll-activity-pub-assets-as-attachments.rb
144
+ - lib/jekyll-activity-pub-fep-fffd-distributed-press.rb
145
+ - lib/jekyll-activity-pub-link-iframes.rb
146
+ - lib/jekyll-activity-pub-nokogiri.rb
142
147
  - lib/jekyll-activity-pub.rb
143
148
  - lib/jekyll/activity_pub.rb
144
149
  - lib/jekyll/activity_pub/activity.rb
145
150
  - lib/jekyll/activity_pub/actor.rb
146
- - lib/jekyll/activity_pub/cache.rb~
147
151
  - lib/jekyll/activity_pub/commands.rb
148
152
  - lib/jekyll/activity_pub/create.rb
149
153
  - lib/jekyll/activity_pub/delete.rb
@@ -155,6 +159,7 @@ files:
155
159
  - lib/jekyll/activity_pub/image.rb
156
160
  - lib/jekyll/activity_pub/instance_v1.rb
157
161
  - lib/jekyll/activity_pub/instance_v2.rb
162
+ - lib/jekyll/activity_pub/link.rb
158
163
  - lib/jekyll/activity_pub/nodeinfo.rb
159
164
  - lib/jekyll/activity_pub/nodeinfo_20.rb
160
165
  - lib/jekyll/activity_pub/nodeinfo_21.rb
@@ -164,7 +169,6 @@ files:
164
169
  - lib/jekyll/activity_pub/outbox.rb
165
170
  - lib/jekyll/activity_pub/property_value.rb
166
171
  - lib/jekyll/activity_pub/public_key.rb
167
- - lib/jekyll/activity_pub/tomsbtone.rb~
168
172
  - lib/jekyll/activity_pub/update.rb
169
173
  - lib/jekyll/activity_pub/version.rb
170
174
  - lib/jekyll/activity_pub/webfinger.rb
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jekyll/cache'
4
-
5
- module Jekyll
6
- module ActivityPub
7
- class Cache < Jekyll::Cache
8
- end
9
- end
10
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'jekyll/page'
4
- require_relative 'helper'
5
-
6
- module Jekyll
7
- module ActivityPub
8
- # Represents a removed activity
9
- class Tombstone < Jekyll::Page
10
- include Helper
11
-
12
- # @param :object_id [String]
13
- attr_reader :object_id
14
-
15
- # Initialize with default data
16
- #
17
- # @param :site [Jekyll::Site]
18
- # @param :object_id [String]
19
- def initialize(site, object_id, )
20
- @context = StubContext.new(registers: { site: site })
21
- @object_id = object_id
22
-
23
- dest = Pathname.new(object.destination(site.dest)).relative_path_from(site.dest)
24
- name = 'tombstone.jsonld'
25
- dir = File.join(
26
- File.dirname(dest),
27
- File.basename(dest, '.jsonld')
28
- )
29
-
30
- super(site, '', dir, name)
31
-
32
- trigger_hooks :post_init
33
- end
34
-
35
- def read_yaml(*)
36
- @data = {
37
- '@context' => 'https://www.w3.org/ns/activitystreams',
38
- 'type' => 'Tomsbtone',
39
- 'id' => absolute_url(object_id),
40
- 'published' => object.data[DATE_ATTRIBUTE],
41
- 'to' => object.data['to'],
42
- 'cc' => object.data['cc'],
43
- 'object' => object.data,
44
- 'inReplyTo' => object.data['in_reply_to']
45
- }
46
- end
47
-
48
- # @return [Time]
49
- def date
50
- @date ||= Time.parse(object.data[DATE_ATTRIBUTE])
51
- end
52
-
53
- private
54
-
55
- def type
56
- @type ||= self.class.name.split('::').last
57
- end
58
- end
59
- end
60
- end
61
-