From 6f6e67078c665dc8b52c05861d6b55f793a0c6af Mon Sep 17 00:00:00 2001 From: zotlabs Date: Thu, 6 Feb 2020 18:50:18 -0800 Subject: [PATCH] more work on polls --- Zotlabs/Lib/Activity.php | 78 +++++++++++++++++++--------------------- Zotlabs/Lib/Libzot.php | 14 ++++++-- Zotlabs/Module/Item.php | 18 +--------- Zotlabs/Module/Vote.php | 21 ++++++++--- include/conversation.php | 6 +++- include/items.php | 2 +- 6 files changed, 72 insertions(+), 67 deletions(-) diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index ea4abc31b..f1ccf6fa4 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -503,8 +503,12 @@ class Activity { } $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if ($i['created'] !== $i['edited']) + if ($i['created'] !== $i['edited']) { $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); + if ($ret['type'] === 'Create') { + $ret['type'] = 'Update'; + } + } if ($i['app']) { $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ]; } @@ -823,10 +827,11 @@ class Activity { } } + if ($i['title']) { + $ret['name'] = $i['title']; + } + if ($i['mimetype'] === 'text/bbcode') { - if ($i['title']) { - $ret['name'] = $i['title']; - } if ($i['summary']) { $ret['summary'] = bbcode($i['summary'], [ $bbopts => true ]); } @@ -1145,10 +1150,6 @@ class Activity { static function activity_mapper($verb) { - if ($verb === 'Answer') { - return 'Note'; - } - if (strpos($verb,'/') === false) { return $verb; } @@ -1771,6 +1772,10 @@ class Activity { return false; } + + + + $o = json_decode($item['obj'],true); if ($o && array_key_exists('anyOf',$o)) { $multi = true; @@ -1815,6 +1820,15 @@ class Activity { } } } + + if ($item['comments_closed'] > NULL_DATE) { + if ($item['comments_closed'] > datetime_convert()) { + $o['closed'] = datetime_convert('UTC','UTC',$item['comments_closed'], ATOM_TIME); + // set this to force an update + $answer_found = true; + } + } + logger('updated_poll: ' . print_r($o,true),LOGGER_DATA); if ($answer_found && ! $found) { $x = q("update item set obj = '%s', edited = '%s' where id = %d", @@ -1839,9 +1853,15 @@ class Activity { if (is_array($act->obj)) { $content = self::get_content($act->obj); - } - + + // These activities should have been handled separately in the Inbox module and should not be turned into posts + + if (in_array($act->type, ['Follow', 'Accept', 'Reject', 'Create', 'Update']) + && is_array($a->obj) && array_key_exists('type',$a->obj) && ActivityStreams::is_an_actor($a->obj['type'])) { + return false; + } + $s['owner_xchan'] = $act->actor['id']; $s['author_xchan'] = $act->actor['id']; @@ -1874,24 +1894,6 @@ class Activity { $s['mid'] = $s['parent_mid'] = $act->id; } - if ($act->obj['type'] === 'Note' && $act->data['name']) { - $parent = self::fetch($act->parent_id); - if ($parent && array_path_exists('object/type',$parent) && $parent['object']['type'] === 'Question') { - $s['mid'] = $act->id; - $s['replyto'] = $act->replyto; - $s['verb'] = 'Answer'; - $content['content'] = EMPTY_STR; - } - } - - if ($act->type === 'Note' && $act->obj['type'] === 'Question' && $act->data['name']) { - $s['mid'] = $act->id; - $s['parent_mid'] = $act->obj['id']; - $s['replyto'] = $act->replyto; - $s['verb'] = 'Answer'; - $content['content'] = EMPTY_STR; - } - if (in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'TentativeReject', 'emojiReaction', 'EmojiReaction', 'EmojiReact' ])) { @@ -1997,11 +1999,6 @@ class Activity { $s['verb'] = self::activity_mapper($act->type); - if ($act->type === 'Note' && $act->obj['type'] === 'Question' && $act->data['name'] && ! $content['content']) { - $s['verb'] = 'Answer'; - $s['title'] = purify_html($act->data['name']); - } - // Mastodon does not provide update timestamps when updating poll tallies which means race conditions may occur here. if ($act->type === 'Update' && $act->obj['type'] === 'Question' && $s['edited'] === $s['created']) { $s['edited'] = datetime_convert(); @@ -2316,7 +2313,7 @@ class Activity { $allowed = false; if ($is_child_node) { - $p = q("select id from item where mid = '%s' and uid = %d and item_wall = 1", + $p = q("select id, obj_type from item where mid = '%s' and uid = %d and item_wall = 1", dbesc($item['parent_mid']), intval($channel['channel_id']) ); @@ -2342,6 +2339,12 @@ class Activity { $allowed = false; } } + + if ($p && $p[0]['obj_type'] === 'Question') { + if ($item['obj_type'] === 'Note' && $item['title'] && (! $item['content']) && (! $item['summary'])) { + $item['obj_type'] = 'Answer'; + } + } } elseif (perm_is_allowed($channel['channel_id'],$observer_hash,'send_stream') || ($is_sys_channel && $pubstream)) { $allowed = true; @@ -2567,14 +2570,7 @@ class Activity { Activity::actor_store($a->actor['id'],$a->actor); } - // we don't support Create/Person which may have landed here accidentally due to an implied_create - // added to an incorrectly translated Hubzilla like of a "new friend" activity. There is no perfect mapping from that AS1 - // construct to AS2. The above fetch of the parent will return a naked Person object which is turned into a Create by the AS parser. - // There may be other solutions but this should catch it. - if ($a->implied_create && is_array($a->obj) && array_key_exists('type',$a->obj) && ActivityStreams::is_an_actor($a->obj['type'])) { - return false; - } $item = Activity::decode_note($a); diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 5b6f04501..2876eea29 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -1784,7 +1784,7 @@ class Libzot { // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise // processing it is pointless. - $r = q("select route, id, parent_mid, mid, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1", + $r = q("select route, id, parent_mid, mid, owner_xchan, item_private, obj_type from item where mid = '%s' and uid = %d limit 1", dbesc($arr['parent_mid']), intval($channel['channel_id']) ); @@ -1794,6 +1794,15 @@ class Libzot { $arr['thr_parent'] = $arr['parent_mid']; $arr['parent_mid'] = $r[0]['parent_mid']; } + + if ($r[0]['obj_type'] === 'Question') { + // route checking doesn't work here because we've changed the privacy + $r[0]['route'] = EMPTY_STR; + // If this is a poll response, convert the obj_type to our (internal-only) "Answer" type + if ($arr['obj_type'] === 'Note' && $arr['title'] && (! $arr['content']) && (! $arr['summary'])) { + $arr['obj_type'] = 'Answer'; + } + } } else { $DR->update('comment parent not found'); @@ -1816,7 +1825,8 @@ class Libzot { } continue; } - + + if ($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) { // reset the route in case it travelled a great distance upstream // use our parent's route so when we go back downstream we'll match diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 214273bff..3cc6f8bf2 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -880,13 +880,6 @@ class Item extends Controller { // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // we may need virtual or template classes to implement the possible alternatives -// $obj = $this->extract_poll_data($body); -// if ($obj) { -// $obj['attributedTo'] = channel_url($channel); -// $datarray['obj'] = $obj; -// $obj_type = 'Question'; -// } - if(strpos($body,'[/summary]') !== false) { $match = ''; $cnt = preg_match("/\[summary\](.*?)\[\/summary\]/ism",$body,$match); @@ -1109,23 +1102,14 @@ class Item extends Controller { } } - // this will generally only happen when creating a poll and it cannot be - // done earlier because the $mid has only just now been defined - $obj = $this->extract_poll_data($body,[ 'item_private' => $private, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_contact_deny ]); if ($obj) { + $obj['url'] = $mid; $obj['attributedTo'] = channel_url($channel); $datarray['obj'] = $obj; $obj_type = 'Question'; } - - - - if ($datarray['obj'] && ! $datarray['obj']['url']) { - $datarray['obj']['url'] = $mid; - } - if(! $parent_mid) { $parent_mid = $mid; } diff --git a/Zotlabs/Module/Vote.php b/Zotlabs/Module/Vote.php index 52d6a4bea..a267f1ddd 100644 --- a/Zotlabs/Module/Vote.php +++ b/Zotlabs/Module/Vote.php @@ -31,7 +31,6 @@ class Vote extends Controller { ); } - if ($fetch && $fetch[0]['obj_type'] === 'Question') { $obj = json_decode($fetch[0]['obj'],true); @@ -80,22 +79,34 @@ class Vote extends Controller { $item = []; - $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; $item['item_origin'] = true; $item['parent'] = $fetch[0]['id']; $item['parent_mid'] = $fetch[0]['mid']; + $item['thr_parent'] = $fetch[0]['mid']; $item['uuid'] = new_uuid(); $item['mid'] = z_root() . '/item/' . $item['uuid']; - $item['verb'] = 'Answer'; + $item['verb'] = 'Create'; $item['title'] = $res; $item['author_xchan'] = $channel['channel_hash']; $item['owner_xchan'] = $fetch[0]['author_xchan']; + $item['allow_cid'] = '<' . $fetch[0]['author_xchan'] . '>'; + $item['item_private'] = 1; - $item['obj'] = $obj; - $item['obj_type'] = 'Question'; + // These two are placeholder values that will be reset after + // we encode the item + + $item['obj_type'] = 'Note'; + $item['author'] = channelx_by_n($channel['channel_id']); + $item['obj'] = Activity::encode_item($item,((get_config('system','activitypub')) ? true : false)); + + // now reset the placeholders + + $item['obj_type'] = 'Answer'; + unset($item['author']); + $x = item_store($item); retain_item($fetch[0]['id']); diff --git a/include/conversation.php b/include/conversation.php index 7090d55ba..921b34f90 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -403,11 +403,15 @@ function count_descendants($item) { * @return boolean */ function visible_activity($item) { - $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Answer' ]; + $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE ]; if(intval($item['item_notshown'])) return false; + if ($item['obj_type'] === 'Answer') { + return false; + } + foreach($hidden_activities as $act) { if((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) { return false; diff --git a/include/items.php b/include/items.php index 1b5c0499d..7a1ac8665 100644 --- a/include/items.php +++ b/include/items.php @@ -1813,7 +1813,7 @@ function item_store($arr, $allow_exec = false, $deliver = true, $linkid = true) if(intval($r[0]['item_uplink']) && (! $r[0]['item_private'])) $arr['item_private'] = 0; - if(in_array($arr['verb'], ['Note','Answer']) && $arr['obj_type'] === 'Question' && intval($r[0]['item_wall'])) { + if(in_array($arr['obj_type'], ['Note','Answer']) && $r[0]['obj_type'] === 'Question' && intval($r[0]['item_wall'])) { Activity::update_poll($r[0],$arr['mid'],$arr['title']); } }