openwrite 0.15.0__tar.gz → 0.16.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.15.0/openwrite.egg-info → openwrite-0.16.0}/PKG-INFO +3 -1
- {openwrite-0.15.0 → openwrite-0.16.0}/README.md +2 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/__init__.py +1 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/cli.py +1 -1
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/auth.py +7 -2
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/blog.py +114 -5
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/dashboard.py +7 -4
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/blog_design.css +74 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/default.css +55 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/themes/cyberpunk.css +69 -0
- openwrite-0.16.0/openwrite/static/style/themes/light.css +111 -0
- openwrite-0.16.0/openwrite/static/style/themes/obscura.css +82 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/themes/paper.css +73 -0
- openwrite-0.16.0/openwrite/static/style/themes/sepia.css +344 -0
- openwrite-0.16.0/openwrite/static/style/themes/stickynote.css +117 -0
- openwrite-0.16.0/openwrite/static/style/themes/synthwave.css +389 -0
- openwrite-0.16.0/openwrite/static/style/themes/terminal.css +126 -0
- openwrite-0.16.0/openwrite/static/style/themes/warmnight.css +141 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/themes/windows98.css +81 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/new_post.html +130 -4
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/post.html +28 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/migrations.py +3 -3
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/models.py +33 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/worker.py +4 -0
- {openwrite-0.15.0 → openwrite-0.16.0/openwrite.egg-info}/PKG-INFO +3 -1
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite.egg-info/SOURCES.txt +2 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/pyproject.toml +1 -1
- openwrite-0.15.0/openwrite/static/style/themes/light.css +0 -58
- openwrite-0.15.0/openwrite/static/style/themes/obscura.css +0 -27
- openwrite-0.15.0/openwrite/static/style/themes/stickynote.css +0 -60
- openwrite-0.15.0/openwrite/static/style/themes/terminal.css +0 -46
- openwrite-0.15.0/openwrite/static/style/themes/warmnight.css +0 -79
- {openwrite-0.15.0 → openwrite-0.16.0}/LICENSE +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/MANIFEST.in +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/db/__init__.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/db/base.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/gemini_multi.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/gemini_single.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/admin.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/federation.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/main.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/routes/upload.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/IBMPlexSans-Regular.ttf +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/Merriweather.ttf +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/avatar.png +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/emoji.json +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/favicon.ico +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/favicon.png +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/.gitkeep +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/74.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/a.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/anim3sh1t.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/b.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/corrode.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/dirtysanchez.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/eikerammen.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/fedi.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/friskt.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/gordi.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/igilq.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/io.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/isyuricunha.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/kenny.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/m0bi.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/main.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/mywritese.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/nay-thoughs.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/nintendoflesz.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/phronima.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/ram.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/samah.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/themes-test.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/totylkoja.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/vegafjord.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/feed/vijayvaradharaj.xml +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/logo.png +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/chart.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/highlight.min.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/nativeEmoji.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/openwrite.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/papaparse.min.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/showdown.min.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/showdown.min.js.map +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/site.compat.min.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/script/site.min.js +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/hl.css +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/nativeEmoji.min.css +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/static/style/style.css +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/admin.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/blog.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/blog_footer.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/blog_header.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/blog_preview.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/changepw.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/create.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/dashboard.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/discover.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/edit.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/footer.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/header.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/import.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/index.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/instances.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/login.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/new_page.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/page_preview.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/preview.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/register.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/settings.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/stats.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/templates/translations.html +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/create_db.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/db.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/helpers.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/i18n.json +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/tags.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite/utils/valkey_client.py +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite.egg-info/dependency_links.txt +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite.egg-info/entry_points.txt +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite.egg-info/requires.txt +0 -0
- {openwrite-0.15.0 → openwrite-0.16.0}/openwrite.egg-info/top_level.txt +0 -0
- {openwrite-0.15.0 → openwrite-0.16.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.16.0
|
|
4
4
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
5
5
|
Version 3, 19 November 2007
|
|
6
6
|
|
|
@@ -726,6 +726,8 @@ openwrite is a minimalist blogging platform built for writing freely, hosting in
|
|
|
726
726
|
- Subpages per blog
|
|
727
727
|
- Valkey support
|
|
728
728
|
- Post tags, filtering
|
|
729
|
+
- Optional comments per post
|
|
730
|
+
- Auto save during typing
|
|
729
731
|
|
|
730
732
|
|
|
731
733
|
## Installation
|
|
@@ -167,6 +167,7 @@ def create_app(test_config=None):
|
|
|
167
167
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
|
|
168
168
|
app.secret_key = os.getenv("SECRET_KEY")
|
|
169
169
|
app.config['SERVER_NAME'] = os.getenv("DOMAIN")
|
|
170
|
+
app.config['SESSION_COOKIE_DOMAIN'] = '.' + os.getenv("DOMAIN", "").split(':')[0]
|
|
170
171
|
|
|
171
172
|
from .routes.auth import auth_bp
|
|
172
173
|
from .routes.dashboard import dashboard_bp
|
|
@@ -152,8 +152,8 @@ def init():
|
|
|
152
152
|
if captcha_enabled:
|
|
153
153
|
f.write(f"FRIENDLY_CAPTCHA_SITEKEY={captcha_sitekey}\n")
|
|
154
154
|
f.write(f"FRIENDLY_CAPTCHA_APIKEY={captcha_apikey}\n")
|
|
155
|
+
f.write(f"VALKEY_ENABLED={'yes' if valkey_enabled else 'no'}\n")
|
|
155
156
|
if valkey_enabled:
|
|
156
|
-
f.write(f"VALKEY_ENABLED={'yes' if valkey_enabled else 'no'}\n")
|
|
157
157
|
f.write(f"VALKEY_HOST={valkey_host}\n")
|
|
158
158
|
f.write(f"VALKEY_PORT={valkey_port}\n")
|
|
159
159
|
f.write(f"VALKEY_PASSWORD={valkey_password}\n")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from flask import Blueprint, render_template, redirect, request, session, g
|
|
1
|
+
from flask import Blueprint, render_template, redirect, request, session, g, current_app
|
|
2
2
|
from openwrite.utils.models import User
|
|
3
3
|
import bcrypt
|
|
4
4
|
import re
|
|
@@ -74,5 +74,10 @@ def login():
|
|
|
74
74
|
@auth_bp.route("/logout")
|
|
75
75
|
def logout():
|
|
76
76
|
session.clear()
|
|
77
|
-
|
|
77
|
+
response = redirect("/")
|
|
78
|
+
cookie_name = current_app.config.get('SESSION_COOKIE_NAME', 'session')
|
|
79
|
+
cookie_domain = current_app.config.get('SESSION_COOKIE_DOMAIN')
|
|
80
|
+
response.delete_cookie(cookie_name, path='/', domain=cookie_domain)
|
|
81
|
+
response.delete_cookie(cookie_name, path='/')
|
|
82
|
+
return response
|
|
78
83
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from flask import Blueprint, render_template, redirect, request, g, Response, abort, send_file
|
|
2
|
-
from openwrite.utils.models import Blog, Post, User, View, Like, Page
|
|
1
|
+
from flask import Blueprint, render_template, redirect, request, g, Response, abort, send_file, jsonify
|
|
2
|
+
from openwrite.utils.models import Blog, Post, User, View, Like, Page, PostDraft, Comment
|
|
3
3
|
from openwrite.utils.tags import get_post_tags, parse_tags_from_posts_syntax, get_posts_by_tags
|
|
4
4
|
from openwrite.utils.helpers import gen_link, sanitize_html, anonymize, get_ip
|
|
5
5
|
from openwrite.utils.valkey_client import valkey_client
|
|
@@ -255,9 +255,10 @@ def show_post(blog, post):
|
|
|
255
255
|
one_post.liked = liked
|
|
256
256
|
|
|
257
257
|
post_tags = get_post_tags(g.db, one_post)
|
|
258
|
+
comments = g.db.query(Comment).filter_by(blog_id=blog_obj.id, post_id=one_post.id).order_by(Comment.created_at.asc()).all() if one_post.comments_allowed == "1" else []
|
|
258
259
|
|
|
259
260
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
260
|
-
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags)
|
|
261
|
+
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags, comments=comments)
|
|
261
262
|
|
|
262
263
|
@blog_bp.route("/<post>", subdomain="<blog>")
|
|
263
264
|
def show_subpost(blog, post):
|
|
@@ -325,9 +326,10 @@ def show_subpost(blog, post):
|
|
|
325
326
|
one_post.liked = liked
|
|
326
327
|
|
|
327
328
|
post_tags = get_post_tags(g.db, one_post)
|
|
329
|
+
comments = g.db.query(Comment).filter_by(blog_id=blog_obj.id, post_id=one_post.id).order_by(Comment.created_at.asc()).all() if one_post.comments_allowed == "1" else []
|
|
328
330
|
|
|
329
331
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
330
|
-
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags)
|
|
332
|
+
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags, comments=comments)
|
|
331
333
|
|
|
332
334
|
@blog_bp.route("/.well-known/webfinger")
|
|
333
335
|
def webfinger():
|
|
@@ -438,9 +440,10 @@ def single_showpost(post):
|
|
|
438
440
|
one_post.liked = liked
|
|
439
441
|
|
|
440
442
|
post_tags = get_post_tags(g.db, one_post)
|
|
443
|
+
comments = g.db.query(Comment).filter_by(blog_id=blog.id, post_id=one_post.id).order_by(Comment.created_at.asc()).all() if one_post.comments_allowed == "1" else []
|
|
441
444
|
|
|
442
445
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
443
|
-
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags)
|
|
446
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes, pages=pages, post_tags=post_tags, comments=comments)
|
|
444
447
|
|
|
445
448
|
@blog_bp.route("/rss")
|
|
446
449
|
def single_rss():
|
|
@@ -504,3 +507,109 @@ def serve_rss(blog):
|
|
|
504
507
|
Here we just serve static RSS files :)
|
|
505
508
|
"""
|
|
506
509
|
return send_file(f"{g.mainpath}/static/feed/{blog.name}.xml")
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
@blog_bp.route("/comment", methods=['POST'])
|
|
513
|
+
def add_comment():
|
|
514
|
+
blog_id = request.form.get('blog_id', type=int)
|
|
515
|
+
post_id = request.form.get('post_id', type=int)
|
|
516
|
+
author_name = request.form.get('author_name', '').strip()[:50]
|
|
517
|
+
content = request.form.get('content', '').strip()[:1000]
|
|
518
|
+
redirect_to = request.referrer or '/'
|
|
519
|
+
|
|
520
|
+
if not blog_id or not post_id:
|
|
521
|
+
abort(400)
|
|
522
|
+
|
|
523
|
+
post = g.db.query(Post).filter_by(id=post_id, blog=blog_id).first()
|
|
524
|
+
if not post or post.comments_allowed != '1':
|
|
525
|
+
abort(403)
|
|
526
|
+
|
|
527
|
+
if not author_name or not content:
|
|
528
|
+
return redirect(redirect_to)
|
|
529
|
+
|
|
530
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
531
|
+
comment = Comment(blog_id=blog_id, post_id=post_id, author_name=author_name, content=content, created_at=now)
|
|
532
|
+
g.db.add(comment)
|
|
533
|
+
g.db.commit()
|
|
534
|
+
return redirect(redirect_to)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
@blog_bp.route("/comment/<int:comment_id>/delete", methods=['POST'])
|
|
538
|
+
def delete_comment(comment_id):
|
|
539
|
+
if g.user is None:
|
|
540
|
+
abort(403)
|
|
541
|
+
|
|
542
|
+
comment = g.db.query(Comment).filter_by(id=comment_id).first()
|
|
543
|
+
if not comment:
|
|
544
|
+
abort(404)
|
|
545
|
+
|
|
546
|
+
blog = g.db.query(Blog).filter_by(id=comment.blog_id).first()
|
|
547
|
+
if not blog or blog.owner != g.user:
|
|
548
|
+
abort(403)
|
|
549
|
+
|
|
550
|
+
redirect_to = request.referrer or '/'
|
|
551
|
+
g.db.delete(comment)
|
|
552
|
+
g.db.commit()
|
|
553
|
+
return redirect(redirect_to)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@blog_bp.route("/dashboard/autosave/<blog_name>", methods=['POST'])
|
|
557
|
+
def autosave_post(blog_name):
|
|
558
|
+
if g.user is None:
|
|
559
|
+
abort(403)
|
|
560
|
+
|
|
561
|
+
blog = g.db.query(Blog).filter_by(name=blog_name).first()
|
|
562
|
+
if blog is None or blog.owner != g.user:
|
|
563
|
+
abort(403)
|
|
564
|
+
|
|
565
|
+
data = request.get_json()
|
|
566
|
+
if not data:
|
|
567
|
+
abort(400)
|
|
568
|
+
|
|
569
|
+
title = data.get('title', '')[:128]
|
|
570
|
+
content_raw = data.get('content_raw', '')
|
|
571
|
+
post_id = data.get('post_id')
|
|
572
|
+
session_key = data.get('session_key') if post_id is None else None
|
|
573
|
+
|
|
574
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
575
|
+
draft = PostDraft(post_id=post_id, blog_id=blog.id, session_key=session_key, title=title, content_raw=content_raw, saved_at=now)
|
|
576
|
+
g.db.add(draft)
|
|
577
|
+
g.db.flush()
|
|
578
|
+
|
|
579
|
+
if post_id is not None:
|
|
580
|
+
existing = g.db.query(PostDraft).filter_by(post_id=post_id, blog_id=blog.id).order_by(PostDraft.saved_at.desc()).all()
|
|
581
|
+
else:
|
|
582
|
+
existing = g.db.query(PostDraft).filter_by(session_key=session_key, blog_id=blog.id).order_by(PostDraft.saved_at.desc()).all()
|
|
583
|
+
|
|
584
|
+
for old in existing[20:]:
|
|
585
|
+
g.db.delete(old)
|
|
586
|
+
|
|
587
|
+
g.db.commit()
|
|
588
|
+
return {'status': 'ok'}, 200
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
@blog_bp.route("/dashboard/autosaves/<blog_name>", methods=['GET'])
|
|
592
|
+
def get_autosaves(blog_name):
|
|
593
|
+
if g.user is None:
|
|
594
|
+
abort(403)
|
|
595
|
+
|
|
596
|
+
blog = g.db.query(Blog).filter_by(name=blog_name).first()
|
|
597
|
+
if blog is None or blog.owner != g.user:
|
|
598
|
+
abort(403)
|
|
599
|
+
|
|
600
|
+
post_id = request.args.get('post_id', type=int)
|
|
601
|
+
session_key = request.args.get('session_key')
|
|
602
|
+
|
|
603
|
+
if post_id is not None:
|
|
604
|
+
drafts = g.db.query(PostDraft).filter_by(post_id=post_id, blog_id=blog.id).order_by(PostDraft.saved_at.desc()).limit(20).all()
|
|
605
|
+
elif session_key:
|
|
606
|
+
drafts = g.db.query(PostDraft).filter_by(session_key=session_key, blog_id=blog.id).order_by(PostDraft.saved_at.desc()).limit(20).all()
|
|
607
|
+
else:
|
|
608
|
+
drafts = []
|
|
609
|
+
|
|
610
|
+
return jsonify([{
|
|
611
|
+
'id': d.id,
|
|
612
|
+
'title': d.title,
|
|
613
|
+
'content_raw': d.content_raw,
|
|
614
|
+
'saved_at': d.saved_at.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
615
|
+
} for d in drafts])
|
|
@@ -250,10 +250,10 @@ def new_post(name):
|
|
|
250
250
|
u = g.db.query(User).filter_by(id=g.user).first()
|
|
251
251
|
title = request.form.get('title')
|
|
252
252
|
if len(title) > 120:
|
|
253
|
-
return render_template("new_post.html", blog=blog, error="title-too-long")
|
|
253
|
+
return render_template("new_post.html", blog=blog, error="title-too-long", form_data=request.form)
|
|
254
254
|
|
|
255
255
|
if len(title) < 1:
|
|
256
|
-
return render_template("new_post.html", blog=blog, error="title-empty")
|
|
256
|
+
return render_template("new_post.html", blog=blog, error="title-empty", form_data=request.form)
|
|
257
257
|
|
|
258
258
|
link = gen_link(title)
|
|
259
259
|
if link == "rss":
|
|
@@ -276,7 +276,8 @@ def new_post(name):
|
|
|
276
276
|
link=link,
|
|
277
277
|
date=now,
|
|
278
278
|
feed=request.form.get('feed'),
|
|
279
|
-
isdraft=request.form.get('draft', '0')
|
|
279
|
+
isdraft=request.form.get('draft', '0'),
|
|
280
|
+
comments_allowed=request.form.get('comments_allowed', '0')
|
|
280
281
|
)
|
|
281
282
|
g.db.add(post)
|
|
282
283
|
g.db.flush() # Get the post ID
|
|
@@ -392,7 +393,8 @@ def edit_post(blog, post):
|
|
|
392
393
|
|
|
393
394
|
title = request.form.get("title")
|
|
394
395
|
if len(title) > 120:
|
|
395
|
-
|
|
396
|
+
existing_tags = get_post_tags(g.db, p) if p else []
|
|
397
|
+
return render_template("new_post.html", blog=blog_obj, post=p, existing_tags=existing_tags, error="title-too-long", form_data=request.form)
|
|
396
398
|
link = gen_link(title)
|
|
397
399
|
if link == "rss":
|
|
398
400
|
link = "p_rss"
|
|
@@ -408,6 +410,7 @@ def edit_post(blog, post):
|
|
|
408
410
|
p.content_html = sanitize_html(request.form.get("content"))
|
|
409
411
|
p.author = request.form.get("author")
|
|
410
412
|
p.feed = request.form.get("feed")
|
|
413
|
+
p.comments_allowed = request.form.get("comments_allowed", "0")
|
|
411
414
|
p.link = link
|
|
412
415
|
if request.form.get("publish") == "1":
|
|
413
416
|
p.date = now
|
|
@@ -256,6 +256,80 @@ i.caption {
|
|
|
256
256
|
cursor: pointer;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
.post-comments {
|
|
260
|
+
margin-top: 2rem;
|
|
261
|
+
padding-top: 1rem;
|
|
262
|
+
border-top: 1px solid #333;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.post-comments h3 {
|
|
266
|
+
margin-bottom: 1rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.comment-item {
|
|
270
|
+
margin: 1rem 0;
|
|
271
|
+
padding-bottom: 1rem;
|
|
272
|
+
border-bottom: 1px solid #222;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.comment-meta {
|
|
276
|
+
font-size: 0.85em;
|
|
277
|
+
color: #999;
|
|
278
|
+
margin-bottom: 0.3rem;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.comment-content {
|
|
282
|
+
white-space: pre-wrap;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.comment-empty {
|
|
286
|
+
color: #999;
|
|
287
|
+
font-size: 0.9em;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.comment-form {
|
|
291
|
+
margin-top: 1.5rem;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.comment-form input[type="text"],
|
|
295
|
+
.comment-form textarea {
|
|
296
|
+
display: block;
|
|
297
|
+
width: 100%;
|
|
298
|
+
box-sizing: border-box;
|
|
299
|
+
margin-bottom: 0.5rem;
|
|
300
|
+
padding: 0.4rem;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.comment-form textarea {
|
|
304
|
+
resize: vertical;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.comment-form input[type="submit"] {
|
|
308
|
+
padding: 0.4rem 1rem;
|
|
309
|
+
cursor: pointer;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.comment-delete-form {
|
|
313
|
+
display: inline;
|
|
314
|
+
margin-left: 0.5rem;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.comment-delete {
|
|
318
|
+
background: none;
|
|
319
|
+
border: none;
|
|
320
|
+
cursor: pointer;
|
|
321
|
+
padding: 0;
|
|
322
|
+
font-size: 1em;
|
|
323
|
+
line-height: 1;
|
|
324
|
+
opacity: 0.4;
|
|
325
|
+
color: inherit;
|
|
326
|
+
vertical-align: middle;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.comment-delete:hover {
|
|
330
|
+
opacity: 1;
|
|
331
|
+
}
|
|
332
|
+
|
|
259
333
|
@media only screen and (max-width: 850px) {
|
|
260
334
|
.container {
|
|
261
335
|
width: 80% !important;
|
|
@@ -133,6 +133,61 @@ a.action:hover {
|
|
|
133
133
|
text-decoration: underline;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.post-comments {
|
|
137
|
+
border-top: 1px solid #3333333b;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.comment-item {
|
|
141
|
+
border-bottom: 1px solid #3333333b;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.comment-meta {
|
|
145
|
+
color: #757575;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.comment-empty {
|
|
149
|
+
color: #757575;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.comment-form input[type="text"],
|
|
153
|
+
.comment-form textarea {
|
|
154
|
+
background: #282c34;
|
|
155
|
+
border: 1px solid #3333333b;
|
|
156
|
+
color: #e6e6e6;
|
|
157
|
+
border-radius: 4px;
|
|
158
|
+
font-family: "IBMPlexSans";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.comment-form input[type="text"]:focus,
|
|
162
|
+
.comment-form textarea:focus {
|
|
163
|
+
outline: none;
|
|
164
|
+
border-color: #af9cfb;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.comment-form input[type="submit"] {
|
|
168
|
+
background: #795EB5;
|
|
169
|
+
border: none;
|
|
170
|
+
color: #fff;
|
|
171
|
+
border-radius: 4px;
|
|
172
|
+
font-family: "IBMPlexSans";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.comment-form input[type="submit"]:hover {
|
|
176
|
+
background: #8f72cc;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.comment-form input[type="text"]::placeholder,
|
|
180
|
+
.comment-form textarea::placeholder {
|
|
181
|
+
color: #555;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.comment-form {
|
|
185
|
+
background: #1a1a20;
|
|
186
|
+
border: 1px solid #3333333b;
|
|
187
|
+
border-radius: 4px;
|
|
188
|
+
padding: 1rem;
|
|
189
|
+
}
|
|
190
|
+
|
|
136
191
|
@media only screen and (max-width: 850px) {
|
|
137
192
|
.content h1 {
|
|
138
193
|
font-size: 30px !important;
|
|
@@ -384,6 +384,75 @@ p code {
|
|
|
384
384
|
text-shadow: 0 0 5px rgba(255, 0, 255, 0.5);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
.post-comments {
|
|
388
|
+
border-top: 1px solid rgba(0, 255, 255, 0.3);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.post-comments h3 {
|
|
392
|
+
color: #ff00ff;
|
|
393
|
+
text-shadow: 0 0 10px rgba(255, 0, 255, 0.3);
|
|
394
|
+
border-bottom: 2px solid rgba(255, 0, 255, 0.3);
|
|
395
|
+
padding-bottom: 0.5rem;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.comment-item {
|
|
399
|
+
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.comment-meta {
|
|
403
|
+
color: #888;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.comment-empty {
|
|
407
|
+
color: #888;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.comment-form input[type="text"],
|
|
411
|
+
.comment-form textarea {
|
|
412
|
+
background: rgba(16, 16, 32, 0.8);
|
|
413
|
+
border: 1px solid rgba(0, 255, 255, 0.3);
|
|
414
|
+
color: #e0e0e0;
|
|
415
|
+
border-radius: 4px;
|
|
416
|
+
font-family: 'Courier New', monospace;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.comment-form input[type="text"]:focus,
|
|
420
|
+
.comment-form textarea:focus {
|
|
421
|
+
outline: none;
|
|
422
|
+
border-color: #00ffff;
|
|
423
|
+
box-shadow: 0 0 8px rgba(0, 255, 255, 0.3);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.comment-form input[type="submit"] {
|
|
427
|
+
background: transparent;
|
|
428
|
+
border: 1px solid #00ffff;
|
|
429
|
+
color: #00ffff;
|
|
430
|
+
border-radius: 20px;
|
|
431
|
+
font-size: 0.8rem;
|
|
432
|
+
letter-spacing: 1px;
|
|
433
|
+
text-transform: uppercase;
|
|
434
|
+
transition: all 0.3s ease;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.comment-form input[type="submit"]:hover {
|
|
438
|
+
background: rgba(0, 255, 255, 0.1);
|
|
439
|
+
box-shadow: 0 0 15px rgba(0, 255, 255, 0.3);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.comment-form input[type="text"]::placeholder,
|
|
443
|
+
.comment-form textarea::placeholder {
|
|
444
|
+
color: rgba(0, 255, 255, 0.35);
|
|
445
|
+
font-style: italic;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.comment-form {
|
|
449
|
+
border: 1px solid rgba(255, 0, 255, 0.2);
|
|
450
|
+
border-radius: 8px;
|
|
451
|
+
padding: 1.2rem;
|
|
452
|
+
background: rgba(255, 0, 255, 0.02);
|
|
453
|
+
box-shadow: inset 0 0 20px rgba(255, 0, 255, 0.03);
|
|
454
|
+
}
|
|
455
|
+
|
|
387
456
|
.postlist_item i.datetime {
|
|
388
457
|
color: #888;
|
|
389
458
|
font-size: 0.9rem;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
@font-face {
|
|
2
|
+
font-family: 'IBMPlexSans';
|
|
3
|
+
src: url('/static/IBMPlexSans-Regular.ttf') format('truetype');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@font-face {
|
|
7
|
+
font-family: 'Merriweather';
|
|
8
|
+
src: url('/static/Merriweather.ttf') format('truetype');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
body {
|
|
13
|
+
background: #efefef;
|
|
14
|
+
color: #464646;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
article.post-content {
|
|
18
|
+
color: #3c3c3c;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
article.post-content a {
|
|
22
|
+
color: #6834ab;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h1.blog-name {
|
|
26
|
+
font-family: "Merriweather";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
h1.blog-name a {
|
|
30
|
+
color: #6834ab;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.header {
|
|
34
|
+
border-bottom: 1px solid #3333333b;
|
|
35
|
+
font-family: "IBMPlexSans";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.header #menu a {
|
|
39
|
+
color: #6834ab;
|
|
40
|
+
font-weight: bold;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.header #menu a:hover {
|
|
44
|
+
text-decoration: underline;
|
|
45
|
+
color: #6834ab;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
i.post-date {
|
|
49
|
+
color: #505050;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
svg {
|
|
53
|
+
fill: #6834ab;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pre code.hljs {
|
|
57
|
+
background: #fff;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.post-comments {
|
|
61
|
+
border-top: 1px solid #3333333b;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.comment-item {
|
|
65
|
+
border-bottom: 1px solid #3333331a;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.comment-meta {
|
|
69
|
+
color: #888;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.comment-empty {
|
|
73
|
+
color: #888;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.comment-form input[type="text"],
|
|
77
|
+
.comment-form textarea {
|
|
78
|
+
background: #fff;
|
|
79
|
+
border: 1px solid #ccc;
|
|
80
|
+
color: #464646;
|
|
81
|
+
border-radius: 4px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.comment-form input[type="text"]:focus,
|
|
85
|
+
.comment-form textarea:focus {
|
|
86
|
+
outline: none;
|
|
87
|
+
border-color: #6834ab;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.comment-form input[type="submit"] {
|
|
91
|
+
background: #6834ab;
|
|
92
|
+
border: none;
|
|
93
|
+
color: #fff;
|
|
94
|
+
border-radius: 4px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.comment-form input[type="submit"]:hover {
|
|
98
|
+
background: #7a3fc8;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.comment-form input[type="text"]::placeholder,
|
|
102
|
+
.comment-form textarea::placeholder {
|
|
103
|
+
color: #aaa;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.comment-form {
|
|
107
|
+
background: #f5f5f5;
|
|
108
|
+
border: 1px solid #d0d0d0;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
padding: 1rem;
|
|
111
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
body {
|
|
2
|
+
background: #11111a;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
h1.blog-name {
|
|
6
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
h1.blog-name a, article.blog-content h3, article.post-content a {
|
|
10
|
+
color: #e3bc5e;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
div#menu a svg {
|
|
14
|
+
fill: #e3bc5e;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
article.post-content {
|
|
18
|
+
color: #bbbaba;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
h1.post-title {
|
|
22
|
+
color: #fff;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
i.post-date {
|
|
26
|
+
color: #737373;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.post-comments {
|
|
30
|
+
border-top: 1px solid rgba(227, 188, 94, 0.3);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.post-comments h3 {
|
|
34
|
+
color: #e3bc5e;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.comment-item {
|
|
38
|
+
border-bottom: 1px solid rgba(227, 188, 94, 0.15);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.comment-meta {
|
|
42
|
+
color: #737373;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.comment-empty {
|
|
46
|
+
color: #737373;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.comment-form input[type="text"],
|
|
50
|
+
.comment-form textarea {
|
|
51
|
+
background: #1a1a25;
|
|
52
|
+
border: 1px solid rgba(227, 188, 94, 0.3);
|
|
53
|
+
color: #bbbaba;
|
|
54
|
+
border-radius: 2px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.comment-form input[type="text"]:focus,
|
|
58
|
+
.comment-form textarea:focus {
|
|
59
|
+
outline: none;
|
|
60
|
+
border-color: #e3bc5e;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.comment-form input[type="submit"] {
|
|
64
|
+
background: transparent;
|
|
65
|
+
border: 1px solid #e3bc5e;
|
|
66
|
+
color: #e3bc5e;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.comment-form input[type="submit"]:hover {
|
|
70
|
+
background: rgba(227, 188, 94, 0.1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.comment-form input[type="text"]::placeholder,
|
|
74
|
+
.comment-form textarea::placeholder {
|
|
75
|
+
color: rgba(227, 188, 94, 0.3);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.comment-form {
|
|
79
|
+
background: rgba(227, 188, 94, 0.03);
|
|
80
|
+
border: 1px solid rgba(227, 188, 94, 0.15);
|
|
81
|
+
padding: 1rem;
|
|
82
|
+
}
|