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 +4 -4
- data/lib/jekyll/activity_pub/activity.rb +2 -1
- data/lib/jekyll/activity_pub/helper.rb +12 -12
- data/lib/jekyll/activity_pub/link.rb +32 -0
- data/lib/jekyll/activity_pub/notifier.rb +15 -17
- data/lib/jekyll-activity-pub-absolute-assets.rb +29 -0
- data/lib/jekyll-activity-pub-assets-as-attachments.rb +15 -0
- data/lib/jekyll-activity-pub-fep-fffd-distributed-press.rb +82 -0
- data/lib/jekyll-activity-pub-link-iframes.rb +16 -0
- data/lib/jekyll-activity-pub-nokogiri.rb +16 -0
- metadata +10 -6
- data/lib/jekyll/activity_pub/cache.rb~ +0 -10
- data/lib/jekyll/activity_pub/tomsbtone.rb~ +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bbfcc9584215764c7097b8dbd857fba16fb9638676ef80a646dd77021f9b72c
|
4
|
+
data.tar.gz: a7d019c0613766a3c107e9aa24775032e1721348a4057d62424da9aa48e8340a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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 =
|
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(
|
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 =
|
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(
|
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 =
|
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
|
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:
|
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.
|
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.
|
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,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
|
-
|