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
815d13b1f7
6 changed files with 183 additions and 7 deletions
|
@ -2,7 +2,7 @@ from app.api.alpha import bp
|
||||||
from app.api.alpha.utils import get_site, post_site_block, \
|
from app.api.alpha.utils import get_site, post_site_block, \
|
||||||
get_search, \
|
get_search, \
|
||||||
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, post_reply, \
|
get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, \
|
||||||
get_community_list, get_community, post_community_follow, post_community_block, \
|
get_community_list, get_community, post_community_follow, post_community_block, \
|
||||||
get_user, post_user_block
|
get_user, post_user_block
|
||||||
from app.shared.auth import log_user_in
|
from app.shared.auth import log_user_in
|
||||||
|
@ -218,6 +218,18 @@ def post_alpha_comment():
|
||||||
return jsonify({"error": str(ex)}), 400
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/api/alpha/comment', methods=['PUT'])
|
||||||
|
def put_alpha_comment():
|
||||||
|
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(put_reply(auth, data))
|
||||||
|
except Exception as ex:
|
||||||
|
return jsonify({"error": str(ex)}), 400
|
||||||
|
|
||||||
|
|
||||||
# User
|
# User
|
||||||
@bp.route('/api/alpha/user', methods=['GET'])
|
@bp.route('/api/alpha/user', methods=['GET'])
|
||||||
def get_alpha_user():
|
def get_alpha_user():
|
||||||
|
@ -298,7 +310,6 @@ def alpha_post():
|
||||||
|
|
||||||
# Reply - not yet implemented
|
# Reply - not yet implemented
|
||||||
@bp.route('/api/alpha/comment', methods=['GET'])
|
@bp.route('/api/alpha/comment', methods=['GET'])
|
||||||
@bp.route('/api/alpha/comment', methods=['PUT'])
|
|
||||||
@bp.route('/api/alpha/comment/delete', methods=['POST'])
|
@bp.route('/api/alpha/comment/delete', methods=['POST'])
|
||||||
@bp.route('/api/alpha/comment/remove', methods=['POST'])
|
@bp.route('/api/alpha/comment/remove', methods=['POST'])
|
||||||
@bp.route('/api/alpha/comment/mark_as_read', methods=['POST'])
|
@bp.route('/api/alpha/comment/mark_as_read', methods=['POST'])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from app.api.alpha.utils.site import get_site, post_site_block
|
from app.api.alpha.utils.site import get_site, post_site_block
|
||||||
from app.api.alpha.utils.misc import get_search
|
from app.api.alpha.utils.misc import get_search
|
||||||
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, post_reply
|
from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply
|
||||||
from app.api.alpha.utils.community import get_community, get_community_list, post_community_follow, post_community_block
|
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, post_user_block
|
from app.api.alpha.utils.user import get_user, post_user_block
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i
|
||||||
posts = Post.query.filter_by(deleted=False).join(Community, Community.id == Post.community_id).filter_by(show_all=True)
|
posts = Post.query.filter_by(deleted=False).join(Community, Community.id == Post.community_id).filter_by(show_all=True)
|
||||||
elif type == "Local":
|
elif type == "Local":
|
||||||
posts = Post.query.filter_by(deleted=False).join(Community, Community.id == Post.community_id).filter_by(ap_id=None)
|
posts = Post.query.filter_by(deleted=False).join(Community, Community.id == Post.community_id).filter_by(ap_id=None)
|
||||||
|
elif type == "Popular":
|
||||||
|
posts = Post.query.filter_by(deleted=False).join(Community, Community.id == Post.community_id).filter(Community.show_popular == True, Post.score > 100)
|
||||||
elif type == "Subscribed" and user_id is not None:
|
elif type == "Subscribed" and user_id is not None:
|
||||||
posts = Post.query.filter_by(deleted=False).join(CommunityMember, Post.community_id == CommunityMember.community_id).filter_by(is_banned=False, user_id=user_id)
|
posts = Post.query.filter_by(deleted=False).join(CommunityMember, Post.community_id == CommunityMember.community_id).filter_by(is_banned=False, user_id=user_id)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -2,7 +2,7 @@ from app import cache
|
||||||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected, string_expected
|
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected, string_expected
|
||||||
from app.api.alpha.views import reply_view
|
from app.api.alpha.views import reply_view
|
||||||
from app.models import PostReply, Post
|
from app.models import PostReply, Post
|
||||||
from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification, make_reply
|
from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification, make_reply, edit_reply
|
||||||
from app.utils import authorise_api_user, blocked_users, blocked_instances
|
from app.utils import authorise_api_user, blocked_users, blocked_instances
|
||||||
|
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
@ -117,7 +117,7 @@ def put_reply_subscribe(auth, data):
|
||||||
return reply_json
|
return reply_json
|
||||||
|
|
||||||
|
|
||||||
def post_reply(auth,data):
|
def post_reply(auth, data):
|
||||||
required(['body', 'post_id'], data)
|
required(['body', 'post_id'], data)
|
||||||
string_expected(['body',], data)
|
string_expected(['body',], data)
|
||||||
integer_expected(['post_id', 'parent_id', 'language_id'], data)
|
integer_expected(['post_id', 'parent_id', 'language_id'], data)
|
||||||
|
@ -136,3 +136,26 @@ def post_reply(auth,data):
|
||||||
|
|
||||||
reply_json = reply_view(reply=reply, variant=4, user_id=user_id)
|
reply_json = reply_view(reply=reply, variant=4, user_id=user_id)
|
||||||
return reply_json
|
return reply_json
|
||||||
|
|
||||||
|
|
||||||
|
def put_reply(auth, data):
|
||||||
|
required(['comment_id'], data)
|
||||||
|
string_expected(['body',], data)
|
||||||
|
integer_expected(['comment_id', 'language_id'], data)
|
||||||
|
|
||||||
|
reply_id = data['comment_id']
|
||||||
|
body = data['body'] if 'body' in data else ''
|
||||||
|
language_id = data['language_id'] if 'language_id' in data else 2
|
||||||
|
|
||||||
|
input = {'body': body, 'notify_author': True, 'language_id': language_id}
|
||||||
|
reply = PostReply.query.get(reply_id)
|
||||||
|
if not reply:
|
||||||
|
raise Exception('reply_not_found')
|
||||||
|
post = Post.query.get(reply.post_id)
|
||||||
|
if not post:
|
||||||
|
raise Exception('post_not_found')
|
||||||
|
|
||||||
|
user_id, reply = edit_reply(input, reply, post, SRC_API, auth)
|
||||||
|
|
||||||
|
reply_json = reply_view(reply=reply, variant=4, user_id=user_id)
|
||||||
|
return reply_json
|
||||||
|
|
|
@ -280,6 +280,8 @@ def reply_view(reply: PostReply | int, variant, user_id=None, my_vote=0):
|
||||||
v1 = {column.name: getattr(reply, column.name) for column in reply.__table__.columns if column.name in include}
|
v1 = {column.name: getattr(reply, column.name) for column in reply.__table__.columns if column.name in include}
|
||||||
|
|
||||||
v1['path'] = calculate_path(reply)
|
v1['path'] = calculate_path(reply)
|
||||||
|
if reply.edited_at:
|
||||||
|
v1['edited_at'] = reply.edited_at.isoformat() + 'Z'
|
||||||
|
|
||||||
v1.update({'published': reply.posted_at.isoformat() + 'Z',
|
v1.update({'published': reply.posted_at.isoformat() + 'Z',
|
||||||
'ap_id': reply.profile_id(),
|
'ap_id': reply.profile_id(),
|
||||||
|
@ -287,6 +289,7 @@ def reply_view(reply: PostReply | int, variant, user_id=None, my_vote=0):
|
||||||
'language_id': reply.language_id if reply.language_id else 0,
|
'language_id': reply.language_id if reply.language_id else 0,
|
||||||
'removed': reply.deleted,
|
'removed': reply.deleted,
|
||||||
'distinguished': False})
|
'distinguished': False})
|
||||||
|
|
||||||
return v1
|
return v1
|
||||||
|
|
||||||
# Variant 2 - views/comment_view.dart - /comment/list api endpoint
|
# Variant 2 - views/comment_view.dart - /comment/list api endpoint
|
||||||
|
|
|
@ -26,7 +26,7 @@ def vote_for_reply(reply_id: int, vote_direction, src, auth=None):
|
||||||
raise Exception('reply_not_found')
|
raise Exception('reply_not_found')
|
||||||
user = authorise_api_user(auth, return_type='model')
|
user = authorise_api_user(auth, return_type='model')
|
||||||
else:
|
else:
|
||||||
reply = PostReply.query.get_or_404(post_id)
|
reply = PostReply.query.get_or_404(reply_id)
|
||||||
user = current_user
|
user = current_user
|
||||||
|
|
||||||
undo = reply.vote(user, vote_direction)
|
undo = reply.vote(user, vote_direction)
|
||||||
|
@ -241,6 +241,9 @@ def make_reply(input, post, parent_id, src, auth=None):
|
||||||
user.language_id = language_id
|
user.language_id = language_id
|
||||||
reply.ap_id = reply.profile_id()
|
reply.ap_id = reply.profile_id()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
if src == SRC_WEB:
|
||||||
|
input.body.data = ''
|
||||||
|
flash('Your comment has been added.')
|
||||||
|
|
||||||
# federation
|
# federation
|
||||||
if parent_id:
|
if parent_id:
|
||||||
|
@ -270,6 +273,10 @@ def make_reply(input, post, parent_id, src, auth=None):
|
||||||
'audience': post.community.public_url(),
|
'audience': post.community.public_url(),
|
||||||
'contentMap': {
|
'contentMap': {
|
||||||
'en': reply.body_html
|
'en': reply.body_html
|
||||||
|
},
|
||||||
|
'language': {
|
||||||
|
'identifier': reply.language_code(),
|
||||||
|
'name': reply.language_name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
create_json = {
|
create_json = {
|
||||||
|
@ -305,7 +312,7 @@ def make_reply(input, post, parent_id, src, auth=None):
|
||||||
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
||||||
success = post_request(post.community.ap_inbox_url, create_json, user.private_key,
|
success = post_request(post.community.ap_inbox_url, create_json, user.private_key,
|
||||||
user.public_url() + '#main-key')
|
user.public_url() + '#main-key')
|
||||||
if src != SRC_API:
|
if src == SRC_WEB:
|
||||||
if success is False or isinstance(success, str):
|
if success is False or isinstance(success, str):
|
||||||
flash('Failed to send reply', 'error')
|
flash('Failed to send reply', 'error')
|
||||||
else: # local community - send it to followers on remote instances
|
else: # local community - send it to followers on remote instances
|
||||||
|
@ -341,3 +348,133 @@ def make_reply(input, post, parent_id, src, auth=None):
|
||||||
return user.id, reply
|
return user.id, reply
|
||||||
else:
|
else:
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
|
def edit_reply(input, reply, post, src, auth=None):
|
||||||
|
if src == SRC_API:
|
||||||
|
user = authorise_api_user(auth, return_type='model')
|
||||||
|
content = input['body']
|
||||||
|
notify_author = input['notify_author']
|
||||||
|
language_id = input['language_id']
|
||||||
|
else:
|
||||||
|
user = current_user
|
||||||
|
content = input.body.data
|
||||||
|
notify_author = input.notify_author.data
|
||||||
|
language_id = input.language_id.data
|
||||||
|
|
||||||
|
reply.body = piefed_markdown_to_lemmy_markdown(content)
|
||||||
|
reply.body_html = markdown_to_html(content)
|
||||||
|
reply.notify_author = notify_author
|
||||||
|
reply.community.last_active = utcnow()
|
||||||
|
reply.edited_at = utcnow()
|
||||||
|
reply.language_id = language_id
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
if src == SRC_WEB:
|
||||||
|
flash(_('Your changes have been saved.'), 'success')
|
||||||
|
|
||||||
|
if reply.parent_id:
|
||||||
|
in_reply_to = PostReply.query.get(reply.parent_id)
|
||||||
|
else:
|
||||||
|
in_reply_to = post
|
||||||
|
|
||||||
|
# federate edit
|
||||||
|
if not post.community.local_only:
|
||||||
|
reply_json = {
|
||||||
|
'type': 'Note',
|
||||||
|
'id': reply.public_url(),
|
||||||
|
'attributedTo': user.public_url(),
|
||||||
|
'to': [
|
||||||
|
'https://www.w3.org/ns/activitystreams#Public'
|
||||||
|
],
|
||||||
|
'cc': [
|
||||||
|
post.community.public_url(),
|
||||||
|
in_reply_to.author.public_url()
|
||||||
|
],
|
||||||
|
'content': reply.body_html,
|
||||||
|
'inReplyTo': in_reply_to.profile_id(),
|
||||||
|
'url': reply.public_url(),
|
||||||
|
'mediaType': 'text/html',
|
||||||
|
'source': {'content': reply.body, 'mediaType': 'text/markdown'},
|
||||||
|
'published': ap_datetime(reply.posted_at),
|
||||||
|
'updated': ap_datetime(reply.edited_at),
|
||||||
|
'distinguished': False,
|
||||||
|
'audience': post.community.public_url(),
|
||||||
|
'contentMap': {
|
||||||
|
'en': reply.body_html
|
||||||
|
},
|
||||||
|
'language': {
|
||||||
|
'identifier': reply.language_code(),
|
||||||
|
'name': reply.language_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_json = {
|
||||||
|
'@context': default_context(),
|
||||||
|
'type': 'Update',
|
||||||
|
'actor': user.public_url(),
|
||||||
|
'audience': post.community.public_url(),
|
||||||
|
'to': [
|
||||||
|
'https://www.w3.org/ns/activitystreams#Public'
|
||||||
|
],
|
||||||
|
'cc': [
|
||||||
|
post.community.public_url(),
|
||||||
|
in_reply_to.author.public_url()
|
||||||
|
],
|
||||||
|
'object': reply_json,
|
||||||
|
'id': f"https://{current_app.config['SERVER_NAME']}/activities/update/{gibberish(15)}"
|
||||||
|
}
|
||||||
|
if in_reply_to.notify_author and in_reply_to.author.ap_id is not None:
|
||||||
|
reply_json['tag'] = [
|
||||||
|
{
|
||||||
|
'href': in_reply_to.author.public_url(),
|
||||||
|
'name': in_reply_to.author.mention_tag(),
|
||||||
|
'type': 'Mention'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
update_json['tag'] = [
|
||||||
|
{
|
||||||
|
'href': in_reply_to.author.public_url(),
|
||||||
|
'name': in_reply_to.author.mention_tag(),
|
||||||
|
'type': 'Mention'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
||||||
|
success = post_request(post.community.ap_inbox_url, update_json, user.private_key,
|
||||||
|
user.public_url() + '#main-key')
|
||||||
|
if src == SRC_WEB:
|
||||||
|
if success is False or isinstance(success, str):
|
||||||
|
flash('Failed to send send edit to remote server', 'error')
|
||||||
|
else: # local community - send it to followers on remote instances
|
||||||
|
del update_json['@context']
|
||||||
|
announce = {
|
||||||
|
'id': f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}",
|
||||||
|
'type': 'Announce',
|
||||||
|
'to': [
|
||||||
|
'https://www.w3.org/ns/activitystreams#Public'
|
||||||
|
],
|
||||||
|
'actor': post.community.public_url(),
|
||||||
|
'cc': [
|
||||||
|
post.community.ap_followers_url
|
||||||
|
],
|
||||||
|
'@context': default_context(),
|
||||||
|
'object': update_json
|
||||||
|
}
|
||||||
|
|
||||||
|
for instance in post.community.following_instances():
|
||||||
|
if instance.inbox and not user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
|
||||||
|
send_to_remote_instance(instance.id, post.community.id, announce)
|
||||||
|
|
||||||
|
# send copy of Note to post author (who won't otherwise get it if no-one else on their instance is subscribed to the community)
|
||||||
|
if not in_reply_to.author.is_local() and in_reply_to.author.ap_domain != reply.community.ap_domain:
|
||||||
|
if not post.community.is_local() or (post.community.is_local and not post.community.has_followers_from_domain(in_reply_to.author.ap_domain)):
|
||||||
|
success = post_request(in_reply_to.author.ap_inbox_url, update_json, user.private_key, user.public_url() + '#main-key')
|
||||||
|
if success is False or isinstance(success, str):
|
||||||
|
# sending to shared inbox is good enough for Mastodon, but Lemmy will reject it the local community has no followers
|
||||||
|
personal_inbox = in_reply_to.author.public_url() + '/inbox'
|
||||||
|
post_request(personal_inbox, update_json, user.private_key, user.public_url() + '#main-key')
|
||||||
|
|
||||||
|
if src == SRC_API:
|
||||||
|
return user.id, reply
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
Loading…
Add table
Reference in a new issue