community blocking #283

This commit is contained in:
rimu 2024-08-12 20:54:10 +12:00
parent 85726d5638
commit 1f0c8eb6ff
11 changed files with 74 additions and 13 deletions

View file

@ -25,7 +25,7 @@ from app.models import Settings, BannedInstances, Interest, Role, User, RolePerm
utcnow, Site, Instance, File, Notification, Post, CommunityMember, NotificationSubscription, PostReply, Language, \ utcnow, Site, Instance, File, Notification, Post, CommunityMember, NotificationSubscription, PostReply, Language, \
Tag, InstanceRole Tag, InstanceRole
from app.utils import file_get_contents, retrieve_block_list, blocked_domains, retrieve_peertube_block_list, \ from app.utils import file_get_contents, retrieve_block_list, blocked_domains, retrieve_peertube_block_list, \
shorten_string, get_request, html_to_text shorten_string, get_request, html_to_text, blocked_communities
def register(app): def register(app):
@ -339,6 +339,9 @@ def register(app):
domains_ids = blocked_domains(user.id) domains_ids = blocked_domains(user.id)
if domains_ids: if domains_ids:
posts = posts.filter(or_(Post.domain_id.not_in(domains_ids), Post.domain_id == None)) posts = posts.filter(or_(Post.domain_id.not_in(domains_ids), Post.domain_id == None))
community_ids = blocked_communities(user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
posts = posts.filter(Post.posted_at > user.last_seen).order_by(desc(Post.score)) posts = posts.filter(Post.posted_at > user.last_seen).order_by(desc(Post.score))
posts = posts.limit(20).all() posts = posts.limit(20).all()

View file

@ -35,7 +35,8 @@ 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, \ 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, communities_banned_from, show_ban_message, recently_upvoted_posts, recently_downvoted_posts, \ community_moderators, communities_banned_from, show_ban_message, recently_upvoted_posts, recently_downvoted_posts, \
blocked_users, post_ranking, languages_for_form, english_language_id, menu_topics, add_to_modlog blocked_users, post_ranking, languages_for_form, english_language_id, menu_topics, add_to_modlog, \
blocked_communities
from feedgen.feed import FeedGenerator from feedgen.feed import FeedGenerator
from datetime import timezone, timedelta from datetime import timezone, timedelta
from copy import copy from copy import copy
@ -219,6 +220,9 @@ def show_community(community: Community):
instance_ids = blocked_instances(current_user.id) instance_ids = blocked_instances(current_user.id)
if instance_ids: if instance_ids:
posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None)) posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None))
community_ids = blocked_communities(current_user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
# filter blocked users # filter blocked users
blocked_accounts = blocked_users(current_user.id) blocked_accounts = blocked_users(current_user.id)

View file

@ -26,7 +26,7 @@ from app.utils import render_template, get_setting, gibberish, request_etag_matc
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, communities_banned_from, topic_tree, recently_upvoted_posts, recently_downvoted_posts, \ blocked_instances, communities_banned_from, topic_tree, recently_upvoted_posts, recently_downvoted_posts, \
generate_image_from_video_url, blocked_users, microblog_content_to_title, menu_topics, languages_for_form, \ generate_image_from_video_url, blocked_users, microblog_content_to_title, menu_topics, languages_for_form, \
make_cache_key make_cache_key, blocked_communities
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, Language, community_language, PostReply, ModLog InstanceRole, Notification, Language, community_language, PostReply, ModLog
@ -119,6 +119,9 @@ def home_page(type, sort):
instance_ids = blocked_instances(current_user.id) instance_ids = blocked_instances(current_user.id)
if instance_ids: if instance_ids:
posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None)) posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None))
community_ids = blocked_communities(current_user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
# filter blocked users # filter blocked users
blocked_accounts = blocked_users(current_user.id) blocked_accounts = blocked_users(current_user.id)
if blocked_accounts: if blocked_accounts:
@ -153,6 +156,9 @@ def home_page(type, sort):
banned_from = communities_banned_from(current_user.id) banned_from = communities_banned_from(current_user.id)
if banned_from: if banned_from:
active_communities = active_communities.filter(Community.id.not_in(banned_from)) active_communities = active_communities.filter(Community.id.not_in(banned_from))
community_ids = blocked_communities(current_user.id)
if community_ids:
active_communities = active_communities.filter(Community.id.not_in(community_ids))
active_communities = active_communities.order_by(desc(Community.last_active)).limit(5).all() active_communities = active_communities.order_by(desc(Community.last_active)).limit(5).all()
# Voting history # Voting history

View file

@ -23,7 +23,7 @@ from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, SUBSCRIPTION_
from app.models import Post, PostReply, \ from app.models import Post, PostReply, \
PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community, \ PostReplyVote, PostVote, Notification, utcnow, UserBlock, DomainBlock, InstanceBlock, Report, Site, Community, \
Topic, User, Instance, NotificationSubscription, UserFollower, Poll, PollChoice, PollChoiceVote, PostBookmark, \ Topic, User, Instance, NotificationSubscription, UserFollower, Poll, PollChoice, PollChoiceVote, PostBookmark, \
PostReplyBookmark PostReplyBookmark, CommunityBlock
from app.post import bp from app.post import bp
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \ from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \
shorten_string, markdown_to_text, gibberish, ap_datetime, return_304, \ shorten_string, markdown_to_text, gibberish, ap_datetime, return_304, \
@ -31,7 +31,7 @@ from app.utils import get_setting, render_template, allowlist_html, markdown_to_
reply_already_exists, reply_is_just_link_to_gif_reaction, confidence, moderating_communities, joined_communities, \ 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, \ 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, menu_topics, add_to_modlog languages_for_form, menu_topics, add_to_modlog, blocked_communities
def show_post(post_id: int): def show_post(post_id: int):
@ -1464,6 +1464,19 @@ def post_block_domain(post_id: int):
return redirect(post.community.local_url()) return redirect(post.community.local_url())
@bp.route('/post/<int:post_id>/block_community', methods=['GET', 'POST'])
@login_required
def post_block_community(post_id: int):
post = Post.query.get_or_404(post_id)
existing = CommunityBlock.query.filter_by(user_id=current_user.id, community_id=post.community_id).first()
if not existing:
db.session.add(CommunityBlock(user_id=current_user.id, community_id=post.community_id))
db.session.commit()
cache.delete_memoized(blocked_communities, current_user.id)
flash(_('Posts in %(name)s will be hidden.', name=post.community.display_name()))
return redirect(post.community.local_url())
@bp.route('/post/<int:post_id>/block_instance', methods=['GET', 'POST']) @bp.route('/post/<int:post_id>/block_instance', methods=['GET', 'POST'])
@login_required @login_required
def post_block_instance(post_id: int): def post_block_instance(post_id: int):

View file

@ -6,7 +6,8 @@ from sqlalchemy import or_
from app.models import Post, Language, Community from app.models import Post, Language, Community
from app.search import bp from app.search import bp
from app.utils import moderating_communities, joined_communities, render_template, blocked_domains, blocked_instances, \ from app.utils import moderating_communities, joined_communities, render_template, blocked_domains, blocked_instances, \
communities_banned_from, recently_upvoted_posts, recently_downvoted_posts, blocked_users, menu_topics communities_banned_from, recently_upvoted_posts, recently_downvoted_posts, blocked_users, menu_topics, \
blocked_communities
from app.community.forms import RetrieveRemotePost from app.community.forms import RetrieveRemotePost
from app.activitypub.util import resolve_remote_post_from_search from app.activitypub.util import resolve_remote_post_from_search
@ -42,6 +43,9 @@ def run_search():
instance_ids = blocked_instances(current_user.id) instance_ids = blocked_instances(current_user.id)
if instance_ids: if instance_ids:
posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None)) posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None))
community_ids = blocked_communities(current_user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
# filter blocked users # filter blocked users
blocked_accounts = blocked_users(current_user.id) blocked_accounts = blocked_users(current_user.id)
if blocked_accounts: if blocked_accounts:

View file

@ -11,7 +11,8 @@ from app.inoculation import inoculation
from app.models import Post, Community, Tag, post_tag from app.models import Post, Community, Tag, post_tag
from app.tag import bp from app.tag import bp
from app.utils import render_template, permission_required, joined_communities, moderating_communities, \ from app.utils import render_template, permission_required, joined_communities, moderating_communities, \
user_filters_posts, blocked_instances, blocked_users, blocked_domains, menu_topics, mimetype_from_url user_filters_posts, blocked_instances, blocked_users, blocked_domains, menu_topics, mimetype_from_url, \
blocked_communities
from sqlalchemy import desc, or_ from sqlalchemy import desc, or_
@ -36,6 +37,9 @@ def show_tag(tag):
instance_ids = blocked_instances(current_user.id) instance_ids = blocked_instances(current_user.id)
if instance_ids: if instance_ids:
posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None)) posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None))
community_ids = blocked_communities(current_user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
# filter blocked users # filter blocked users
blocked_accounts = blocked_users(current_user.id) blocked_accounts = blocked_users(current_user.id)
if blocked_accounts: if blocked_accounts:

View file

@ -40,9 +40,11 @@
{% if post.user_id != current_user.id -%} {% if post.user_id != current_user.id -%}
<li><a href="{{ url_for('post.post_block_user', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span> <li><a href="{{ url_for('post.post_block_user', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>
{{ _('Block post author @%(author_name)s', author_name=post.author.user_name) }}</a></li> {{ _('Block post author @%(author_name)s', author_name=post.author.user_name) }}</a></li>
<li><a href="{{ url_for('post.post_block_community', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>
{{ _('Block community %(community_name)s', community_name=post.community.display_name()) }}</a></li>
{% if post.community.is_moderator() or current_user.is_admin() -%} {% if post.community.is_moderator() or current_user.is_admin() -%}
<li><a href="{{ url_for('community.community_ban_user', community_id=post.community.id, user_id=post.author.id) }}" class="no-underline"><span class="fe fe-block red"></span> <li><a href="{{ url_for('community.community_ban_user', community_id=post.community.id, user_id=post.author.id) }}" class="no-underline"><span class="fe fe-block red"></span>
{{ _('Ban post author @%(author_name)s from<br>%(community_name)s', author_name=post.author.user_name, community_name=post.community.title) }}</a></li> {{ _('Ban post author @%(author_name)s from %(community_name)s', author_name=post.author.user_name, community_name=post.community.title) }}</a></li>
{% endif -%} {% endif -%}
{% if post.domain_id -%} {% if post.domain_id -%}
<li><a href="{{ url_for('post.post_block_domain', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span> <li><a href="{{ url_for('post.post_block_domain', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>

View file

@ -106,7 +106,7 @@
{% for community in blocked_communities %} {% for community in blocked_communities %}
<tr> <tr>
<td><a href="{{ url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not none else community.name) }}">{{ community.title }}</a></td> <td><a href="{{ url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not none else community.name) }}">{{ community.title }}</a></td>
<td><a class="no-underline confirm_first" href="#" rel="nofollow"><span class="fe fe-delete"></span> {{ _('Unblock') }}</a></td> <td><a class="no-underline confirm_first" href="{{ url_for('user.user_community_unblock', community_id=community.id) }}" rel="nofollow"><span class="fe fe-delete"></span> {{ _('Unblock') }}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@ -141,7 +141,7 @@
</tr> </tr>
{% for instance in blocked_instances %} {% for instance in blocked_instances %}
<tr> <tr>
<td><a href="#">{{ instance.domain }}</a></td> <td><a href="https://{{ instance.domain }}">{{ instance.domain }}</a></td>
<td><a class="no-underline confirm_first" href="{{ url_for('user.instance_unblock', instance_id=instance.id) }}" rel="nofollow"><span class="fe fe-delete"></span> {{ _('Unblock') }}</a></td> <td><a class="no-underline confirm_first" href="{{ url_for('user.instance_unblock', instance_id=instance.id) }}" rel="nofollow"><span class="fe fe-delete"></span> {{ _('Unblock') }}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -21,7 +21,7 @@ from app import db, celery, cache
from app.topic.forms import ChooseTopicsForm, SuggestTopicsForm from app.topic.forms import ChooseTopicsForm, SuggestTopicsForm
from app.utils import render_template, user_filters_posts, moderating_communities, joined_communities, \ from app.utils import render_template, user_filters_posts, moderating_communities, joined_communities, \
community_membership, blocked_domains, validation_required, mimetype_from_url, blocked_instances, \ community_membership, blocked_domains, validation_required, mimetype_from_url, blocked_instances, \
communities_banned_from, blocked_users, menu_topics communities_banned_from, blocked_users, menu_topics, blocked_communities
@bp.route('/topic/<path:topic_path>', methods=['GET']) @bp.route('/topic/<path:topic_path>', methods=['GET'])
@ -75,6 +75,9 @@ def show_topic(topic_path):
instance_ids = blocked_instances(current_user.id) instance_ids = blocked_instances(current_user.id)
if instance_ids: if instance_ids:
posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None)) posts = posts.filter(or_(Post.instance_id.not_in(instance_ids), Post.instance_id == None))
community_ids = blocked_communities(current_user.id)
if community_ids:
posts = posts.filter(Post.community_id.not_in(community_ids))
# filter blocked users # filter blocked users
blocked_accounts = blocked_users(current_user.id) blocked_accounts = blocked_users(current_user.id)
if blocked_accounts: if blocked_accounts:

View file

@ -23,7 +23,8 @@ from app.user.utils import purge_user_then_delete, unsubscribe_from_community
from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \ from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \
is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership, user_filters_home, \ is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership, user_filters_home, \
user_filters_posts, user_filters_replies, moderating_communities, joined_communities, theme_list, blocked_instances, \ user_filters_posts, user_filters_replies, moderating_communities, joined_communities, theme_list, blocked_instances, \
allowlist_html, recently_upvoted_posts, recently_downvoted_posts, blocked_users, menu_topics, add_to_modlog allowlist_html, recently_upvoted_posts, recently_downvoted_posts, blocked_users, menu_topics, add_to_modlog, \
blocked_communities
from sqlalchemy import desc, or_, text from sqlalchemy import desc, or_, text
import os import os
@ -509,6 +510,21 @@ def instance_unblock(instance_id):
return redirect(goto) return redirect(goto)
@bp.route('/user/community/<int:community_id>/unblock', methods=['GET'])
@login_required
def user_community_unblock(community_id):
community = Community.query.get_or_404(community_id)
existing_block = CommunityBlock.query.filter_by(user_id=current_user.id, community_id=community.id).first()
if existing_block:
db.session.delete(existing_block)
db.session.commit()
cache.delete_memoized(blocked_communities, current_user.id)
flash(f'{community.display_name()} has been unblocked.')
goto = request.args.get('redirect') if 'redirect' in request.args else url_for('user.user_settings_filters')
return redirect(goto)
@bp.route('/delete_account', methods=['GET', 'POST']) @bp.route('/delete_account', methods=['GET', 'POST'])
@login_required @login_required
def delete_account(): def delete_account():

View file

@ -36,7 +36,7 @@ from PIL import Image, ImageOps
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, CommunityBan, Topic, UserBlock, Language, \ Site, Post, PostReply, utcnow, Filter, CommunityMember, InstanceBlock, CommunityBan, Topic, UserBlock, Language, \
File, ModLog File, ModLog, CommunityBlock
# Flask's render_template function, with support for themes added # Flask's render_template function, with support for themes added
@ -436,6 +436,12 @@ def blocked_domains(user_id) -> List[int]:
return [block.domain_id for block in blocks] return [block.domain_id for block in blocks]
@cache.memoize(timeout=86400)
def blocked_communities(user_id) -> List[int]:
blocks = CommunityBlock.query.filter_by(user_id=user_id)
return [block.community_id for block in blocks]
@cache.memoize(timeout=86400) @cache.memoize(timeout=86400)
def blocked_instances(user_id) -> List[int]: def blocked_instances(user_id) -> List[int]:
blocks = InstanceBlock.query.filter_by(user_id=user_id) blocks = InstanceBlock.query.filter_by(user_id=user_id)