add mentions

This commit is contained in:
2025-12-02 06:13:50 +03:00
parent 414298b4b4
commit 1d5d5a8c64
28 changed files with 366 additions and 64 deletions

View File

@@ -41,7 +41,7 @@ def babycode_preview():
if not markup or not isinstance(markup, str):
return {'error': 'markup field missing or invalid type'}, 400
banned_tags = request.json.get('banned_tags', [])
rendered = babycode_to_html(markup, banned_tags)
rendered = babycode_to_html(markup, banned_tags).result
return {'html': rendered}
@@ -213,3 +213,16 @@ def bookmark_thread(thread_id):
return {'error': 'bad request'}, 400
return {'status': 'ok'}, 200
@bp.get('/current-user')
def get_current_user_info():
if not is_logged_in():
return {'user': null}
user = get_active_user()
return {
'user': {
'username': user.username,
'display_name': user.display_name,
}
}

View File

@@ -73,7 +73,7 @@ def motd_editor_form():
data = {
'title': title,
'body_original_markup': orig_body,
'body_rendered': babycode_to_html(orig_body, banned_tags=MOTD_BANNED_TAGS),
'body_rendered': babycode_to_html(orig_body, banned_tags=MOTD_BANNED_TAGS).result,
'format_version': BABYCODE_VERSION,
'edited_at': int(time.time()),
}

View File

@@ -5,7 +5,7 @@ from .users import login_required, get_active_user
from ..lib.babycode import babycode_to_html, BABYCODE_VERSION
from ..constants import InfoboxKind
from ..db import db
from ..models import Posts, PostHistory, Threads, Topics
from ..models import Posts, PostHistory, Threads, Topics, Mentions
bp = Blueprint("posts", __name__, url_prefix = "/post")
@@ -21,13 +21,22 @@ def create_post(thread_id, user_id, content, markup_language="babycode"):
revision = PostHistory.create({
"post_id": post.id,
"content": parsed_content,
"content": parsed_content.result,
"is_initial_revision": True,
"original_markup": content,
"markup_language": markup_language,
"format_version": BABYCODE_VERSION,
})
for mention in parsed_content.mentions:
Mentions.create({
'revision_id': revision.id,
'mentioned_user_id': mention['mentioned_user_id'],
'original_mention_text': mention['mention_text'],
'start_index': mention['start'],
'end_index': mention['end'],
})
post.update({"current_revision_id": revision.id})
return post
@@ -38,13 +47,22 @@ def update_post(post_id, new_content, markup_language='babycode'):
post = Posts.find({'id': post_id})
new_revision = PostHistory.create({
'post_id': post.id,
'content': parsed_content,
'content': parsed_content.result,
'is_initial_revision': False,
'original_markup': new_content,
'markup_language': markup_language,
'format_version': BABYCODE_VERSION,
})
for mention in parsed_content.mentions:
Mentions.create({
'revision_id': new_revision.id,
'mentioned_user_id': mention['mentioned_user_id'],
'original_mention_text': mention['mention_text'],
'start_index': mention['start'],
'end_index': mention['end'],
})
post.update({'current_revision_id': new_revision.id})

View File

@@ -48,7 +48,6 @@ def thread(slug):
page = math.ceil((post_position) / POSTS_PER_PAGE)
else:
page = max(1, min(page_count, int(request.args.get("page", default = 1))))
posts = thread.get_posts(POSTS_PER_PAGE, (page - 1) * POSTS_PER_PAGE)
topic = Topics.find({"id": thread.topic_id})
other_topics = Topics.select()

View File

