From 6d555c4c54292b7c2ce9d806904f31d5212cb690 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:01:22 +0800 Subject: [PATCH] when adding moderators do a fuzzy search and present a list of people to add. fixes #248 --- app/community/forms.py | 2 +- app/community/routes.py | 114 ++++++++++-------- app/community/util.py | 5 + .../community/community_add_moderator.html | 19 --- .../community/community_find_moderator.html | 51 ++++++++ .../community/community_mod_list.html | 2 +- .../community/community_moderate.html | 2 +- .../community_moderate_subscribers.html | 2 +- pyfedi.py | 6 +- 9 files changed, 126 insertions(+), 77 deletions(-) delete mode 100644 app/templates/community/community_add_moderator.html create mode 100644 app/templates/community/community_find_moderator.html diff --git a/app/community/forms.py b/app/community/forms.py index dfeb5f10..f4298838 100644 --- a/app/community/forms.py +++ b/app/community/forms.py @@ -65,7 +65,7 @@ class EditCommunityForm(FlaskForm): class AddModeratorForm(FlaskForm): user_name = StringField(_l('User name'), validators=[DataRequired()]) - submit = SubmitField(_l('Add')) + submit = SubmitField(_l('Find')) class EscalateReportForm(FlaskForm): diff --git a/app/community/routes.py b/app/community/routes.py index e9e944b9..f0e8404e 100644 --- a/app/community/routes.py +++ b/app/community/routes.py @@ -18,7 +18,7 @@ from app.community.forms import SearchRemoteCommunity, CreateDiscussionForm, Cre EscalateReportForm, ResolveReportForm, CreateVideoForm, CreatePollForm, RetrieveRemotePost from app.community.util import search_for_community, actor_to_community, \ save_post, save_icon_file, save_banner_file, send_to_remote_instance, \ - delete_post_from_community, delete_post_reply_from_community, community_in_list + delete_post_from_community, delete_post_reply_from_community, community_in_list, find_local_users from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE, \ SUBSCRIPTION_PENDING, SUBSCRIPTION_MODERATOR, REPORT_STATE_NEW, REPORT_STATE_ESCALATED, REPORT_STATE_RESOLVED, \ REPORT_STATE_DISCARDED, POST_TYPE_VIDEO, NOTIF_COMMUNITY, POST_TYPE_POLL, MICROBLOG_APPS @@ -988,65 +988,75 @@ def community_mod_list(community_id: int): ) -@bp.route('/community//moderators/add', methods=['GET', 'POST']) +@bp.route('/community//moderators/add/', methods=['GET', 'POST']) @login_required -def community_add_moderator(community_id: int): +def community_add_moderator(community_id: int, user_id: int): + if current_user.banned: + return show_ban_message() + community = Community.query.get_or_404(community_id) + new_moderator = User.query.get_or_404(user_id) + if community.is_owner() or current_user.is_admin() and not new_moderator.banned: + existing_member = CommunityMember.query.filter(CommunityMember.user_id == new_moderator.id, + CommunityMember.community_id == community_id).first() + if existing_member: + existing_member.is_moderator = True + else: + new_member = CommunityMember(community_id=community_id, user_id=new_moderator.id, is_moderator=True) + db.session.add(new_member) + db.session.commit() + flash(_('Moderator added')) + + # Notify new mod + if new_moderator.is_local(): + notify = Notification(title=_('You are now a moderator of %(name)s', name=community.display_name()), + url='/c/' + community.name, user_id=new_moderator.id, + author_id=current_user.id) + new_moderator.unread_notifications += 1 + db.session.add(notify) + db.session.commit() + else: + # for remote users, send a chat message to let them know + existing_conversation = Conversation.find_existing_conversation(recipient=new_moderator, + sender=current_user) + if not existing_conversation: + existing_conversation = Conversation(user_id=current_user.id) + existing_conversation.members.append(new_moderator) + existing_conversation.members.append(current_user) + db.session.add(existing_conversation) + db.session.commit() + server = current_app.config['SERVER_NAME'] + send_message(f"Hi there. I've added you as a moderator to the community !{community.name}@{server}.", + existing_conversation.id) + + add_to_modlog('add_mod', community_id=community_id, link_text=new_moderator.display_name(), + link=new_moderator.link()) + + # Flush cache + cache.delete_memoized(moderating_communities, new_moderator.id) + cache.delete_memoized(joined_communities, new_moderator.id) + cache.delete_memoized(community_moderators, community_id) + return redirect(url_for('community.community_mod_list', community_id=community.id)) + + +@bp.route('/community//moderators/find', methods=['GET', 'POST']) +@login_required +def community_find_moderator(community_id: int): if current_user.banned: return show_ban_message() community = Community.query.get_or_404(community_id) if community.is_owner() or current_user.is_admin(): form = AddModeratorForm() + potential_moderators = None if form.validate_on_submit(): - new_moderator = search_for_user(form.user_name.data) - if new_moderator: - existing_member = CommunityMember.query.filter(CommunityMember.user_id == new_moderator.id, CommunityMember.community_id == community_id).first() - if existing_member: - existing_member.is_moderator = True - else: - new_member = CommunityMember(community_id=community_id, user_id=new_moderator.id, is_moderator=True) - db.session.add(new_member) - db.session.commit() - flash(_('Moderator added')) + potential_moderators = find_local_users(form.user_name.data) - # Notify new mod - if new_moderator.is_local(): - notify = Notification(title=_('You are now a moderator of %(name)s', name=community.display_name()), - url='/c/' + community.name, user_id=new_moderator.id, - author_id=current_user.id) - new_moderator.unread_notifications += 1 - db.session.add(notify) - db.session.commit() - else: - # for remote users, send a chat message to let them know - existing_conversation = Conversation.find_existing_conversation(recipient=new_moderator, - sender=current_user) - if not existing_conversation: - existing_conversation = Conversation(user_id=current_user.id) - existing_conversation.members.append(new_moderator) - existing_conversation.members.append(current_user) - db.session.add(existing_conversation) - db.session.commit() - server = current_app.config['SERVER_NAME'] - send_message(f"Hi there. I've added you as a moderator to the community !{community.name}@{server}.", - existing_conversation.id) - - add_to_modlog('add_mod', community_id=community_id, link_text=new_moderator.display_name(), - link=new_moderator.link()) - - # Flush cache - cache.delete_memoized(moderating_communities, new_moderator.id) - cache.delete_memoized(joined_communities, new_moderator.id) - cache.delete_memoized(community_moderators, community_id) - return redirect(url_for('community.community_mod_list', community_id=community.id)) - else: - flash(_('Account not found'), 'warning') - - return render_template('community/community_add_moderator.html', title=_('Add moderator to %(community)s', community=community.display_name()), - community=community, form=form, - moderating_communities=moderating_communities(current_user.get_id()), - joined_communities=joined_communities(current_user.get_id()), - menu_topics=menu_topics(), site=g.site - ) + return render_template('community/community_find_moderator.html', title=_('Add moderator to %(community)s', + community=community.display_name()), + community=community, form=form, potential_moderators=potential_moderators, + moderating_communities=moderating_communities(current_user.get_id()), + joined_communities=joined_communities(current_user.get_id()), + menu_topics=menu_topics(), site=g.site + ) @bp.route('/community//moderators/remove/', methods=['GET', 'POST']) diff --git a/app/community/util.py b/app/community/util.py index 6b8782ce..17b75643 100644 --- a/app/community/util.py +++ b/app/community/util.py @@ -727,3 +727,8 @@ def community_in_list(community_id, community_list): if community_id == tup[0]: return True return False + + +def find_local_users(search: str) -> List[User]: + return User.query.filter(User.banned == False, User.deleted == False, User.ap_id == None, User.user_name.ilike(f"%{search}%")).\ + order_by(desc(User.reputation)).all() diff --git a/app/templates/community/community_add_moderator.html b/app/templates/community/community_add_moderator.html deleted file mode 100644 index 016efe74..00000000 --- a/app/templates/community/community_add_moderator.html +++ /dev/null @@ -1,19 +0,0 @@ -{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} - {% extends 'themes/' + theme() + '/base.html' %} -{% else %} - {% extends "base.html" %} -{% endif %} %} -{% from 'bootstrap/form.html' import render_form %} - -{% block app_content %} -
- -
-{% endblock %} \ No newline at end of file diff --git a/app/templates/community/community_find_moderator.html b/app/templates/community/community_find_moderator.html new file mode 100644 index 00000000..5f321070 --- /dev/null +++ b/app/templates/community/community_find_moderator.html @@ -0,0 +1,51 @@ +{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %} + {% extends 'themes/' + theme() + '/base.html' %} +{% else %} + {% extends "base.html" %} +{% endif %} %} +{% from 'bootstrap/form.html' import render_form %} + +{% block app_content %} +
+ +
+ {% if potential_moderators != None %} +
+
+
+
+ {% if potential_moderators -%} +
{{ _('Found some people:') }}
+ + {% for user in potential_moderators -%} + + + + + {% endfor -%} +
{{ render_username(user) }} + {% if community_membership(user, community) != SUBSCRIPTION_MODERATOR %} + {{ _('Add') }} + {% else %} + {{ _('Already a moderator') }} + {% endif %} +
+
+ {% else -%} +
{{ _('No people found') }}
+

