add language choices to UI #51

This commit is contained in:
rimu 2024-05-09 17:54:30 +12:00
parent 29c2a05d38
commit af4dee7ea3
24 changed files with 287 additions and 59 deletions

View file

@ -21,11 +21,11 @@ from app.activitypub.util import public_key, users_total, active_half_year, acti
upvote_post, delete_post_or_comment, community_members, \
user_removed_from_remote_server, create_post, create_post_reply, update_post_reply_from_activity, \
update_post_from_activity, undo_vote, undo_downvote, post_to_page, get_redis_connection, find_reported_object, \
process_report, ensure_domains_match
process_report, ensure_domains_match, can_edit, can_delete
from app.utils import gibberish, get_setting, is_image_url, allowlist_html, render_template, \
domain_from_url, markdown_to_html, community_membership, ap_datetime, ip_address, can_downvote, \
can_upvote, can_create_post, awaken_dormant_instance, shorten_string, can_create_post_reply, sha256_digest, \
community_moderators, blocked_users
community_moderators
import werkzeug.exceptions
@ -933,17 +933,23 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
if request_json['object']['type'] == 'Page': # Editing a post
post = Post.query.filter_by(ap_id=request_json['object']['id']).first()
if post:
update_post_from_activity(post, request_json)
announce_activity_to_followers(post.community, post.author, request_json)
activity_log.result = 'success'
if can_edit(request_json['actor'], post):
update_post_from_activity(post, request_json)
announce_activity_to_followers(post.community, post.author, request_json)
activity_log.result = 'success'
else:
activity_log.exception_message = 'Edit attempt denied'
else:
activity_log.exception_message = 'Post not found'
elif request_json['object']['type'] == 'Note': # Editing a reply
reply = PostReply.query.filter_by(ap_id=request_json['object']['id']).first()
if reply:
update_post_reply_from_activity(reply, request_json)
announce_activity_to_followers(reply.community, reply.author, request_json)
activity_log.result = 'success'
if can_edit(request_json['actor'], reply):
update_post_reply_from_activity(reply, request_json)
announce_activity_to_followers(reply.community, reply.author, request_json)
activity_log.result = 'success'
else:
activity_log.exception_message = 'Edit attempt denied'
else:
activity_log.exception_message = 'PostReply not found'
elif request_json['type'] == 'Delete':
@ -954,28 +960,34 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
post = Post.query.filter_by(ap_id=ap_id).first()
# Delete post
if post:
if post.url and post.cross_posts is not None:
old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all()
post.cross_posts.clear()
for ocp in old_cross_posts:
if ocp.cross_posts is not None:
ocp.cross_posts.remove(post.id)
post.delete_dependencies()
post.community.post_count -= 1
announce_activity_to_followers(post.community, post.author, request_json)
db.session.delete(post)
db.session.commit()
activity_log.result = 'success'
if can_delete(request_json['actor'], post):
if post.url and post.cross_posts is not None:
old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all()
post.cross_posts.clear()
for ocp in old_cross_posts:
if ocp.cross_posts is not None:
ocp.cross_posts.remove(post.id)
post.delete_dependencies()
post.community.post_count -= 1
announce_activity_to_followers(post.community, post.author, request_json)
db.session.delete(post)
db.session.commit()
activity_log.result = 'success'
else:
activity_log.exception_message = 'Delete attempt denied'
else:
# Delete PostReply
reply = PostReply.query.filter_by(ap_id=ap_id).first()
if reply:
reply.body_html = '<p><em>deleted</em></p>'
reply.body = 'deleted'
reply.post.reply_count -= 1
announce_activity_to_followers(reply.community, reply.author, request_json)
db.session.commit()
activity_log.result = 'success'
if can_delete(request_json['actor'], reply):
reply.body_html = '<p><em>deleted</em></p>'
reply.body = 'deleted'
reply.post.reply_count -= 1
announce_activity_to_followers(reply.community, reply.author, request_json)
db.session.commit()
activity_log.result = 'success'
else:
activity_log.exception_message = 'Delete attempt denied'
else:
# Delete User
user = find_actor_or_create(ap_id, create_if_not_found=False)
@ -1399,7 +1411,11 @@ def comment_ap(comment_id):
'mediaType': 'text/html',
'published': ap_datetime(reply.created_at),
'distinguished': False,
'audience': reply.community.profile_id()
'audience': reply.community.profile_id(),
'language': {
'identifier': reply.language_code(),
'name': reply.language_name()
}
}
if reply.edited_at:
reply_data['updated'] = ap_datetime(reply.edited_at)

View file

@ -156,7 +156,11 @@ def post_to_activity(post: Post, community: Community):
"sensitive": post.nsfw or post.nsfl,
"published": ap_datetime(post.created_at),
"stickied": post.sticky,
"audience": f"https://{current_app.config['SERVER_NAME']}/c/{community.name}"
"audience": f"https://{current_app.config['SERVER_NAME']}/c/{community.name}",
'language': {
'identifier': post.language_code(),
'name': post.language_name()
}
},
"cc": [
f"https://{current_app.config['SERVER_NAME']}/c/{community.name}"
@ -756,6 +760,11 @@ def post_json_to_model(activity_log, post_json, user, community) -> Post:
domain.post_count += 1
post.domain = domain
if 'language' in post_json:
language = find_language_or_create(post_json['language']['identifier'], post_json['language']['name'])
if language:
post.language_id = language.id
if post is not None:
if 'image' in post_json and post.image is None:
image = File(source_url=post_json['image']['url'])
@ -1312,6 +1321,11 @@ def create_post_reply(activity_log: ActivityPubLog, community: Community, in_rep
elif 'content' in request_json['object']: # Kbin
post_reply.body_html = allowlist_html(request_json['object']['content'])
post_reply.body = ''
if 'language' in request_json['object'] and isinstance(request_json['object']['language'], dict):
language = find_language_or_create(request_json['object']['language']['identifier'],
request_json['object']['language']['name'])
post_reply.language_id = language.id
if post_id is not None:
# Discard post_reply if it contains certain phrases. Good for stopping spam floods.
if post_reply.body:
@ -1579,6 +1593,10 @@ def update_post_reply_from_activity(reply: PostReply, request_json: dict):
elif 'content' in request_json['object']:
reply.body_html = allowlist_html(request_json['object']['content'])
reply.body = ''
# Language
if 'language' in request_json['object'] and isinstance(request_json['object']['language'], dict):
language = find_language_or_create(request_json['object']['language']['identifier'], request_json['object']['language']['name'])
reply.language_id = language.id
reply.edited_at = utcnow()
db.session.commit()
@ -1636,7 +1654,7 @@ def update_post_from_activity(post: Post, request_json: dict):
db.session.add(image)
post.image = image
elif is_video_url(post.url):
post.type == POST_TYPE_VIDEO
post.type = POST_TYPE_VIDEO
image = File(source_url=post.url)
db.session.add(image)
post.image = image
@ -2049,3 +2067,16 @@ def ensure_domains_match(activity: dict) -> bool:
return False
def can_edit(user_ap_id, post):
user = find_actor_or_create(user_ap_id, create_if_not_found=False)
if user:
if post.user_id == user.id:
return True
if post.community.is_moderator(user) or post.community.is_owner(user) or post.community.is_instance_admin(user):
return True
return False
def can_delete(user_ap_id, post):
return can_edit(user_ap_id, post)

View file

@ -87,6 +87,7 @@ def register(app):
db.session.add(Settings(name='allow_remote_image_posts', value=json.dumps(True)))
db.session.add(Settings(name='federation', value=json.dumps(True)))
db.session.add(Language(name='Undetermined', code='und'))
db.session.add(Language(name='English', code='en'))
banned_instances = ['anonib.al','lemmygrad.ml', 'gab.com', 'rqd2.net', 'exploding-heads.com', 'hexbear.net',
'threads.net', 'noauthority.social', 'pieville.net', 'links.hackliberty.org',
'poa.st', 'freespeechextremist.com', 'bae.st', 'nicecrew.digital', 'detroitriotcity.com',

View file

@ -100,6 +100,7 @@ class CreateDiscussionForm(FlaskForm):
nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross'))
notify_author = BooleanField(_l('Notify about replies'))
language_id = SelectField(_l('Language'), validators=[DataRequired()], coerce=int, render_kw={'class': 'form-select'})
submit = SubmitField(_l('Save'))
@ -113,6 +114,7 @@ class CreateLinkForm(FlaskForm):
nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross'))
notify_author = BooleanField(_l('Notify about replies'))
language_id = SelectField(_l('Language'), validators=[DataRequired()], coerce=int, render_kw={'class': 'form-select'})
submit = SubmitField(_l('Save'))
def validate(self, extra_validators=None) -> bool:
@ -133,6 +135,7 @@ class CreateVideoForm(FlaskForm):
nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross'))
notify_author = BooleanField(_l('Notify about replies'))
language_id = SelectField(_l('Language'), validators=[DataRequired()], coerce=int, render_kw={'class': 'form-select'})
submit = SubmitField(_l('Save'))
def validate(self, extra_validators=None) -> bool:
@ -153,6 +156,7 @@ class CreateImageForm(FlaskForm):
nsfw = BooleanField(_l('NSFW'))
nsfl = BooleanField(_l('Gore/gross'))
notify_author = BooleanField(_l('Notify about replies'))
language_id = SelectField(_l('Language'), validators=[DataRequired()], coerce=int, render_kw={'class': 'form-select'})
submit = SubmitField(_l('Save'))
def validate(self, extra_validators=None) -> bool:

View file

@ -33,7 +33,7 @@ from app.utils import get_setting, render_template, allowlist_html, markdown_to_
request_etag_matches, return_304, instance_banned, can_create_post, can_upvote, can_downvote, user_filters_posts, \
joined_communities, moderating_communities, blocked_domains, mimetype_from_url, blocked_instances, \
community_moderators, communities_banned_from, show_ban_message, recently_upvoted_posts, recently_downvoted_posts, \
blocked_users, post_ranking, languages_for_form
blocked_users, post_ranking, languages_for_form, english_language_id
from feedgen.feed import FeedGenerator
from datetime import timezone, timedelta
@ -488,6 +488,8 @@ def add_discussion_post(actor):
if not community_in_list(community.id, form.communities.choices):
form.communities.choices.append((community.id, community.display_name()))
form.language_id.choices = languages_for_form()
if not can_create_post(current_user, community):
abort(401)
@ -515,6 +517,7 @@ def add_discussion_post(actor):
else:
form.communities.data = community.id
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
if community.posting_warning:
flash(community.posting_warning)
@ -551,6 +554,8 @@ def add_image_post(actor):
if not community_in_list(community.id, form.communities.choices):
form.communities.choices.append((community.id, community.display_name()))
form.language_id.choices = languages_for_form()
if not can_create_post(current_user, community):
abort(401)
@ -594,6 +599,7 @@ def add_image_post(actor):
else:
form.communities.data = community.id
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
return render_template('community/add_image_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
@ -628,6 +634,8 @@ def add_link_post(actor):
if not community_in_list(community.id, form.communities.choices):
form.communities.choices.append((community.id, community.display_name()))
form.language_id.choices = languages_for_form()
if not can_create_post(current_user, community):
abort(401)
@ -671,6 +679,7 @@ def add_link_post(actor):
else:
form.communities.data = community.id
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
return render_template('community/add_link_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
@ -705,6 +714,8 @@ def add_video_post(actor):
if not community_in_list(community.id, form.communities.choices):
form.communities.choices.append((community.id, community.display_name()))
form.language_id.choices = languages_for_form()
if not can_create_post(current_user, community):
abort(401)
@ -748,6 +759,7 @@ def add_video_post(actor):
else:
form.communities.data = community.id
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
return render_template('community/add_video_post.html', title=_('Add post to community'), form=form, community=community,
markdown_editor=current_user.markdown_editor, low_bandwidth=False, actor=actor,
@ -780,7 +792,11 @@ def federate_post(community, post):
'nsfl': post.nsfl,
'stickied': post.sticky,
'published': ap_datetime(utcnow()),
'audience': community.ap_profile_id
'audience': community.ap_profile_id,
'language': {
'identifier': post.language_code(),
'name': post.language_name()
}
}
create = {
"id": f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}",
@ -870,7 +886,11 @@ def federate_post_to_user_followers(post):
'sensitive': post.nsfw,
'nsfl': post.nsfl,
'stickied': post.sticky,
'published': ap_datetime(utcnow())
'published': ap_datetime(utcnow()),
'language': {
'identifier': post.language_code(),
'name': post.language_name()
}
}
create = {
"id": f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}",

View file

@ -216,6 +216,8 @@ def save_post(form, post: Post, type: str):
post.nsfw = form.nsfw.data
post.nsfl = form.nsfl.data
post.notify_author = form.notify_author.data
post.language_id = form.language_id.data
current_user.language_id = form.language_id.data
if type == '' or type == 'discussion':
post.title = form.discussion_title.data
post.body = form.discussion_body.data

View file

@ -944,10 +944,9 @@ class Post(db.Model):
up_votes = db.Column(db.Integer, default=0)
down_votes = db.Column(db.Integer, default=0)
ranking = db.Column(db.Integer, default=0, index=True) # used for 'hot' ranking
language = db.Column(db.String(10))
edited_at = db.Column(db.DateTime)
reports = db.Column(db.Integer, default=0) # how many times this post has been reported. Set to -1 to ignore reports
language_id = db.Column(db.Integer, index=True)
language_id = db.Column(db.Integer, db.ForeignKey('language.id'), index=True)
cross_posts = db.Column(MutableList.as_mutable(ARRAY(db.Integer)))
tags = db.relationship('Tag', lazy='dynamic', secondary=post_tag, backref=db.backref('posts', lazy='dynamic'))
@ -962,6 +961,7 @@ class Post(db.Model):
author = db.relationship('User', lazy='joined', overlaps='posts', foreign_keys=[user_id])
community = db.relationship('Community', lazy='joined', overlaps='posts', foreign_keys=[community_id])
replies = db.relationship('PostReply', lazy='dynamic', backref='post')
language = db.relationship('Language', foreign_keys=[language_id])
def is_local(self):
return self.ap_id is None or self.ap_id.startswith('https://' + current_app.config['SERVER_NAME'])
@ -1006,6 +1006,18 @@ class Post(db.Model):
NotificationSubscription.type == NOTIF_POST).first()
return existing_notification is not None
def language_code(self):
if self.language_id:
return self.language.code
else:
return 'en'
def language_name(self):
if self.language_id:
return self.language.name
else:
return 'English'
class PostReply(db.Model):
query_class = FullTextSearchQuery
@ -1033,7 +1045,7 @@ class PostReply(db.Model):
up_votes = db.Column(db.Integer, default=0)
down_votes = db.Column(db.Integer, default=0)
ranking = db.Column(db.Float, default=0.0, index=True) # used for 'hot' sorting
language = db.Column(db.String(10))
language_id = db.Column(db.Integer, db.ForeignKey('language.id'), index=True)
edited_at = db.Column(db.DateTime)
reports = db.Column(db.Integer, default=0) # how many times this post has been reported. Set to -1 to ignore reports
@ -1045,6 +1057,19 @@ class PostReply(db.Model):
author = db.relationship('User', lazy='joined', foreign_keys=[user_id], single_parent=True, overlaps="post_replies")
community = db.relationship('Community', lazy='joined', overlaps='replies', foreign_keys=[community_id])
language = db.relationship('Language', foreign_keys=[language_id])
def language_code(self):
if self.language_id:
return self.language.code
else:
return 'en'
def language_name(self):
if self.language_id:
return self.language.name
else:
return 'English'
def is_local(self):
return self.ap_id is None or self.ap_id.startswith('https://' + current_app.config['SERVER_NAME'])

View file

@ -1,5 +1,6 @@
from flask_wtf import FlaskForm
from wtforms import TextAreaField, SubmitField, BooleanField, StringField
from wtforms.fields.choices import SelectField
from wtforms.validators import DataRequired, Length, ValidationError
from flask_babel import _, lazy_gettext as _l
@ -9,6 +10,7 @@ from app.utils import MultiCheckboxField
class NewReplyForm(FlaskForm):
body = TextAreaField(_l('Body'), render_kw={'placeholder': 'What are your thoughts?', 'rows': 5}, validators={DataRequired(), Length(min=1, max=5000)})
notify_author = BooleanField(_l('Notify about replies'))
language_id = SelectField(_l('Language'), validators=[DataRequired()], coerce=int, render_kw={'class': 'form-select language-float-right'})
submit = SubmitField(_l('Comment'))

View file

@ -27,7 +27,8 @@ from app.utils import get_setting, render_template, allowlist_html, markdown_to_
request_etag_matches, ip_address, user_ip_banned, instance_banned, can_downvote, can_upvote, post_ranking, \
reply_already_exists, reply_is_just_link_to_gif_reaction, confidence, moderating_communities, joined_communities, \
blocked_instances, blocked_domains, community_moderators, blocked_phrases, show_ban_message, recently_upvoted_posts, \
recently_downvoted_posts, recently_upvoted_post_replies, recently_downvoted_post_replies, reply_is_stupid
recently_downvoted_posts, recently_upvoted_post_replies, recently_downvoted_post_replies, reply_is_stupid, \
languages_for_form, english_language_id
def show_post(post_id: int):
@ -58,6 +59,7 @@ def show_post(post_id: int):
# handle top-level comments/replies
form = NewReplyForm()
form.language_id.choices = languages_for_form()
if current_user.is_authenticated and current_user.verified and form.validate_on_submit():
if not post.comments_enabled:
@ -98,11 +100,12 @@ def show_post(post_id: int):
reply = PostReply(user_id=current_user.id, post_id=post.id, community_id=community.id, body=form.body.data,
body_html=markdown_to_html(form.body.data), body_html_safe=True,
from_bot=current_user.bot, nsfw=post.nsfw, nsfl=post.nsfl,
notify_author=form.notify_author.data, instance_id=1)
notify_author=form.notify_author.data, language_id=form.language_id.data, instance_id=1)
post.last_active = community.last_active = utcnow()
post.reply_count += 1
community.post_reply_count += 1
current_user.language_id = form.language_id.data
db.session.add(reply)
db.session.commit()
@ -163,7 +166,11 @@ def show_post(post_id: int):
'href': post.author.public_url(),
'name': post.author.mention_tag(),
'type': 'Mention'
}]
}],
'language': {
'identifier': reply.language_code(),
'name': reply.language_name()
}
}
create_json = {
'type': 'Create',
@ -222,6 +229,7 @@ def show_post(post_id: int):
else:
replies = post_replies(post.id, sort)
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
og_image = post.image.source_url if post.image_id else None
description = shorten_string(markdown_to_text(post.body), 150) if post.body else None
@ -588,6 +596,7 @@ def add_reply(post_id: int, comment_id: int):
return redirect(url_for('activitypub.post_ap', post_id=post_id))
form = NewReplyForm()
form.language_id.choices = languages_for_form()
if form.validate_on_submit():
if reply_already_exists(user_id=current_user.id, post_id=post.id, parent_id=in_reply_to.id, body=form.body.data):
if in_reply_to.depth <= constants.THREAD_CUTOFF_DEPTH:
@ -617,11 +626,12 @@ def add_reply(post_id: int, comment_id: int):
current_user.last_seen = utcnow()
current_user.ip_address = ip_address()
current_user.language_id = form.language_id.data
reply = PostReply(user_id=current_user.id, post_id=post.id, parent_id=in_reply_to.id, depth=in_reply_to.depth + 1,
community_id=post.community.id, body=form.body.data,
body_html=markdown_to_html(form.body.data), body_html_safe=True,
from_bot=current_user.bot, nsfw=post.nsfw, nsfl=post.nsfl,
notify_author=form.notify_author.data, instance_id=1)
notify_author=form.notify_author.data, instance_id=1, language_id=form.language_id.data)
if reply.body:
for blocked_phrase in blocked_phrases():
if blocked_phrase in reply.body:
@ -761,6 +771,7 @@ def add_reply(post_id: int, comment_id: int):
return redirect(url_for('post.continue_discussion', post_id=post_id, comment_id=reply.parent_id))
else:
form.notify_author.data = True
form.language_id.data = current_user.language_id if current_user.language_id else english_language_id()
return render_template('post/add_reply.html', title=_('Discussing %(title)s', title=post.title), post=post,
is_moderator=is_moderator, form=form, comment=in_reply_to, markdown_editor=current_user.is_authenticated and current_user.markdown_editor,
@ -828,6 +839,8 @@ def post_edit_discussion_post(post_id: int):
form.nsfl.data = True
form.nsfw.render_kw = {'disabled': True}
form.language_id.choices = languages_for_form()
if form.validate_on_submit():
save_post(form, post, 'discussion')
post.community.last_active = utcnow()
@ -848,6 +861,7 @@ def post_edit_discussion_post(post_id: int):
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
form.language_id.data = post.language_id
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_discussion.html', title=_('Edit post'), form=form, post=post,
@ -886,6 +900,8 @@ def post_edit_image_post(post_id: int):
old_url = post.url
form.language_id.choices = languages_for_form()
if form.validate_on_submit():
save_post(form, post, 'image')
post.community.last_active = utcnow()
@ -929,6 +945,7 @@ def post_edit_image_post(post_id: int):
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
form.language_id.data = post.language_id
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_image.html', title=_('Edit post'), form=form, post=post,
@ -967,6 +984,8 @@ def post_edit_link_post(post_id: int):
old_url = post.url
form.language_id.choices = languages_for_form()
if form.validate_on_submit():
save_post(form, post, 'link')
post.community.last_active = utcnow()
@ -1010,6 +1029,7 @@ def post_edit_link_post(post_id: int):
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
form.language_id.data = post.language_id
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_link.html', title=_('Edit post'), form=form, post=post,
@ -1048,6 +1068,8 @@ def post_edit_video_post(post_id: int):
old_url = post.url
form.language_id.choices = languages_for_form()
if form.validate_on_submit():
save_post(form, post, 'video')
post.community.last_active = utcnow()
@ -1091,6 +1113,7 @@ def post_edit_video_post(post_id: int):
form.nsfw.data = post.nsfw
form.nsfl.data = post.nsfl
form.sticky.data = post.sticky
form.language_id.data = post.language_id
if not (post.community.is_moderator() or post.community.is_owner() or current_user.is_admin()):
form.sticky.render_kw = {'disabled': True}
return render_template('post/post_edit_video.html', title=_('Edit post'), form=form, post=post,
@ -1126,7 +1149,11 @@ def federate_post_update(post):
'stickied': post.sticky,
'published': ap_datetime(post.posted_at),
'updated': ap_datetime(post.edited_at),
'audience': post.community.ap_profile_id
'audience': post.community.ap_profile_id,
'language': {
'identifier': post.language_code(),
'name': post.language_name()
}
}
update_json = {
'id': f"https://{current_app.config['SERVER_NAME']}/activities/update/{gibberish(15)}",
@ -1487,6 +1514,7 @@ def post_reply_edit(post_id: int, comment_id: int):
else:
comment = None
form = NewReplyForm()
form.language_id.choices = languages_for_form()
if post_reply.user_id == current_user.id or post.community.is_moderator():
if form.validate_on_submit():
post_reply.body = form.body.data
@ -1494,6 +1522,7 @@ def post_reply_edit(post_id: int, comment_id: int):
post_reply.notify_author = form.notify_author.data
post.community.last_active = utcnow()
post_reply.edited_at = utcnow()
post_reply.language_id = form.language_id.data
db.session.commit()
flash(_('Your changes have been saved.'), 'success')
@ -1528,6 +1557,10 @@ def post_reply_edit(post_id: int, comment_id: int):
'audience': post.community.public_url(),
'contentMap': {
'en': post_reply.body_html
},
'language': {
'identifier': post_reply.language_code(),
'name': post_reply.language_name()
}
}
update_json = {
@ -1599,6 +1632,7 @@ def post_reply_edit(post_id: int, comment_id: int):
else:
form.body.data = post_reply.body
form.notify_author.data = post_reply.notify_author
form.language_id.data = post_reply.language_id
return render_template('post/post_reply_edit.html', title=_('Edit comment'), form=form, post=post, post_reply=post_reply,
comment=comment, markdown_editor=current_user.markdown_editor, moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()), community=post.community,

View file

@ -1376,4 +1376,22 @@ h1 .warning_badge {
max-height: 90vh;
}
@media (min-width: 768px) {
.post_language_chooser label {
display: none;
}
.post_language_chooser select {
max-width: 150px;
float: right;
margin-top: -5px;
}
}
@media (min-width: 768px) {
.language-float-right {
max-width: 150px;
float: right;
}
}
/*# sourceMappingURL=structure.css.map */

View file

@ -1050,4 +1050,24 @@ h1 .warning_badge {
.responsive-video {
max-width: 100%;
max-height: 90vh;
}
.post_language_chooser {
@include breakpoint(phablet) {
label {
display: none;
}
select {
max-width: 150px;
float: right;
margin-top: -5px;
}
}
}
.language-float-right {
@include breakpoint(phablet) {
max-width: 150px;
float: right;
}
}

View file

@ -58,8 +58,8 @@
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -60,8 +60,8 @@
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -59,8 +59,8 @@
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -60,8 +60,8 @@
<div class="col-md-1">
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -39,7 +39,7 @@
});
</script>
{% else %}
<a href="#" aria-hidden="true" id="post_reply_markdown_editor_enabler" class="markdown_editor_enabler" data-id="body">{{ _('Enable markdown editor') }}</a>
<!-- <a href="#" aria-hidden="true" id="post_reply_markdown_editor_enabler" class="markdown_editor_enabler" data-id="body">{{ _('Enable markdown editor') }}</a> -->
{% endif %}
{% endif %}
</div>

View file

@ -41,8 +41,8 @@
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -44,8 +44,8 @@
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -41,8 +41,8 @@
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -41,8 +41,8 @@
{{ render_field(form.nsfl) }}
</div>
<div class="col">
<div class="col post_language_chooser">
{{ render_field(form.language_id) }}
</div>
</div>

View file

@ -13,9 +13,11 @@
<div class="card-title">{{ _('Options for "%(post_title)s"', post_title=post.title) }}</div>
<ul class="option_list">
{% if current_user.is_authenticated %}
{% if post.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin() %}
{% if post.user_id == current_user.id %}
<li><a href="{{ url_for('post.post_edit', post_id=post.id) }}" class="no-underline" rel="nofollow"><span class="fe fe-edit"></span>
{{ _('Edit') }}</a></li>
{% endif %}
{% if post.user_id == current_user.id or post.community.is_moderator() or post.community.is_owner() or current_user.is_admin() %}
<li><a href="{{ url_for('post.post_delete', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span>
{{ _('Delete') }}</a></li>
{% endif %}

View file

@ -13,9 +13,11 @@
<div class="card-title">{{ _('Options for comment on "%(post_title)s"', post_title=post.title) }}</div>
<ul class="option_list">
{% if current_user.is_authenticated %}
{% if post_reply.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin() %}
{% if post_reply.user_id == current_user.id %}
<li><a href="{{ url_for('post.post_reply_edit', post_id=post.id, comment_id=post_reply.id) }}" class="no-underline" rel="nofollow"><span class="fe fe-edit"></span>
{{ _('Edit') }}</a></li>
{% endif %}
{% if post_reply.user_id == current_user.id or post.community.is_moderator() or post.community.is_owner() or current_user.is_admin() %}
<li><a href="{{ url_for('post.post_reply_delete', post_id=post.id, comment_id=post_reply.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span>
{{ _('Delete') }}</a></li>
{% endif %}

View file

@ -984,3 +984,8 @@ def languages_for_form():
if language.code != 'und':
result.append((language.id, language.name))
return result
def english_language_id():
english = Language.query.filter(Language.code == 'en').first()
return english.id if english else None

View file

@ -0,0 +1,46 @@
"""post_reply language
Revision ID: 9ad372b72d7c
Revises: e73996747d7e
Create Date: 2024-05-09 14:26:48.888908
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9ad372b72d7c'
down_revision = 'e73996747d7e'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('post', schema=None) as batch_op:
batch_op.create_foreign_key(None, 'language', ['language_id'], ['id'])
batch_op.drop_column('language')
with op.batch_alter_table('post_reply', schema=None) as batch_op:
batch_op.add_column(sa.Column('language_id', sa.Integer(), nullable=True))
batch_op.create_index(batch_op.f('ix_post_reply_language_id'), ['language_id'], unique=False)
batch_op.create_foreign_key(None, 'language', ['language_id'], ['id'])
batch_op.drop_column('language')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('post_reply', schema=None) as batch_op:
batch_op.add_column(sa.Column('language', sa.VARCHAR(length=10), autoincrement=False, nullable=True))
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_index(batch_op.f('ix_post_reply_language_id'))
batch_op.drop_column('language_id')
with op.batch_alter_table('post', schema=None) as batch_op:
batch_op.add_column(sa.Column('language', sa.VARCHAR(length=10), autoincrement=False, nullable=True))
batch_op.drop_constraint(None, type_='foreignkey')
# ### end Alembic commands ###