mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
federation - editing posts and sending replies to subscribers
This commit is contained in:
parent
4c58a04f8a
commit
01f3cf212b
9 changed files with 284 additions and 149 deletions
|
@ -453,7 +453,7 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
activity_log.exception_message = 'Activity about local content which is already present'
|
||||
activity_log.result = 'ignored'
|
||||
|
||||
# Announce is new content and votes, lemmy and mastodon style
|
||||
# Announce is new content and votes that happened on a remote server.
|
||||
if request_json['type'] == 'Announce':
|
||||
if request_json['object']['type'] == 'Create':
|
||||
activity_log.activity_type = request_json['object']['type']
|
||||
|
@ -511,6 +511,7 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
if post is not None:
|
||||
db.session.add(post)
|
||||
community.post_count += 1
|
||||
activity_log.result = 'success'
|
||||
db.session.commit()
|
||||
if post.image_id:
|
||||
make_image_sizes(post.image_id, 266, None, 'posts')
|
||||
|
@ -623,6 +624,39 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
to_be_deleted_ap_id = request_json['object']['object']
|
||||
delete_post_or_comment(user_ap_id, community_ap_id, to_be_deleted_ap_id)
|
||||
activity_log.result = 'success'
|
||||
elif request_json['object']['type'] == 'Page': # Editing a post
|
||||
post = Post.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||
if post:
|
||||
post.title = request_json['object']['name']
|
||||
if 'source' in request_json['object'] and request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||
post.body = request_json['object']['source']['content']
|
||||
post.body_html = markdown_to_html(post.body)
|
||||
elif 'content' in request_json['object']:
|
||||
post.body_html = allowlist_html(request_json['object']['content'])
|
||||
post.body = html_to_markdown(post.body_html)
|
||||
if 'attachment' in request_json['object'] and 'href' in request_json['object']['attachment']:
|
||||
post.url = request_json['object']['attachment']['href']
|
||||
post.edited_at = utcnow()
|
||||
db.session.commit()
|
||||
activity_log.result = 'success'
|
||||
else:
|
||||
activity_log.exception_message = 'Post not found'
|
||||
elif request_json['object']['type'] == 'Note': # Editing a reply
|
||||
reply = PostReply.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||
if reply:
|
||||
if 'source' in request_json['object'] and request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||
reply.body = request_json['object']['source']['content']
|
||||
reply.body_html = markdown_to_html(reply.body)
|
||||
elif 'content' in request_json['object']:
|
||||
reply.body_html = allowlist_html(request_json['object']['content'])
|
||||
reply.body = html_to_markdown(reply.body_html)
|
||||
reply.edited_at = utcnow()
|
||||
db.session.commit()
|
||||
activity_log.result = 'success'
|
||||
else:
|
||||
activity_log.exception_message = 'PostReply not found'
|
||||
else:
|
||||
activity_log.exception_message = 'Invalid type for Announce'
|
||||
|
||||
# Follow: remote user wants to join/follow one of our communities
|
||||
elif request_json['type'] == 'Follow': # Follow is when someone wants to join a community
|
||||
|
@ -781,6 +815,7 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
activity_log.result = 'ignored'
|
||||
|
||||
elif request_json['type'] == 'Update':
|
||||
activity_log.activity_type = 'Update'
|
||||
if request_json['object']['type'] == 'Page': # Editing a post
|
||||
post = Post.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||
if post:
|
||||
|
@ -790,9 +825,13 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
elif 'content' in request_json['object']:
|
||||
post.body_html = allowlist_html(request_json['object']['content'])
|
||||
post.body = html_to_markdown(post.body_html)
|
||||
if 'attachment' in request_json['object'] and 'href' in request_json['object']['attachment']:
|
||||
post.url = request_json['object']['attachment']['href']
|
||||
post.edited_at = utcnow()
|
||||
db.session.commit()
|
||||
activity_log.result = 'success'
|
||||
else:
|
||||
activity_log.exception_message = 'Post not found'
|
||||
elif request_json['object']['type'] == 'Note': # Editing a reply
|
||||
reply = PostReply.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||
if reply:
|
||||
|
@ -805,6 +844,8 @@ def process_inbox_request(request_json, activitypublog_id):
|
|||
reply.edited_at = utcnow()
|
||||
db.session.commit()
|
||||
activity_log.result = 'success'
|
||||
else:
|
||||
activity_log.exception_message = 'PostReply not found'
|
||||
elif request_json['type'] == 'Delete':
|
||||
if isinstance(request_json['object'], str):
|
||||
ap_id = request_json['object'] # lemmy
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort
|
||||
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, g
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
from flask_babel import _
|
||||
from sqlalchemy import or_, desc
|
||||
|
@ -9,7 +9,7 @@ from app.activitypub.util import default_context
|
|||
from app.community.forms import SearchRemoteCommunity, AddLocalCommunity, CreatePostForm, ReportCommunityForm, \
|
||||
DeleteCommunityForm
|
||||
from app.community.util import search_for_community, community_url_exists, actor_to_community, \
|
||||
opengraph_parse, url_to_thumbnail_file, save_post, save_icon_file, save_banner_file
|
||||
opengraph_parse, url_to_thumbnail_file, save_post, save_icon_file, save_banner_file, send_to_remote_instance
|
||||
from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE, \
|
||||
SUBSCRIPTION_PENDING
|
||||
from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, \
|
||||
|
@ -303,59 +303,83 @@ def add_post(actor):
|
|||
form.communities.choices = [(c.id, c.display_name()) for c in current_user.communities()]
|
||||
|
||||
if form.validate_on_submit():
|
||||
community = Community.query.get_or_404(form.communities.data)
|
||||
post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1)
|
||||
save_post(form, post)
|
||||
community.post_count += 1
|
||||
community.last_active = utcnow()
|
||||
community.last_active = g.site.last_active = utcnow()
|
||||
db.session.commit()
|
||||
post.ap_id = f"https://{current_app.config['SERVER_NAME']}/post/{post.id}"
|
||||
db.session.commit()
|
||||
|
||||
page = {
|
||||
'type': 'Page',
|
||||
'id': post.ap_id,
|
||||
'attributedTo': current_user.ap_profile_id,
|
||||
'to': [
|
||||
community.ap_profile_id,
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'name': post.title,
|
||||
'cc': [],
|
||||
'content': post.body_html,
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': post.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'attachment': [],
|
||||
'commentsEnabled': post.comments_enabled,
|
||||
'sensitive': post.nsfw,
|
||||
'nsfl': post.nsfl,
|
||||
'published': ap_datetime(utcnow()),
|
||||
'audience': community.ap_profile_id
|
||||
}
|
||||
create = {
|
||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}",
|
||||
"actor": current_user.ap_profile_id,
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
community.ap_profile_id
|
||||
],
|
||||
"type": "Create",
|
||||
"audience": community.ap_profile_id,
|
||||
"object": page,
|
||||
'@context': default_context()
|
||||
}
|
||||
if not community.is_local(): # this is a remote community - send the post to the instance that hosts it
|
||||
page = {
|
||||
'type': 'Page',
|
||||
'id': post.ap_id,
|
||||
'attributedTo': current_user.ap_profile_id,
|
||||
'to': [
|
||||
community.ap_profile_id,
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'name': post.title,
|
||||
'cc': [],
|
||||
'content': post.body_html,
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': post.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'attachment': [],
|
||||
'commentsEnabled': post.comments_enabled,
|
||||
'sensitive': post.nsfw,
|
||||
'nsfl': post.nsfl,
|
||||
'published': ap_datetime(utcnow()),
|
||||
'audience': community.ap_profile_id
|
||||
}
|
||||
create = {
|
||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}",
|
||||
"actor": current_user.ap_profile_id,
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
community.ap_profile_id
|
||||
],
|
||||
"type": "Create",
|
||||
"audience": community.ap_profile_id,
|
||||
"object": page
|
||||
}
|
||||
success = post_request(community.ap_inbox_url, create, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if success:
|
||||
flash('Your post has been sent to ' + community.title)
|
||||
flash(_('Your post to %(name)s has been made.', name=community.title))
|
||||
else:
|
||||
flash('There was a problem sending your post to ' + community.title)
|
||||
flash('There was a problem making your post to ' + community.title)
|
||||
else: # local community - send post out to followers
|
||||
...
|
||||
announce = {
|
||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}",
|
||||
"type": 'Announce',
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"actor": community.ap_profile_id,
|
||||
"cc": [
|
||||
community.ap_followers_url
|
||||
],
|
||||
'@context': default_context(),
|
||||
'object': create
|
||||
}
|
||||
|
||||
sent_to = 0
|
||||
for instance in community.following_instances():
|
||||
if instance[1] and not current_user.has_blocked_instance(instance[0]):
|
||||
send_to_remote_instance(instance[1], community.id, announce)
|
||||
sent_to += 1
|
||||
if sent_to:
|
||||
flash(_('Your post to %(name)s has been made.', name=community.title))
|
||||
else:
|
||||
flash(_('Your post to %(name)s has been made.', name=community.title))
|
||||
|
||||
return redirect(f"/c/{community.link()}")
|
||||
else:
|
||||
|
@ -366,8 +390,8 @@ def add_post(actor):
|
|||
images_disabled=images_disabled, markdown_editor=True)
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/community/<int:community_id>/report', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def community_report(community_id: int):
|
||||
community = Community.query.get_or_404(community_id)
|
||||
form = ReportCommunityForm()
|
||||
|
@ -396,8 +420,8 @@ def community_report(community_id: int):
|
|||
return render_template('community/community_report.html', title=_('Report community'), form=form, community=community)
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/community/<int:community_id>/delete', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def community_delete(community_id: int):
|
||||
community = Community.query.get_or_404(community_id)
|
||||
if community.is_owner() or current_user.is_admin():
|
||||
|
@ -415,8 +439,8 @@ def community_delete(community_id: int):
|
|||
abort(401)
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/community/<int:community_id>/block_instance', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def community_block_instance(community_id: int):
|
||||
community = Community.query.get_or_404(community_id)
|
||||
existing = InstanceBlock.query.filter_by(user_id=current_user.id, instance_id=community.instance_id).first()
|
||||
|
|
|
@ -9,6 +9,7 @@ from flask_login import current_user
|
|||
from pillow_heif import register_heif_opener
|
||||
|
||||
from app import db, cache, celery
|
||||
from app.activitypub.signature import post_request
|
||||
from app.activitypub.util import find_actor_or_create, actor_json_to_model, post_json_to_model
|
||||
from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE
|
||||
from app.models import Community, File, BannedInstances, PostReply, PostVote, Post, utcnow, CommunityMember, Site
|
||||
|
@ -350,4 +351,18 @@ def save_banner_file(banner_file, directory='communities') -> File:
|
|||
file = File(file_path=final_place, file_name=new_filename + file_ext, alt_text=f'{directory} banner',
|
||||
width=img_width, height=img_height, thumbnail_width=thumbnail_width, thumbnail_height=thumbnail_height)
|
||||
db.session.add(file)
|
||||
return file
|
||||
return file
|
||||
|
||||
|
||||
def send_to_remote_instance(inbox, community_id, payload):
|
||||
if current_app.debug:
|
||||
send_to_remote_instance_task(inbox, community_id, payload)
|
||||
else:
|
||||
send_to_remote_instance_task.delay(inbox, community_id, payload)
|
||||
|
||||
|
||||
@celery.task
|
||||
def send_to_remote_instance_task(inbox, community_id, payload):
|
||||
community = Community.query.get(community_id)
|
||||
if community:
|
||||
post_request(inbox, payload, community.private_key, community.ap_profile_id + '#main-key')
|
||||
|
|
|
@ -201,6 +201,12 @@ class Community(db.Model):
|
|||
else:
|
||||
return f"https://{current_app.config['SERVER_NAME']}/c/{self.ap_id}"
|
||||
|
||||
# returns a list of tuples (instance.id, instance.inbox)
|
||||
def following_instances(self):
|
||||
sql = 'select distinct i.id, i.inbox from "instance" as i inner join "user" as u on u.instance_id = i.id inner join "community_member" as cm on cm.user_id = u.id '
|
||||
sql += 'where cm.community_id = :community_id and cm.is_banned = false'
|
||||
return db.session.execute(text(sql), {'community_id': self.id})
|
||||
|
||||
def delete_dependencies(self):
|
||||
# this will be fine for remote communities but for local ones it is necessary to federate every deletion out to subscribers
|
||||
for post in self.posts:
|
||||
|
@ -410,6 +416,10 @@ class User(UserMixin, db.Model):
|
|||
def created_recently(self):
|
||||
return self.created and self.created > utcnow() - timedelta(days=7)
|
||||
|
||||
def has_blocked_instance(self, instance_id):
|
||||
instance_block = InstanceBlock.query.filter_by(user_id=self.id, instance_id=instance_id).first()
|
||||
return instance_block is not None
|
||||
|
||||
@staticmethod
|
||||
def verify_reset_password_token(token):
|
||||
try:
|
||||
|
|
|
@ -8,7 +8,7 @@ from sqlalchemy import or_, desc
|
|||
from app import db, constants
|
||||
from app.activitypub.signature import HttpSignature, post_request
|
||||
from app.activitypub.util import default_context
|
||||
from app.community.util import save_post
|
||||
from app.community.util import save_post, send_to_remote_instance
|
||||
from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm
|
||||
from app.community.forms import CreatePostForm
|
||||
from app.post.util import post_replies, get_comment_branch, post_reply_count
|
||||
|
@ -68,48 +68,63 @@ def show_post(post_id: int):
|
|||
post.flush_cache()
|
||||
|
||||
# federation
|
||||
reply_json = {
|
||||
'type': 'Note',
|
||||
'id': reply.profile_id(),
|
||||
'attributedTo': current_user.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
],
|
||||
'content': reply.body_html,
|
||||
'inReplyTo': post.profile_id(),
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': reply.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'published': ap_datetime(utcnow()),
|
||||
'distinguished': False,
|
||||
'audience': post.community.profile_id()
|
||||
}
|
||||
create_json = {
|
||||
'type': 'Create',
|
||||
'actor': current_user.profile_id(),
|
||||
'audience': post.community.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'cc': [
|
||||
post.community.ap_profile_id
|
||||
],
|
||||
'object': reply_json,
|
||||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}"
|
||||
}
|
||||
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
||||
reply_json = {
|
||||
'type': 'Note',
|
||||
'id': reply.profile_id(),
|
||||
'attributedTo': current_user.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
],
|
||||
'content': reply.body_html,
|
||||
'inReplyTo': post.profile_id(),
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': reply.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'published': ap_datetime(utcnow()),
|
||||
'distinguished': False,
|
||||
'audience': post.community.profile_id()
|
||||
}
|
||||
create_json = {
|
||||
'type': 'Create',
|
||||
'actor': current_user.profile_id(),
|
||||
'audience': post.community.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'cc': [
|
||||
post.community.ap_profile_id
|
||||
],
|
||||
'object': reply_json,
|
||||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}"
|
||||
}
|
||||
|
||||
success = post_request(post.community.ap_inbox_url, create_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if not success:
|
||||
flash('Failed to send to remote instance', 'error')
|
||||
else: # local community - send it to followers on remote instances
|
||||
...
|
||||
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.ap_profile_id,
|
||||
"cc": [
|
||||
post.community.ap_followers_url
|
||||
],
|
||||
'@context': default_context(),
|
||||
'object': create_json
|
||||
}
|
||||
|
||||
for instance in post.community.following_instances():
|
||||
if instance[1] and not current_user.has_blocked_instance(instance[0]):
|
||||
send_to_remote_instance(instance[1], post.community.id, announce)
|
||||
|
||||
return redirect(url_for('activitypub.post_ap',
|
||||
post_id=post_id)) # redirect to current page to avoid refresh resubmitting the form
|
||||
|
@ -319,64 +334,81 @@ def add_reply(post_id: int, comment_id: int):
|
|||
post.flush_cache()
|
||||
|
||||
# federation
|
||||
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
||||
reply_json = {
|
||||
'type': 'Note',
|
||||
'id': reply.profile_id(),
|
||||
'attributedTo': current_user.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
in_reply_to.author.profile_id()
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
current_user.followers_url()
|
||||
],
|
||||
'content': reply.body_html,
|
||||
'inReplyTo': in_reply_to.profile_id(),
|
||||
'url': reply.profile_id(),
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': reply.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'published': ap_datetime(utcnow()),
|
||||
'distinguished': False,
|
||||
'audience': post.community.profile_id(),
|
||||
'contentMap': {
|
||||
'en': reply.body_html
|
||||
reply_json = {
|
||||
'type': 'Note',
|
||||
'id': reply.profile_id(),
|
||||
'attributedTo': current_user.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
in_reply_to.author.profile_id()
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
current_user.followers_url()
|
||||
],
|
||||
'content': reply.body_html,
|
||||
'inReplyTo': in_reply_to.profile_id(),
|
||||
'url': reply.profile_id(),
|
||||
'mediaType': 'text/html',
|
||||
'source': {
|
||||
'content': reply.body,
|
||||
'mediaType': 'text/markdown'
|
||||
},
|
||||
'published': ap_datetime(utcnow()),
|
||||
'distinguished': False,
|
||||
'audience': post.community.profile_id(),
|
||||
'contentMap': {
|
||||
'en': reply.body_html
|
||||
}
|
||||
}
|
||||
create_json = {
|
||||
'@context': default_context(),
|
||||
'type': 'Create',
|
||||
'actor': current_user.profile_id(),
|
||||
'audience': post.community.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
in_reply_to.author.profile_id()
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
current_user.followers_url()
|
||||
],
|
||||
'object': reply_json,
|
||||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{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.ap_profile_id,
|
||||
'name': '@' + in_reply_to.author.ap_id,
|
||||
'type': 'Mention'
|
||||
}
|
||||
}
|
||||
create_json = {
|
||||
'@context': default_context(),
|
||||
'type': 'Create',
|
||||
'actor': current_user.profile_id(),
|
||||
'audience': post.community.profile_id(),
|
||||
'to': [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
in_reply_to.author.profile_id()
|
||||
],
|
||||
'cc': [
|
||||
post.community.profile_id(),
|
||||
current_user.followers_url()
|
||||
],
|
||||
'object': reply_json,
|
||||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{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.ap_profile_id,
|
||||
'name': '@' + in_reply_to.author.ap_id,
|
||||
'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, create_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if not success:
|
||||
flash('Failed to send reply', 'error')
|
||||
else: # local community - send it to followers on remote instances
|
||||
...
|
||||
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.ap_profile_id,
|
||||
"cc": [
|
||||
post.community.ap_followers_url
|
||||
],
|
||||
'@context': default_context(),
|
||||
'object': create_json
|
||||
}
|
||||
|
||||
for instance in post.community.following_instances():
|
||||
if instance[1] and not current_user.has_blocked_instance(instance[0]):
|
||||
send_to_remote_instance(instance[1], post.community.id, announce)
|
||||
|
||||
if reply.depth <= constants.THREAD_CUTOFF_DEPTH:
|
||||
return redirect(url_for('activitypub.post_ap', post_id=post_id, _anchor=f'comment_{reply.parent_id}'))
|
||||
else:
|
||||
|
@ -392,8 +424,9 @@ def post_options(post_id: int):
|
|||
post = Post.query.get_or_404(post_id)
|
||||
return render_template('post/post_options.html', post=post)
|
||||
|
||||
@login_required
|
||||
|
||||
@bp.route('/post/<int:post_id>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_edit(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
form = CreatePostForm()
|
||||
|
@ -427,13 +460,14 @@ def post_edit(post_id: int):
|
|||
form.type.data = 'image'
|
||||
form.image_title.data = post.title
|
||||
form.notify_author.data = post.notify_author
|
||||
return render_template('post/post_edit.html', title=_('Edit post'), form=form, post=post, images_disabled=images_disabled)
|
||||
return render_template('post/post_edit.html', title=_('Edit post'), form=form, post=post,
|
||||
images_disabled=images_disabled, markdown_editor=True)
|
||||
else:
|
||||
abort(401)
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/delete', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_delete(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
community = post.community
|
||||
|
@ -447,8 +481,8 @@ def post_delete(post_id: int):
|
|||
return redirect(url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name))
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/report', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_report(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
form = ReportPostForm()
|
||||
|
@ -479,8 +513,8 @@ def post_report(post_id: int):
|
|||
return render_template('post/post_report.html', title=_('Report post'), form=form, post=post)
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/block_user', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_block_user(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
existing = UserBlock.query.filter_by(blocker_id=current_user.id, blocked_id=post.author.id).first()
|
||||
|
@ -494,8 +528,8 @@ def post_block_user(post_id: int):
|
|||
return redirect(post.community.local_url())
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/block_domain', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_block_domain(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
existing = DomainBlock.query.filter_by(user_id=current_user.id, domain_id=post.domain_id).first()
|
||||
|
@ -506,8 +540,8 @@ def post_block_domain(post_id: int):
|
|||
return redirect(post.community.local_url())
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/block_instance', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_block_instance(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
existing = InstanceBlock.query.filter_by(user_id=current_user.id, instance_id=post.instance_id).first()
|
||||
|
@ -518,8 +552,8 @@ def post_block_instance(post_id: int):
|
|||
return redirect(post.community.local_url())
|
||||
|
||||
|
||||
@login_required
|
||||
@bp.route('/post/<int:post_id>/mea_culpa', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def post_mea_culpa(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
form = MeaCulpaForm()
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap');
|
||||
|
||||
.downarea, .downarea * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -7,7 +5,6 @@
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Open Sans', -apple-system, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.downarea::-webkit-scrollbar , .downarea *::-webkit-scrollbar {
|
||||
|
@ -195,8 +192,6 @@
|
|||
padding: 8px;
|
||||
margin-bottom: 20px;
|
||||
resize: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.downarea .downarea-bottom {
|
||||
|
|
|
@ -532,6 +532,10 @@ nav.navbar {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #777;
|
||||
|
|
|
@ -239,6 +239,11 @@ nav.navbar {
|
|||
width: 41px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: $dark-grey;
|
||||
|
|
|
@ -24,6 +24,13 @@
|
|||
if(toClick) {
|
||||
toClick.click();
|
||||
}
|
||||
|
||||
var downarea = new DownArea({
|
||||
elem: document.querySelector('#discussion_body'),
|
||||
resize: DownArea.RESIZE_VERTICAL,
|
||||
hide: ['heading', 'bold-italic'],
|
||||
value: {{ form.discussion_body.data | tojson | safe }}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="row">
|
||||
|
|
Loading…
Reference in a new issue