jekyll-activitypub-static 0.7.6 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49162e1c4e1cb17333e5d10c9b68e68e498076c2caa9785b2f0c3311b1d9e47b
4
- data.tar.gz: d458b1ad1f0ddcf392ce2dae23943019983bea2bf7bdc7c7a534cb4a40f6753b
3
+ metadata.gz: '007741693ce63e45f543662e7130a98a1e4ccf9ac738250d6c074d9a50677d48'
4
+ data.tar.gz: 1573599c4e182de63d2072da417d638456716a137529bdd658019a96f83a7584
5
5
  SHA512:
6
- metadata.gz: 4b8aaa86e67f9e8dde9abc80f9876989fa9d236a766a34725a61749acb64658b2850669a560ad72b7cc282549509bacfad3a2f1cb42e7fb9a1b544ed1184af20
7
- data.tar.gz: 5fc88d809d155b46781322c3863da80e54eb51addb1a25c57ef7bf0f5f65c67a8b49b0c40b9bba4b4699fd1d5031c86603cf5d84f038f2ceabc46bac941476d1
6
+ metadata.gz: eac0999198d787cd640863823f0863b9618e17e6476a0dc3f100774940978f88f9d4f7619f65b5540d4a649112dafe4fbca810506d0b9d9b164bfd9b07d2b037
7
+ data.tar.gz: 23b124c60df634ecaf9be0c507e4d7ab5773ca97d14771a5ae9278e50079c6b226ac4c099aa086745610456d83e5d7c30ecc0446ed5532932efbee77a7a67a85
data/README.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  A Jekyll plugin that generates a static ActivityPoll feed, the read-only polling subset of ActivityPub defined by FEP-b06c.
4
4
 
5
- ## Install
5
+ ## Installation
6
+
7
+ Add this to the `Gemfile` of your Jekyll site:
8
+
9
+ ```Gemfile
10
+ gem "jekyll-activitypub-static"
11
+ ```
6
12
 
7
13
  To build from source:
8
14
 
@@ -13,19 +19,113 @@ gem install ./jekyll-activitypub-static-*.gem
13
19
 
14
20
  ## Usage
15
21
 
16
- Add this to the `Gemfile` of your Jekyll site:
22
+ Add this to the `_config.yml` for your site:
17
23
 
18
- ```Gemfile
19
- gem "jekyll-activitypub-static"
24
+ ```yaml
25
+ plugins:
26
+ - jekyll-activitypub-static
20
27
  ```
21
28
 
22
- Then, add this to the `_config.yml` for your site:
29
+ ## Configuration
30
+
31
+ The plugin uses your site's standard `url`, `author`, and `description`
32
+ settings, plus the `activitypub` configuration block.
23
33
 
24
34
  ```yaml
