From af4dee7ea3e074773499ed698a1319b1421dc523 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Thu, 9 May 2024 17:54:30 +1200 Subject: [PATCH] add language choices to UI #51 --- app/activitypub/routes.py | 70 ++++++++++++------- app/activitypub/util.py | 35 +++++++++- app/cli.py | 1 + app/community/forms.py | 4 ++ app/community/routes.py | 26 ++++++- app/community/util.py | 2 + app/models.py | 31 +++++++- app/post/forms.py | 2 + app/post/routes.py | 44 ++++++++++-- app/static/structure.css | 18 +++++ app/static/structure.scss | 20 ++++++ .../community/add_discussion_post.html | 4 +- app/templates/community/add_image_post.html | 4 +- app/templates/community/add_link_post.html | 4 +- app/templates/community/add_video_post.html | 4 +- app/templates/post/post.html | 2 +- app/templates/post/post_edit_discussion.html | 4 +- app/templates/post/post_edit_image.html | 4 +- app/templates/post/post_edit_link.html | 4 +- app/templates/post/post_edit_video.html | 4 +- app/templates/post/post_options.html | 4 +- app/templates/post/post_reply_options.html | 4 +- app/utils.py | 5 ++ .../9ad372b72d7c_post_reply_language.py | 46 ++++++++++++ 24 files changed, 287 insertions(+), 59 deletions(-) create mode 100644 migrations/versions/9ad372b72d7c_post_reply_language.py diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index f5e79822..0bff76ef 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -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 = '

deleted

' - 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 = '

deleted

' + 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) diff --git a/app/activitypub/util.py b/app/activitypub/util.py index a587975b..5bde2753 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -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) diff --git a/app/cli.py b/app/cli.py index 3dc5534e..6739fcd4 100644 --- a/app/cli.py +++ b/app/cli.py @@ -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', diff --git a/app/community/forms.py b/app/community/forms.py index 96e1b27f..8eaea74a 100644 --- a/app/community/forms.py +++ b/app/community/forms.py @@ -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: diff --git a/app/community/routes.py b/app/community/routes.py index 21c4d2b2..b05b5993 100644 --- a/app/community/routes.py +++ b/app/community/routes.py @@ -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)}", diff --git a/app/community/util.py b/app/community/util.py index 09a99a49..222a72c5 100644 --- a/app/community/util.py +++ b/app/community/util.py @@ -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 diff --git a/app/models.py b/app/models.py index d1ac093c..a6867805 100644 --- a/app/models.py +++ b/app/models.py @@ -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']) diff --git a/app/post/forms.py b/app/post/forms.py index 085bf92d..23a51c01 100644 --- a/app/post/forms.py +++ b/app/post/forms.py @@ -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')) diff --git a/app/post/routes.py b/app/post/routes.py index d9c7d3bb..35cbdac0 100644 --- a/app/post/routes.py +++ b/app/post/routes.py @@ -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, diff --git a/app/static/structure.css b/app/static/structure.css index e7bb1f38..812e5ee2 100644 --- a/app/static/structure.css +++ b/app/static/structure.css @@ -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 */ diff --git a/app/static/structure.scss b/app/static/structure.scss index b741fbb6..e3d5e443 100644 --- a/app/static/structure.scss +++ b/app/static/structure.scss @@ -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; + } } \ No newline at end of file diff --git a/app/templates/community/add_discussion_post.html b/app/templates/community/add_discussion_post.html index c9e3a4a0..0cb3eeb1 100644 --- a/app/templates/community/add_discussion_post.html +++ b/app/templates/community/add_discussion_post.html @@ -58,8 +58,8 @@
{{ render_field(form.nsfl) }}
-
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/community/add_image_post.html b/app/templates/community/add_image_post.html index 24c5701f..a3408741 100644 --- a/app/templates/community/add_image_post.html +++ b/app/templates/community/add_image_post.html @@ -60,8 +60,8 @@
{{ render_field(form.nsfl) }}
-
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/community/add_link_post.html b/app/templates/community/add_link_post.html index 02a9990c..87c58189 100644 --- a/app/templates/community/add_link_post.html +++ b/app/templates/community/add_link_post.html @@ -59,8 +59,8 @@
{{ render_field(form.nsfl) }}
-
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/community/add_video_post.html b/app/templates/community/add_video_post.html index 31dca5d3..40f417f6 100644 --- a/app/templates/community/add_video_post.html +++ b/app/templates/community/add_video_post.html @@ -60,8 +60,8 @@
{{ render_field(form.nsfl) }}
-
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/post/post.html b/app/templates/post/post.html index f856a12b..ecaa1d15 100644 --- a/app/templates/post/post.html +++ b/app/templates/post/post.html @@ -39,7 +39,7 @@ }); {% else %} - + {% endif %} {% endif %} diff --git a/app/templates/post/post_edit_discussion.html b/app/templates/post/post_edit_discussion.html index d8cba771..441a75d8 100644 --- a/app/templates/post/post_edit_discussion.html +++ b/app/templates/post/post_edit_discussion.html @@ -41,8 +41,8 @@ {{ render_field(form.nsfl) }} -
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/post/post_edit_image.html b/app/templates/post/post_edit_image.html index db0a9d9f..953c3641 100644 --- a/app/templates/post/post_edit_image.html +++ b/app/templates/post/post_edit_image.html @@ -44,8 +44,8 @@ {{ render_field(form.nsfl) }} -
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/post/post_edit_link.html b/app/templates/post/post_edit_link.html index 82fa2781..50f37bbe 100644 --- a/app/templates/post/post_edit_link.html +++ b/app/templates/post/post_edit_link.html @@ -41,8 +41,8 @@ {{ render_field(form.nsfl) }} -
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/post/post_edit_video.html b/app/templates/post/post_edit_video.html index 9ded804d..31bd08f6 100644 --- a/app/templates/post/post_edit_video.html +++ b/app/templates/post/post_edit_video.html @@ -41,8 +41,8 @@ {{ render_field(form.nsfl) }} -
- +
+ {{ render_field(form.language_id) }}
diff --git a/app/templates/post/post_options.html b/app/templates/post/post_options.html index 34c8831f..a1a680a9 100644 --- a/app/templates/post/post_options.html +++ b/app/templates/post/post_options.html @@ -13,9 +13,11 @@
{{ _('Options for "%(post_title)s"', post_title=post.title) }}