openwrite 0.12.0__tar.gz → 0.13.0__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.12.0 → openwrite-0.13.0}/MANIFEST.in +3 -0
- {openwrite-0.12.0/openwrite.egg-info → openwrite-0.13.0}/PKG-INFO +2 -1
- {openwrite-0.12.0 → openwrite-0.13.0}/README.md +1 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/__init__.py +1 -1
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/gemini_multi.py +33 -12
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/gemini_single.py +37 -12
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/blog.py +12 -9
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/dashboard.py +11 -3
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/federation.py +176 -24
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/main.py +4 -2
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/discover.html +1 -1
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/edit.html +4 -4
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/new_post.html +51 -3
- openwrite-0.13.0/openwrite/utils/i18n.json +1 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/utils/models.py +1 -0
- {openwrite-0.12.0 → openwrite-0.13.0/openwrite.egg-info}/PKG-INFO +2 -1
- {openwrite-0.12.0 → openwrite-0.13.0}/pyproject.toml +4 -1
- openwrite-0.12.0/openwrite/utils/i18n.json +0 -227
- {openwrite-0.12.0 → openwrite-0.13.0}/LICENSE +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/cli.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/db/__init__.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/db/base.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/admin.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/auth.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/routes/upload.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/IBMPlexSans-Regular.ttf +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/Merriweather.ttf +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/avatar.png +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/emoji.json +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/favicon.ico +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/logo.png +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/chart.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/highlight.min.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/nativeEmoji.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/openwrite.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/papaparse.min.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/showdown.min.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/showdown.min.js.map +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/site.compat.min.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/script/site.min.js +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/hl.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/nativeEmoji.min.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/style.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/cyberpunk.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/light.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/obscura.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/stickynote.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/terminal.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/warmnight.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/static/style/themes/windows98.css +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/admin.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/blog.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/blog_footer.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/blog_header.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/blog_preview.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/changepw.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/create.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/dashboard.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/footer.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/header.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/import.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/index.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/instances.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/login.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/new_page.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/page_preview.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/post.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/preview.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/register.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/settings.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/stats.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/templates/translations.html +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/utils/create_db.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/utils/db.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite/utils/helpers.py +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite.egg-info/SOURCES.txt +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite.egg-info/dependency_links.txt +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite.egg-info/entry_points.txt +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite.egg-info/requires.txt +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/openwrite.egg-info/top_level.txt +0 -0
- {openwrite-0.12.0 → openwrite-0.13.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openwrite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
5
5
|
Version 3, 19 November 2007
|
|
6
6
|
|
|
@@ -704,6 +704,7 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
704
704
|
- Single-blog mode
|
|
705
705
|
- Supports sqlite and mysql databases
|
|
706
706
|
- Upload images to local storage or bunny cdn
|
|
707
|
+
- Drafts
|
|
707
708
|
- Simple markdown editor in posting
|
|
708
709
|
- Discover section
|
|
709
710
|
- Privacy:
|
|
@@ -15,6 +15,7 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
15
15
|
- Single-blog mode
|
|
16
16
|
- Supports sqlite and mysql databases
|
|
17
17
|
- Upload images to local storage or bunny cdn
|
|
18
|
+
- Drafts
|
|
18
19
|
- Simple markdown editor in posting
|
|
19
20
|
- Discover section
|
|
20
21
|
- Privacy:
|
|
@@ -85,7 +85,7 @@ def create_app(test_config=None):
|
|
|
85
85
|
url="",
|
|
86
86
|
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](https://{domain}/dashboard/page/1/edit)\n\n---\n### Posts\n\n{{posts}}",
|
|
87
87
|
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=\"https://{domain}/dashboard/page/1/edit\">dashboard</a></p>\n\n<hr>\n<h3>Posts\n\n{{posts}}",
|
|
88
|
-
show="
|
|
88
|
+
show="0"
|
|
89
89
|
)
|
|
90
90
|
SessionLocal.add(new_page)
|
|
91
91
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import os, subprocess
|
|
2
2
|
from dotenv import load_dotenv
|
|
3
3
|
from openwrite.utils.db import init_engine, SessionLocal
|
|
4
|
-
from openwrite.utils.models import Blog, Post, User, View
|
|
4
|
+
from openwrite.utils.models import Blog, Post, User, View, Page
|
|
5
5
|
from openwrite.utils.helpers import anonymize
|
|
6
6
|
from md2gemini import md2gemini
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
|
+
import re
|
|
8
9
|
session = None
|
|
9
10
|
|
|
10
11
|
openwrite_logo = """
|
|
@@ -25,7 +26,7 @@ def root(req):
|
|
|
25
26
|
resp += "In here you can read posts published.\n"
|
|
26
27
|
resp += "\n## 🗞️ Latest posts:\n"
|
|
27
28
|
try:
|
|
28
|
-
posts = (session.query(Post).
|
|
29
|
+
posts = (session.query(Post).filter(Post.feed == "1", Post.isdraft == "0").order_by(Post.id.desc()).limit(10).all())
|
|
29
30
|
for p in posts:
|
|
30
31
|
blog = session.query(Blog).filter_by(id=p.blog).first()
|
|
31
32
|
resp += f"\n{p.date} - 📓 {blog.title}\n"
|
|
@@ -45,15 +46,24 @@ def blog_index(req):
|
|
|
45
46
|
if not blog:
|
|
46
47
|
return "not found"
|
|
47
48
|
posts = (session.query(Post)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
.filter_by(blog=blog.id, isdraft="0")
|
|
50
|
+
.order_by(Post.id.desc())
|
|
51
|
+
.all())
|
|
52
|
+
pages = (session.query(Page)
|
|
53
|
+
.filter(Page.blog == blog.id, Page.show == "1").all())
|
|
54
|
+
homepage = session.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
51
55
|
body = f"# 📓 {blog.title}\n\n"
|
|
52
|
-
|
|
53
|
-
body += "\n--------------------------------------------------\n"
|
|
54
|
-
body += "### Posts:\n\n"
|
|
56
|
+
posts_data = ""
|
|
55
57
|
for post in posts:
|
|
56
|
-
|
|
58
|
+
posts_data += f"=> /b/{blogname}/{post.link} {post.title}\n\n"
|
|
59
|
+
pages_data = ""
|
|
60
|
+
for page in pages:
|
|
61
|
+
pages_data += f"=> /b/{blogname}/{page.url} {page.name}\n\n"
|
|
62
|
+
content = pages_data + "\n"
|
|
63
|
+
content += homepage.content_raw.replace('{posts}', f"\n{posts_data}")
|
|
64
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
65
|
+
body += md2gemini(content)
|
|
66
|
+
|
|
57
67
|
return body
|
|
58
68
|
finally:
|
|
59
69
|
session.close()
|
|
@@ -67,10 +77,19 @@ def blog_index(req):
|
|
|
67
77
|
return "not found"
|
|
68
78
|
|
|
69
79
|
post = (session.query(Post)
|
|
70
|
-
.filter_by(blog=blog.id, link=slug)
|
|
80
|
+
.filter_by(blog=blog.id, link=slug, isdraft="0")
|
|
71
81
|
.first())
|
|
72
82
|
if not post:
|
|
73
|
-
|
|
83
|
+
page = (session.query(Page)
|
|
84
|
+
.filter_by(blog=blog.id, url=slug)
|
|
85
|
+
.first())
|
|
86
|
+
if page:
|
|
87
|
+
content = f"=> /b/{blog.name} 📓 {blog.title}\n\n"
|
|
88
|
+
content += page.content_raw
|
|
89
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
90
|
+
return md2gemini(content)
|
|
91
|
+
else:
|
|
92
|
+
return "not found"
|
|
74
93
|
user = session.query(User).filter_by(id=blog.owner).first()
|
|
75
94
|
|
|
76
95
|
now = datetime.now(timezone.utc)
|
|
@@ -88,7 +107,9 @@ def blog_index(req):
|
|
|
88
107
|
if post.author != "0":
|
|
89
108
|
gemtext += f"by {post.authorname}"
|
|
90
109
|
gemtext += "\n--------------------------------------------------\n"
|
|
91
|
-
|
|
110
|
+
content = post.content_raw
|
|
111
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
112
|
+
gemtext += f"\n\n{md2gemini(content)}\n\n"
|
|
92
113
|
return gemtext
|
|
93
114
|
|
|
94
115
|
finally:
|
|
@@ -11,17 +11,28 @@ def blog_index(req):
|
|
|
11
11
|
global session
|
|
12
12
|
try:
|
|
13
13
|
blog = session.query(Blog).filter_by(name="default").first()
|
|
14
|
-
|
|
14
|
+
if not blog:
|
|
15
|
+
return "not found"
|
|
16
|
+
posts = (session.query(Post)
|
|
15
17
|
.filter_by(blog=blog.id)
|
|
16
18
|
.order_by(Post.id.desc())
|
|
17
19
|
.all())
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
pages = (session.query(Page)
|
|
21
|
+
.filter(Page.blog == blog.id, Page.show == "1").all())
|
|
22
|
+
homepage = session.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
23
|
+
body = f"# 📓 {blog.title}\n\n"
|
|
24
|
+
posts_data = ""
|
|
25
|
+
for post in posts:
|
|
26
|
+
posts_data += f"=> /b/{blogname}/{post.link} {post.title}\n\n"
|
|
27
|
+
pages_data = ""
|
|
28
|
+
for page in pages:
|
|
29
|
+
pages_data += f"=> /b/{blogname}/{page.url} {page.name}\n\n"
|
|
30
|
+
content = pages_data + "\n"
|
|
31
|
+
content += homepage.content_raw.replace('{posts}', f"\n{posts_data}")
|
|
32
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
33
|
+
body += md2gemini(content)
|
|
34
|
+
|
|
35
|
+
return body
|
|
25
36
|
finally:
|
|
26
37
|
session.close()
|
|
27
38
|
|
|
@@ -33,11 +44,23 @@ def blog_post(req):
|
|
|
33
44
|
slug = path.split("/")[2]
|
|
34
45
|
try:
|
|
35
46
|
blog = session.query(Blog).filter_by(name="default").first()
|
|
47
|
+
if not blog:
|
|
48
|
+
return "not found"
|
|
49
|
+
|
|
36
50
|
post = (session.query(Post)
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
.filter_by(blog=blog.id, link=slug, isdraft="0")
|
|
52
|
+
.first())
|
|
39
53
|
if not post:
|
|
40
|
-
|
|
54
|
+
page = (session.query(Page)
|
|
55
|
+
.filter_by(blog=blog.id, url=slug)
|
|
56
|
+
.first())
|
|
57
|
+
if page:
|
|
58
|
+
content = f"=> /b/{blog.name} 📓 {blog.title}\n\n"
|
|
59
|
+
content += page.content_raw
|
|
60
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
61
|
+
return md2gemini(content)
|
|
62
|
+
else:
|
|
63
|
+
return "not found"
|
|
41
64
|
user = session.query(User).filter_by(id=blog.owner).first()
|
|
42
65
|
|
|
43
66
|
now = datetime.now(timezone.utc)
|
|
@@ -55,7 +78,9 @@ def blog_post(req):
|
|
|
55
78
|
if post.author != "0":
|
|
56
79
|
gemtext += f"by {post.authorname}"
|
|
57
80
|
gemtext += "\n--------------------------------------------------\n"
|
|
58
|
-
|
|
81
|
+
content = post.content_raw
|
|
82
|
+
content = re.sub(r'!\[(.*?)\]\((.*?)\s*=\d+x\d+\)', r'', content)
|
|
83
|
+
gemtext += f"\n\n{md2gemini(content)}\n\n"
|
|
59
84
|
return gemtext
|
|
60
85
|
|
|
61
86
|
finally:
|
|
@@ -24,7 +24,7 @@ def show_blog(blog):
|
|
|
24
24
|
blog.url = f"/b/{blog.name}"
|
|
25
25
|
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
26
26
|
if "{posts}" in homepage.content_raw:
|
|
27
|
-
posts = g.db.query(Post).
|
|
27
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
28
28
|
return render_template("blog.html", blog=blog, page=homepage, posts=posts, pages=pages)
|
|
29
29
|
|
|
30
30
|
return render_template("blog.html", blog=blog, page=homepage, pages=pages)
|
|
@@ -40,10 +40,11 @@ def show_subblog(blog):
|
|
|
40
40
|
if blog.access == "path":
|
|
41
41
|
return redirect(f"https://{os.getenv('DOMAIN')}/b/{blog.name}")
|
|
42
42
|
|
|
43
|
+
blog.url = f"https://{blog.name}.{os.getenv('DOMAIN')}"
|
|
43
44
|
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
44
45
|
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
45
46
|
if "{posts}" in homepage.content_raw:
|
|
46
|
-
posts = g.db.query(Post).
|
|
47
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
47
48
|
return render_template("blog.html", blog=blog, page=homepage, posts=posts, pages=pages)
|
|
48
49
|
|
|
49
50
|
return render_template("blog.html", blog=blog, page=homepage, pages=pages)
|
|
@@ -64,14 +65,14 @@ def show_post(blog, post):
|
|
|
64
65
|
|
|
65
66
|
blog.url = f"/b/{blog.name}"
|
|
66
67
|
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
67
|
-
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post).first()
|
|
68
|
+
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post, Post.isdraft == "0").first()
|
|
68
69
|
if not one_post:
|
|
69
70
|
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
70
71
|
if not page:
|
|
71
72
|
return redirect("/")
|
|
72
73
|
|
|
73
74
|
if "{posts}" in page.content_raw:
|
|
74
|
-
posts = g.db.query(Post).
|
|
75
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
75
76
|
|
|
76
77
|
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
77
78
|
|
|
@@ -112,8 +113,10 @@ def show_subpost(blog, post):
|
|
|
112
113
|
if blog.access == "path":
|
|
113
114
|
return redirect(f"https://{os.getenv('DOMAIN')}/b/{blog.name}/{post}")
|
|
114
115
|
|
|
116
|
+
blog.url = f"https://{blog.name}.{os.getenv('DOMAIN')}"
|
|
117
|
+
|
|
115
118
|
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
116
|
-
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post).first()
|
|
119
|
+
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post, Post.isdraft == "0").first()
|
|
117
120
|
|
|
118
121
|
if not one_post:
|
|
119
122
|
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
@@ -121,7 +124,7 @@ def show_subpost(blog, post):
|
|
|
121
124
|
return redirect("/")
|
|
122
125
|
|
|
123
126
|
if "{posts}" in page.content_raw:
|
|
124
|
-
posts = g.db.query(Post).
|
|
127
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
125
128
|
|
|
126
129
|
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
127
130
|
|
|
@@ -155,7 +158,7 @@ def single_showpost(post):
|
|
|
155
158
|
|
|
156
159
|
blog = g.db.query(Blog).filter_by(id=1).first()
|
|
157
160
|
blog.url = f"http://{g.main_domain}"
|
|
158
|
-
one_post = g.db.query(Post).filter(Post.blog == 1, Post.link == post).first()
|
|
161
|
+
one_post = g.db.query(Post).filter(Post.blog == 1, Post.link == post, Post.isdraft == "0").first()
|
|
159
162
|
|
|
160
163
|
pages = g.db.query(Page).filter_by(blog=1).all()
|
|
161
164
|
|
|
@@ -165,7 +168,7 @@ def single_showpost(post):
|
|
|
165
168
|
return redirect("/")
|
|
166
169
|
|
|
167
170
|
if "{posts}" in page.content_raw:
|
|
168
|
-
posts = g.db.query(Post).
|
|
171
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
169
172
|
|
|
170
173
|
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
171
174
|
|
|
@@ -230,7 +233,7 @@ def like():
|
|
|
230
233
|
|
|
231
234
|
|
|
232
235
|
def _generate_rss(blog):
|
|
233
|
-
posts = g.db.query(Post).
|
|
236
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").all()
|
|
234
237
|
fg = FeedGenerator()
|
|
235
238
|
fg.title(blog.title)
|
|
236
239
|
fg.link(href=f"https://{os.getenv('DOMAIN')}/b/{blog.name}", rel="alternate")
|
|
@@ -99,8 +99,8 @@ def create_blog():
|
|
|
99
99
|
name="Home",
|
|
100
100
|
url="",
|
|
101
101
|
content_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog home page in [dashboard](https://{g.main_domain}/dashboard/edit/{form_url})\n\n---\n### Posts\n\n{{posts}}",
|
|
102
|
-
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=\"https://{g.main_domain}/dashboard/edit/{form_url}\">dashboard</a></p>\n\n<hr>\n<h3>Posts
|
|
103
|
-
show="
|
|
102
|
+
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=\"https://{g.main_domain}/dashboard/edit/{form_url}\">dashboard</a></p>\n\n<hr>\n<h3>Posts</h3>\n\n{{posts}}",
|
|
103
|
+
show="0"
|
|
104
104
|
)
|
|
105
105
|
g.db.add(new_page)
|
|
106
106
|
g.db.commit()
|
|
@@ -177,6 +177,9 @@ def new_post(name):
|
|
|
177
177
|
title = request.form.get('title')
|
|
178
178
|
if len(title) > 120:
|
|
179
179
|
return render_template("new_post.html", blog=blog, error="Title too long! Max 120 characters.")
|
|
180
|
+
|
|
181
|
+
if len(title) < 1:
|
|
182
|
+
return render_template("new_post.html", blog=blog, error="Title cannot be empty!")
|
|
180
183
|
|
|
181
184
|
link = gen_link(title)
|
|
182
185
|
if link == "rss":
|
|
@@ -198,7 +201,8 @@ def new_post(name):
|
|
|
198
201
|
author=request.form.get('author'),
|
|
199
202
|
link=link,
|
|
200
203
|
date=now,
|
|
201
|
-
feed=request.form.get('feed')
|
|
204
|
+
feed=request.form.get('feed'),
|
|
205
|
+
isdraft=request.form.get('draft', '0')
|
|
202
206
|
)
|
|
203
207
|
g.db.add(post)
|
|
204
208
|
g.db.commit()
|
|
@@ -319,6 +323,7 @@ def edit_post(blog, post):
|
|
|
319
323
|
p.feed = request.form.get("feed")
|
|
320
324
|
p.link = link
|
|
321
325
|
p.updated = now
|
|
326
|
+
p.isdraft = request.form.get("draft", "0")
|
|
322
327
|
g.db.commit()
|
|
323
328
|
return redirect(f"/dashboard/edit/{blog_obj.name}")
|
|
324
329
|
|
|
@@ -557,6 +562,9 @@ def page_delete(page):
|
|
|
557
562
|
if blog.owner != g.user:
|
|
558
563
|
return abort(403)
|
|
559
564
|
|
|
565
|
+
if page.url == "":
|
|
566
|
+
return redirect("/dashboard")
|
|
567
|
+
|
|
560
568
|
g.db.delete(page)
|
|
561
569
|
g.db.commit()
|
|
562
570
|
|
|
@@ -78,7 +78,100 @@ def activity(blog):
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
return Response(json.dumps(actor), content_type="application/activity+json"
|
|
81
|
+
return Response(json.dumps(actor), content_type="application/activity+json", headers={
|
|
82
|
+
'Cache-Control': 'max-age=3600',
|
|
83
|
+
'Vary': 'Accept'
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
@federation_bp.route("/activity/<blog>/create/<int:post_id>")
|
|
87
|
+
def activity_create(blog, post_id):
|
|
88
|
+
b = g.db.query(Blog).filter_by(name=blog).first()
|
|
89
|
+
if not b:
|
|
90
|
+
abort(404)
|
|
91
|
+
|
|
92
|
+
post = g.db.query(Post).filter_by(id=post_id, blog=b.id).first()
|
|
93
|
+
if not post:
|
|
94
|
+
abort(404)
|
|
95
|
+
|
|
96
|
+
if b.access == "path":
|
|
97
|
+
url = f"https://{g.main_domain}/b/{blog}"
|
|
98
|
+
else:
|
|
99
|
+
url = f"https://{blog}.{g.main_domain}"
|
|
100
|
+
|
|
101
|
+
dt = datetime.strptime(str(post.date), "%Y-%m-%d %H:%M:%S")
|
|
102
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
103
|
+
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
104
|
+
|
|
105
|
+
create_activity = {
|
|
106
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
107
|
+
"id": f"https://{g.main_domain}/activity/{blog}/create/{post.id}",
|
|
108
|
+
"type": "Create",
|
|
109
|
+
"actor": f"https://{g.main_domain}/activity/{blog}",
|
|
110
|
+
"published": iso,
|
|
111
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
112
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
113
|
+
"object": {
|
|
114
|
+
"id": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
115
|
+
"type": "Note",
|
|
116
|
+
"summary": None,
|
|
117
|
+
"attributedTo": f"https://{g.main_domain}/activity/{blog}",
|
|
118
|
+
"content": f"<h3>{post.title}</h3><p><a href=\"{url}/{post.link}\">{url}/{post.link}</a></p>",
|
|
119
|
+
"published": iso,
|
|
120
|
+
"url": f"{url}/{post.link}",
|
|
121
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
122
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
123
|
+
"sensitive": False,
|
|
124
|
+
"atomUri": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
125
|
+
"attachment": [],
|
|
126
|
+
"tag": []
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return Response(json.dumps(create_activity), content_type="application/activity+json", headers={
|
|
131
|
+
'Cache-Control': 'max-age=3600',
|
|
132
|
+
'Vary': 'Accept'
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
@federation_bp.route("/activity/<blog>/posts/<int:post_id>")
|
|
136
|
+
def activity_post(blog, post_id):
|
|
137
|
+
b = g.db.query(Blog).filter_by(name=blog).first()
|
|
138
|
+
if not b:
|
|
139
|
+
abort(404)
|
|
140
|
+
|
|
141
|
+
post = g.db.query(Post).filter_by(id=post_id, blog=b.id).first()
|
|
142
|
+
if not post:
|
|
143
|
+
abort(404)
|
|
144
|
+
|
|
145
|
+
if b.access == "path":
|
|
146
|
+
url = f"https://{g.main_domain}/b/{blog}"
|
|
147
|
+
else:
|
|
148
|
+
url = f"https://{blog}.{g.main_domain}"
|
|
149
|
+
|
|
150
|
+
dt = datetime.strptime(str(post.date), "%Y-%m-%d %H:%M:%S")
|
|
151
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
152
|
+
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
153
|
+
|
|
154
|
+
note = {
|
|
155
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
156
|
+
"id": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
157
|
+
"type": "Note",
|
|
158
|
+
"summary": None,
|
|
159
|
+
"attributedTo": f"https://{g.main_domain}/activity/{blog}",
|
|
160
|
+
"content": f"<h3>{post.title}</h3><p><a href=\"{url}/{post.link}\">{url}/{post.link}</a></p>",
|
|
161
|
+
"published": iso,
|
|
162
|
+
"url": f"{url}/{post.link}",
|
|
163
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
164
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
165
|
+
"sensitive": False,
|
|
166
|
+
"atomUri": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
167
|
+
"attachment": [],
|
|
168
|
+
"tag": []
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return Response(json.dumps(note), content_type="application/activity+json", headers={
|
|
172
|
+
'Cache-Control': 'max-age=3600',
|
|
173
|
+
'Vary': 'Accept'
|
|
174
|
+
})
|
|
82
175
|
|
|
83
176
|
@federation_bp.route("/inbox/<blog>", methods=["POST"])
|
|
84
177
|
def inbox(blog):
|
|
@@ -90,12 +183,6 @@ def inbox(blog):
|
|
|
90
183
|
if not data:
|
|
91
184
|
return "Bad Request", 400
|
|
92
185
|
|
|
93
|
-
#print(f"""
|
|
94
|
-
# Data received:
|
|
95
|
-
|
|
96
|
-
# {data}
|
|
97
|
-
#""")
|
|
98
|
-
|
|
99
186
|
body = request.get_data(as_text=True)
|
|
100
187
|
sign = verify_http_signature(request.headers, body, blog)
|
|
101
188
|
if not sign:
|
|
@@ -190,24 +277,66 @@ def inbox(blog):
|
|
|
190
277
|
@federation_bp.route("/outbox/<blog>")
|
|
191
278
|
def outbox(blog):
|
|
192
279
|
page = request.args.get("page")
|
|
280
|
+
|
|
193
281
|
b = g.db.query(Blog).filter_by(name=blog).first()
|
|
194
282
|
if not b:
|
|
195
283
|
abort(404)
|
|
196
284
|
|
|
197
|
-
p = g.db.query(Post).filter_by(blog=b.id)
|
|
285
|
+
p = g.db.query(Post).filter_by(blog=b.id).order_by(Post.date.desc())
|
|
198
286
|
total = p.count()
|
|
199
287
|
posts = p.all()
|
|
288
|
+
|
|
200
289
|
first_outbox = {
|
|
201
290
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
202
|
-
"id": f"https://
|
|
291
|
+
"id": f"https://{g.main_domain}/outbox/{blog}",
|
|
203
292
|
"type": "OrderedCollection",
|
|
204
293
|
"totalItems": total,
|
|
205
|
-
"first": f"https://
|
|
294
|
+
"first": f"https://{g.main_domain}/outbox/{blog}?page=true"
|
|
206
295
|
}
|
|
207
296
|
|
|
208
|
-
if
|
|
209
|
-
|
|
210
|
-
|
|
297
|
+
if not page:
|
|
298
|
+
if total <= 10:
|
|
299
|
+
orderedPosts = []
|
|
300
|
+
if b.access == "path":
|
|
301
|
+
url = f"https://{g.main_domain}/b/{blog}"
|
|
302
|
+
else:
|
|
303
|
+
url = f"https://{blog}.{g.main_domain}"
|
|
304
|
+
|
|
305
|
+
for post in posts:
|
|
306
|
+
dt = datetime.strptime(str(post.date), "%Y-%m-%d %H:%M:%S")
|
|
307
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
308
|
+
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
309
|
+
orderedPosts.append({
|
|
310
|
+
"id": f"https://{g.main_domain}/activity/{blog}/create/{post.id}",
|
|
311
|
+
"type": "Create",
|
|
312
|
+
"actor": f"https://{g.main_domain}/activity/{blog}",
|
|
313
|
+
"published": iso,
|
|
314
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
315
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
316
|
+
"object": {
|
|
317
|
+
"id": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
318
|
+
"type": "Note",
|
|
319
|
+
"attributedTo": f"https://{g.main_domain}/activity/{blog}",
|
|
320
|
+
"content": f"<p><strong>{post.title}</strong></p><p><a href=\"{url}/{post.link}\">{url}/{post.link}</a></p>",
|
|
321
|
+
"published": iso,
|
|
322
|
+
"url": f"{url}/{post.link}",
|
|
323
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
324
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
325
|
+
"sensitive": False
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
first_outbox["orderedItems"] = orderedPosts
|
|
330
|
+
|
|
331
|
+
return Response(json.dumps(first_outbox), content_type="application/activity+json; charset=utf-8", headers={
|
|
332
|
+
'Cache-Control': 'max-age=300',
|
|
333
|
+
'Vary': 'Accept'
|
|
334
|
+
})
|
|
335
|
+
else:
|
|
336
|
+
return Response(json.dumps(first_outbox), content_type="application/activity+json; charset=utf-8", headers={
|
|
337
|
+
'Cache-Control': 'max-age=300',
|
|
338
|
+
'Vary': 'Accept'
|
|
339
|
+
})
|
|
211
340
|
|
|
212
341
|
orderedPosts = []
|
|
213
342
|
if b.access == "path":
|
|
@@ -219,29 +348,52 @@ def outbox(blog):
|
|
|
219
348
|
dt = dt.replace(tzinfo=timezone.utc)
|
|
220
349
|
iso = dt.isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
221
350
|
orderedPosts.append({
|
|
222
|
-
"id": f"https://{g.main_domain}/activity/{blog}
|
|
351
|
+
"id": f"https://{g.main_domain}/activity/{blog}/create/{post.id}",
|
|
223
352
|
"type": "Create",
|
|
224
353
|
"actor": f"https://{g.main_domain}/activity/{blog}",
|
|
354
|
+
"published": iso,
|
|
355
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
356
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
225
357
|
"object": {
|
|
226
|
-
"id": f"{
|
|
358
|
+
"id": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
227
359
|
"type": "Note",
|
|
360
|
+
"summary": None,
|
|
228
361
|
"attributedTo": f"https://{g.main_domain}/activity/{blog}",
|
|
229
|
-
"content": f"<
|
|
230
|
-
"published": iso
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
362
|
+
"content": f"<h3>{post.title}</h3><p><a href=\"{url}/{post.link}\">{url}/{post.link}</a></p>",
|
|
363
|
+
"published": iso,
|
|
364
|
+
"url": f"{url}/{post.link}",
|
|
365
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
366
|
+
"cc": [f"https://{g.main_domain}/followers/{blog}"],
|
|
367
|
+
"sensitive": False,
|
|
368
|
+
"atomUri": f"https://{g.main_domain}/activity/{blog}/posts/{post.id}",
|
|
369
|
+
"attachment": [],
|
|
370
|
+
"tag": []
|
|
371
|
+
}
|
|
234
372
|
})
|
|
235
373
|
|
|
236
374
|
outbox = {
|
|
237
|
-
"@context":
|
|
238
|
-
|
|
375
|
+
"@context": [
|
|
376
|
+
"https://www.w3.org/ns/activitystreams",
|
|
377
|
+
{
|
|
378
|
+
"ostatus": "http://ostatus.org#",
|
|
379
|
+
"atomUri": "ostatus:atomUri",
|
|
380
|
+
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
|
381
|
+
"conversation": "ostatus:conversation",
|
|
382
|
+
"sensitive": "as:sensitive",
|
|
383
|
+
"toot": "http://joinmastodon.org/ns#",
|
|
384
|
+
"votersCount": "toot:votersCount"
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
"id": f"https://{g.main_domain}/outbox/{blog}?page={page}",
|
|
239
388
|
"type": "OrderedCollectionPage",
|
|
240
389
|
"partOf": f"https://{g.main_domain}/outbox/{blog}",
|
|
241
390
|
"orderedItems": orderedPosts
|
|
242
391
|
}
|
|
243
392
|
|
|
244
|
-
return Response(json.dumps(outbox), content_type="application/activity+json"
|
|
393
|
+
return Response(json.dumps(outbox), content_type="application/activity+json", headers={
|
|
394
|
+
'Cache-Control': 'max-age=300',
|
|
395
|
+
'Vary': 'Accept'
|
|
396
|
+
})
|
|
245
397
|
|
|
246
398
|
@federation_bp.route("/followers/<blog>")
|
|
247
399
|
def followers(blog):
|
|
@@ -264,7 +416,7 @@ def followers(blog):
|
|
|
264
416
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
265
417
|
"id": f"https://{g.main_domain}/followers/{blog}?page=true",
|
|
266
418
|
"type": "OrderedCollectionPage",
|
|
267
|
-
"partOf": f"https://{g.main_domain}
|
|
419
|
+
"partOf": f"https://{g.main_domain}/followers/{blog}",
|
|
268
420
|
"totalItems": len(followers),
|
|
269
421
|
"orderedItems": followers
|
|
270
422
|
}
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import json
|
|
4
4
|
import datetime
|
|
5
5
|
import requests
|
|
6
|
-
from openwrite.utils.models import Post, Blog, Home, Settings, Page
|
|
6
|
+
from openwrite.utils.models import Post, Blog, Home, Settings, Page, Like
|
|
7
7
|
import time
|
|
8
8
|
from openwrite import start_time
|
|
9
9
|
|
|
@@ -71,7 +71,7 @@ def instances():
|
|
|
71
71
|
def discover():
|
|
72
72
|
if g.mode == "single":
|
|
73
73
|
return redirect("/")
|
|
74
|
-
posts = g.db.query(Post).
|
|
74
|
+
posts = g.db.query(Post).filter(Post.feed == "1", Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
75
75
|
for p in posts:
|
|
76
76
|
b = g.db.query(Blog).filter_by(id=p.blog).first()
|
|
77
77
|
if not b:
|
|
@@ -82,6 +82,8 @@ def discover():
|
|
|
82
82
|
url = f"https://{b.name}.{os.getenv('DOMAIN')}/{p.link}"
|
|
83
83
|
else:
|
|
84
84
|
url = "#"
|
|
85
|
+
l = g.db.query(Like).filter(Like.post == p.id, Like.blog == b.id).count()
|
|
86
|
+
p.likes = l
|
|
85
87
|
p.url = url
|
|
86
88
|
p.blogname = b.title
|
|
87
89
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div style="display: flex; flex-direction: column; gap: 1rem">
|
|
5
5
|
{% for p in posts %}
|
|
6
6
|
<div class="discover_post">
|
|
7
|
-
<i class="datetime">{{ p.date }}</i>{{ p.blogname }}: <a class="action" href="{{ p.url }}" target="_blank">{{ p.title }}</a>
|
|
7
|
+
<i class="datetime">{{ p.date }}</i>{{ p.blogname }}: <a class="action" href="{{ p.url }}" target="_blank">{{ p.title }}</a> {{ p.likes }} ♡
|
|
8
8
|
</div>
|
|
9
9
|
{% endfor %}
|
|
10
10
|
</div>
|