25
- plugins:
26
- - jekyll-activitypub-static
35
+ url: "https://example.com"
36
+ author: "Example Author"
37
+ description: "A short description of the site"
38
+
39
+ activitypub:
40
+ output_path: "activitypub"
41
+ preferred_username: "example"
42
+ summary_property: "description"
43
+ note_max_characters: 500
44
+ update_interval: "P1D"
45
+ ```
46
+
47
+ Available `activitypub` options:
48
+
49
+ - `output_path`: directory for generated ActivityPub files. Defaults to
50
+ `activitypub`.
51
+ - `preferred_username`: username for the generated actor and WebFinger
52
+ document. Defaults to the host name from `url`. The WebFinger account ID is
53
+ `preferred_username` at the domain from `url`, like `example@example.com`.
54
+ - `summary_property`: post front matter property to use for generated Article
55
+ summaries. Defaults to `description`; when the property is missing or empty,
56
+ the plugin uses the rendered post excerpt.
57
+ - `note_max_characters`: maximum plain-text character count for generated
58
+ Note objects. Defaults to `500`.
59
+ - `update_interval`: ActivityPoll polling interval for the generated Actor
60
+ object, as an ISO 8601 duration. Defaults to `P1D`.
61
+
62
+ ## Articles and Notes
63
+
64
+ Posts are generated as Activity Streams `Article` objects by default.
65
+
66
+ Short, untitled posts without an explicit summary are generated as
67
+ Activity Streams `Note` objects. `Note` is the native Activity Streams object
68
+ type used by Mastodon for statuses. To make a post eligible for `Note`
69
+ generation, set an explicit blank title:
70
+
71
+ ```yaml
72
+ ---
73
+ title: ""
74
+ ---
75
+ ```
76
+
77
+ Jekyll generates titles from post filenames when `title` is omitted, so an
78
+ omitted title is treated as a normal title. A blank-titled post is generated as
79
+ a `Note` when it has no configured summary property, has one rendered paragraph,
80
+ and its plain-text content is no longer than `activitypub.note_max_characters`.
81
+
82
+ Blank-titled posts may need layout support on home pages, archive pages, or
83
+ post lists. Use the excerpt, date, or another fallback when displaying links to
84
+ untitled posts.
85
+
86
+ ## Layouts
87
+
88
+ To link from HTML pages to their ActivityPub representation, add this to the
89
+ `<head>` element of your layouts:
90
+
91
+ ```html
92
+ {% if page.activitypub_url %}
93
+ <link rel="alternate" type="application/activity+json" href="{{ page.activitypub_url }}">
94
+ {% endif %}
27
95
  ```
28
96
 
97
+ The plugin sets `page.activitypub_url` before rendering posts and the site
98
+ index. For posts, the URL points to the generated JSON-LD Article or Note
99
+ object. For the site index, the URL points to the generated Actor object.
100
+
101
+ ## Generated Files
102
+
103
+ The plugin generates these static files:
104
+
105
+ - `actor.jsonld`
106
+ - `.well-known/webfinger`
107
+ - `activitypub/inbox.jsonld`
108
+ - `activitypub/outbox.jsonld`
109
+ - `activitypub/outbox/page-*.jsonld`
110
+ - `activitypub/posts/*.jsonld`
111
+ - `activitypub/activities/create-*.jsonld`
112
+
113
+ The `activitypub` path changes when `activitypub.output_path` is configured.
114
+
115
+ ### WebFinger
116
+
117
+ The WebFinger document is generated at `.well-known/webfinger` and uses an
118
+ account ID in the form `preferred_username` at the site domain. For example,
119
+ with `url: "https://example.com"` and `preferred_username: "evan"`, the
120
+ account ID is `evan@example.com`.
121
+
122
+ WebFinger discovery only works when the generated site is served from the root
123
+ of its domain, because clients request `https://example.com/.well-known/webfinger`.
124
+ Sites served from a subdirectory cannot provide a domain-level WebFinger
125
+ endpoint with this plugin alone.
126
+
127
+ ## Example Site
128
+
29
129
  The `example-site` directory has a minimal example site (thus the name). You can build and run it with these commands:
30
130
 
31
131
  ```sh
@@ -30,21 +30,16 @@ module Jekyll
30
30
  generate_webfinger(site)
31
31
  generate_actor(site)
32
32
  generate_inbox(site)
33
- generate_activities(site)
34
- generate_outbox_pages(site)
35
- generate_outbox(site)
36
33
  end
37
34
 
38
35
  def generate_webfinger(site)
39
36
  Jekyll.logger.info LOG_TAG, "Generating .well-known/webfinger"
40
37
 
41
38
  url = site.config["url"]
42
- host = URI(url).host
43
- username = preferred_username(site)
44
39
  actor_url = "#{url}/actor.jsonld"
45
40
 
46
41
  webfinger = {
47
- "subject" => "acct:#{username}@#{host}",
42
+ "subject" => "acct:#{webfinger_address(site)}",
48
43
  "links" => [
49
44
  {
50
45
  "rel" => "self",
@@ -103,14 +98,21 @@ module Jekyll
103
98
  article = {
104
99
  "@context" => "https://www.w3.org/ns/activitystreams",
105
100
  "id" => article_id,
106
- "type" => "Article",
107
- "name" => post.data["title"],
101
+ "type" => post_type(site, post),
108
102
  "content" => post.content,
103
+ "summary" => article_summary(site, post),
109
104
  "published" => post.date.iso8601,
110
105
  "attributedTo" => "#{url}/actor.jsonld",
106
+ "url" => {
107
+ "type" => "Link",
108
+ "mediaType" => "text/html",
109
+ "href" => "#{url}#{post.url}"
110
+ },
111
111
  "to" => "as:Public"
112
112
  }
113
113
 
114
+ article["name"] = post.data["title"] unless post.data["title"].to_s.strip.empty?
115
+
114
116
  site.static_files << JsonStaticFile.new(site, output_dir, filename, article)
115
117
  end
116
118
  end
@@ -137,12 +139,13 @@ module Jekyll
137
139
  "published" => post.date.iso8601,
138
140
  "object" => {
139
141
  "id" => article_id,
140
- "type" => "Article",
141
- "name" => post.data["title"]
142
+ "type" => post_type(site, post)
142
143
  },
143
144
  "to" => "as:Public"
144
145
  }
145
146
 
147
+ activity["object"]["name"] = post.data["title"] unless post.data["title"].to_s.strip.empty?
148
+
146
149
  site.static_files << JsonStaticFile.new(site, output_dir, filename, activity)
147
150
  Jekyll.logger.info LOG_TAG, "Wrote activity to #{path}"
148
151
  end
@@ -221,22 +224,31 @@ module Jekyll
221
224
  url = site.config["url"]
222
225
  summary = site.config["description"]
223
226
  output_path = site.config.dig("activitypub", "output_path") || "activitypub"
227
+ update_interval = site.config.dig("activitypub", "update_interval") || "P1D"
224
228
 
225
229
  {
226
230
  "@context": [
227
231
  "https://www.w3.org/ns/activitystreams",
228
232
  "https://purl.archive.org/miscellany/1.0",
233
+ "https://purl.archive.org/socialweb/webfinger",
229
234
  "https://w3id.org/fep/b06c"
230
235
  ],
231
236
  "type": "Person",
232
237
  "id": "#{url}/actor.jsonld",
233
238
  "pollOnly": true,
239
+ "updateInterval": update_interval,
234
240
  "name": name(site),
235
241
  "preferredUsername": preferred_username(site),
236
242
  "summary": (summary unless summary.to_s.strip.empty?),
237
243
  "inbox": "#{url}/#{output_path}/inbox.jsonld",
238
244
  "outbox": "#{url}/#{output_path}/outbox.jsonld",
239
245
  "attributedTo": "#{url}/actor.jsonld",
246
+ "url" => {
247
+ "type" => "Link",
248
+ "mediaType" => "text/html",
249
+ "href" => homepage_url(site)
250
+ },
251
+ "webfinger" => webfinger_address(site),
240
252
  "cc": "as:Public"
241
253
  }
242
254
  end
@@ -276,10 +288,83 @@ module Jekyll
276
288
  page["prev"] = "#{url}/#{output_path}/outbox/page-#{page_number - 1}.jsonld" if page_number > 1
277
289
  page
278
290
  end
291
+
292
+ def article_summary(site, post)
293
+ property = site.config.dig("activitypub", "summary_property") || "description"
294
+ explicit = post.data[property]
295
+ return explicit unless explicit.to_s.strip.empty?
296
+
297
+ excerpt = post.data["excerpt"]
298
+ excerpt.output
299
+ end
300
+
301
+ def note?(site, post)
302
+ title = post.data["title"]
303
+ return false unless title.to_s.strip.empty?
304
+
305
+ summary = explicit_summary(site, post)
306
+ return false unless summary.to_s.strip.empty?
307
+
308
+ return false unless paragraph_count(post.content) == 1
309
+
310
+ plain_text(post.content).length <= note_max_characters(site)
311
+ end
312
+
313
+ def post_type(site, post)
314
+ note?(site, post) ? "Note" : "Article"
315
+ end
316
+
317
+ def explicit_summary(site, post)
318
+ property = site.config.dig("activitypub", "summary_property") || "description"
319
+ post.data[property]
320
+ end
321
+
322
+ def note_max_characters(site)
323
+ site.config.dig("activitypub", "note_max_characters") || 500
324
+ end
325
+
326
+ def paragraph_count(content)
327
+ content.scan(/<p\b[^>]*>/).length
328
+ end
329
+
330
+ def plain_text(content)
331
+ content.gsub(/<[^>]*>/, "")
332
+ end
333
+
334
+ def homepage_url(site)
335
+ url = site.config["url"]
336
+ url.end_with?("/") ? url : "#{url}/"
337
+ end
338
+
339
+ def webfinger_address(site)
340
+ url = site.config["url"]
341
+ host = URI(url).host
342
+ "#{preferred_username(site)}@#{host}"
343
+ end
279
344
  end
280
345
  end
281
346
  end
282
347
 
283
348
  Jekyll::Hooks.register :site, :post_render do |site|
284
- Jekyll::ActivityPubStatic::Generator.new(site.config).generate_articles(site)
349
+ generator = Jekyll::ActivityPubStatic::Generator.new(site.config)
350
+
351
+ generator.generate_articles(site)
352
+ generator.generate_activities(site)
353
+ generator.generate_outbox_pages(site)
354
+ generator.generate_outbox(site)
355
+ end
356
+
357
+ Jekyll::Hooks.register :posts, :pre_render do |post|
358
+ site = post.site
359
+ url = site.config["url"]
360
+ output_path = site.config.dig("activitypub", "output_path") || "activitypub"
361
+ slug = post.basename_without_ext.sub(/^\d{4}-\d{2}-\d{2}-/, "")
362
+ post.data["activitypub_url"] = "#{url}/#{output_path}/posts/#{slug}.jsonld"
363
+ end
364
+
365
+ Jekyll::Hooks.register :pages, :post_init do |page|
366
+ next unless page.url == "/"
367
+
368
+ site = page.site
369
+ page.data["activitypub_url"] = "#{site.config["url"]}/actor.jsonld"
285
370
  end
@@ -1,6 +1,6 @@
1
1
  # lib/jekyll/activitypub_static/version.rb
2
2
  module Jekyll
3
3
  module ActivityPubStatic
4
- VERSION = "0.7.6"
4
+ VERSION = "0.8.0"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-activitypub-static
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Prodromou