mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
88e7bdca57
17 changed files with 567 additions and 60 deletions
|
@ -945,6 +945,7 @@ def post_json_to_model(activity_log, post_json, user, community) -> Post:
|
||||||
post.image = image
|
post.image = image
|
||||||
db.session.add(post)
|
db.session.add(post)
|
||||||
community.post_count += 1
|
community.post_count += 1
|
||||||
|
user.post_count += 1
|
||||||
activity_log.result = 'success'
|
activity_log.result = 'success'
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -662,6 +662,7 @@ def admin_communities_no_topic():
|
||||||
def admin_community_edit(community_id):
|
def admin_community_edit(community_id):
|
||||||
form = EditCommunityForm()
|
form = EditCommunityForm()
|
||||||
community = Community.query.get_or_404(community_id)
|
community = Community.query.get_or_404(community_id)
|
||||||
|
old_topic_id = community.topic_id if community.topic_id else None
|
||||||
form.topic.choices = topics_for_form(0)
|
form.topic.choices = topics_for_form(0)
|
||||||
form.languages.choices = languages_for_form()
|
form.languages.choices = languages_for_form()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
@ -708,11 +709,17 @@ def admin_community_edit(community_id):
|
||||||
community.languages.append(Language.query.get(language_choice))
|
community.languages.append(Language.query.get(language_choice))
|
||||||
# Always include the undetermined language, so posts with no language will be accepted
|
# Always include the undetermined language, so posts with no language will be accepted
|
||||||
community.languages.append(Language.query.filter(Language.code == 'und').first())
|
community.languages.append(Language.query.filter(Language.code == 'und').first())
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
if community.topic_id != old_topic_id:
|
||||||
|
if community.topic_id:
|
||||||
|
community.topic.num_communities = community.topic.communities.count()
|
||||||
|
if old_topic_id:
|
||||||
|
topic = Topic.query.get(old_topic_id)
|
||||||
|
if topic:
|
||||||
|
topic.num_communities = topic.communities.count()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
if community.topic_id:
|
|
||||||
community.topic.num_communities = community.topic.communities.count()
|
|
||||||
db.session.commit()
|
|
||||||
flash(_('Saved'))
|
flash(_('Saved'))
|
||||||
return redirect(url_for('admin.admin_communities'))
|
return redirect(url_for('admin.admin_communities'))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -2,8 +2,8 @@ from app.api.alpha import bp
|
||||||
from app.api.alpha.utils import get_site, \
|
from app.api.alpha.utils import get_site, \
|
||||||
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, \
|
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, \
|
||||||
get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, \
|
get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, \
|
||||||
get_community_list, get_community, \
|
get_community_list, get_community, post_community_follow, post_community_block, \
|
||||||
get_user
|
get_user, post_user_block
|
||||||
from app.shared.auth import log_user_in
|
from app.shared.auth import log_user_in
|
||||||
|
|
||||||
from flask import current_app, jsonify, request
|
from flask import current_app, jsonify, request
|
||||||
|
@ -27,7 +27,7 @@ def get_alpha_community():
|
||||||
if not current_app.debug:
|
if not current_app.debug:
|
||||||
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||||
try:
|
try:
|
||||||
auth = None
|
auth = request.headers.get('Authorization')
|
||||||
data = request.args.to_dict() or None
|
data = request.args.to_dict() or None
|
||||||
return jsonify(get_community(auth, data))
|
return jsonify(get_community(auth, data))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -46,6 +46,30 @@ def get_alpha_community_list():
|
||||||
return jsonify({"error": str(ex)}), 400
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/api/alpha/community/follow', methods=['POST'])
|
||||||
|
def post_alpha_community_follow():
|
||||||
|
if not current_app.debug:
|
||||||
|
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||||
|
try:
|
||||||
|
auth = request.headers.get('Authorization')
|
||||||
|
data = request.get_json(force=True) or {}
|
||||||
|
return jsonify(post_community_follow(auth, data))
|
||||||
|
except Exception as ex:
|
||||||
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/api/alpha/community/block', methods=['POST'])
|
||||||
|
def post_alpha_community_block():
|
||||||
|
if not current_app.debug:
|
||||||
|
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||||
|
try:
|
||||||
|
auth = request.headers.get('Authorization')
|
||||||
|
data = request.get_json(force=True) or {}
|
||||||
|
return jsonify(post_community_block(auth, data))
|
||||||
|
except Exception as ex:
|
||||||
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
# Post
|
# Post
|
||||||
@bp.route('/api/alpha/post/list', methods=['GET'])
|
@bp.route('/api/alpha/post/list', methods=['GET'])
|
||||||
def get_alpha_post_list():
|
def get_alpha_post_list():
|
||||||
|
@ -170,7 +194,7 @@ def get_alpha_user():
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/api/alpha/user/login', methods=['POST'])
|
@bp.route('/api/alpha/user/login', methods=['POST'])
|
||||||
def post_alpha_login():
|
def post_alpha_user_login():
|
||||||
if not current_app.debug:
|
if not current_app.debug:
|
||||||
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||||
try:
|
try:
|
||||||
|
@ -181,6 +205,18 @@ def post_alpha_login():
|
||||||
return jsonify({"error": str(ex)}), 400
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/api/alpha/user/block', methods=['POST'])
|
||||||
|
def post_alpha_user_block():
|
||||||
|
if not current_app.debug:
|
||||||
|
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||||
|
try:
|
||||||
|
auth = request.headers.get('Authorization')
|
||||||
|
data = request.get_json(force=True) or {}
|
||||||
|
return jsonify(post_user_block(auth, data))
|
||||||
|
except Exception as ex:
|
||||||
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
# Not yet implemented. Copied from lemmy's V3 api, so some aren't needed, and some need changing
|
# Not yet implemented. Copied from lemmy's V3 api, so some aren't needed, and some need changing
|
||||||
|
|
||||||
# Site - not yet implemented
|
# Site - not yet implemented
|
||||||
|
@ -202,8 +238,6 @@ def alpha_miscellaneous():
|
||||||
@bp.route('/api/alpha/community', methods=['POST'])
|
@bp.route('/api/alpha/community', methods=['POST'])
|
||||||
@bp.route('/api/alpha/community', methods=['PUT'])
|
@bp.route('/api/alpha/community', methods=['PUT'])
|
||||||
@bp.route('/api/alpha/community/hide', methods=['PUT'])
|
@bp.route('/api/alpha/community/hide', methods=['PUT'])
|
||||||
@bp.route('/api/alpha/community/follow', methods=['POST'])
|
|
||||||
@bp.route('/api/alpha/community/block', methods=['POST'])
|
|
||||||
@bp.route('/api/alpha/community/delete', methods=['POST'])
|
@bp.route('/api/alpha/community/delete', methods=['POST'])
|
||||||
@bp.route('/api/alpha/community/remove', methods=['POST'])
|
@bp.route('/api/alpha/community/remove', methods=['POST'])
|
||||||
@bp.route('/api/alpha/community/transfer', methods=['POST'])
|
@bp.route('/api/alpha/community/transfer', methods=['POST'])
|
||||||
|
@ -219,7 +253,6 @@ def alpha_community():
|
||||||
@bp.route('/api/alpha/post/remove', methods=['POST'])
|
@bp.route('/api/alpha/post/remove', methods=['POST'])
|
||||||
@bp.route('/api/alpha/post/lock', methods=['POST'])
|
@bp.route('/api/alpha/post/lock', methods=['POST'])
|
||||||
@bp.route('/api/alpha/post/feature', methods=['POST'])
|
@bp.route('/api/alpha/post/feature', methods=['POST'])
|
||||||
@bp.route('/api/alpha/post/save', methods=['PUT'])
|
|
||||||
@bp.route('/api/alpha/post/report', methods=['POST'])
|
@bp.route('/api/alpha/post/report', methods=['POST'])
|
||||||
@bp.route('/api/alpha/post/report/resolve', methods=['PUT'])
|
@bp.route('/api/alpha/post/report/resolve', methods=['PUT'])
|
||||||
@bp.route('/api/alpha/post/report/list', methods=['GET'])
|
@bp.route('/api/alpha/post/report/list', methods=['GET'])
|
||||||
|
@ -261,7 +294,6 @@ def alpha_chat():
|
||||||
@bp.route('/api/alpha/user/replies', methods=['GET'])
|
@bp.route('/api/alpha/user/replies', methods=['GET'])
|
||||||
@bp.route('/api/alpha/user/ban', methods=['POST'])
|
@bp.route('/api/alpha/user/ban', methods=['POST'])
|
||||||
@bp.route('/api/alpha/user/banned', methods=['GET'])
|
@bp.route('/api/alpha/user/banned', methods=['GET'])
|
||||||
@bp.route('/api/alpha/user/block', methods=['POST'])
|
|
||||||
@bp.route('/api/alpha/user/delete_account', methods=['POST'])
|
@bp.route('/api/alpha/user/delete_account', methods=['POST'])
|
||||||
@bp.route('/api/alpha/user/password_reset', methods=['POST'])
|
@bp.route('/api/alpha/user/password_reset', methods=['POST'])
|
||||||
@bp.route('/api/alpha/user/password_change', methods=['POST'])
|
@bp.route('/api/alpha/user/password_change', methods=['POST'])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from app.api.alpha.utils.site import get_site
|
from app.api.alpha.utils.site import get_site
|
||||||
from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe
|
from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe
|
||||||
from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe
|
from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe
|
||||||
from app.api.alpha.utils.community import get_community, get_community_list
|
from app.api.alpha.utils.community import get_community, get_community_list, post_community_follow, post_community_block
|
||||||
from app.api.alpha.utils.user import get_user
|
from app.api.alpha.utils.user import get_user, post_user_block
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from app import cache
|
from app import cache
|
||||||
from app.api.alpha.views import community_view
|
from app.api.alpha.views import community_view
|
||||||
|
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||||
from app.utils import authorise_api_user
|
from app.utils import authorise_api_user
|
||||||
from app.models import Community, CommunityMember
|
from app.models import Community, CommunityMember
|
||||||
|
from app.shared.community import join_community, leave_community, block_community, unblock_community
|
||||||
from app.utils import communities_banned_from
|
from app.utils import communities_banned_from
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,8 +28,8 @@ def get_community_list(auth, data):
|
||||||
if auth:
|
if auth:
|
||||||
try:
|
try:
|
||||||
user_id = authorise_api_user(auth)
|
user_id = authorise_api_user(auth)
|
||||||
except Exception as e:
|
except:
|
||||||
raise e
|
raise
|
||||||
else:
|
else:
|
||||||
user_id = None
|
user_id = None
|
||||||
|
|
||||||
|
@ -55,8 +57,71 @@ def get_community(auth, data):
|
||||||
elif 'name' in data:
|
elif 'name' in data:
|
||||||
community = data['name']
|
community = data['name']
|
||||||
|
|
||||||
|
if auth:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
community_json = community_view(community=community, variant=3)
|
community_json = community_view(community=community, variant=3, stub=False, user_id=user_id)
|
||||||
|
return community_json
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
# would be in app/constants.py
|
||||||
|
SRC_API = 3
|
||||||
|
|
||||||
|
def post_community_follow(auth, data):
|
||||||
|
try:
|
||||||
|
required(['community_id', 'follow'], data)
|
||||||
|
integer_expected(['community_id'], data)
|
||||||
|
boolean_expected(['follow'], data)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
community_id = data['community_id']
|
||||||
|
follow = data['follow']
|
||||||
|
|
||||||
|
if auth:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if follow == True:
|
||||||
|
user_id = join_community(community_id, SRC_API, auth)
|
||||||
|
else:
|
||||||
|
user_id = leave_community(community_id, SRC_API, auth)
|
||||||
|
community_json = community_view(community=community_id, variant=4, stub=False, user_id=user_id)
|
||||||
|
return community_json
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def post_community_block(auth, data):
|
||||||
|
try:
|
||||||
|
required(['community_id', 'block'], data)
|
||||||
|
integer_expected(['community_id'], data)
|
||||||
|
boolean_expected(['block'], data)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
community_id = data['community_id']
|
||||||
|
block = data['block']
|
||||||
|
|
||||||
|
try:
|
||||||
|
if block == True:
|
||||||
|
user_id = block_community(community_id, SRC_API, auth)
|
||||||
|
else:
|
||||||
|
user_id = unblock_community(community_id, SRC_API, auth)
|
||||||
|
community_json = community_view(community=community_id, variant=5, user_id=user_id)
|
||||||
return community_json
|
return community_json
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -3,7 +3,7 @@ from app.api.alpha.views import post_view
|
||||||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||||
from app.models import Post, Community, CommunityMember, utcnow
|
from app.models import Post, Community, CommunityMember, utcnow
|
||||||
from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification
|
from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification
|
||||||
from app.utils import authorise_api_user
|
from app.utils import authorise_api_user, blocked_users, blocked_communities
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
@ -28,6 +28,14 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i
|
||||||
else:
|
else:
|
||||||
posts = Post.query.filter_by(deleted=False)
|
posts = Post.query.filter_by(deleted=False)
|
||||||
|
|
||||||
|
if user_id is not None:
|
||||||
|
blocked_person_ids = blocked_users(user_id)
|
||||||
|
if blocked_person_ids:
|
||||||
|
posts = posts.filter(Post.user_id.not_in(blocked_person_ids))
|
||||||
|
blocked_community_ids = blocked_communities(user_id)
|
||||||
|
if blocked_community_ids:
|
||||||
|
posts = posts.filter(Post.community_id.not_in(blocked_community_ids))
|
||||||
|
|
||||||
if sort == "Hot":
|
if sort == "Hot":
|
||||||
posts = posts.order_by(desc(Post.ranking)).order_by(desc(Post.posted_at))
|
posts = posts.order_by(desc(Post.ranking)).order_by(desc(Post.posted_at))
|
||||||
elif sort == "TopDay":
|
elif sort == "TopDay":
|
||||||
|
@ -49,8 +57,8 @@ def get_post_list(auth, data, user_id=None):
|
||||||
if auth:
|
if auth:
|
||||||
try:
|
try:
|
||||||
user_id = authorise_api_user(auth)
|
user_id = authorise_api_user(auth)
|
||||||
except Exception as e:
|
except:
|
||||||
raise e
|
raise
|
||||||
|
|
||||||
# user_id: the logged in user
|
# user_id: the logged in user
|
||||||
# person_id: the author of the posts being requested
|
# person_id: the author of the posts being requested
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
from app import cache
|
from app import cache
|
||||||
from app.utils import authorise_api_user
|
|
||||||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||||
from app.api.alpha.views import reply_view
|
from app.api.alpha.views import reply_view
|
||||||
from app.models import PostReply
|
from app.models import PostReply
|
||||||
from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification
|
from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification
|
||||||
|
from app.utils import authorise_api_user, blocked_users
|
||||||
|
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
|
||||||
|
# person_id param: the author of the reply; user_id param: the current logged-in user
|
||||||
@cache.memoize(timeout=3)
|
@cache.memoize(timeout=3)
|
||||||
def cached_reply_list(post_id, person_id, sort, max_depth):
|
def cached_reply_list(post_id, person_id, sort, max_depth, user_id):
|
||||||
if post_id:
|
if post_id:
|
||||||
replies = PostReply.query.filter(PostReply.deleted == False, PostReply.post_id == post_id, PostReply.depth <= max_depth)
|
replies = PostReply.query.filter(PostReply.deleted == False, PostReply.post_id == post_id, PostReply.depth <= max_depth)
|
||||||
if person_id:
|
if person_id:
|
||||||
replies = PostReply.query.filter_by(deleted=False, user_id=person_id)
|
replies = PostReply.query.filter_by(deleted=False, user_id=person_id)
|
||||||
|
|
||||||
|
if user_id is not None:
|
||||||
|
blocked_person_ids = blocked_users(user_id)
|
||||||
|
if blocked_person_ids:
|
||||||
|
replies = replies.filter(PostReply.user_id.not_in(blocked_person_ids))
|
||||||
|
|
||||||
if sort == "Hot":
|
if sort == "Hot":
|
||||||
replies = replies.order_by(desc(PostReply.ranking)).order_by(desc(PostReply.posted_at))
|
replies = replies.order_by(desc(PostReply.ranking)).order_by(desc(PostReply.posted_at))
|
||||||
elif sort == "Top":
|
elif sort == "Top":
|
||||||
|
@ -36,13 +41,12 @@ def get_reply_list(auth, data, user_id=None):
|
||||||
if data and not post_id and not person_id:
|
if data and not post_id and not person_id:
|
||||||
raise Exception('missing_parameters')
|
raise Exception('missing_parameters')
|
||||||
else:
|
else:
|
||||||
replies = cached_reply_list(post_id, person_id, sort, max_depth)
|
if auth:
|
||||||
|
try:
|
||||||
if auth:
|
user_id = authorise_api_user(auth)
|
||||||
try:
|
except:
|
||||||
user_id = authorise_api_user(auth)
|
raise
|
||||||
except Exception as e:
|
replies = cached_reply_list(post_id, person_id, sort, max_depth, user_id)
|
||||||
raise e
|
|
||||||
|
|
||||||
# user_id: the logged in user
|
# user_id: the logged in user
|
||||||
# person_id: the author of the posts being requested
|
# person_id: the author of the posts being requested
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from app import db
|
from app import db
|
||||||
|
from app.api.alpha.views import user_view, community_view
|
||||||
from app.utils import authorise_api_user
|
from app.utils import authorise_api_user
|
||||||
from app.models import Language
|
from app.models import Language
|
||||||
|
|
||||||
|
@ -69,9 +70,9 @@ def get_site(auth):
|
||||||
},
|
},
|
||||||
#"moderates": [],
|
#"moderates": [],
|
||||||
#"follows": [],
|
#"follows": [],
|
||||||
"community_blocks": [], # TODO
|
"community_blocks": [],
|
||||||
"instance_blocks": [], # TODO
|
"instance_blocks": [], # TODO
|
||||||
"person_blocks": [], # TODO
|
"person_blocks": [],
|
||||||
"discussion_languages": [] # TODO
|
"discussion_languages": [] # TODO
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
@ -83,7 +84,12 @@ def get_site(auth):
|
||||||
for cm in cms:
|
for cm in cms:
|
||||||
my_user['follows'].append({'community': Community.api_json(variant=1, id=cm.community_id, stub=True), 'follower': User.api_json(variant=1, id=user_id, stub=True)})
|
my_user['follows'].append({'community': Community.api_json(variant=1, id=cm.community_id, stub=True), 'follower': User.api_json(variant=1, id=user_id, stub=True)})
|
||||||
"""
|
"""
|
||||||
|
blocked_ids = db.session.execute(text('SELECT blocked_id FROM "user_block" WHERE blocker_id = :blocker_id'), {"blocker_id": user.id}).scalars()
|
||||||
|
for blocked_id in blocked_ids:
|
||||||
|
my_user['person_blocks'].append({'person': user_view(user, variant=1, stub=True), 'target': user_view(blocked_id, variant=1, stub=True)})
|
||||||
|
blocked_ids = db.session.execute(text('SELECT community_id FROM "community_block" WHERE user_id = :user_id'), {"user_id": user.id}).scalars()
|
||||||
|
for blocked_id in blocked_ids:
|
||||||
|
my_user['community_blocks'].append({'person': user_view(user, variant=1, stub=True), 'community': community_view(blocked_id, variant=1, stub=True)})
|
||||||
data = {
|
data = {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"site": site
|
"site": site
|
||||||
|
|
|
@ -2,6 +2,8 @@ from app.api.alpha.views import user_view
|
||||||
from app.utils import authorise_api_user
|
from app.utils import authorise_api_user
|
||||||
from app.api.alpha.utils.post import get_post_list
|
from app.api.alpha.utils.post import get_post_list
|
||||||
from app.api.alpha.utils.reply import get_reply_list
|
from app.api.alpha.utils.reply import get_reply_list
|
||||||
|
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||||
|
from app.shared.user import block_another_user, unblock_another_user
|
||||||
|
|
||||||
|
|
||||||
def get_user(auth, data):
|
def get_user(auth, data):
|
||||||
|
@ -39,4 +41,26 @@ def get_user(auth, data):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
# would be in app/constants.py
|
||||||
|
SRC_API = 3
|
||||||
|
|
||||||
|
def post_user_block(auth, data):
|
||||||
|
try:
|
||||||
|
required(['person_id', 'block'], data)
|
||||||
|
integer_expected(['post_id'], data)
|
||||||
|
boolean_expected(['block'], data)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
person_id = data['person_id']
|
||||||
|
block = data['block']
|
||||||
|
|
||||||
|
try:
|
||||||
|
if block == True:
|
||||||
|
user_id = block_another_user(person_id, SRC_API, auth)
|
||||||
|
else:
|
||||||
|
user_id = unblock_another_user(person_id, SRC_API, auth)
|
||||||
|
user_json = user_view(user=person_id, variant=4, user_id=user_id)
|
||||||
|
return user_json
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
from app import cache, db
|
from app import cache, db
|
||||||
from app.constants import *
|
from app.constants import *
|
||||||
from app.models import Community, CommunityMember, Post, PostReply, PostVote, User
|
from app.models import Community, CommunityMember, Post, PostReply, PostVote, User
|
||||||
|
from app.utils import blocked_communities
|
||||||
|
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
@ -50,8 +51,12 @@ def post_view(post: Post | int, variant, stub=False, user_id=None, my_vote=0):
|
||||||
# counts - models/post/post_aggregates.dart
|
# counts - models/post/post_aggregates.dart
|
||||||
counts = {'post_id': post.id, 'comments': post.reply_count, 'score': post.score, 'upvotes': post.up_votes, 'downvotes': post.down_votes,
|
counts = {'post_id': post.id, 'comments': post.reply_count, 'score': post.score, 'upvotes': post.up_votes, 'downvotes': post.down_votes,
|
||||||
'published': post.posted_at.isoformat() + 'Z', 'newest_comment_time': post.last_active.isoformat() + 'Z'}
|
'published': post.posted_at.isoformat() + 'Z', 'newest_comment_time': post.last_active.isoformat() + 'Z'}
|
||||||
bookmarked = db.session.execute(text('SELECT user_id FROM "post_bookmark" WHERE post_id = :post_id and user_id = :user_id'), {'post_id': post.id, 'user_id': user_id}).scalar()
|
if user_id:
|
||||||
post_sub = db.session.execute(text('SELECT user_id FROM "notification_subscription" WHERE type = :type and entity_id = :entity_id and user_id = :user_id'), {'type': NOTIF_POST, 'entity_id': post.id, 'user_id': user_id}).scalar()
|
bookmarked = db.session.execute(text('SELECT user_id FROM "post_bookmark" WHERE post_id = :post_id and user_id = :user_id'), {'post_id': post.id, 'user_id': user_id}).scalar()
|
||||||
|
post_sub = db.session.execute(text('SELECT user_id FROM "notification_subscription" WHERE type = :type and entity_id = :entity_id and user_id = :user_id'), {'type': NOTIF_POST, 'entity_id': post.id, 'user_id': user_id}).scalar()
|
||||||
|
followed = db.session.execute(text('SELECT user_id FROM "community_member" WHERE community_id = :community_id and user_id = :user_id'), {"community_id": post.community_id, "user_id": user_id}).scalar()
|
||||||
|
else:
|
||||||
|
bookmarked = post_sub = followed = False
|
||||||
if not stub:
|
if not stub:
|
||||||
banned = db.session.execute(text('SELECT user_id FROM "community_ban" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
banned = db.session.execute(text('SELECT user_id FROM "community_ban" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
||||||
moderator = db.session.execute(text('SELECT is_moderator FROM "community_member" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
moderator = db.session.execute(text('SELECT is_moderator FROM "community_member" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
||||||
|
@ -72,8 +77,9 @@ def post_view(post: Post | int, variant, stub=False, user_id=None, my_vote=0):
|
||||||
creator_banned_from_community = True if banned else False
|
creator_banned_from_community = True if banned else False
|
||||||
creator_is_moderator = True if moderator else False
|
creator_is_moderator = True if moderator else False
|
||||||
creator_is_admin = True if admin else False
|
creator_is_admin = True if admin else False
|
||||||
v2 = {'post': post_view(post=post, variant=1, stub=stub), 'counts': counts, 'banned_from_community': False, 'subscribed': 'NotSubscribed',
|
subscribe_type = 'Subscribed' if followed else 'NotSubscribed'
|
||||||
'saved': saved, 'read': False, 'hidden': False, 'creator_blocked': False, 'unread_comments': post.reply_count, 'my_vote': my_vote, 'activity_alert': activity_alert,
|
v2 = {'post': post_view(post=post, variant=1, stub=stub), 'counts': counts, 'banned_from_community': False, 'subscribed': subscribe_type,
|
||||||
|
'saved': saved, 'read': False, 'hidden': False, 'unread_comments': post.reply_count, 'my_vote': my_vote, 'activity_alert': activity_alert,
|
||||||
'creator_banned_from_community': creator_banned_from_community, 'creator_is_moderator': creator_is_moderator, 'creator_is_admin': creator_is_admin}
|
'creator_banned_from_community': creator_banned_from_community, 'creator_is_moderator': creator_is_moderator, 'creator_is_admin': creator_is_admin}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -127,7 +133,8 @@ def cached_user_view_variant_1(user: User, stub=False):
|
||||||
return v1
|
return v1
|
||||||
|
|
||||||
|
|
||||||
def user_view(user: User | int, variant, stub=False):
|
# 'user' param can be anyone (including the logged in user), 'user_id' param belongs to the user making the request
|
||||||
|
def user_view(user: User | int, variant, stub=False, user_id=None):
|
||||||
if isinstance(user, int):
|
if isinstance(user, int):
|
||||||
user = User.query.get(user)
|
user = User.query.get(user)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -144,13 +151,22 @@ def user_view(user: User | int, variant, stub=False):
|
||||||
return v2
|
return v2
|
||||||
|
|
||||||
# Variant 3 - models/user/get_person_details.dart - /user?person_id api endpoint
|
# Variant 3 - models/user/get_person_details.dart - /user?person_id api endpoint
|
||||||
modlist = cached_modlist_for_user(user)
|
if variant == 3:
|
||||||
|
modlist = cached_modlist_for_user(user)
|
||||||
|
|
||||||
v3 = {'person_view': user_view(user=user, variant=2),
|
v3 = {'person_view': user_view(user=user, variant=2),
|
||||||
'moderates': modlist,
|
'moderates': modlist,
|
||||||
'posts': [],
|
'posts': [],
|
||||||
'comments': []}
|
'comments': []}
|
||||||
return v3
|
return v3
|
||||||
|
|
||||||
|
# Variant 4 - models/user/block_person_response.dart - /user/block api endpoint
|
||||||
|
if variant == 4:
|
||||||
|
block = db.session.execute(text('SELECT blocker_id FROM "user_block" WHERE blocker_id = :blocker_id and blocked_id = :blocked_id'), {'blocker_id': user_id, 'blocked_id': user.id}).scalar()
|
||||||
|
blocked = True if block else False
|
||||||
|
v4 = {'person_view': user_view(user=user, variant=2),
|
||||||
|
'blocked': blocked}
|
||||||
|
return v4
|
||||||
|
|
||||||
|
|
||||||
@cache.memoize(timeout=600)
|
@cache.memoize(timeout=600)
|
||||||
|
@ -194,18 +210,37 @@ def community_view(community: Community | int | str, variant, stub=False, user_i
|
||||||
include = ['id', 'subscriptions_count', 'post_count', 'post_reply_count']
|
include = ['id', 'subscriptions_count', 'post_count', 'post_reply_count']
|
||||||
counts = {column.name: getattr(community, column.name) for column in community.__table__.columns if column.name in include}
|
counts = {column.name: getattr(community, column.name) for column in community.__table__.columns if column.name in include}
|
||||||
counts.update({'published': community.created_at.isoformat() + 'Z'})
|
counts.update({'published': community.created_at.isoformat() + 'Z'})
|
||||||
v2 = {'community': community_view(community=community, variant=1, stub=stub), 'subscribed': 'NotSubscribed', 'blocked': False, 'counts': counts}
|
if user_id:
|
||||||
|
followed = db.session.execute(text('SELECT user_id FROM "community_member" WHERE community_id = :community_id and user_id = :user_id'), {"community_id": community.id, "user_id": user_id}).scalar()
|
||||||
|
blocked = True if community.id in blocked_communities(user_id) else False
|
||||||
|
else:
|
||||||
|
followed = blocked = False
|
||||||
|
subscribe_type = 'Subscribed' if followed else 'NotSubscribed'
|
||||||
|
v2 = {'community': community_view(community=community, variant=1, stub=stub), 'subscribed': subscribe_type, 'blocked': blocked, 'counts': counts}
|
||||||
return v2
|
return v2
|
||||||
|
|
||||||
# Variant 3 - models/community/get_community_response.dart - /community api endpoint
|
# Variant 3 - models/community/get_community_response.dart - /community api endpoint
|
||||||
if variant == 3:
|
if variant == 3:
|
||||||
modlist = cached_modlist_for_community(community.id)
|
modlist = cached_modlist_for_community(community.id)
|
||||||
|
|
||||||
v3 = {'community_view': community_view(community=community, variant=2),
|
v3 = {'community_view': community_view(community=community, variant=2, stub=False, user_id=user_id),
|
||||||
'moderators': modlist,
|
'moderators': modlist,
|
||||||
'discussion_languages': []}
|
'discussion_languages': []}
|
||||||
return v3
|
return v3
|
||||||
|
|
||||||
|
# Variant 4 - models/community/community_response.dart - /community/follow api endpoint
|
||||||
|
if variant == 4:
|
||||||
|
v4 = {'community_view': community_view(community=community, variant=2, stub=False, user_id=user_id),
|
||||||
|
'discussion_languages': []}
|
||||||
|
return v4
|
||||||
|
|
||||||
|
# Variant 5 - models/community/block_community_response.dart - /community/block api endpoint
|
||||||
|
if variant == 5:
|
||||||
|
block = db.session.execute(text('SELECT user_id FROM "community_block" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': user_id, 'community_id': community.id}).scalar()
|
||||||
|
blocked = True if block else False
|
||||||
|
v5 = {'community_view': community_view(community=community, variant=2, stub=False, user_id=user_id),
|
||||||
|
'blocked': blocked}
|
||||||
|
return v5
|
||||||
|
|
||||||
|
|
||||||
# would be better to incrementally add to a post_reply.path field
|
# would be better to incrementally add to a post_reply.path field
|
||||||
|
|
|
@ -929,6 +929,7 @@ def community_edit(community_id: int):
|
||||||
if current_user.banned:
|
if current_user.banned:
|
||||||
return show_ban_message()
|
return show_ban_message()
|
||||||
community = Community.query.get_or_404(community_id)
|
community = Community.query.get_or_404(community_id)
|
||||||
|
old_topic_id = community.topic_id if community.topic_id else None
|
||||||
if community.is_owner() or current_user.is_admin():
|
if community.is_owner() or current_user.is_admin():
|
||||||
form = EditCommunityForm()
|
form = EditCommunityForm()
|
||||||
form.topic.choices = topics_for_form(0)
|
form.topic.choices = topics_for_form(0)
|
||||||
|
@ -970,11 +971,16 @@ def community_edit(community_id: int):
|
||||||
community.languages.append(Language.query.get(language_choice))
|
community.languages.append(Language.query.get(language_choice))
|
||||||
# Always include the undetermined language, so posts with no language will be accepted
|
# Always include the undetermined language, so posts with no language will be accepted
|
||||||
community.languages.append(Language.query.filter(Language.code == 'und').first())
|
community.languages.append(Language.query.filter(Language.code == 'und').first())
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
db.session.commit()
|
if community.topic_id != old_topic_id:
|
||||||
if community.topic:
|
if community.topic_id:
|
||||||
community.topic.num_communities = community.topic.communities.count()
|
community.topic.num_communities = community.topic.communities.count()
|
||||||
db.session.commit()
|
if old_topic_id:
|
||||||
|
topic = Topic.query.get(old_topic_id)
|
||||||
|
if topic:
|
||||||
|
topic.num_communities = topic.communities.count()
|
||||||
|
db.session.commit()
|
||||||
flash(_('Saved'))
|
flash(_('Saved'))
|
||||||
return redirect(url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name))
|
return redirect(url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -32,7 +32,7 @@ from app.utils import get_setting, render_template, allowlist_html, markdown_to_
|
||||||
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, blocked_communities, piefed_markdown_to_lemmy_markdown, \
|
languages_for_form, menu_topics, add_to_modlog, blocked_communities, piefed_markdown_to_lemmy_markdown, \
|
||||||
permission_required
|
permission_required, blocked_users
|
||||||
|
|
||||||
|
|
||||||
def show_post(post_id: int):
|
def show_post(post_id: int):
|
||||||
|
@ -1258,6 +1258,7 @@ def post_block_user(post_id: int):
|
||||||
db.session.add(UserBlock(blocker_id=current_user.id, blocked_id=post.author.id))
|
db.session.add(UserBlock(blocker_id=current_user.id, blocked_id=post.author.id))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(_('%(name)s has been blocked.', name=post.author.user_name))
|
flash(_('%(name)s has been blocked.', name=post.author.user_name))
|
||||||
|
cache.delete_memoized(blocked_users, current_user.id)
|
||||||
|
|
||||||
# todo: federate block to post author instance
|
# todo: federate block to post author instance
|
||||||
|
|
||||||
|
@ -1428,6 +1429,7 @@ def post_reply_block_user(post_id: int, comment_id: int):
|
||||||
db.session.add(UserBlock(blocker_id=current_user.id, blocked_id=post_reply.author.id))
|
db.session.add(UserBlock(blocker_id=current_user.id, blocked_id=post_reply.author.id))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(_('%(name)s has been blocked.', name=post_reply.author.user_name))
|
flash(_('%(name)s has been blocked.', name=post_reply.author.user_name))
|
||||||
|
cache.delete_memoized(blocked_users, current_user.id)
|
||||||
|
|
||||||
# todo: federate block to post_reply author instance
|
# todo: federate block to post_reply author instance
|
||||||
|
|
||||||
|
|
223
app/shared/community.py
Normal file
223
app/shared/community.py
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
from app import db, cache
|
||||||
|
from app.activitypub.signature import post_request
|
||||||
|
from app.constants import *
|
||||||
|
from app.models import Community, CommunityBan, CommunityBlock, CommunityJoinRequest, CommunityMember
|
||||||
|
from app.utils import authorise_api_user, blocked_communities, community_membership, joined_communities, gibberish
|
||||||
|
|
||||||
|
from flask import abort, current_app, flash
|
||||||
|
from flask_babel import _
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
|
# would be in app/constants.py
|
||||||
|
SRC_WEB = 1
|
||||||
|
SRC_PUB = 2
|
||||||
|
SRC_API = 3
|
||||||
|
|
||||||
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
|
# call from admin.federation not tested
|
||||||
|
def join_community(community_id: int, src, auth=None, user_id=None, main_user_name=True):
|
||||||
|
if src == SRC_API:
|
||||||
|
community = Community.query.get(community_id)
|
||||||
|
if not community:
|
||||||
|
raise Exception('community_not_found')
|
||||||
|
try:
|
||||||
|
user = authorise_api_user(auth, return_type='model')
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
community = Community.query.get_or_404(community_id)
|
||||||
|
if not user_id:
|
||||||
|
user = current_user
|
||||||
|
else:
|
||||||
|
user = User.query.get(user_id)
|
||||||
|
|
||||||
|
pre_load_message = {}
|
||||||
|
if community_membership(user, community) != SUBSCRIPTION_MEMBER and community_membership(user, community) != SUBSCRIPTION_PENDING:
|
||||||
|
banned = CommunityBan.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
|
if banned:
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('banned_from_community')
|
||||||
|
else:
|
||||||
|
if main_user_name:
|
||||||
|
flash(_('You cannot join this community'))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
pre_load_message['user_banned'] = True
|
||||||
|
return pre_load_message
|
||||||
|
else:
|
||||||
|
if src == SRC_API:
|
||||||
|
return user.id
|
||||||
|
else:
|
||||||
|
if not main_user_name:
|
||||||
|
pre_load_message['status'] = 'already subscribed, or subsciption pending'
|
||||||
|
return pre_load_message
|
||||||
|
|
||||||
|
success = True
|
||||||
|
remote = not community.is_local()
|
||||||
|
if remote:
|
||||||
|
# send ActivityPub message to remote community, asking to follow. Accept message will be sent to our shared inbox
|
||||||
|
join_request = CommunityJoinRequest(user_id=user.id, community_id=community.id)
|
||||||
|
db.session.add(join_request)
|
||||||
|
db.session.commit()
|
||||||
|
if community.instance.online():
|
||||||
|
follow = {
|
||||||
|
"actor": user.public_url(main_user_name=main_user_name),
|
||||||
|
"to": [community.public_url()],
|
||||||
|
"object": community.public_url(),
|
||||||
|
"type": "Follow",
|
||||||
|
"id": f"https://{current_app.config['SERVER_NAME']}/activities/follow/{join_request.id}"
|
||||||
|
}
|
||||||
|
success = post_request(community.ap_inbox_url, follow, user.private_key,
|
||||||
|
user.public_url(main_user_name=main_user_name) + '#main-key', timeout=10)
|
||||||
|
if success is False or isinstance(success, str):
|
||||||
|
if 'is not in allowlist' in success:
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('not_in_remote_instance_allowlist')
|
||||||
|
else:
|
||||||
|
msg_to_user = f'{community.instance.domain} does not allow us to join their communities.'
|
||||||
|
if main_user_name:
|
||||||
|
flash(_(msg_to_user), 'error')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
pre_load_message['status'] = msg_to_user
|
||||||
|
return pre_load_message
|
||||||
|
else:
|
||||||
|
if src != SRC_API:
|
||||||
|
msg_to_user = "There was a problem while trying to communicate with remote server. If other people have already joined this community it won't matter."
|
||||||
|
if main_user_name:
|
||||||
|
flash(_(msg_to_user), 'error')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
pre_load_message['status'] = msg_to_user
|
||||||
|
return pre_load_message
|
||||||
|
|
||||||
|
# for local communities, joining is instant
|
||||||
|
member = CommunityMember(user_id=user.id, community_id=community.id)
|
||||||
|
db.session.add(member)
|
||||||
|
db.session.commit()
|
||||||
|
if success is True:
|
||||||
|
cache.delete_memoized(community_membership, user, community)
|
||||||
|
cache.delete_memoized(joined_communities, user.id)
|
||||||
|
if src == SRC_API:
|
||||||
|
return user.id
|
||||||
|
else:
|
||||||
|
if main_user_name:
|
||||||
|
flash('You joined ' + community.title)
|
||||||
|
else:
|
||||||
|
pre_load_message['status'] = 'joined'
|
||||||
|
|
||||||
|
if not main_user_name:
|
||||||
|
return pre_load_message
|
||||||
|
|
||||||
|
# for SRC_WEB, calling function should handle if the community isn't found
|
||||||
|
|
||||||
|
|
||||||
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
|
def leave_community(community_id: int, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
community = Community.query.get(community_id)
|
||||||
|
if not community:
|
||||||
|
raise Exception('community_not_found')
|
||||||
|
try:
|
||||||
|
user = authorise_api_user(auth, return_type='model')
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
community = Community.query.get_or_404(community_id)
|
||||||
|
user = current_user
|
||||||
|
|
||||||
|
subscription = community_membership(user, community)
|
||||||
|
if subscription:
|
||||||
|
if subscription != SUBSCRIPTION_OWNER:
|
||||||
|
proceed = True
|
||||||
|
# Undo the Follow
|
||||||
|
if not community.is_local():
|
||||||
|
success = True
|
||||||
|
if not community.instance.gone_forever:
|
||||||
|
undo_id = f"https://{current_app.config['SERVER_NAME']}/activities/undo/" + gibberish(15)
|
||||||
|
follow = {
|
||||||
|
"actor": user.public_url(),
|
||||||
|
"to": [community.public_url()],
|
||||||
|
"object": community.public_url(),
|
||||||
|
"type": "Follow",
|
||||||
|
"id": f"https://{current_app.config['SERVER_NAME']}/activities/follow/{gibberish(15)}"
|
||||||
|
}
|
||||||
|
undo = {
|
||||||
|
'actor': user.public_url(),
|
||||||
|
'to': [community.public_url()],
|
||||||
|
'type': 'Undo',
|
||||||
|
'id': undo_id,
|
||||||
|
'object': follow
|
||||||
|
}
|
||||||
|
success = post_request(community.ap_inbox_url, undo, user.private_key,
|
||||||
|
user.public_url() + '#main-key', timeout=10)
|
||||||
|
if success is False or isinstance(success, str):
|
||||||
|
if src != SRC_API:
|
||||||
|
flash('There was a problem while trying to unsubscribe', 'error')
|
||||||
|
return
|
||||||
|
|
||||||
|
if proceed:
|
||||||
|
db.session.query(CommunityMember).filter_by(user_id=user.id, community_id=community.id).delete()
|
||||||
|
db.session.query(CommunityJoinRequest).filter_by(user_id=user.id, community_id=community.id).delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
if src != SRC_API:
|
||||||
|
flash('You have left ' + community.title)
|
||||||
|
|
||||||
|
cache.delete_memoized(community_membership, user, community)
|
||||||
|
cache.delete_memoized(joined_communities, user.id)
|
||||||
|
else:
|
||||||
|
# todo: community deletion
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('need_to_make_someone_else_owner')
|
||||||
|
else:
|
||||||
|
flash('You need to make someone else the owner before unsubscribing.', 'warning')
|
||||||
|
return
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user.id
|
||||||
|
else:
|
||||||
|
# let calling function handle redirect
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def block_community(community_id, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = current_user.id
|
||||||
|
|
||||||
|
existing = CommunityBlock.query.filter_by(user_id=user_id, community_id=community_id).first()
|
||||||
|
if not existing:
|
||||||
|
db.session.add(CommunityBlock(user_id=user_id, community_id=community_id))
|
||||||
|
db.session.commit()
|
||||||
|
cache.delete_memoized(blocked_communities, user_id)
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user_id
|
||||||
|
else:
|
||||||
|
return # let calling function handle confirmation flash message and redirect
|
||||||
|
|
||||||
|
|
||||||
|
def unblock_community(community_id, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = current_user.id
|
||||||
|
|
||||||
|
existing_block = CommunityBlock.query.filter_by(user_id=user_id, community_id=community_id).first()
|
||||||
|
if existing_block:
|
||||||
|
db.session.delete(existing_block)
|
||||||
|
db.session.commit()
|
||||||
|
cache.delete_memoized(blocked_communities, user_id)
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user_id
|
||||||
|
else:
|
||||||
|
return # let calling function handle confirmation flash message and redirect
|
|
@ -19,7 +19,7 @@ SRC_API = 3
|
||||||
# post_vote in app/post/routes would just need to do 'return vote_for_post(post_id, vote_direction, SRC_WEB)'
|
# post_vote in app/post/routes would just need to do 'return vote_for_post(post_id, vote_direction, SRC_WEB)'
|
||||||
|
|
||||||
def vote_for_post(post_id: int, vote_direction, src, auth=None):
|
def vote_for_post(post_id: int, vote_direction, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post = Post.query.get(post_id)
|
post = Post.query.get(post_id)
|
||||||
if not post:
|
if not post:
|
||||||
raise Exception('post_not_found')
|
raise Exception('post_not_found')
|
||||||
|
@ -99,7 +99,7 @@ def vote_for_post(post_id: int, vote_direction, src, auth=None):
|
||||||
# function can be shared between WEB and API (only API calls it for now)
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
# post_bookmark in app/post/routes would just need to do 'return bookmark_the_post(post_id, SRC_WEB)'
|
# post_bookmark in app/post/routes would just need to do 'return bookmark_the_post(post_id, SRC_WEB)'
|
||||||
def bookmark_the_post(post_id: int, src, auth=None):
|
def bookmark_the_post(post_id: int, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post = Post.query.get(post_id)
|
post = Post.query.get(post_id)
|
||||||
if not post or post.deleted:
|
if not post or post.deleted:
|
||||||
raise Exception('post_not_found')
|
raise Exception('post_not_found')
|
||||||
|
@ -132,7 +132,7 @@ def bookmark_the_post(post_id: int, src, auth=None):
|
||||||
# function can be shared between WEB and API (only API calls it for now)
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
# post_remove_bookmark in app/post/routes would just need to do 'return remove_the_bookmark_from_post(post_id, SRC_WEB)'
|
# post_remove_bookmark in app/post/routes would just need to do 'return remove_the_bookmark_from_post(post_id, SRC_WEB)'
|
||||||
def remove_the_bookmark_from_post(post_id: int, src, auth=None):
|
def remove_the_bookmark_from_post(post_id: int, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post = Post.query.get(post_id)
|
post = Post.query.get(post_id)
|
||||||
if not post or post.deleted:
|
if not post or post.deleted:
|
||||||
raise Exception('post_not_found')
|
raise Exception('post_not_found')
|
||||||
|
@ -164,7 +164,7 @@ def remove_the_bookmark_from_post(post_id: int, src, auth=None):
|
||||||
# post_notification in app/post/routes would just need to do 'return toggle_post_notification(post_id, SRC_WEB)'
|
# post_notification in app/post/routes would just need to do 'return toggle_post_notification(post_id, SRC_WEB)'
|
||||||
def toggle_post_notification(post_id: int, src, auth=None):
|
def toggle_post_notification(post_id: int, src, auth=None):
|
||||||
# Toggle whether the current user is subscribed to notifications about top-level replies to this post or not
|
# Toggle whether the current user is subscribed to notifications about top-level replies to this post or not
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post = Post.query.get(post_id)
|
post = Post.query.get(post_id)
|
||||||
if not post or post.deleted:
|
if not post or post.deleted:
|
||||||
raise Exception('post_not_found')
|
raise Exception('post_not_found')
|
||||||
|
|
|
@ -19,7 +19,7 @@ SRC_API = 3
|
||||||
# comment_vote in app/post/routes would just need to do 'return vote_for_reply(reply_id, vote_direction, SRC_WEB)'
|
# comment_vote in app/post/routes would just need to do 'return vote_for_reply(reply_id, vote_direction, SRC_WEB)'
|
||||||
|
|
||||||
def vote_for_reply(reply_id: int, vote_direction, src, auth=None):
|
def vote_for_reply(reply_id: int, vote_direction, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
reply = PostReply.query.get(reply_id)
|
reply = PostReply.query.get(reply_id)
|
||||||
if not reply:
|
if not reply:
|
||||||
raise Exception('reply_not_found')
|
raise Exception('reply_not_found')
|
||||||
|
@ -99,7 +99,7 @@ def vote_for_reply(reply_id: int, vote_direction, src, auth=None):
|
||||||
# function can be shared between WEB and API (only API calls it for now)
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
# post_reply_bookmark in app/post/routes would just need to do 'return bookmark_the_post_reply(comment_id, SRC_WEB)'
|
# post_reply_bookmark in app/post/routes would just need to do 'return bookmark_the_post_reply(comment_id, SRC_WEB)'
|
||||||
def bookmark_the_post_reply(comment_id: int, src, auth=None):
|
def bookmark_the_post_reply(comment_id: int, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post_reply = PostReply.query.get(comment_id)
|
post_reply = PostReply.query.get(comment_id)
|
||||||
if not post_reply or post_reply.deleted:
|
if not post_reply or post_reply.deleted:
|
||||||
raise Exception('comment_not_found')
|
raise Exception('comment_not_found')
|
||||||
|
@ -133,7 +133,7 @@ def bookmark_the_post_reply(comment_id: int, src, auth=None):
|
||||||
# function can be shared between WEB and API (only API calls it for now)
|
# function can be shared between WEB and API (only API calls it for now)
|
||||||
# post_reply_remove_bookmark in app/post/routes would just need to do 'return remove_the_bookmark_from_post_reply(comment_id, SRC_WEB)'
|
# post_reply_remove_bookmark in app/post/routes would just need to do 'return remove_the_bookmark_from_post_reply(comment_id, SRC_WEB)'
|
||||||
def remove_the_bookmark_from_post_reply(comment_id: int, src, auth=None):
|
def remove_the_bookmark_from_post_reply(comment_id: int, src, auth=None):
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post_reply = PostReply.query.get(comment_id)
|
post_reply = PostReply.query.get(comment_id)
|
||||||
if not post_reply or post_reply.deleted:
|
if not post_reply or post_reply.deleted:
|
||||||
raise Exception('comment_not_found')
|
raise Exception('comment_not_found')
|
||||||
|
@ -165,7 +165,7 @@ def remove_the_bookmark_from_post_reply(comment_id: int, src, auth=None):
|
||||||
# post_reply_notification in app/post/routes would just need to do 'return toggle_post_reply_notification(post_reply_id, SRC_WEB)'
|
# post_reply_notification in app/post/routes would just need to do 'return toggle_post_reply_notification(post_reply_id, SRC_WEB)'
|
||||||
def toggle_post_reply_notification(post_reply_id: int, src, auth=None):
|
def toggle_post_reply_notification(post_reply_id: int, src, auth=None):
|
||||||
# Toggle whether the current user is subscribed to notifications about replies to this reply or not
|
# Toggle whether the current user is subscribed to notifications about replies to this reply or not
|
||||||
if src == SRC_API and auth is not None:
|
if src == SRC_API:
|
||||||
post_reply = PostReply.query.get(post_reply_id)
|
post_reply = PostReply.query.get(post_reply_id)
|
||||||
if not post_reply or post_reply.deleted:
|
if not post_reply or post_reply.deleted:
|
||||||
raise Exception('comment_not_found')
|
raise Exception('comment_not_found')
|
||||||
|
|
92
app/shared/user.py
Normal file
92
app/shared/user.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
from app import db, cache
|
||||||
|
from app.constants import ROLE_STAFF, ROLE_ADMIN
|
||||||
|
from app.models import UserBlock
|
||||||
|
from app.utils import authorise_api_user, blocked_users
|
||||||
|
|
||||||
|
from flask import flash
|
||||||
|
from flask_babel import _
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
# would be in app/constants.py
|
||||||
|
SRC_WEB = 1
|
||||||
|
SRC_PUB = 2
|
||||||
|
SRC_API = 3
|
||||||
|
|
||||||
|
# only called from API for now, but can be called from web using [un]block_another_user(user.id, SRC_WEB)
|
||||||
|
|
||||||
|
# user_id: the local, logged-in user
|
||||||
|
# person_id: the person they want to block
|
||||||
|
|
||||||
|
def block_another_user(person_id, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = current_user.id
|
||||||
|
|
||||||
|
if user_id == person_id:
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('cannot_block_self')
|
||||||
|
else:
|
||||||
|
flash(_('You cannot block yourself.'), 'error')
|
||||||
|
return
|
||||||
|
|
||||||
|
role = db.session.execute(text('SELECT role_id FROM "user_role" WHERE user_id = :person_id'), {'person_id': person_id}).scalar()
|
||||||
|
if role == ROLE_ADMIN or role == ROLE_STAFF:
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('cannot_block_admin_or_staff')
|
||||||
|
else:
|
||||||
|
flash(_('You cannot block admin or staff.'), 'error')
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_block = UserBlock.query.filter_by(blocker_id=user_id, blocked_id=person_id).first()
|
||||||
|
if not existing_block:
|
||||||
|
block = UserBlock(blocker_id=user_id, blocked_id=person_id)
|
||||||
|
db.session.add(block)
|
||||||
|
db.session.execute(text('DELETE FROM "notification_subscription" WHERE entity_id = :current_user AND user_id = :user_id'),
|
||||||
|
{'current_user': user_id, 'user_id': person_id})
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
cache.delete_memoized(blocked_users, user_id)
|
||||||
|
|
||||||
|
# Nothing to fed? (Lemmy doesn't federate anything to the blocked person)
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user_id
|
||||||
|
else:
|
||||||
|
return # let calling function handle confirmation flash message and redirect
|
||||||
|
|
||||||
|
|
||||||
|
def unblock_another_user(person_id, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
try:
|
||||||
|
user_id = authorise_api_user(auth)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
user_id = current_user.id
|
||||||
|
|
||||||
|
if user_id == person_id:
|
||||||
|
if src == SRC_API:
|
||||||
|
raise Exception('cannot_unblock_self')
|
||||||
|
else:
|
||||||
|
flash(_('You cannot unblock yourself.'), 'error')
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_block = UserBlock.query.filter_by(blocker_id=user_id, blocked_id=person_id).first()
|
||||||
|
if existing_block:
|
||||||
|
db.session.delete(existing_block)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
cache.delete_memoized(blocked_users, user_id)
|
||||||
|
|
||||||
|
# Nothing to fed? (Lemmy doesn't federate anything to the unblocked person)
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user_id
|
||||||
|
else:
|
||||||
|
return # let calling function handle confirmation flash message and redirect
|
|
@ -1282,6 +1282,8 @@ def add_to_modlog_activitypub(action: str, actor: User, community_id: int = None
|
||||||
|
|
||||||
|
|
||||||
def authorise_api_user(auth, return_type='id'):
|
def authorise_api_user(auth, return_type='id'):
|
||||||
|
if not auth:
|
||||||
|
raise Exception('incorrect_login')
|
||||||
token = auth[7:] # remove 'Bearer '
|
token = auth[7:] # remove 'Bearer '
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1294,7 +1296,7 @@ def authorise_api_user(auth, return_type='id'):
|
||||||
if return_type == 'model':
|
if return_type == 'model':
|
||||||
return user
|
return user
|
||||||
else:
|
else:
|
||||||
return user_id
|
return user.id
|
||||||
else:
|
else:
|
||||||
raise Exception('incorrect_login')
|
raise Exception('incorrect_login')
|
||||||
except jwt.InvalidTokenError:
|
except jwt.InvalidTokenError:
|
||||||
|
|
Loading…
Reference in a new issue