mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
ban people from community, and unban them #21
This commit is contained in:
parent
c562202588
commit
9fd4ac1c53
5 changed files with 90 additions and 10 deletions
|
@ -68,7 +68,7 @@ class SearchRemoteCommunity(FlaskForm):
|
||||||
|
|
||||||
class BanUserCommunityForm(FlaskForm):
|
class BanUserCommunityForm(FlaskForm):
|
||||||
reason = StringField(_l('Reason'), render_kw={'autofocus': True}, validators=[DataRequired()])
|
reason = StringField(_l('Reason'), render_kw={'autofocus': True}, validators=[DataRequired()])
|
||||||
ban_until = DateField(_l('Ban until'))
|
ban_until = DateField(_l('Ban until'), validators=[Optional()])
|
||||||
delete_posts = BooleanField(_l('Also delete all their posts'))
|
delete_posts = BooleanField(_l('Also delete all their posts'))
|
||||||
delete_post_replies = BooleanField(_l('Also delete all their comments'))
|
delete_post_replies = BooleanField(_l('Also delete all their comments'))
|
||||||
submit = SubmitField(_l('Ban'))
|
submit = SubmitField(_l('Ban'))
|
||||||
|
|
|
@ -27,7 +27,7 @@ from app.utils import get_setting, render_template, allowlist_html, markdown_to_
|
||||||
shorten_string, gibberish, community_membership, ap_datetime, \
|
shorten_string, gibberish, community_membership, ap_datetime, \
|
||||||
request_etag_matches, return_304, instance_banned, can_create_post, can_upvote, can_downvote, user_filters_posts, \
|
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, \
|
joined_communities, moderating_communities, blocked_domains, mimetype_from_url, blocked_instances, \
|
||||||
community_moderators
|
community_moderators, communities_banned_from
|
||||||
from feedgen.feed import FeedGenerator
|
from feedgen.feed import FeedGenerator
|
||||||
from datetime import timezone, timedelta
|
from datetime import timezone, timedelta
|
||||||
|
|
||||||
|
@ -793,6 +793,9 @@ def community_ban_user(community_id: int, user_id: int):
|
||||||
|
|
||||||
form = BanUserCommunityForm()
|
form = BanUserCommunityForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
# Both CommunityBan and CommunityMember need to be updated. CommunityBan is under the control of moderators while
|
||||||
|
# CommunityMember can be cleared by the user by leaving the group and rejoining. CommunityMember.is_banned stops
|
||||||
|
# posts from the community from showing up in the banned person's home feed.
|
||||||
if not existing:
|
if not existing:
|
||||||
new_ban = CommunityBan(community_id=community_id, user_id=user.id, banned_by=current_user.id,
|
new_ban = CommunityBan(community_id=community_id, user_id=user.id, banned_by=current_user.id,
|
||||||
reason=form.reason.data)
|
reason=form.reason.data)
|
||||||
|
@ -800,7 +803,13 @@ def community_ban_user(community_id: int, user_id: int):
|
||||||
new_ban.ban_until = form.ban_until.data
|
new_ban.ban_until = form.ban_until.data
|
||||||
db.session.add(new_ban)
|
db.session.add(new_ban)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(_('%(name)s has been banned.', name=user.display_name()))
|
|
||||||
|
community_membership_record = CommunityMember.query.filter_by(community_id=community.id, user_id=user.id).first()
|
||||||
|
if community_membership_record:
|
||||||
|
community_membership_record.is_banned = True
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash(_('%(name)s has been banned.', name=user.display_name()))
|
||||||
|
|
||||||
if form.delete_posts.data:
|
if form.delete_posts.data:
|
||||||
posts = Post.query.filter(Post.user_id == user.id, Post.community_id == community.id).all()
|
posts = Post.query.filter(Post.user_id == user.id, Post.community_id == community.id).all()
|
||||||
|
@ -819,8 +828,11 @@ def community_ban_user(community_id: int, user_id: int):
|
||||||
|
|
||||||
# notify banned person
|
# notify banned person
|
||||||
if user.is_local():
|
if user.is_local():
|
||||||
|
cache.delete_memoized(communities_banned_from, user.id)
|
||||||
|
cache.delete_memoized(joined_communities, user.id)
|
||||||
|
cache.delete_memoized(moderating_communities, user.id)
|
||||||
notify = Notification(title=shorten_string('You have been banned from ' + community.title),
|
notify = Notification(title=shorten_string('You have been banned from ' + community.title),
|
||||||
url=f'/', user_id=user.id,
|
url=f'/notifications', user_id=user.id,
|
||||||
author_id=1)
|
author_id=1)
|
||||||
db.session.add(notify)
|
db.session.add(notify)
|
||||||
user.unread_notifications += 1
|
user.unread_notifications += 1
|
||||||
|
@ -839,6 +851,42 @@ def community_ban_user(community_id: int, user_id: int):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/community/<int:community_id>/<int:user_id>/unban_user_community', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def community_unban_user(community_id: int, user_id: int):
|
||||||
|
community = Community.query.get_or_404(community_id)
|
||||||
|
user = User.query.get_or_404(user_id)
|
||||||
|
existing_ban = CommunityBan.query.filter_by(community_id=community.id, user_id=user.id).first()
|
||||||
|
if existing_ban:
|
||||||
|
db.session.delete(existing_ban)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
community_membership_record = CommunityMember.query.filter_by(community_id=community.id, user_id=user.id).first()
|
||||||
|
if community_membership_record:
|
||||||
|
community_membership_record.is_banned = False
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash(_('%(name)s has been unbanned.', name=user.display_name()))
|
||||||
|
|
||||||
|
# todo: federate ban to post author instance
|
||||||
|
|
||||||
|
# notify banned person
|
||||||
|
if user.is_local():
|
||||||
|
cache.delete_memoized(communities_banned_from, user.id)
|
||||||
|
cache.delete_memoized(joined_communities, user.id)
|
||||||
|
cache.delete_memoized(moderating_communities, user.id)
|
||||||
|
notify = Notification(title=shorten_string('You have been un-banned from ' + community.title),
|
||||||
|
url=f'/notifications', user_id=user.id,
|
||||||
|
author_id=1)
|
||||||
|
db.session.add(notify)
|
||||||
|
user.unread_notifications += 1
|
||||||
|
db.session.commit()
|
||||||
|
else:
|
||||||
|
...
|
||||||
|
# todo: send chatmessage to remote user and federate it
|
||||||
|
|
||||||
|
return redirect(url_for('community.community_moderate_banned', actor=community.link()))
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:community_id>/notification', methods=['GET', 'POST'])
|
@bp.route('/<int:community_id>/notification', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -24,7 +24,7 @@ from sqlalchemy_searchable import search
|
||||||
from app.utils import render_template, get_setting, gibberish, request_etag_matches, return_304, blocked_domains, \
|
from app.utils import render_template, get_setting, gibberish, request_etag_matches, return_304, blocked_domains, \
|
||||||
ap_datetime, ip_address, retrieve_block_list, shorten_string, markdown_to_text, user_filters_home, \
|
ap_datetime, ip_address, retrieve_block_list, shorten_string, markdown_to_text, user_filters_home, \
|
||||||
joined_communities, moderating_communities, parse_page, theme_list, get_request, markdown_to_html, allowlist_html, \
|
joined_communities, moderating_communities, parse_page, theme_list, get_request, markdown_to_html, allowlist_html, \
|
||||||
blocked_instances
|
blocked_instances, communities_banned_from
|
||||||
from app.models import Community, CommunityMember, Post, Site, User, utcnow, Domain, Topic, File, Instance, \
|
from app.models import Community, CommunityMember, Post, Site, User, utcnow, Domain, Topic, File, Instance, \
|
||||||
InstanceRole, Notification
|
InstanceRole, Notification
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
@ -131,7 +131,13 @@ def home_page(type, sort):
|
||||||
next_url = url_for('main.all_posts', page=posts.next_num, sort=sort) if posts.has_next else None
|
next_url = url_for('main.all_posts', page=posts.next_num, sort=sort) if posts.has_next else None
|
||||||
prev_url = url_for('main.all_posts', page=posts.prev_num, sort=sort) if posts.has_prev and page != 1 else None
|
prev_url = url_for('main.all_posts', page=posts.prev_num, sort=sort) if posts.has_prev and page != 1 else None
|
||||||
|
|
||||||
active_communities = Community.query.filter_by(banned=False).order_by(desc(Community.last_active)).limit(5).all()
|
# Active Communities
|
||||||
|
active_communities = Community.query.filter_by(banned=False)
|
||||||
|
if current_user.is_authenticated: # do not show communities current user is banned from
|
||||||
|
banned_from = communities_banned_from(current_user.id)
|
||||||
|
if banned_from:
|
||||||
|
active_communities = active_communities.filter(Community.id.not_in(banned_from))
|
||||||
|
active_communities = active_communities.order_by(desc(Community.last_active)).limit(5).all()
|
||||||
|
|
||||||
return render_template('index.html', posts=posts, active_communities=active_communities, show_post_community=True,
|
return render_template('index.html', posts=posts, active_communities=active_communities, show_post_community=True,
|
||||||
POST_TYPE_IMAGE=POST_TYPE_IMAGE, POST_TYPE_LINK=POST_TYPE_LINK,
|
POST_TYPE_IMAGE=POST_TYPE_IMAGE, POST_TYPE_LINK=POST_TYPE_LINK,
|
||||||
|
@ -178,6 +184,11 @@ def list_communities():
|
||||||
if topic_id != 0:
|
if topic_id != 0:
|
||||||
communities = communities.filter_by(topic_id=topic_id)
|
communities = communities.filter_by(topic_id=topic_id)
|
||||||
|
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
banned_from = communities_banned_from(current_user.id)
|
||||||
|
if banned_from:
|
||||||
|
communities = communities.filter(Community.id.not_in(banned_from))
|
||||||
|
|
||||||
return render_template('list_communities.html', communities=communities.order_by(sort_by).all(), search=search_param, title=_('Communities'),
|
return render_template('list_communities.html', communities=communities.order_by(sort_by).all(), search=search_param, title=_('Communities'),
|
||||||
SUBSCRIPTION_PENDING=SUBSCRIPTION_PENDING, SUBSCRIPTION_MEMBER=SUBSCRIPTION_MEMBER,
|
SUBSCRIPTION_PENDING=SUBSCRIPTION_PENDING, SUBSCRIPTION_MEMBER=SUBSCRIPTION_MEMBER,
|
||||||
SUBSCRIPTION_OWNER=SUBSCRIPTION_OWNER, SUBSCRIPTION_MODERATOR=SUBSCRIPTION_MODERATOR,
|
SUBSCRIPTION_OWNER=SUBSCRIPTION_OWNER, SUBSCRIPTION_MODERATOR=SUBSCRIPTION_MODERATOR,
|
||||||
|
|
|
@ -48,11 +48,12 @@
|
||||||
<td>{{ user.reports if user.reports > 0 }} </td>
|
<td>{{ user.reports if user.reports > 0 }} </td>
|
||||||
<td>{{ user.ip_address if user.ip_address }} </td>
|
<td>{{ user.ip_address if user.ip_address }} </td>
|
||||||
<td>{% if user.is_local() %}
|
<td>{% if user.is_local() %}
|
||||||
<a href="/u/{{ user.link() }}">View local</a>
|
<a href="/u/{{ user.link() }}">View</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<a href="/u/{{ user.link() }}">View local</a> |
|
||||||
<a href="{{ user.ap_profile_id }}">View remote</a>
|
<a href="{{ user.ap_profile_id }}">View remote</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
| <a href="#" class="confirm_first">{{ _('Un ban') }}</a>
|
| <a href="{{ url_for('community.community_unban_user', community_id=community.id, user_id=user.id) }}" class="confirm_first">{{ _('Un ban') }}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
24
app/utils.py
24
app/utils.py
|
@ -28,7 +28,7 @@ import re
|
||||||
|
|
||||||
from app.email import send_welcome_email
|
from app.email import send_welcome_email
|
||||||
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock, ActivityPubLog, IpBan, \
|
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock, ActivityPubLog, IpBan, \
|
||||||
Site, Post, PostReply, utcnow, Filter, CommunityMember, InstanceBlock
|
Site, Post, PostReply, utcnow, Filter, CommunityMember, InstanceBlock, CommunityBan
|
||||||
|
|
||||||
|
|
||||||
# Flask's render_template function, with support for themes added
|
# Flask's render_template function, with support for themes added
|
||||||
|
@ -312,6 +312,12 @@ def community_membership(user: User, community: Community) -> int:
|
||||||
return user.subscribed(community.id)
|
return user.subscribed(community.id)
|
||||||
|
|
||||||
|
|
||||||
|
@cache.memoize(timeout=86400)
|
||||||
|
def communities_banned_from(user_id) -> List[int]:
|
||||||
|
community_bans = CommunityBan.query.filter(CommunityBan.user_id == user_id).all()
|
||||||
|
return [cb.community_id for cb in community_bans]
|
||||||
|
|
||||||
|
|
||||||
@cache.memoize(timeout=86400)
|
@cache.memoize(timeout=86400)
|
||||||
def blocked_domains(user_id) -> List[int]:
|
def blocked_domains(user_id) -> List[int]:
|
||||||
blocks = DomainBlock.query.filter_by(user_id=user_id)
|
blocks = DomainBlock.query.filter_by(user_id=user_id)
|
||||||
|
@ -460,6 +466,9 @@ def can_downvote(user, community: Community, site=None) -> bool:
|
||||||
if user.attitude < -0.40 or user.reputation < -10: # this should exclude about 3.7% of users.
|
if user.attitude < -0.40 or user.reputation < -10: # this should exclude about 3.7% of users.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if community.id in communities_banned_from(user.id):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -467,6 +476,9 @@ def can_upvote(user, community: Community) -> bool:
|
||||||
if user is None or community is None or user.banned:
|
if user is None or community is None or user.banned:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if community.id in communities_banned_from(user.id):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -483,6 +495,9 @@ def can_create_post(user, content: Community) -> bool:
|
||||||
if content.local_only and not user.is_local():
|
if content.local_only and not user.is_local():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if content.id in communities_banned_from(user.id):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,6 +511,9 @@ def can_create_post_reply(user, content: Community) -> bool:
|
||||||
if content.local_only and not user.is_local():
|
if content.local_only and not user.is_local():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if content.id in communities_banned_from(user.id):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -602,7 +620,8 @@ def moderating_communities(user_id):
|
||||||
return []
|
return []
|
||||||
return Community.query.join(CommunityMember, Community.id == CommunityMember.community_id).\
|
return Community.query.join(CommunityMember, Community.id == CommunityMember.community_id).\
|
||||||
filter(Community.banned == False).\
|
filter(Community.banned == False).\
|
||||||
filter(or_(CommunityMember.is_moderator == True, CommunityMember.is_owner == True)).\
|
filter(or_(CommunityMember.is_moderator == True, CommunityMember.is_owner == True)). \
|
||||||
|
filter(CommunityMember.is_banned == False). \
|
||||||
filter(CommunityMember.user_id == user_id).order_by(Community.title).all()
|
filter(CommunityMember.user_id == user_id).order_by(Community.title).all()
|
||||||
|
|
||||||
|
|
||||||
|
@ -613,6 +632,7 @@ def joined_communities(user_id):
|
||||||
return Community.query.join(CommunityMember, Community.id == CommunityMember.community_id).\
|
return Community.query.join(CommunityMember, Community.id == CommunityMember.community_id).\
|
||||||
filter(Community.banned == False). \
|
filter(Community.banned == False). \
|
||||||
filter(CommunityMember.is_moderator == False, CommunityMember.is_owner == False). \
|
filter(CommunityMember.is_moderator == False, CommunityMember.is_owner == False). \
|
||||||
|
filter(CommunityMember.is_banned == False). \
|
||||||
filter(CommunityMember.user_id == user_id).order_by(Community.title).all()
|
filter(CommunityMember.user_id == user_id).order_by(Community.title).all()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue