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

|
|
@@ -719,6 +720,8 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
719
720
|
- Gemini protocol
|
|
720
721
|
- Statistics per post
|
|
721
722
|
- Multilanguage (currently English and Polish)
|
|
723
|
+
- Custom favicon per blog
|
|
724
|
+
- Subpages per blog
|
|
722
725
|
|
|
723
726
|
|
|
724
727
|
## 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
|

|
|
@@ -30,6 +31,8 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
30
31
|
- Gemini protocol
|
|
31
32
|
- Statistics per post
|
|
32
33
|
- Multilanguage (currently English and Polish)
|
|
34
|
+
- Custom favicon per blog
|
|
35
|
+
- Subpages per blog
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
## 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="off"
|
|
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,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_by(blog=blog.id).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,13 @@ 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
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
44
|
+
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
45
|
+
if "{posts}" in homepage.content_raw:
|
|
46
|
+
posts = g.db.query(Post).filter_by(blog=blog.id).order_by(desc(Post.id)).all()
|
|
47
|
+
return render_template("blog.html", blog=blog, page=homepage, posts=posts, pages=pages)
|
|
40
48
|
|
|
41
|
-
return render_template("blog.html", blog=blog,
|
|
49
|
+
return render_template("blog.html", blog=blog, page=homepage, pages=pages)
|
|
42
50
|
|
|
43
51
|
@blog_bp.route("/b/<blog>/<post>")
|
|
44
52
|
def show_post(blog, post):
|
|
@@ -54,9 +62,21 @@ def show_post(blog, post):
|
|
|
54
62
|
if blog.access == "domain":
|
|
55
63
|
return redirect(f"https://{blog.name}.{os.getenv('DOMAIN')}/{post}")
|
|
56
64
|
|
|
65
|
+
blog.url = f"/b/{blog.name}"
|
|
66
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
57
67
|
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post).first()
|
|
58
68
|
if not one_post:
|
|
59
|
-
|
|
69
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
70
|
+
if not page:
|
|
71
|
+
return redirect("/")
|
|
72
|
+
|
|
73
|
+
if "{posts}" in page.content_raw:
|
|
74
|
+
posts = g.db.query(Post).filter_by(blog=blog.id).order_by(desc(Post.id)).all()
|
|
75
|
+
|
|
76
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
77
|
+
|
|
78
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
79
|
+
|
|
60
80
|
|
|
61
81
|
post_author = g.db.query(User).filter_by(id=blog.owner).first()
|
|
62
82
|
one_post.authorname = post_author.username
|
|
@@ -76,7 +96,7 @@ def show_post(blog, post):
|
|
|
76
96
|
one_post.liked = liked
|
|
77
97
|
|
|
78
98
|
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)
|
|
99
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes, pages=pages)
|
|
80
100
|
|
|
81
101
|
@blog_bp.route("/<post>", subdomain="<blog>")
|
|
82
102
|
def show_subpost(blog, post):
|
|
@@ -92,9 +112,20 @@ def show_subpost(blog, post):
|
|
|
92
112
|
if blog.access == "path":
|
|
93
113
|
return redirect(f"https://{os.getenv('DOMAIN')}/b/{blog.name}/{post}")
|
|
94
114
|
|
|
115
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
95
116
|
one_post = g.db.query(Post).filter(Post.blog == blog.id, Post.link == post).first()
|
|
117
|
+
|
|
96
118
|
if not one_post:
|
|
97
|
-
|
|
119
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
120
|
+
if not page:
|
|
121
|
+
return redirect("/")
|
|
122
|
+
|
|
123
|
+
if "{posts}" in page.content_raw:
|
|
124
|
+
posts = g.db.query(Post).filter_by(blog=blog.id).order_by(desc(Post.id)).all()
|
|
125
|
+
|
|
126
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
127
|
+
|
|
128
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
98
129
|
|
|
99
130
|
post_author = g.db.query(User).filter_by(id=blog.owner).first()
|
|
100
131
|
one_post.authorname = post_author.username
|
|
@@ -115,7 +146,7 @@ def show_subpost(blog, post):
|
|
|
115
146
|
one_post.liked = liked
|
|
116
147
|
|
|
117
148
|
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)
|
|
149
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, pages=pages)
|
|
119
150
|
|
|
120
151
|
@blog_bp.route("/p/<post>")
|
|
121
152
|
def single_showpost(post):
|
|
@@ -123,9 +154,22 @@ def single_showpost(post):
|
|
|
123
154
|
return redirect("/")
|
|
124
155
|
|
|
125
156
|
blog = g.db.query(Blog).filter_by(id=1).first()
|
|
157
|
+
blog.url = f"http://{g.main_domain}"
|
|
126
158
|
one_post = g.db.query(Post).filter(Post.blog == 1, Post.link == post).first()
|
|
159
|
+
|
|
160
|
+
pages = g.db.query(Page).filter_by(blog=1).all()
|
|
161
|
+
|
|
127
162
|
if not one_post:
|
|
128
|
-
|
|
163
|
+
page = g.db.query(Page).filter(Page.blog == blog.id, Page.url == post).first()
|
|
164
|
+
if not page:
|
|
165
|
+
return redirect("/")
|
|
166
|
+
|
|
167
|
+
if "{posts}" in page.content_raw:
|
|
168
|
+
posts = g.db.query(Post).filter_by(blog=blog.id).order_by(desc(Post.id)).all()
|
|
169
|
+
|
|
170
|
+
return render_template("blog.html", blog=blog, page=page, posts=posts, pages=pages)
|
|
171
|
+
|
|
172
|
+
return render_template("blog.html", blog=blog, page=page, pages=pages)
|
|
129
173
|
|
|
130
174
|
post_author = g.db.query(User).filter_by(id=1).first()
|
|
131
175
|
one_post.authorname = post_author.username
|
|
@@ -145,7 +189,7 @@ def single_showpost(post):
|
|
|
145
189
|
one_post.liked = liked
|
|
146
190
|
|
|
147
191
|
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)
|
|
192
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, pages=pages)
|
|
149
193
|
|
|
150
194
|
@blog_bp.route("/rss")
|
|
151
195
|
def single_rss():
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from flask import Blueprint, render_template, redirect, request, g, abort
|
|
2
|
-
from openwrite.utils.models import Blog, Post, User, View
|
|
2
|
+
from openwrite.utils.models import Blog, Post, User, View, Page
|
|
3
3
|
from openwrite.utils.helpers import sanitize_html, gen_link, safe_css, send_activity, is_html, get_themes
|
|
4
4
|
import requests
|
|
5
5
|
from sqlalchemy import desc
|
|
@@ -12,6 +12,7 @@ import bcrypt
|
|
|
12
12
|
import markdown
|
|
13
13
|
from bs4 import BeautifulSoup
|
|
14
14
|
from user_agents import parse
|
|
15
|
+
import re
|
|
15
16
|
|
|
16
17
|
dashboard_bp = Blueprint("dashboard", __name__)
|
|
17
18
|
|
|
@@ -79,16 +80,31 @@ def create_blog():
|
|
|
79
80
|
name=form_url,
|
|
80
81
|
index=form_index,
|
|
81
82
|
access=form_access,
|
|
82
|
-
description_raw=f"\n\n# Hello there! 👋\n\nYou can edit your blog description in [dashboard](https://{g.main_domain}/dashboard/edit/{form_url})",
|
|
83
|
-
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=\"https://{g.main_domain}/dashboard/edit/{form_url}\">dashboard</a></p>",
|
|
84
83
|
css="",
|
|
84
|
+
description_raw="x",
|
|
85
|
+
description_html="x",
|
|
85
86
|
pub_key=public_pem,
|
|
86
87
|
priv_key=private_pem,
|
|
87
88
|
theme="default",
|
|
89
|
+
favicon="",
|
|
88
90
|
created=now
|
|
89
91
|
)
|
|
90
92
|
g.db.add(new_blog)
|
|
91
93
|
g.db.commit()
|
|
94
|
+
|
|
95
|
+
b_id = g.db.query(Blog).filter_by(name=form_url).first().id
|
|
96
|
+
|
|
97
|
+
new_page = Page(
|
|
98
|
+
blog=b_id,
|
|
99
|
+
name="Home",
|
|
100
|
+
url="",
|
|
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\n\n{{posts}}",
|
|
103
|
+
show="off"
|
|
104
|
+
)
|
|
105
|
+
g.db.add(new_page)
|
|
106
|
+
g.db.commit()
|
|
107
|
+
|
|
92
108
|
return redirect("/dashboard")
|
|
93
109
|
except Exception:
|
|
94
110
|
return render_template("create.html", error=g.trans['error'])
|
|
@@ -122,27 +138,28 @@ def edit_blog(name):
|
|
|
122
138
|
v = g.db.query(View).filter(View.post == p.id, View.blog == blog.id).count()
|
|
123
139
|
p.views = v
|
|
124
140
|
|
|
141
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
142
|
+
|
|
125
143
|
themes = get_themes()
|
|
126
144
|
themes.append("default")
|
|
127
145
|
|
|
128
146
|
if request.method == "GET":
|
|
129
|
-
return render_template("edit.html", blog=blog, posts=posts, themes=themes)
|
|
147
|
+
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages)
|
|
130
148
|
|
|
131
149
|
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
132
|
-
blog.description_raw = request.form.get("description_raw")
|
|
133
|
-
blog.description_html = sanitize_html(request.form.get("description_html"))
|
|
134
150
|
if len(request.form.get("title")) > 30:
|
|
135
|
-
return render_template("edit.html", blog=blog, posts=posts, themes=themes, error="Title too long! Max 30 characters.")
|
|
151
|
+
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages, error="Title too long! Max 30 characters.")
|
|
136
152
|
blog.css = safe_css(request.form.get("css"))
|
|
137
153
|
blog.updated = now
|
|
138
154
|
blog.title = request.form.get("title")
|
|
155
|
+
blog.favicon = request.form.get("icon")[:10]
|
|
139
156
|
selected_theme = request.form.get("theme")
|
|
140
157
|
if selected_theme not in themes:
|
|
141
|
-
return render_template("edit.html", blog=blog, posts=posts, themes=themes, error="Wrong theme!")
|
|
158
|
+
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages, error="Wrong theme!")
|
|
142
159
|
blog.theme = selected_theme
|
|
143
160
|
g.db.commit()
|
|
144
161
|
|
|
145
|
-
return render_template("edit.html", blog=blog, posts=posts, themes=themes)
|
|
162
|
+
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages)
|
|
146
163
|
|
|
147
164
|
@dashboard_bp.route("/dashboard/post/<name>", methods=['GET', 'POST'])
|
|
148
165
|
def new_post(name):
|
|
@@ -164,7 +181,9 @@ def new_post(name):
|
|
|
164
181
|
link = gen_link(title)
|
|
165
182
|
if link == "rss":
|
|
166
183
|
link = "p_rss"
|
|
167
|
-
|
|
184
|
+
dupes_posts = g.db.query(Post).filter(Post.link.startswith(link), Post.blog == blog.id).count()
|
|
185
|
+
dupes_pages = g.db.query(Page).filter(Page.url == link, Page.blog == blog.id).count()
|
|
186
|
+
dupes = dupes_posts + dupes_pages
|
|
168
187
|
if dupes > 0:
|
|
169
188
|
link += f"-{dupes + 1}"
|
|
170
189
|
|
|
@@ -246,12 +265,21 @@ def blog_preview():
|
|
|
246
265
|
return redirect("/login")
|
|
247
266
|
|
|
248
267
|
u = g.db.query(User).filter_by(id=g.user).first()
|
|
268
|
+
blog_name = request.referrer.split("/")[-1]
|
|
269
|
+
blog = g.db.query(Blog).filter_by(name=blog_name).first()
|
|
270
|
+
if not blog:
|
|
271
|
+
return redirect("/")
|
|
272
|
+
|
|
273
|
+
homepage = g.db.query(Page).filter(Page.blog == blog.id, Page.url == "").first()
|
|
249
274
|
data = {
|
|
250
275
|
'title': request.form.get('title'),
|
|
251
|
-
'
|
|
276
|
+
'css': request.form.get('css'),
|
|
277
|
+
'icon': request.form.get('icon'),
|
|
278
|
+
'content': homepage.content_html,
|
|
252
279
|
'theme': request.form.get('theme')
|
|
253
280
|
}
|
|
254
281
|
|
|
282
|
+
|
|
255
283
|
return render_template("blog_preview.html", data=data)
|
|
256
284
|
|
|
257
285
|
@dashboard_bp.route("/dashboard/edit/<blog>/<post>", methods=['GET', 'POST'])
|
|
@@ -277,7 +305,9 @@ def edit_post(blog, post):
|
|
|
277
305
|
link = gen_link(title)
|
|
278
306
|
if link == "rss":
|
|
279
307
|
link = "p_rss"
|
|
280
|
-
|
|
308
|
+
dupes_posts = g.db.query(Post).filter(Post.link.startswith(link), Post.blog == blog_obj.id).count()
|
|
309
|
+
dupes_pages = g.db.query(Page).filter(Page.url == link, Page.blog == blog_obj.id).count()
|
|
310
|
+
dupes = dupes_posts + dupes_pages
|
|
281
311
|
if dupes > 0 and link != post:
|
|
282
312
|
link += f"-{dupes + 1}"
|
|
283
313
|
|
|
@@ -407,3 +437,127 @@ def get_stats(blog, post, limit):
|
|
|
407
437
|
views_obj["views"].append([v.date, os, browser])
|
|
408
438
|
|
|
409
439
|
return views_obj
|
|
440
|
+
|
|
441
|
+
@dashboard_bp.route("/dashboard/page/<blog>", methods=['GET', 'POST'])
|
|
442
|
+
def new_page(blog):
|
|
443
|
+
if g.user is None:
|
|
444
|
+
abort(403)
|
|
445
|
+
blog = g.db.query(Blog).filter_by(name=blog).first()
|
|
446
|
+
|
|
447
|
+
if not blog:
|
|
448
|
+
abort(404)
|
|
449
|
+
|
|
450
|
+
if blog.owner != g.user:
|
|
451
|
+
abort(403)
|
|
452
|
+
|
|
453
|
+
if request.method == "GET":
|
|
454
|
+
return render_template("new_page.html", blog=blog)
|
|
455
|
+
|
|
456
|
+
name = request.form.get("name")
|
|
457
|
+
url = request.form.get("url")
|
|
458
|
+
content_raw = request.form.get("content_raw")
|
|
459
|
+
content_html = request.form.get("content")
|
|
460
|
+
show = request.form.get("show")
|
|
461
|
+
if len(name) > 120:
|
|
462
|
+
return render_template("new_page.html", blog=blog, error="Name too long! Max 120 characters")
|
|
463
|
+
if not re.match(r"^\/[a-zA-Z0-9\-\_]+$", url):
|
|
464
|
+
return render_template("new_page.html", blog=blog, error="Invalid URL!")
|
|
465
|
+
|
|
466
|
+
url = url[1:]
|
|
467
|
+
if url == "rss":
|
|
468
|
+
url = "p_rss"
|
|
469
|
+
dupes_posts = g.db.query(Post).filter(Post.link.startswith(url), Post.blog == blog.id).count()
|
|
470
|
+
dupes_pages = g.db.query(Page).filter(Page.url == url, Page.blog == blog.id).count()
|
|
471
|
+
dupes = dupes_posts + dupes_pages
|
|
472
|
+
if dupes > 0:
|
|
473
|
+
url = f"{url}-{dupes + 1}"
|
|
474
|
+
|
|
475
|
+
new_page = Page(blog=blog.id, name=name, url=url, content_raw=content_raw, content_html=sanitize_html(content_html), show=show)
|
|
476
|
+
|
|
477
|
+
g.db.add(new_page)
|
|
478
|
+
g.db.commit()
|
|
479
|
+
return redirect(f"/dashboard/edit/{blog.name}")
|
|
480
|
+
|
|
481
|
+
@dashboard_bp.route("/dashboard/page/<page>/edit", methods=['GET', 'POST'])
|
|
482
|
+
def edit_page(page):
|
|
483
|
+
if g.user is None:
|
|
484
|
+
abort(403)
|
|
485
|
+
|
|
486
|
+
page = g.db.query(Page).filter_by(id=page).first()
|
|
487
|
+
if not page:
|
|
488
|
+
abort(404)
|
|
489
|
+
blog = g.db.query(Blog).filter_by(id=page.blog).first()
|
|
490
|
+
|
|
491
|
+
if not blog:
|
|
492
|
+
abort(404)
|
|
493
|
+
|
|
494
|
+
if blog.owner != g.user:
|
|
495
|
+
abort(403)
|
|
496
|
+
home = False
|
|
497
|
+
if page.url == "":
|
|
498
|
+
home = True
|
|
499
|
+
|
|
500
|
+
if request.method == 'GET':
|
|
501
|
+
return render_template("new_page.html", blog=blog, page=page, home=home)
|
|
502
|
+
|
|
503
|
+
name = request.form.get("name")
|
|
504
|
+
content_raw = request.form.get("content_raw")
|
|
505
|
+
content_html = request.form.get("content")
|
|
506
|
+
show = request.form.get("show")
|
|
507
|
+
url = request.form.get("url")
|
|
508
|
+
if len(name) > 120:
|
|
509
|
+
return render_template("new_page.html", blog=blog, page=page, home=home, error="Name too long!")
|
|
510
|
+
if home == False:
|
|
511
|
+
if not re.match(r"^\/[a-zA-Z0-9\-\_]+$", url):
|
|
512
|
+
return render_template("new_page.html", blog=blog, error="Invalid URL!")
|
|
513
|
+
|
|
514
|
+
url = url[1:]
|
|
515
|
+
if url == "rss":
|
|
516
|
+
url = "p_rss"
|
|
517
|
+
dupes_posts = g.db.query(Post).filter(Post.link.startswith(url), Post.blog == blog.id).count()
|
|
518
|
+
dupes_pages = g.db.query(Page).filter(Page.url == url, Page.blog == blog.id).count()
|
|
519
|
+
dupes = dupes_posts + dupes_pages
|
|
520
|
+
if dupes > 0 and url != page.url:
|
|
521
|
+
url = f"{url}-{dupes + 1}"
|
|
522
|
+
|
|
523
|
+
page.name = name
|
|
524
|
+
page.content_raw = content_raw
|
|
525
|
+
page.content_html = sanitize_html(content_html)
|
|
526
|
+
page.show = show
|
|
527
|
+
if home == False:
|
|
528
|
+
page.url = url
|
|
529
|
+
|
|
530
|
+
g.db.commit()
|
|
531
|
+
return redirect(f"/dashboard/edit/{blog.name}")
|
|
532
|
+
|
|
533
|
+
@dashboard_bp.route("/dashboard/page_preview", methods=['POST'])
|
|
534
|
+
def page_preview():
|
|
535
|
+
if g.user is None:
|
|
536
|
+
return redirect("/")
|
|
537
|
+
|
|
538
|
+
data = {
|
|
539
|
+
'name': request.form.get('name'),
|
|
540
|
+
'content': sanitize_html(request.form.get('content')),
|
|
541
|
+
'blog_title': request.form.get('blog_title'),
|
|
542
|
+
'blog_name': request.form.get('blog_name'),
|
|
543
|
+
'theme': request.form.get('theme')
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return render_template("page_preview.html", data=data)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@dashboard_bp.route("/dashboard/page/<page>/delete")
|
|
550
|
+
def page_delete(page):
|
|
551
|
+
if g.user is None:
|
|
552
|
+
return redirect("/")
|
|
553
|
+
|
|
554
|
+
page = g.db.query(Page).filter_by(id=page).first()
|
|
555
|
+
blog = g.db.query(Blog).filter_by(id=page.blog).first()
|
|
556
|
+
|
|
557
|
+
if blog.owner != g.user:
|
|
558
|
+
return abort(403)
|
|
559
|
+
|
|
560
|
+
g.db.delete(page)
|
|
561
|
+
g.db.commit()
|
|
562
|
+
|
|
563
|
+
return redirect(f"/dashboard/edit/{blog.name}")
|
|
@@ -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
|
|
6
|
+
from openwrite.utils.models import Post, Blog, Home, Settings, Page
|
|
7
7
|
import time
|
|
8
8
|
from openwrite import start_time
|
|
9
9
|
|
|
@@ -22,8 +22,14 @@ def index():
|
|
|
22
22
|
|
|
23
23
|
elif g.mode == "single":
|
|
24
24
|
blog = g.db.query(Blog).filter_by(name="default").first()
|
|
25
|
+
blog.url = f"http://{g.main_domain}"
|
|
26
|
+
page = g.db.query(Page).filter_by(blog=blog.id, url="").first()
|
|
27
|
+
pages = g.db.query(Page).filter_by(blog=blog.id).all()
|
|
28
|
+
if '{posts}' in page.content_raw:
|
|
29
|
+
posts = g.db.query(Post).filter_by(blog=blog.id).order_by(desc(Post.id)).all()
|
|
30
|
+
return render_template('blog.html', blog=blog, page=page, posts=posts, pages=pages)
|
|
25
31
|
posts = g.db.query(Post).filter_by(blog=blog.id).all()
|
|
26
|
-
return render_template('blog.html', blog=blog, posts=posts)
|
|
32
|
+
return render_template('blog.html', blog=blog, posts=posts, page=page, pages=pages)
|
|
27
33
|
|
|
28
34
|
@main_bp.route("/set-lang/<lang_code>")
|
|
29
35
|
def set_lang(lang_code):
|