{{ _('Only local accounts can be added as moderators.') }}

+ {% endif -%} +
+
+
+ + {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/community/community_mod_list.html b/app/templates/community/community_mod_list.html index 998a34a1..48ea4ca1 100644 --- a/app/templates/community/community_mod_list.html +++ b/app/templates/community/community_mod_list.html @@ -23,7 +23,7 @@

{{ _('See and change who moderates this community') }}

diff --git a/app/templates/community/community_moderate.html b/app/templates/community/community_moderate.html index 36fa1a08..98a8d5dd 100644 --- a/app/templates/community/community_moderate.html +++ b/app/templates/community/community_moderate.html @@ -21,7 +21,7 @@

{{ _('Reports') }}

- +

{{ _('See and handle all reports made about %(community)s', community=community.display_name()) }}.

diff --git a/app/templates/community/community_moderate_subscribers.html b/app/templates/community/community_moderate_subscribers.html index 80588538..6c267e7f 100644 --- a/app/templates/community/community_moderate_subscribers.html +++ b/app/templates/community/community_moderate_subscribers.html @@ -21,7 +21,7 @@

{{ _('Subscribers') }}

- +

{{ _('See who is subscribed to %(community)s', community=community.display_name()) }}

diff --git a/pyfedi.py b/pyfedi.py index d82f222c..7cdada0b 100644 --- a/pyfedi.py +++ b/pyfedi.py @@ -8,7 +8,8 @@ from flask_login import current_user from app import create_app, db, cli import os, click from flask import session, g, json, request, current_app -from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_ARTICLE, POST_TYPE_VIDEO, POST_TYPE_POLL +from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_ARTICLE, POST_TYPE_VIDEO, POST_TYPE_POLL, \ + SUBSCRIPTION_MODERATOR from app.models import Site from app.utils import getmtime, gibberish, shorten_string, shorten_url, digits, user_access, community_membership, \ can_create_post, can_upvote, can_downvote, shorten_number, ap_datetime, current_theme, community_link_to_href, \ @@ -24,7 +25,8 @@ def app_context_processor(): return os.path.getmtime('app/static/' + filename) return dict(getmtime=getmtime, instance_domain=current_app.config['SERVER_NAME'], POST_TYPE_LINK=POST_TYPE_LINK, POST_TYPE_IMAGE=POST_TYPE_IMAGE, - POST_TYPE_ARTICLE=POST_TYPE_ARTICLE, POST_TYPE_VIDEO=POST_TYPE_VIDEO, POST_TYPE_POLL=POST_TYPE_POLL) + POST_TYPE_ARTICLE=POST_TYPE_ARTICLE, POST_TYPE_VIDEO=POST_TYPE_VIDEO, POST_TYPE_POLL=POST_TYPE_POLL, + SUBSCRIPTION_MODERATOR=SUBSCRIPTION_MODERATOR) @app.shell_context_processor