@@ -4,7 +4,7 @@ from flask import (
from functools import wraps
from ..db import db
from ..lib.babycode import babycode_to_html, BABYCODE_VERSION
from ..models import Users, Sessions, Subscriptions, Avatars, PasswordResetLinks, InviteKeys, BookmarkCollections, BookmarkedThreads
from ..models import Users, Sessions, Subscriptions, Avatars, PasswordResetLinks, InviteKeys, BookmarkCollections, BookmarkedThreads, Mentions, PostHistory
from ..constants import InfoboxKind, PermissionLevel
from ..auth import digest, verify
from wand.image import Image
@@ -90,6 +90,15 @@ def validate_username(username):
return bool(re.fullmatch(pattern, username))
def validate_display_name(display_name):
if not display_name:
return True
pattern = r'^[\w!#$%^*\(\)\-_=+\[\]\{\}\|;:,.?\s]{3,50}$'
display_name = display_name.replace('@', '_')
return bool(re.fullmatch(pattern, display_name))
def redirect_if_logged_in(*args, **kwargs):
def decorator(view_func):
@wraps(view_func)
@@ -184,7 +193,7 @@ def log_in():
@redirect_if_logged_in(".page", username = lambda: get_active_user().username)
def log_in_post():
target_user = Users.find({
"username": request.form['username']
"username": request.form['username'].lower()
})
if not target_user:
flash("Incorrect username or password.", InfoboxKind.ERROR)
@@ -239,7 +248,7 @@ def sign_up_post():
flash("Invalid username.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up", key=key))
user_exists = Users.count({"username": username}) > 0
user_exists = Users.count({"username": username.lower()}) > 0
if user_exists:
flash(f"Username '{username}' is already taken.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up", key=key))
@@ -254,8 +263,14 @@ def sign_up_post():
hashed = digest(password)
if username.lower() != username:
display_name = username
else:
display_name = ''
new_user = Users.create({
"username": username,
'display_name': display_name,
"password_hash": hashed,
"permission": PermissionLevel.GUEST.value,
})
@@ -279,14 +294,14 @@ def sign_up_post():
@bp.get("/<username>")
def page(username):
target_user = Users.find({"username": username})
target_user = Users.find({"username": username.lower()})
return render_template("users/user.html", target_user = target_user)
@bp.get("/<username>/settings")
@login_required
def settings(username):
target_user = Users.find({'username': username})
target_user = Users.find({'username': username.lower()})
if target_user.id != get_active_user().id:
return redirect('.settings', username = get_active_user().username)
@@ -311,10 +326,16 @@ def settings_form(username):
status = request.form.get('status', default="")[:100]
original_sig = request.form.get('signature', default='').strip()
if original_sig:
rendered_sig = babycode_to_html(original_sig)
rendered_sig = babycode_to_html(original_sig).result
else:
rendered_sig = ''
session['subscribe_by_default'] = request.form.get('subscribe_by_default', default='off') == 'on'
display_name = request.form.get('display_name', default='')
if not validate_display_name(display_name):
flash('Invalid display name.', InfoboxKind.ERROR)
return redirect('.settings', username=user.username)
old_dn = user.display_name
user.update({
'status': status,
@@ -322,7 +343,22 @@ def settings_form(username):
'signature_rendered': rendered_sig,
'signature_format_version': BABYCODE_VERSION,
'signature_markup_language': 'babycode',
'display_name': display_name,
})
if old_dn != display_name:
# re-parse mentions
q = """SELECT DISTINCT m.revision_id FROM mentions m
JOIN post_history ph ON m.revision_id = ph.id
JOIN posts p ON p.current_revision_id = ph.id
WHERE m.mentioned_user_id = ?"""
mentions = db.query(q, int(user.id))
with db.transaction():
for mention in mentions:
rev = PostHistory.find({'id': int(mention['revision_id'])})
parsed_content = babycode_to_html(rev.original_markup).result
rev.update({'content': parsed_content})
flash('Settings updated.', InfoboxKind.INFO)
return redirect(url_for('.settings', username=user.username))
@@ -488,7 +524,7 @@ def guest_user(user_id):
@login_required
def inbox(username):
user = get_active_user()
if username != user.username:
if username.lower() != user.username:
return redirect(url_for(".inbox", username = user.username))
new_posts = []
@@ -630,7 +666,7 @@ def reset_link_login_form(key):
@login_required
def invite_links(username):
target_user = Users.find({
'username': username
'username': username.lower()
})
if not target_user or not target_user.can_invite():
return redirect(url_for('.page', username=username))
@@ -649,10 +685,10 @@ def invite_links(username):
@login_required
def create_invite_link(username):
target_user = Users.find({
'username': username
'username': username.lower()
})
if not target_user or not target_user.can_invite():
return redirect(url_for('.page', username=username))
return redirect(url_for('.page', username=username.lower()))
if target_user.username != get_active_user().username:
return redirect(url_for('.invite_links', username=target_user.username))
@@ -669,10 +705,10 @@ def create_invite_link(username):
@login_required
def revoke_invite_link(username):
target_user = Users.find({
'username': username
'username': username.lower()
})
if not target_user or not target_user.can_invite():
return redirect(url_for('.page', username=username))
return redirect(url_for('.page', username=username.lower()))
if target_user.username != get_active_user().username:
return redirect(url_for('.invite_links', username=target_user.username))
@@ -695,7 +731,7 @@ def revoke_invite_link(username):
@bp.get('/<username>/bookmarks')
@login_required
def bookmarks(username):
target_user = Users.find({'username': username})
target_user = Users.find({'username': username.lower()})
if not target_user or target_user.username != get_active_user().username:
return redirect(url_for('.bookmarks', username=get_active_user().username))
@@ -707,7 +743,7 @@ def bookmarks(username):
@bp.get('/<username>/bookmarks/collections')
@login_required
def bookmark_collections(username):
target_user = Users.find({'username': username})
target_user = Users.find({'username': username.lower()})
if not target_user or target_user.username != get_active_user().username:
return redirect(url_for('.bookmark_collections', username=get_active_user().username))