openwrite 0.11.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.11.0 → openwrite-0.13.0}/MANIFEST.in +3 -0
- {openwrite-0.11.0/openwrite.egg-info → openwrite-0.13.0}/PKG-INFO +5 -1
- {openwrite-0.11.0 → openwrite-0.13.0}/README.md +4 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/__init__.py +14 -3
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/gemini_multi.py +33 -12
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/gemini_single.py +37 -12
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/admin.py +49 -1
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/blog.py +62 -15
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/dashboard.py +175 -13
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/federation.py +176 -24
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/main.py +11 -3
- openwrite-0.13.0/openwrite/static/emoji.json +15627 -0
- openwrite-0.13.0/openwrite/static/script/nativeEmoji.js +389 -0
- openwrite-0.13.0/openwrite/static/style/nativeEmoji.min.css +1 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/style.css +27 -2
- openwrite-0.13.0/openwrite/static/style/themes/cyberpunk.css +391 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/light.css +11 -1
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/stickynote.css +11 -1
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/terminal.css +9 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/warmnight.css +5 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/windows98.css +58 -3
- openwrite-0.13.0/openwrite/templates/blog.html +18 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/blog_header.html +16 -1
- openwrite-0.13.0/openwrite/templates/blog_preview.html +57 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/dashboard.html +1 -1
- openwrite-0.13.0/openwrite/templates/discover.html +11 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/edit.html +40 -54
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/header.html +4 -0
- openwrite-0.13.0/openwrite/templates/new_page.html +218 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/new_post.html +51 -3
- openwrite-0.11.0/openwrite/templates/blog_preview.html → openwrite-0.13.0/openwrite/templates/page_preview.html +15 -11
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/translations.html +1 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/utils/create_db.py +1 -1
- openwrite-0.13.0/openwrite/utils/i18n.json +1 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/utils/models.py +16 -0
- {openwrite-0.11.0 → openwrite-0.13.0/openwrite.egg-info}/PKG-INFO +5 -1
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite.egg-info/SOURCES.txt +6 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/pyproject.toml +4 -1
- openwrite-0.11.0/openwrite/templates/blog.html +0 -16
- openwrite-0.11.0/openwrite/templates/discover.html +0 -11
- openwrite-0.11.0/openwrite/utils/i18n.json +0 -227
- {openwrite-0.11.0 → openwrite-0.13.0}/LICENSE +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/cli.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/db/__init__.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/db/base.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/auth.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/routes/upload.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/IBMPlexSans-Regular.ttf +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/Merriweather.ttf +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/avatar.png +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/favicon.ico +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/logo.png +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/chart.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/highlight.min.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/openwrite.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/papaparse.min.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/showdown.min.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/showdown.min.js.map +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/site.compat.min.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/script/site.min.js +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/hl.css +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/static/style/themes/obscura.css +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/admin.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/blog_footer.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/changepw.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/create.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/footer.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/import.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/index.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/instances.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/login.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/post.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/preview.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/register.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/settings.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/templates/stats.html +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/utils/db.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite/utils/helpers.py +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite.egg-info/dependency_links.txt +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite.egg-info/entry_points.txt +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite.egg-info/requires.txt +0 -0
- {openwrite-0.11.0 → openwrite-0.13.0}/openwrite.egg-info/top_level.txt +0 -0
- {openwrite-0.11.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
|
|
|
@@ -690,6 +690,7 @@ Dynamic: license-file
|
|
|
690
690
|

|
|
691
691
|
openwrite is a minimalist blogging platform built for writing freely, hosting independently, and publishing without noise.
|
|
692
692
|
|
|
693
|
+

|
|
693
694
|

|
|
694
695
|
[](LICENSE)
|
|
695
696
|

|
|
@@ -703,6 +704,7 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
703
704
|
- Single-blog mode
|
|
704
705
|
- Supports sqlite and mysql databases
|
|
705
706
|
- Upload images to local storage or bunny cdn
|
|
707
|
+
- Drafts
|
|
706
708
|
- Simple markdown editor in posting
|
|
707
709
|
- Discover section
|
|
708
710
|
- Privacy:
|
|
@@ -719,6 +721,8 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
719
721
|
- Gemini protocol
|
|
720
722
|
- Statistics per post
|
|
721
723
|
- Multilanguage (currently English and Polish)
|
|
724
|
+
- Custom favicon per blog
|
|
725
|
+
- Subpages per blog
|
|
722
726
|
|
|
723
727
|
|
|
724
728
|
## Installation
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|
openwrite is a minimalist blogging platform built for writing freely, hosting independently, and publishing without noise.
|
|
3
3
|
|
|
4
|
+

|
|
4
5
|

|
|
5
6
|
[](LICENSE)
|
|
6
7
|

|
|
@@ -14,6 +15,7 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
14
15
|
- Single-blog mode
|
|
15
16
|
- Supports sqlite and mysql databases
|
|
16
17
|
- Upload images to local storage or bunny cdn
|
|
18
|
+
- Drafts
|
|
17
19
|
- Simple markdown editor in posting
|
|
18
20
|
- Discover section
|
|
19
21
|
- Privacy:
|
|
@@ -30,6 +32,8 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
30
32
|
- Gemini protocol
|
|
31
33
|
- Statistics per post
|
|
32
34
|
- Multilanguage (currently English and Polish)
|
|
35
|
+
- Custom favicon per blog
|
|
36
|
+
- Subpages per blog
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
## Installation
|
|
@@ -32,7 +32,7 @@ def create_app(test_config=None):
|
|
|
32
32
|
file_cwd = "/".join(f_abs_path.split("/")[:-1])
|
|
33
33
|
from .utils.create_db import init_db
|
|
34
34
|
init_db(db_type, db_path)
|
|
35
|
-
from .utils.models import User, Blog, Post, Settings, Home
|
|
35
|
+
from .utils.models import User, Blog, Post, Settings, Home, Page
|
|
36
36
|
from .utils.db import init_engine, SessionLocal
|
|
37
37
|
init_engine(db_type, db_path)
|
|
38
38
|
from .utils.db import SessionLocal
|
|
@@ -68,15 +68,26 @@ def create_app(test_config=None):
|
|
|
68
68
|
title=domain,
|
|
69
69
|
index="on",
|
|
70
70
|
access="domain",
|
|
71
|
-
description_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog description in [dashboard](http://{domain}/dashboard/edit/default)",
|
|
72
|
-
description_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 description in <a href=\"http://{domain}/dashboard/edit/default\">dashboard</a></p>",
|
|
73
71
|
css="",
|
|
72
|
+
description_raw="x",
|
|
73
|
+
description_html="x",
|
|
74
74
|
pub_key=public_pem,
|
|
75
75
|
priv_key=private_pem,
|
|
76
76
|
theme="default",
|
|
77
|
+
favicon="",
|
|
77
78
|
created=now
|
|
78
79
|
)
|
|
79
80
|
SessionLocal.add(new_blog)
|
|
81
|
+
|
|
82
|
+
new_page = Page(
|
|
83
|
+
blog=1,
|
|
84
|
+
name="Home",
|
|
85
|
+
url="",
|
|
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
|
+
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="0"
|
|
89
|
+
)
|
|
90
|
+
SessionLocal.add(new_page)
|
|
80
91
|
|
|
81
92
|
hometext_pl = """
|
|
82
93
|
<h1 class="centered"><span style="color: #f2d2c8">cicha</span> przestrzeń na <span style="color: #5a36bf">głośne</span> myśli.</h1>
|
|
@@ -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:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from flask import Blueprint, render_template, redirect, g, request
|
|
1
|
+
from flask import Blueprint, render_template, redirect, g, request, make_response
|
|
2
|
+
import json
|
|
2
3
|
from openwrite.utils.models import Blog, User, Settings, Home
|
|
3
4
|
import re
|
|
4
5
|
import bcrypt
|
|
@@ -13,6 +14,9 @@ ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "webp"}
|
|
|
13
14
|
|
|
14
15
|
@admin_bp.route("/admin")
|
|
15
16
|
def admin():
|
|
17
|
+
"""
|
|
18
|
+
Admin dashboard to manage blogs and users.
|
|
19
|
+
"""
|
|
16
20
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
17
21
|
return redirect("/")
|
|
18
22
|
|
|
@@ -26,6 +30,9 @@ def admin():
|
|
|
26
30
|
|
|
27
31
|
@admin_bp.route("/admin/delete_blog/<blog>")
|
|
28
32
|
def admin_delete_blog(blog):
|
|
33
|
+
"""
|
|
34
|
+
Delete a blog by its name.
|
|
35
|
+
"""
|
|
29
36
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
30
37
|
return redirect("/")
|
|
31
38
|
|
|
@@ -38,6 +45,9 @@ def admin_delete_blog(blog):
|
|
|
38
45
|
|
|
39
46
|
@admin_bp.route("/admin/delete_user/<username>")
|
|
40
47
|
def admin_delete_user(username):
|
|
48
|
+
"""
|
|
49
|
+
Delete a user and all their blogs.
|
|
50
|
+
"""
|
|
41
51
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
42
52
|
return redirect("/")
|
|
43
53
|
|
|
@@ -59,6 +69,9 @@ def admin_delete_user(username):
|
|
|
59
69
|
|
|
60
70
|
@admin_bp.route("/admin/make_admin/<username>")
|
|
61
71
|
def admin_make_admin(username):
|
|
72
|
+
"""
|
|
73
|
+
Grant admin privileges to a user.
|
|
74
|
+
"""
|
|
62
75
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
63
76
|
return redirect("/")
|
|
64
77
|
|
|
@@ -73,6 +86,9 @@ def admin_make_admin(username):
|
|
|
73
86
|
|
|
74
87
|
@admin_bp.route("/admin/remove_admin/<username>")
|
|
75
88
|
def admin_remove_admin(username):
|
|
89
|
+
"""
|
|
90
|
+
Remove admin privileges from a user.
|
|
91
|
+
"""
|
|
76
92
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
77
93
|
return redirect("/")
|
|
78
94
|
|
|
@@ -90,6 +106,9 @@ def admin_remove_admin(username):
|
|
|
90
106
|
|
|
91
107
|
@admin_bp.route("/admin/add_user", methods=['GET', 'POST'])
|
|
92
108
|
def admin_add_user():
|
|
109
|
+
"""
|
|
110
|
+
Add a new user to the system.
|
|
111
|
+
"""
|
|
93
112
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
94
113
|
return redirect("/")
|
|
95
114
|
if request.method == "GET":
|
|
@@ -122,6 +141,9 @@ def admin_add_user():
|
|
|
122
141
|
|
|
123
142
|
@admin_bp.route("/admin/settings", methods=['GET', 'POST'])
|
|
124
143
|
def admin_settings():
|
|
144
|
+
"""
|
|
145
|
+
Manage application settings.
|
|
146
|
+
"""
|
|
125
147
|
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
126
148
|
return redirect("/")
|
|
127
149
|
|
|
@@ -174,6 +196,14 @@ def admin_settings():
|
|
|
174
196
|
|
|
175
197
|
@admin_bp.route("/admin/translations", methods=['GET', 'POST'])
|
|
176
198
|
def translations():
|
|
199
|
+
"""
|
|
200
|
+
Manage translations for the application.
|
|
201
|
+
Allows viewing, editing, and saving translations.
|
|
202
|
+
(I can't believe I didn't secured it earlier lol)
|
|
203
|
+
"""
|
|
204
|
+
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
205
|
+
return redirect("/")
|
|
206
|
+
|
|
177
207
|
if request.method == "GET":
|
|
178
208
|
trans = g.db.query(Home).filter_by(type="translation").all()
|
|
179
209
|
grouped = defaultdict(dict)
|
|
@@ -198,3 +228,21 @@ def translations():
|
|
|
198
228
|
|
|
199
229
|
g.db.commit()
|
|
200
230
|
return redirect("/admin/translations")
|
|
231
|
+
|
|
232
|
+
@admin_bp.route("/admin/download_translations")
|
|
233
|
+
def download_translations():
|
|
234
|
+
"""
|
|
235
|
+
Download all translations as a json file.
|
|
236
|
+
"""
|
|
237
|
+
if g.user is None or g.isadmin == 0 or g.mode == "single":
|
|
238
|
+
return redirect("/")
|
|
239
|
+
|
|
240
|
+
translations = g.db.query(Home).filter_by(type="translation").all()
|
|
241
|
+
trans_dict = defaultdict(dict)
|
|
242
|
+
for t in translations:
|
|
243
|
+
trans_dict[t.language][t.name] = t.content
|
|
244
|
+
|
|
245
|
+
response = make_response(json.dumps(dict(trans_dict), ensure_ascii=False))
|
|
246
|
+
response.headers["Content-Disposition"] = "attachment; filename=i18n.json"
|
|
247
|
+
response.headers["Content-Type"] = "application/json"
|
|
248
|
+
return response
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from flask import Blueprint, render_template, redirect, request, g, Response, abort
|
|
2
|
-
from openwrite.utils.models import Blog, Post, User, View, Like
|
|
2
|
+
from openwrite.utils.models import Blog, Post, User, View, Like, Page
|
|
3
3
|
from openwrite.utils.helpers import gen_link, sanitize_html, anonymize, get_ip
|
|
4
4
|
from feedgen.feed import FeedGenerator
|
|
5
5
|
from sqlalchemy import desc
|
|
@@ -20,10 +20,14 @@ def show_blog(blog):
|
|
|
20
20
|
if blog.access == "domain":
|
|
21
21
|
return redirect(f"https://{blog.name}.{os.getenv('DOMAIN')}/")
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
24
24
|
blog.url = f"/b/{blog.name}"
|
|
25
|
+
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
26
|
+
if "{posts}" in homepage.content_raw:
|
|
27
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
28
|
+
return render_template("blog.html", blog=blog, page=homepage, posts=posts, pages=pages)
|
|
25
29
|
|
|
26
|
-
return render_template("blog.html", blog=blog,
|
|
30
|
+
return render_template("blog.html", blog=blog, page=homepage, pages=pages)
|
|
27
31
|
|
|
28
32
|
@blog_bp.route("/", subdomain="<blog>")
|
|
29
33
|
def show_subblog(blog):
|
|
@@ -36,9 +40,14 @@ def show_subblog(blog):
|
|
|
36
40
|
if blog.access == "path":
|
|
37
41
|
return redirect(f"https://{os.getenv('DOMAIN')}/b/{blog.name}")
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
blog.url = f"https://{blog.name}.{os.getenv('DOMAIN')}"
|
|
44
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
45
|
+
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
46
|
+
if "{posts}" in homepage.content_raw:
|
|
47
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
48
|
+
return render_template("blog.html", blog=blog, page=homepage, posts=posts, pages=pages)
|
|
40
49
|
|
|
41
|
-
return render_template("blog.html", blog=blog,
|
|
50
|
+
return render_template("blog.html", blog=blog, page=homepage, pages=pages)
|
|
42
51
|
|
|
43
52
|
@blog_bp.route("/b/<blog>/<post>")
|
|
44
53
|
def show_post(blog, post):
|
|
@@ -54,9 +63,21 @@ def show_post(blog, post):
|
|
|
54
63
|
if blog.access == "domain":
|
|
55
64
|
return redirect(f"https://{blog.name}.{os.getenv('DOMAIN')}/{post}")
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
blog.url = f"/b/{blog.name}"
|
|
67
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
68
|
+
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post, Post.isdraft == "0").first()
|
|
58
69
|
if not one_post:
|
|
59
|
-
|
|
70
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
71
|
+
if not page:
|
|
72
|
+
return redirect("/")
|
|
73
|
+
|
|
74
|
+
if "{posts}" in page.content_raw:
|
|
75
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
76
|
+
|
|
77
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
78
|
+
|
|
79
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
80
|
+
|
|
60
81
|
|
|
61
82
|
post_author = g.db.query(User).filter_by(id=blog.owner).first()
|
|
62
83
|
one_post.authorname = post_author.username
|
|
@@ -76,7 +97,7 @@ def show_post(blog, post):
|
|
|
76
97
|
one_post.liked = liked
|
|
77
98
|
|
|
78
99
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
79
|
-
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes)
|
|
100
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes, pages=pages)
|
|
80
101
|
|
|
81
102
|
@blog_bp.route("/<post>", subdomain="<blog>")
|
|
82
103
|
def show_subpost(blog, post):
|
|
@@ -92,9 +113,22 @@ def show_subpost(blog, post):
|
|
|
92
113
|
if blog.access == "path":
|
|
93
114
|
return redirect(f"https://{os.getenv('DOMAIN')}/b/{blog.name}/{post}")
|
|
94
115
|
|
|
95
|
-
|
|
116
|
+
blog.url = f"https://{blog.name}.{os.getenv('DOMAIN')}"
|
|
117
|
+
|
|
118
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
119
|
+
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post, Post.isdraft == "0").first()
|
|
120
|
+
|
|
96
121
|
if not one_post:
|
|
97
|
-
|
|
122
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
123
|
+
if not page:
|
|
124
|
+
return redirect("/")
|
|
125
|
+
|
|
126
|
+
if "{posts}" in page.content_raw:
|
|
127
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
128
|
+
|
|
129
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
130
|
+
|
|
131
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
98
132
|
|
|
99
133
|
post_author = g.db.query(User).filter_by(id=blog.owner).first()
|
|
100
134
|
one_post.authorname = post_author.username
|
|
@@ -115,7 +149,7 @@ def show_subpost(blog, post):
|
|
|
115
149
|
one_post.liked = liked
|
|
116
150
|
|
|
117
151
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
118
|
-
return render_template("post.html", blog=blog, post=one_post, user=user, views=v)
|
|
152
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, pages=pages)
|
|
119
153
|
|
|
120
154
|
@blog_bp.route("/p/<post>")
|
|
121
155
|
def single_showpost(post):
|
|
@@ -123,9 +157,22 @@ def single_showpost(post):
|
|
|
123
157
|
return redirect("/")
|
|
124
158
|
|
|
125
159
|
blog = g.db.query(Blog).filter_by(id=1).first()
|
|
126
|
-
|
|
160
|
+
blog.url = f"http://{g.main_domain}"
|
|
161
|
+
one_post = g.db.query(Post).filter(Post.blog == 1, Post.link == post, Post.isdraft == "0").first()
|
|
162
|
+
|
|
163
|
+
pages = g.db.query(Page).filter_by(blog=1).all()
|
|
164
|
+
|
|
127
165
|
if not one_post:
|
|
128
|
-
|
|
166
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
167
|
+
if not page:
|
|
168
|
+
return redirect("/")
|
|
169
|
+
|
|
170
|
+
if "{posts}" in page.content_raw:
|
|
171
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").order_by(desc(Post.id)).all()
|
|
172
|
+
|
|
173
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
174
|
+
|
|
175
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
129
176
|
|
|
130
177
|
post_author = g.db.query(User).filter_by(id=1).first()
|
|
131
178
|
one_post.authorname = post_author.username
|
|
@@ -145,7 +192,7 @@ def single_showpost(post):
|
|
|
145
192
|
one_post.liked = liked
|
|
146
193
|
|
|
147
194
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
148
|
-
return render_template("post.html", blog=blog, post=one_post, user=user, views=v)
|
|
195
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, pages=pages)
|
|
149
196
|
|
|
150
197
|
@blog_bp.route("/rss")
|
|
151
198
|
def single_rss():
|
|
@@ -186,7 +233,7 @@ def like():
|
|
|
186
233
|
|
|
187
234
|
|
|
188
235
|
def _generate_rss(blog):
|
|
189
|
-
posts = g.db.query(Post).
|
|
236
|
+
posts = g.db.query(Post).filter(Post.blog == blog.id, Post.isdraft == "0").all()
|
|
190
237
|
fg = FeedGenerator()
|
|
191
238
|
fg.title(blog.title)
|
|
192
239
|
fg.link(href=f"https://{os.getenv('DOMAIN')}/b/{blog.name}", rel="alternate")
|