poll - federation #181

This commit is contained in:
rimu 2024-05-18 21:06:57 +12:00
parent 79798b28e3
commit 0d9d4f12cb
4 changed files with 111 additions and 8 deletions

View file

@ -549,7 +549,7 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
user.last_seen = community.last_active = site.last_active = utcnow()
object_type = request_json['object']['type']
new_content_types = ['Page', 'Article', 'Link', 'Note']
new_content_types = ['Page', 'Article', 'Link', 'Note', 'Question']
if object_type in new_content_types: # create a new post
in_reply_to = request_json['object']['inReplyTo'] if 'inReplyTo' in request_json['object'] else None
if not in_reply_to:

View file

@ -13,7 +13,7 @@ from sqlalchemy import text, func
from app import db, cache, constants, celery
from app.models import User, Post, Community, BannedInstances, File, PostReply, AllowedInstances, Instance, utcnow, \
PostVote, PostReplyVote, ActivityPubLog, Notification, Site, CommunityMember, InstanceRole, Report, Conversation, \
Language, Tag
Language, Tag, Poll, PollChoice
from app.activitypub.signature import signed_get_request
import time
import base64
@ -179,7 +179,25 @@ def post_to_activity(post: Post, community: Community):
if post.image_id is not None:
activity_data["object"]["object"]["image"] = {"url": post.image.view_url(), "type": "Image"}
if post.image.alt_text:
activity_data["object"]["object"]["image"]['altText'] = post.image.alt_text
activity_data["object"]["object"]["image"]['name'] = post.image.alt_text
if post.type == POST_TYPE_POLL:
poll = Poll.query.filter_by(post_id=post.id).first()
activity_data["object"]["object"]['type'] = 'Question'
mode = 'oneOf' if poll.mode == 'single' else 'anyOf'
choices = []
for choice in PollChoice.query.filter_by(post_id=post.id).order_by(PollChoice.sort_order).all():
choices.append({
"type": "Note",
"name": choice.choice_text,
"replies": {
"type": "Collection",
"totalItems": choice.num_votes
}
})
activity_data["object"]["object"][mode] = choices
activity_data["object"]["object"]['endTime'] = ap_datetime(poll.end_poll)
activity_data["object"]["object"]['votersCount'] = poll.total_votes()
return activity_data
@ -1554,6 +1572,21 @@ def create_post(activity_log: ActivityPubLog, community: Community, request_json
activity_log.result = 'success'
db.session.commit()
# Polls need to be processed quite late because they need a post_id to refer to
if request_json['object']['type'] == 'Question':
post.type = POST_TYPE_POLL
mode = 'single'
if 'anyOf' in request_json['object']:
mode = 'multiple'
poll = Poll(post_id=post.id, end_poll=request_json['object']['endTime'], mode=mode, local_only=False)
db.session.add(poll)
i = 1
for choice_ap in request_json['object']['oneOf' if mode == 'single' else 'anyOf']:
new_choice = PollChoice(post_id=post.id, choice_text=choice_ap['name'], sort_order=i)
db.session.add(new_choice)
i += 1
db.session.commit()
if post.image_id:
make_image_sizes(post.image_id, 150, 512, 'posts') # the 512 sized image is for masonry view

View file

@ -21,11 +21,11 @@ from app.community.util import search_for_community, actor_to_community, \
delete_post_from_community, delete_post_reply_from_community, community_in_list
from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE, \
SUBSCRIPTION_PENDING, SUBSCRIPTION_MODERATOR, REPORT_STATE_NEW, REPORT_STATE_ESCALATED, REPORT_STATE_RESOLVED, \
REPORT_STATE_DISCARDED, POST_TYPE_VIDEO, NOTIF_COMMUNITY
REPORT_STATE_DISCARDED, POST_TYPE_VIDEO, NOTIF_COMMUNITY, POST_TYPE_POLL
from app.inoculation import inoculation
from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, \
File, PostVote, utcnow, Report, Notification, InstanceBlock, ActivityPubLog, Topic, Conversation, PostReply, \
NotificationSubscription, UserFollower, Instance, Language
NotificationSubscription, UserFollower, Instance, Language, Poll, PollChoice
from app.community import bp
from app.user.utils import search_for_user
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \
@ -805,6 +805,7 @@ def add_poll_post(actor):
abort(401)
post = Post(user_id=current_user.id, community_id=form.communities.data, instance_id=1)
save_post(form, post, 'poll')
poll = Poll.query.filter_by(post_id=post.id).first()
community.post_count += 1
community.last_active = g.site.last_active = utcnow()
db.session.commit()
@ -815,9 +816,9 @@ def add_poll_post(actor):
notify_about_post(post)
if not community.local_only:
if not community.local_only and not poll.local_only:
federate_post(community, post)
federate_post_to_user_followers(post)
federate_post_to_user_followers(post)
return redirect(f"/post/{post.id}")
else:
@ -897,6 +898,23 @@ def federate_post(community, post):
if post.type == POST_TYPE_IMAGE:
page['attachment'] = [{'type': 'Link',
'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above
if post.type == POST_TYPE_POLL:
poll = Poll.query.filter_by(post_id=post.id).first()
page['type'] = 'Question'
page['endTime'] = ap_datetime(poll.end_poll)
page['votersCount'] = 0
choices = []
for choice in PollChoice.query.filter_by(post_id=post.id).all():
choices.append({
"type": "Note",
"name": choice.choice_text,
"replies": {
"type": "Collection",
"totalItems": 0
}
})
page['oneOf' if poll.mode == 'single' else 'anyOf'] = choices
if not community.is_local(): # this is a remote community - send the post to the instance that hosts it
success = post_request(community.ap_inbox_url, create, current_user.private_key,
current_user.ap_profile_id + '#main-key')
@ -989,8 +1007,25 @@ def federate_post_to_user_followers(post):
if post.body_html:
note['content'] = note['content'] + '<p>' + post.body_html + '</p>'
if post.type == POST_TYPE_POLL:
poll = Poll.query.filter_by(post_id=post.id).first()
note['type'] = 'Question'
note['endTime'] = ap_datetime(poll.end_poll)
note['votersCount'] = 0
choices = []
for choice in PollChoice.query.filter_by(post_id=post.id).all():
choices.append({
"type": "Note",
"name": choice.choice_text,
"replies": {
"type": "Collection",
"totalItems": 0
}
})
note['oneOf' if poll.mode == 'single' else 'anyOf'] = choices
instances = Instance.query.join(User, User.instance_id == Instance.id).join(UserFollower, UserFollower.remote_user_id == User.id)
instances = instances.filter(UserFollower.local_user_id == post.user_id)
instances = instances.filter(UserFollower.local_user_id == post.user_id).filter(Instance.gone_forever == False)
for i in instances:
post_request(i.inbox, create, current_user.private_key, current_user.ap_profile_id + '#main-key')

View file

@ -1233,6 +1233,8 @@ def post_edit_poll_post(post_id: int):
)
else:
abort(401)
def federate_post_update(post):
page_json = {
'type': 'Page',
@ -1288,6 +1290,23 @@ def federate_post_update(post):
if post.type == POST_TYPE_IMAGE:
page_json['attachment'] = [{'type': 'Link',
'href': post.image.source_url}] # source_url is always a https link, no need for .replace() as done above
if post.type == POST_TYPE_POLL:
poll = Poll.query.filter_by(post_id=post.id).first()
page_json['type'] = 'Question'
page_json['endTime'] = ap_datetime(poll.end_poll)
page_json['votersCount'] = 0
choices = []
for choice in PollChoice.query.filter_by(post_id=post.id).all():
choices.append({
"type": "Note",
"name": choice.choice_text,
"replies": {
"type": "Collection",
"totalItems": 0
}
})
page_json['oneOf' if poll.mode == 'single' else 'anyOf'] = choices
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, current_user.private_key,
current_user.ap_profile_id + '#main-key')
@ -1369,6 +1388,22 @@ def federate_post_edit_to_user_followers(post):
note['attachment'] = [{'type': 'Document', 'url': post.image.source_url, 'name': post.image.alt_text}]
else:
note['attachment'] = [{'type': 'Document', 'url': post.image.source_url}]
elif post.type == POST_TYPE_POLL:
poll = Poll.query.filter_by(post_id=post.id).first()
note['type'] = 'Question'
note['endTime'] = ap_datetime(poll.end_poll)
note['votersCount'] = 0
choices = []
for choice in PollChoice.query.filter_by(post_id=post.id).all():
choices.append({
"type": "Note",
"name": choice.choice_text,
"replies": {
"type": "Collection",
"totalItems": 0
}
})
note['oneOf' if poll.mode == 'single' else 'anyOf'] = choices
if post.body_html:
note['content'] = note['content'] + '<p>' + post.body_html + '</p>'