openwrite 0.16.1__tar.gz → 0.17.1__tar.gz
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.
- {openwrite-0.16.1/openwrite.egg-info → openwrite-0.17.1}/PKG-INFO +21 -3
- {openwrite-0.16.1 → openwrite-0.17.1}/README.md +20 -2
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/__init__.py +12 -6
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/cli.py +4 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/blog.py +24 -68
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/dashboard.py +47 -169
- openwrite-0.17.1/openwrite/routes/federation.py +262 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/main.py +2 -2
- openwrite-0.17.1/openwrite/static/feed/default.xml +2 -0
- openwrite-0.17.1/openwrite/static/feed/test.xml +2 -0
- openwrite-0.17.1/openwrite/static/feed/testx.xml +2 -0
- openwrite-0.17.1/openwrite/static/script/post_preview.js +106 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/style.css +79 -0
- openwrite-0.17.1/openwrite/static/style/toc.css +28 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/blog.html +1 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/blog_header.html +2 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/blog_preview.html +1 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/dashboard.html +2 -2
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/edit.html +6 -6
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/footer.html +1 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/new_post.html +63 -18
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/page_preview.html +1 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/post.html +2 -2
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/preview.html +3 -2
- openwrite-0.17.1/openwrite/utils/activitypub.py +154 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/db.py +4 -1
- openwrite-0.17.1/openwrite/utils/helpers.py +200 -0
- openwrite-0.17.1/openwrite/utils/i18n.json +1 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/migrations.py +3 -3
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/models.py +44 -0
- openwrite-0.17.1/openwrite/utils/toc.py +47 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/worker.py +37 -22
- {openwrite-0.16.1 → openwrite-0.17.1/openwrite.egg-info}/PKG-INFO +21 -3
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite.egg-info/SOURCES.txt +9 -27
- {openwrite-0.16.1 → openwrite-0.17.1}/pyproject.toml +1 -1
- openwrite-0.17.1/tests/test_federation.py +726 -0
- openwrite-0.16.1/openwrite/routes/federation.py +0 -329
- openwrite-0.16.1/openwrite/static/feed/74.xml +0 -12
- openwrite-0.16.1/openwrite/static/feed/a.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/anim3sh1t.xml +0 -12
- openwrite-0.16.1/openwrite/static/feed/b.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/corrode.xml +0 -4
- openwrite-0.16.1/openwrite/static/feed/dirtysanchez.xml +0 -6
- openwrite-0.16.1/openwrite/static/feed/eikerammen.xml +0 -165
- openwrite-0.16.1/openwrite/static/feed/fedi.xml +0 -9
- openwrite-0.16.1/openwrite/static/feed/friskt.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/gordi.xml +0 -6
- openwrite-0.16.1/openwrite/static/feed/igilq.xml +0 -226
- openwrite-0.16.1/openwrite/static/feed/io.xml +0 -50
- openwrite-0.16.1/openwrite/static/feed/isyuricunha.xml +0 -4
- openwrite-0.16.1/openwrite/static/feed/kenny.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/m0bi.xml +0 -6
- openwrite-0.16.1/openwrite/static/feed/main.xml +0 -236
- openwrite-0.16.1/openwrite/static/feed/mywritese.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/nay-thoughs.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/nintendoflesz.xml +0 -7
- openwrite-0.16.1/openwrite/static/feed/phronima.xml +0 -68
- openwrite-0.16.1/openwrite/static/feed/ram.xml +0 -75
- openwrite-0.16.1/openwrite/static/feed/samah.xml +0 -2
- openwrite-0.16.1/openwrite/static/feed/themes-test.xml +0 -18
- openwrite-0.16.1/openwrite/static/feed/totylkoja.xml +0 -78
- openwrite-0.16.1/openwrite/static/feed/vegafjord.xml +0 -48
- openwrite-0.16.1/openwrite/static/feed/vijayvaradharaj.xml +0 -2
- openwrite-0.16.1/openwrite/utils/helpers.py +0 -217
- openwrite-0.16.1/openwrite/utils/i18n.json +0 -1
- {openwrite-0.16.1 → openwrite-0.17.1}/LICENSE +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/MANIFEST.in +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/db/__init__.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/db/base.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/gemini_multi.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/gemini_single.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/admin.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/auth.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/routes/upload.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/IBMPlexSans-Regular.ttf +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/Merriweather.ttf +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/avatar.png +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/emoji.json +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/favicon.ico +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/favicon.png +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/feed/.gitkeep +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/logo.png +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/chart.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/highlight.min.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/nativeEmoji.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/openwrite.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/papaparse.min.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/showdown.min.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/showdown.min.js.map +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/site.compat.min.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/script/site.min.js +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/blog_design.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/default.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/hl.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/nativeEmoji.min.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/cyberpunk.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/light.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/obscura.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/paper.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/sepia.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/stickynote.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/synthwave.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/terminal.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/warmnight.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/static/style/themes/windows98.css +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/admin.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/blog_footer.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/changepw.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/create.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/discover.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/header.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/import.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/index.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/instances.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/login.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/new_page.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/register.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/settings.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/stats.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/templates/translations.html +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/create_db.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/tags.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite/utils/valkey_client.py +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite.egg-info/dependency_links.txt +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite.egg-info/entry_points.txt +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite.egg-info/requires.txt +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/openwrite.egg-info/top_level.txt +0 -0
- {openwrite-0.16.1 → openwrite-0.17.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openwrite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.1
|
|
4
4
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
5
5
|
Version 3, 19 November 2007
|
|
6
6
|
|
|
@@ -761,7 +761,25 @@ to run it in background.
|
|
|
761
761
|
|
|
762
762
|
5. Default user is `admin` with password `openwrite`. You can (and should!) change it in your dashboard. Enjoy!
|
|
763
763
|
|
|
764
|
+
### Local development over HTTP
|
|
764
765
|
|
|
765
|
-
|
|
766
|
+
For `http://localhost:8081`, set these values in `.env`:
|
|
766
767
|
|
|
767
|
-
|
|
768
|
+
```dotenv
|
|
769
|
+
DOMAIN=localhost:8081
|
|
770
|
+
IP=127.0.0.1
|
|
771
|
+
PORT=8081
|
|
772
|
+
GEMINI=no
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
When working from a checkout, install it with `./venv/bin/python -m pip install -e .`
|
|
776
|
+
so changes in this folder are used by the CLI.
|
|
777
|
+
Run `./venv/bin/openwrite debugrun` (or `./venv/bin/openwrite run`) and open
|
|
778
|
+
`http://localhost:8081`. `DOMAIN` must match the hostname and port in the browser,
|
|
779
|
+
without a protocol prefix. For a custom local hostname such as `openwrite.local`,
|
|
780
|
+
use `DOMAIN=openwrite.local:8081` and point that hostname to `127.0.0.1` in your hosts file.
|
|
781
|
+
Restart the server after changing `.env`.
|
|
782
|
+
|
|
783
|
+
Both commands serve plain HTTP. In an HTTPS deployment, terminate TLS at a reverse
|
|
784
|
+
proxy and forward the original host and scheme with `X-Forwarded-Host` and
|
|
785
|
+
`X-Forwarded-Proto` so browser links use the public address.
|
|
@@ -71,7 +71,25 @@ to run it in background.
|
|
|
71
71
|
|
|
72
72
|
5. Default user is `admin` with password `openwrite`. You can (and should!) change it in your dashboard. Enjoy!
|
|
73
73
|
|
|
74
|
+
### Local development over HTTP
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
For `http://localhost:8081`, set these values in `.env`:
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
```dotenv
|
|
79
|
+
DOMAIN=localhost:8081
|
|
80
|
+
IP=127.0.0.1
|
|
81
|
+
PORT=8081
|
|
82
|
+
GEMINI=no
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
When working from a checkout, install it with `./venv/bin/python -m pip install -e .`
|
|
86
|
+
so changes in this folder are used by the CLI.
|
|
87
|
+
Run `./venv/bin/openwrite debugrun` (or `./venv/bin/openwrite run`) and open
|
|
88
|
+
`http://localhost:8081`. `DOMAIN` must match the hostname and port in the browser,
|
|
89
|
+
without a protocol prefix. For a custom local hostname such as `openwrite.local`,
|
|
90
|
+
use `DOMAIN=openwrite.local:8081` and point that hostname to `127.0.0.1` in your hosts file.
|
|
91
|
+
Restart the server after changing `.env`.
|
|
92
|
+
|
|
93
|
+
Both commands serve plain HTTP. In an HTTPS deployment, terminate TLS at a reverse
|
|
94
|
+
proxy and forward the original host and scheme with `X-Forwarded-Host` and
|
|
95
|
+
`X-Forwarded-Proto` so browser links use the public address.
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
from .utils.helpers import generate_nonce, get_ip
|
|
7
7
|
import time
|
|
8
8
|
from .utils.models import Settings, Home
|
|
9
|
-
from sqlalchemy import distinct
|
|
10
9
|
import bcrypt
|
|
11
10
|
import json
|
|
12
11
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
@@ -87,8 +86,8 @@ def create_app(test_config=None):
|
|
|
87
86
|
blog=1,
|
|
88
87
|
name="Home",
|
|
89
88
|
url="",
|
|
90
|
-
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](
|
|
91
|
-
content_html=f"<p><img src=\"https://openwrite.b-cdn.net/hello.jpg\" width=\"500\" height=\"258\"></p><h1>Hello there! 👋</h1><p>You can edit your blog home page in <a href=\"
|
|
89
|
+
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](/dashboard/page/1/edit)\n\n---\n### Posts\n\n{{posts}}",
|
|
90
|
+
content_html=f"<p><img src=\"https://openwrite.b-cdn.net/hello.jpg\" width=\"500\" height=\"258\"></p><h1>Hello there! 👋</h1><p>You can edit your blog home page in <a href=\"/dashboard/page/1/edit\">dashboard</a></p>\n\n<hr>\n<h3>Posts\n\n{{posts}}",
|
|
92
91
|
show="0"
|
|
93
92
|
)
|
|
94
93
|
SessionLocal.add(new_page)
|
|
@@ -193,12 +192,20 @@ def create_app(test_config=None):
|
|
|
193
192
|
|
|
194
193
|
app.jinja_env.globals['_'] = _
|
|
195
194
|
|
|
195
|
+
@app.template_filter('with_toc')
|
|
196
|
+
def with_toc(content, enabled):
|
|
197
|
+
if enabled != "1":
|
|
198
|
+
return content
|
|
199
|
+
from .utils.toc import add_table_of_contents
|
|
200
|
+
label = g.trans.get('table-of-contents', 'Spis treści' if g.lang == 'pl' else 'Table of contents')
|
|
201
|
+
return add_table_of_contents(content, label)
|
|
202
|
+
|
|
196
203
|
@app.before_request
|
|
197
204
|
def before():
|
|
198
205
|
from flask import request, session
|
|
199
206
|
from openwrite.utils.models import Home, Settings
|
|
200
207
|
g.db = SessionLocal
|
|
201
|
-
t_query = g.db.query(
|
|
208
|
+
t_query = g.db.query(Home.language).distinct().all()
|
|
202
209
|
translations = [row[0] for row in t_query]
|
|
203
210
|
lang = request.cookies.get("lang")
|
|
204
211
|
if not lang or lang not in translations:
|
|
@@ -253,7 +260,6 @@ def create_app(test_config=None):
|
|
|
253
260
|
|
|
254
261
|
@app.after_request
|
|
255
262
|
def after(response):
|
|
256
|
-
nonce = g.nonce
|
|
257
263
|
#response.headers["Content-Security-Policy"] = (
|
|
258
264
|
# f"default-src 'none'; "
|
|
259
265
|
# f"script-src 'self' 'nonce-{nonce}' https://cdn.jsdelivr.net http://{g.main_domain}"
|
|
@@ -281,6 +287,6 @@ def create_app(test_config=None):
|
|
|
281
287
|
|
|
282
288
|
@app.teardown_appcontext
|
|
283
289
|
def shutdown_session(exception=None):
|
|
284
|
-
|
|
290
|
+
SessionLocal.remove()
|
|
285
291
|
|
|
286
292
|
return app
|
|
@@ -168,6 +168,9 @@ def init():
|
|
|
168
168
|
@click.option("-d", "--daemon", is_flag=True, help="Run in background (daemon)")
|
|
169
169
|
def run(daemon):
|
|
170
170
|
print_banner()
|
|
171
|
+
# Initialize/migrate once before concurrent web workers and the queue worker start.
|
|
172
|
+
from openwrite import create_app
|
|
173
|
+
create_app()
|
|
171
174
|
|
|
172
175
|
ip = os.getenv("IP", "0.0.0.0")
|
|
173
176
|
port = int(os.getenv("PORT", 8080))
|
|
@@ -184,7 +187,7 @@ def run(daemon):
|
|
|
184
187
|
gunicorn_error_log = os.path.join(logs_dir, "openwrite_error.log")
|
|
185
188
|
|
|
186
189
|
gunicorn_cmd = [
|
|
187
|
-
"gunicorn",
|
|
190
|
+
sys.executable, "-m", "gunicorn",
|
|
188
191
|
"-w", "4",
|
|
189
192
|
"openwrite:create_app()",
|
|
190
193
|
"--bind", f"{ip}:{port}"
|
|
@@ -3,6 +3,7 @@ from openwrite.utils.models import Blog, Post, User, View, Like, Page, PostDraft
|
|
|
3
3
|
from openwrite.utils.tags import get_post_tags, parse_tags_from_posts_syntax, get_posts_by_tags
|
|
4
4
|
from openwrite.utils.helpers import gen_link, sanitize_html, anonymize, get_ip
|
|
5
5
|
from openwrite.utils.valkey_client import valkey_client
|
|
6
|
+
from openwrite.utils.activitypub import actor_url, create_activity_object, iso_date
|
|
6
7
|
from feedgen.feed import FeedGenerator
|
|
7
8
|
from sqlalchemy import desc
|
|
8
9
|
import os
|
|
@@ -33,59 +34,11 @@ def process_posts_content(page, blog_id, blog_url, domain):
|
|
|
33
34
|
|
|
34
35
|
return posts, processed_content
|
|
35
36
|
|
|
36
|
-
def create_activity_object(post, blog, url, full_content=True):
|
|
37
|
-
"""Create ActivityPub object (Article or Note) based on post content length"""
|
|
38
|
-
dt = datetime.strptime(str(post.date), "%Y-%m-%d %H:%M:%S")
|
|
39
|
-
dt = dt.replace(tzinfo=timezone.utc)
|
|
40
|
-
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
41
|
-
|
|
42
|
-
clean_content = re.sub(r'<[^>]+>', '', post.content_html)
|
|
43
|
-
content_length = len(clean_content)
|
|
44
|
-
|
|
45
|
-
base_object = {
|
|
46
|
-
"id": f"{url}/{post.link}",
|
|
47
|
-
"attributedTo": url,
|
|
48
|
-
"published": iso,
|
|
49
|
-
"url": f"{url}/{post.link}",
|
|
50
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
51
|
-
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
52
|
-
"sensitive": False,
|
|
53
|
-
"atomUri": f"{url}/{post.link}",
|
|
54
|
-
"attachment": [],
|
|
55
|
-
"tag": []
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if content_length > 200:
|
|
59
|
-
words = clean_content.split()[:50]
|
|
60
|
-
summary = ' '.join(words) + '...' if len(words) == 50 else ' '.join(words)
|
|
61
|
-
|
|
62
|
-
if full_content:
|
|
63
|
-
content = f"<h1>{post.title}</h1>{post.content_html}"
|
|
64
|
-
else:
|
|
65
|
-
content = f"<p><a href=\"{url}/{post.link}\">Read full article: {post.title}</a></p>"
|
|
66
|
-
|
|
67
|
-
base_object.update({
|
|
68
|
-
"type": "Article",
|
|
69
|
-
"name": post.title,
|
|
70
|
-
"summary": summary,
|
|
71
|
-
"content": content
|
|
72
|
-
})
|
|
73
|
-
else:
|
|
74
|
-
base_object.update({
|
|
75
|
-
"type": "Note",
|
|
76
|
-
"summary": None,
|
|
77
|
-
"content": f"<h3>{post.title}</h3>{post.content_html}<p><a href=\"{url}/{post.link}\">View post</a></p>"
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
return base_object
|
|
81
|
-
|
|
82
37
|
def create_person_object(blog, url):
|
|
83
38
|
"""Create ActivityPub Person object for the blog"""
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
88
|
-
if blog.fedi_description != "":
|
|
39
|
+
url = actor_url(blog)
|
|
40
|
+
iso = iso_date(blog.created)
|
|
41
|
+
if blog.fedi_description:
|
|
89
42
|
summary = blog.fedi_description
|
|
90
43
|
else:
|
|
91
44
|
summary = f"{blog.name} - Blog on {g.main_domain}"
|
|
@@ -131,6 +84,8 @@ def create_person_object(blog, url):
|
|
|
131
84
|
"url": f"https://{g.main_domain}/static/avatar.png"
|
|
132
85
|
}
|
|
133
86
|
}
|
|
87
|
+
if blog.updated:
|
|
88
|
+
actor['updated'] = iso_date(blog.updated)
|
|
134
89
|
return actor
|
|
135
90
|
|
|
136
91
|
def is_activitypub_request():
|
|
@@ -147,7 +102,7 @@ def show_blog(blog):
|
|
|
147
102
|
return redirect("/")
|
|
148
103
|
|
|
149
104
|
if blog_obj.access == "domain":
|
|
150
|
-
return redirect(f"
|
|
105
|
+
return redirect(f"{request.scheme}://{blog_obj.name}.{os.getenv('DOMAIN')}/")
|
|
151
106
|
|
|
152
107
|
if is_activitypub_request():
|
|
153
108
|
url = f"https://{g.main_domain}/b/{blog}"
|
|
@@ -170,10 +125,10 @@ def show_subblog(blog):
|
|
|
170
125
|
return redirect("/")
|
|
171
126
|
blog_obj = g.db.query(Blog).filter_by(name=blog).first()
|
|
172
127
|
if blog_obj is None:
|
|
173
|
-
return redirect(f"
|
|
128
|
+
return redirect(f"{request.scheme}://{os.getenv('DOMAIN')}/")
|
|
174
129
|
|
|
175
130
|
if blog_obj.access == "path":
|
|
176
|
-
return redirect(f"
|
|
131
|
+
return redirect(f"{request.scheme}://{os.getenv('DOMAIN')}/b/{blog_obj.name}")
|
|
177
132
|
|
|
178
133
|
if is_activitypub_request():
|
|
179
134
|
url = f"https://{blog}.{g.main_domain}"
|
|
@@ -183,7 +138,7 @@ def show_subblog(blog):
|
|
|
183
138
|
'Vary': 'Accept'
|
|
184
139
|
})
|
|
185
140
|
|
|
186
|
-
blog_obj.url = f"
|
|
141
|
+
blog_obj.url = f"{request.scheme}://{blog_obj.name}.{os.getenv('DOMAIN')}"
|
|
187
142
|
pages = g.db.query(Page).filter_by(blog=blog_obj.id).all()
|
|
188
143
|
homepage = g.db.query(Page).filter(Page.blog == blog_obj.id, Page.url == "").first()
|
|
189
144
|
|
|
@@ -202,7 +157,7 @@ def show_post(blog, post):
|
|
|
202
157
|
return serve_rss(blog_obj)
|
|
203
158
|
|
|
204
159
|
if blog_obj.access == "domain":
|
|
205
|
-
return redirect(f"
|
|
160
|
+
return redirect(f"{request.scheme}://{blog_obj.name}.{os.getenv('DOMAIN')}/{post}")
|
|
206
161
|
|
|
207
162
|
blog_obj.url = f"/b/{blog_obj.name}"
|
|
208
163
|
pages = g.db.query(Page).filter_by(blog=blog_obj.id).all()
|
|
@@ -266,15 +221,15 @@ def show_subpost(blog, post):
|
|
|
266
221
|
return redirect("/")
|
|
267
222
|
blog_obj = g.db.query(Blog).filter_by(name=blog).first()
|
|
268
223
|
if blog_obj is None:
|
|
269
|
-
return redirect(f"
|
|
224
|
+
return redirect(f"{request.scheme}://{os.getenv('DOMAIN')}/")
|
|
270
225
|
|
|
271
226
|
if post == "rss":
|
|
272
227
|
return serve_rss(blog_obj)
|
|
273
228
|
|
|
274
229
|
if blog_obj.access == "path":
|
|
275
|
-
return redirect(f"
|
|
230
|
+
return redirect(f"{request.scheme}://{os.getenv('DOMAIN')}/b/{blog_obj.name}/{post}")
|
|
276
231
|
|
|
277
|
-
blog_obj.url = f"
|
|
232
|
+
blog_obj.url = f"{request.scheme}://{blog_obj.name}.{os.getenv('DOMAIN')}"
|
|
278
233
|
|
|
279
234
|
pages = g.db.query(Page).filter_by(blog=blog_obj.id).all()
|
|
280
235
|
one_post = g.db.query(Post).filter(Post.blog == blog_obj.id, Post.link == post, Post.isdraft == "0").first()
|
|
@@ -298,7 +253,7 @@ def show_subpost(blog, post):
|
|
|
298
253
|
|
|
299
254
|
post_author = g.db.query(User).filter_by(id=blog_obj.owner).first()
|
|
300
255
|
one_post.authorname = post_author.username
|
|
301
|
-
blog_obj.url = f"
|
|
256
|
+
blog_obj.url = f"{request.scheme}://{blog_obj.name}.{os.getenv('DOMAIN')}"
|
|
302
257
|
|
|
303
258
|
user_agent = request.headers.get('User-Agent')
|
|
304
259
|
ip = anonymize(get_ip())
|
|
@@ -347,11 +302,10 @@ def webfinger():
|
|
|
347
302
|
if not blog_obj:
|
|
348
303
|
abort(404)
|
|
349
304
|
|
|
350
|
-
if
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
305
|
+
if domain != g.main_domain:
|
|
306
|
+
abort(404)
|
|
307
|
+
blog_url = actor_url(blog_obj)
|
|
308
|
+
|
|
355
309
|
data = {
|
|
356
310
|
"subject": f"acct:{username}@{g.main_domain}",
|
|
357
311
|
"links": [{
|
|
@@ -379,9 +333,9 @@ def subdomain_webfinger(blog):
|
|
|
379
333
|
if "@" in account_part:
|
|
380
334
|
username = account_part.split("@")[0]
|
|
381
335
|
corrected_resource = f"acct:{username}@{g.main_domain}"
|
|
382
|
-
return redirect(f"
|
|
336
|
+
return redirect(f"{request.scheme}://{g.main_domain}/.well-known/webfinger?resource={corrected_resource}")
|
|
383
337
|
|
|
384
|
-
return redirect(f"
|
|
338
|
+
return redirect(f"{request.scheme}://{g.main_domain}/.well-known/webfinger?resource={resource}")
|
|
385
339
|
|
|
386
340
|
@blog_bp.route("/p/<post>")
|
|
387
341
|
def single_showpost(post):
|
|
@@ -574,7 +528,8 @@ def autosave_post(blog_name):
|
|
|
574
528
|
session_key = data.get('session_key') if post_id is None else None
|
|
575
529
|
|
|
576
530
|
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
577
|
-
draft = PostDraft(post_id=post_id, blog_id=blog.id, session_key=session_key, title=title, content_raw=content_raw, saved_at=now
|
|
531
|
+
draft = PostDraft(post_id=post_id, blog_id=blog.id, session_key=session_key, title=title, content_raw=content_raw, saved_at=now,
|
|
532
|
+
toc_enabled='1' if data.get('toc_enabled') == '1' else '0')
|
|
578
533
|
g.db.add(draft)
|
|
579
534
|
g.db.flush()
|
|
580
535
|
|
|
@@ -613,5 +568,6 @@ def get_autosaves(blog_name):
|
|
|
613
568
|
'id': d.id,
|
|
614
569
|
'title': d.title,
|
|
615
570
|
'content_raw': d.content_raw,
|
|
571
|
+
'toc_enabled': d.toc_enabled,
|
|
616
572
|
'saved_at': d.saved_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
617
573
|
} for d in drafts])
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from flask import Blueprint, render_template, redirect, request, g, abort, Response
|
|
2
2
|
from openwrite.utils.models import Blog, Post, User, View, Like, Page, Settings, Tag, PostTag
|
|
3
|
-
from openwrite.utils.helpers import sanitize_html, gen_link, safe_css,
|
|
3
|
+
from openwrite.utils.helpers import sanitize_html, gen_link, safe_css, is_html, get_themes
|
|
4
4
|
from openwrite.utils.tags import parse_tags_from_input, update_post_tags, get_post_tags
|
|
5
|
-
from openwrite.routes.blog import
|
|
5
|
+
from openwrite.routes.blog import create_person_object
|
|
6
|
+
from openwrite.utils.activitypub import (
|
|
7
|
+
actor_url, post_url, create_activity_object, activity_for, queue_activity, deliver_pending,
|
|
8
|
+
)
|
|
6
9
|
from openwrite.utils.valkey_client import valkey_client
|
|
7
10
|
import requests
|
|
8
11
|
from sqlalchemy import desc
|
|
@@ -105,8 +108,8 @@ def create_blog():
|
|
|
105
108
|
blog=b_id,
|
|
106
109
|
name="Home",
|
|
107
110
|
url="",
|
|
108
|
-
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](
|
|
109
|
-
content_html=f"<p><img src=\"https://openwrite.b-cdn.net/hello.jpg\" width=\"500\" height=\"258\"></p><h1>Hello there! 👋</h1><p>You can edit your blog home page in <a href=\"
|
|
111
|
+
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](/dashboard/edit/{form_url})\n\n---\n### Posts\n\n{{posts}}",
|
|
112
|
+
content_html=f"<p><img src=\"https://openwrite.b-cdn.net/hello.jpg\" width=\"500\" height=\"258\"></p><h1>Hello there! 👋</h1><p>You can edit your blog home page in <a href=\"/dashboard/edit/{form_url}\">dashboard</a></p>\n\n<hr>\n<h3>Posts</h3>\n\n{{posts}}",
|
|
110
113
|
show="0"
|
|
111
114
|
)
|
|
112
115
|
g.db.add(new_page)
|
|
@@ -127,37 +130,11 @@ def delete_blog(name):
|
|
|
127
130
|
if blog is None or blog.owner != g.user:
|
|
128
131
|
abort(403)
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
else:
|
|
133
|
-
blog_url = f"https://{g.main_domain}/b/{blog.name}"
|
|
134
|
-
|
|
135
|
-
followers = []
|
|
136
|
-
if blog.followers not in (None, "null", "NULL"):
|
|
137
|
-
followers = json.loads(blog.followers)
|
|
138
|
-
|
|
139
|
-
if followers:
|
|
140
|
-
person_object = create_person_object(blog, blog_url)
|
|
141
|
-
|
|
142
|
-
for actor in followers:
|
|
143
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
144
|
-
delete_activity = {
|
|
145
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
146
|
-
"id": f"{blog_url}#delete-{now_iso}",
|
|
147
|
-
"type": "Delete",
|
|
148
|
-
"actor": blog_url,
|
|
149
|
-
"published": now_iso,
|
|
150
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
151
|
-
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
152
|
-
"object": person_object
|
|
153
|
-
}
|
|
154
|
-
if valkey_client.is_enabled():
|
|
155
|
-
valkey_client.queue_federation_activity(delete_activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
156
|
-
else:
|
|
157
|
-
send_activity(delete_activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
158
|
-
|
|
133
|
+
activity = activity_for(blog, 'Delete', {'id': actor_url(blog), 'type': 'Tombstone', 'formerType': 'Person'})
|
|
134
|
+
jobs = queue_activity(g.db, blog, activity)
|
|
159
135
|
g.db.delete(blog)
|
|
160
136
|
g.db.commit()
|
|
137
|
+
deliver_pending(g.db, jobs, budget_seconds=1)
|
|
161
138
|
return redirect("/dashboard")
|
|
162
139
|
|
|
163
140
|
@dashboard_bp.route("/dashboard/edit/<name>", methods=['GET', 'POST'])
|
|
@@ -200,37 +177,12 @@ def edit_blog(name):
|
|
|
200
177
|
if selected_theme not in themes:
|
|
201
178
|
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages, error="wrong-theme")
|
|
202
179
|
blog.theme = selected_theme
|
|
203
|
-
|
|
204
|
-
|
|
180
|
+
jobs = []
|
|
205
181
|
if old_title != new_title or old_fedi_description != new_fedi_description:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
followers = []
|
|
212
|
-
if blog.followers not in (None, "null", "NULL"):
|
|
213
|
-
followers = json.loads(blog.followers)
|
|
214
|
-
|
|
215
|
-
if followers:
|
|
216
|
-
person_object = create_person_object(blog, blog_url)
|
|
217
|
-
|
|
218
|
-
for actor in followers:
|
|
219
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
220
|
-
update_activity = {
|
|
221
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
222
|
-
"id": f"{blog_url}#update-{now_iso}",
|
|
223
|
-
"type": "Update",
|
|
224
|
-
"actor": blog_url,
|
|
225
|
-
"published": now_iso,
|
|
226
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
227
|
-
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
228
|
-
"object": person_object
|
|
229
|
-
}
|
|
230
|
-
if valkey_client.is_enabled():
|
|
231
|
-
valkey_client.queue_federation_activity(update_activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
232
|
-
else:
|
|
233
|
-
send_activity(update_activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
182
|
+
obj = create_person_object(blog, actor_url(blog))
|
|
183
|
+
jobs = queue_activity(g.db, blog, activity_for(blog, 'Update', obj))
|
|
184
|
+
g.db.commit()
|
|
185
|
+
deliver_pending(g.db, jobs, budget_seconds=1)
|
|
234
186
|
|
|
235
187
|
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages)
|
|
236
188
|
|
|
@@ -277,7 +229,8 @@ def new_post(name):
|
|
|
277
229
|
date=now,
|
|
278
230
|
feed=request.form.get('feed'),
|
|
279
231
|
isdraft=request.form.get('draft', '0'),
|
|
280
|
-
comments_allowed=request.form.get('comments_allowed', '0')
|
|
232
|
+
comments_allowed=request.form.get('comments_allowed', '0'),
|
|
233
|
+
toc_enabled='1' if request.form.get('toc_enabled') == '1' else '0'
|
|
281
234
|
)
|
|
282
235
|
g.db.add(post)
|
|
283
236
|
g.db.flush() # Get the post ID
|
|
@@ -288,37 +241,11 @@ def new_post(name):
|
|
|
288
241
|
tag_names = parse_tags_from_input(tags_input)
|
|
289
242
|
update_post_tags(g.db, post, tag_names)
|
|
290
243
|
|
|
244
|
+
jobs = []
|
|
245
|
+
if post.isdraft == '0':
|
|
246
|
+
jobs = queue_activity(g.db, blog, activity_for(blog, 'Create', create_activity_object(post, blog)))
|
|
291
247
|
g.db.commit()
|
|
292
|
-
|
|
293
|
-
if blog.access == "domain":
|
|
294
|
-
blog_url = f"https://{blog.name}.{g.main_domain}"
|
|
295
|
-
else:
|
|
296
|
-
blog_url = f"https://{g.main_domain}/b/{blog.name}"
|
|
297
|
-
|
|
298
|
-
followers = []
|
|
299
|
-
if blog.followers not in (None, "null", "NULL"):
|
|
300
|
-
followers = json.loads(blog.followers)
|
|
301
|
-
|
|
302
|
-
if followers:
|
|
303
|
-
if request.form.get("draft", "0") == "0":
|
|
304
|
-
activity_object = create_activity_object(post, blog.name, blog_url, full_content=False)
|
|
305
|
-
|
|
306
|
-
for actor in followers:
|
|
307
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
308
|
-
activity = {
|
|
309
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
310
|
-
"id": f"{blog_url}/{post.link}#create",
|
|
311
|
-
"type": "Create",
|
|
312
|
-
"actor": blog_url,
|
|
313
|
-
"published": now_iso,
|
|
314
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
315
|
-
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
316
|
-
"object": activity_object
|
|
317
|
-
}
|
|
318
|
-
if valkey_client.is_enabled():
|
|
319
|
-
valkey_client.queue_federation_activity(activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
320
|
-
else:
|
|
321
|
-
send_activity(activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
248
|
+
deliver_pending(g.db, jobs, budget_seconds=1)
|
|
322
249
|
|
|
323
250
|
generate_rss(blog.name)
|
|
324
251
|
return redirect(f"/dashboard/edit/{blog.name}")
|
|
@@ -332,6 +259,7 @@ def preview():
|
|
|
332
259
|
data = {
|
|
333
260
|
'title': request.form.get('title'),
|
|
334
261
|
'content': sanitize_html(request.form.get('content')),
|
|
262
|
+
'toc_enabled': '1' if request.form.get('toc_enabled') == '1' else '0',
|
|
335
263
|
'author': request.form.get('author'),
|
|
336
264
|
'blog_title': request.form.get('blog_title'),
|
|
337
265
|
'blog_name': request.form.get('blog_name'),
|
|
@@ -381,7 +309,9 @@ def edit_post(blog, post):
|
|
|
381
309
|
if blog_obj is None or blog_obj.owner != g.user:
|
|
382
310
|
abort(403)
|
|
383
311
|
|
|
384
|
-
e_post = g.db.query(Post).filter_by(link=post).first()
|
|
312
|
+
e_post = g.db.query(Post).filter_by(link=post, blog=blog_obj.id).first()
|
|
313
|
+
if e_post is None:
|
|
314
|
+
abort(404)
|
|
385
315
|
if request.method == "GET":
|
|
386
316
|
# Get existing tags for the post
|
|
387
317
|
existing_tags = get_post_tags(g.db, e_post) if e_post else []
|
|
@@ -395,25 +325,23 @@ def edit_post(blog, post):
|
|
|
395
325
|
if len(title) > 120:
|
|
396
326
|
existing_tags = get_post_tags(g.db, p) if p else []
|
|
397
327
|
return render_template("new_post.html", blog=blog_obj, post=p, existing_tags=existing_tags, error="title-too-long", form_data=request.form)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
dupes = dupes_posts + dupes_pages
|
|
404
|
-
if dupes > 0 and link != post:
|
|
405
|
-
link += f"-{dupes + 1}"
|
|
328
|
+
if not title:
|
|
329
|
+
existing_tags = get_post_tags(g.db, p)
|
|
330
|
+
return render_template('new_post.html', blog=blog_obj, post=p, existing_tags=existing_tags,
|
|
331
|
+
error='title-empty', form_data=request.form)
|
|
332
|
+
was_draft = p.isdraft == '1'
|
|
406
333
|
|
|
407
334
|
p.title = title
|
|
408
|
-
now = datetime.now(timezone.utc)
|
|
335
|
+
now = datetime.now(timezone.utc)
|
|
409
336
|
p.content_raw = request.form.get("content_raw")
|
|
410
337
|
p.content_html = sanitize_html(request.form.get("content"))
|
|
411
338
|
p.author = request.form.get("author")
|
|
412
339
|
p.feed = request.form.get("feed")
|
|
413
340
|
p.comments_allowed = request.form.get("comments_allowed", "0")
|
|
414
|
-
p.
|
|
415
|
-
if request.form.get("
|
|
341
|
+
p.toc_enabled = "1" if request.form.get("toc_enabled") == "1" else "0"
|
|
342
|
+
if was_draft and request.form.get("draft", "0") == "0":
|
|
416
343
|
p.date = now
|
|
344
|
+
p.updated = None
|
|
417
345
|
else:
|
|
418
346
|
p.updated = now
|
|
419
347
|
p.isdraft = request.form.get("draft", "0")
|
|
@@ -423,36 +351,15 @@ def edit_post(blog, post):
|
|
|
423
351
|
tag_names = parse_tags_from_input(tags_input)
|
|
424
352
|
update_post_tags(g.db, p, tag_names)
|
|
425
353
|
|
|
354
|
+
jobs = []
|
|
355
|
+
if p.isdraft == '0':
|
|
356
|
+
kind = 'Create' if was_draft else 'Update'
|
|
357
|
+
jobs = queue_activity(g.db, blog_obj, activity_for(blog_obj, kind, create_activity_object(p, blog_obj)))
|
|
358
|
+
elif not was_draft:
|
|
359
|
+
tombstone = {'id': post_url(p, blog_obj), 'type': 'Tombstone', 'formerType': 'Note'}
|
|
360
|
+
jobs = queue_activity(g.db, blog_obj, activity_for(blog_obj, 'Delete', tombstone))
|
|
426
361
|
g.db.commit()
|
|
427
|
-
|
|
428
|
-
if blog_obj.access == "domain":
|
|
429
|
-
blog_url = f"https://{blog_obj.name}.{g.main_domain}"
|
|
430
|
-
else:
|
|
431
|
-
blog_url = f"https://{g.main_domain}/b/{blog_obj.name}"
|
|
432
|
-
|
|
433
|
-
followers = []
|
|
434
|
-
if blog_obj.followers not in (None, "null", "NULL"):
|
|
435
|
-
followers = json.loads(blog_obj.followers)
|
|
436
|
-
|
|
437
|
-
if followers:
|
|
438
|
-
activity_object = create_activity_object(p, blog_obj.name, blog_url, full_content=False)
|
|
439
|
-
|
|
440
|
-
for actor in followers:
|
|
441
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
442
|
-
update_activity = {
|
|
443
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
444
|
-
"id": f"{blog_url}/{p.link}#update",
|
|
445
|
-
"type": "Update",
|
|
446
|
-
"actor": blog_url,
|
|
447
|
-
"published": now_iso,
|
|
448
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
449
|
-
"cc": [f"https://{g.main_domain}/followers/{blog_obj.name}"],
|
|
450
|
-
"object": activity_object
|
|
451
|
-
}
|
|
452
|
-
if valkey_client.is_enabled():
|
|
453
|
-
valkey_client.queue_federation_activity(update_activity, blog_obj.id, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
454
|
-
else:
|
|
455
|
-
send_activity(update_activity, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
362
|
+
deliver_pending(g.db, jobs, budget_seconds=1)
|
|
456
363
|
|
|
457
364
|
generate_rss(blog)
|
|
458
365
|
return redirect(f"/dashboard/edit/{blog}")
|
|
@@ -468,42 +375,13 @@ def delete_post(blog, post):
|
|
|
468
375
|
|
|
469
376
|
p = g.db.query(Post).filter_by(link=post, blog=blog_obj.id).first()
|
|
470
377
|
if p:
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
followers = []
|
|
477
|
-
if blog_obj.followers not in (None, "null", "NULL"):
|
|
478
|
-
followers = json.loads(blog_obj.followers)
|
|
479
|
-
|
|
480
|
-
if followers:
|
|
481
|
-
activity_object = create_activity_object(p, blog_obj.name, blog_url, full_content=False)
|
|
482
|
-
|
|
483
|
-
for actor in followers:
|
|
484
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
485
|
-
delete_activity = {
|
|
486
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
487
|
-
"id": f"{blog_url}/{p.link}#delete",
|
|
488
|
-
"type": "Delete",
|
|
489
|
-
"actor": blog_url,
|
|
490
|
-
"published": now_iso,
|
|
491
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
492
|
-
"cc": [f"https://{g.main_domain}/followers/{blog_obj.name}"],
|
|
493
|
-
"object": activity_object
|
|
494
|
-
}
|
|
495
|
-
if valkey_client.is_enabled():
|
|
496
|
-
valkey_client.queue_federation_activity(delete_activity, blog_obj.id, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
497
|
-
else:
|
|
498
|
-
send_activity(
|
|
499
|
-
delete_activity,
|
|
500
|
-
blog_obj.priv_key,
|
|
501
|
-
blog_url,
|
|
502
|
-
f"{actor}/inbox"
|
|
503
|
-
)
|
|
504
|
-
|
|
378
|
+
jobs = []
|
|
379
|
+
if p.isdraft == '0':
|
|
380
|
+
tombstone = {'id': post_url(p, blog_obj), 'type': 'Tombstone', 'formerType': 'Note'}
|
|
381
|
+
jobs = queue_activity(g.db, blog_obj, activity_for(blog_obj, 'Delete', tombstone))
|
|
505
382
|
g.db.delete(p)
|
|
506
383
|
g.db.commit()
|
|
384
|
+
deliver_pending(g.db, jobs, budget_seconds=1)
|
|
507
385
|
|
|
508
386
|
generate_rss(blog)
|
|
509
387
|
return redirect(f"/dashboard/edit/{blog_obj.name}")
|