diff options
Diffstat (limited to 'include/items.php')
-rw-r--r-- | include/items.php | 714 |
1 files changed, 575 insertions, 139 deletions
diff --git a/include/items.php b/include/items.php index 9ef44e147..b80c5672b 100644 --- a/include/items.php +++ b/include/items.php @@ -240,24 +240,46 @@ function comments_are_now_closed($item) { return false; } -function item_normal() { - $profile_uid = App::$profile['profile_uid'] ?? App::$profile_uid ?? null; +function item_normal($profile_uid = null, $prefix = 'item') { + if ($profile_uid === null) { + $profile_uid = App::$profile['profile_uid'] ?? App::$profile_uid ?? null; + } + $uid = local_channel(); $is_owner = ($uid && intval($profile_uid) === $uid); - $sql = " and item.item_hidden = 0 and item.item_type = 0 and item.item_deleted = 0 - and item.item_unpublished = 0 and item.item_pending_remove = 0"; + $sql = " and $prefix.item_hidden = 0 and $prefix.item_type = 0 and $prefix.item_deleted = 0 + and $prefix.item_unpublished = 0 and $prefix.item_pending_remove = 0"; if ($is_owner) { - $sql .= " and item.item_blocked IN (0, " . intval(ITEM_MODERATED) . ") and item.item_delayed IN (0, 1) "; + $sql .= " and $prefix.item_blocked IN (0, " . intval(ITEM_MODERATED) . ") and $prefix.item_delayed IN (0, 1) "; } else { - $sql .= " and item.item_blocked = 0 and item.item_delayed = 0 "; + $sql .= " and $prefix.item_blocked = 0 and $prefix.item_delayed = 0 "; } return $sql; } +function item_forwardable($item) { + if (intval($item['item_unpublished']) || + intval($item['item_delayed']) || + intval($item['item_blocked']) || + intval($item['item_hidden']) || + intval($item['item_restrict']) || // this might change in the future + // internal follow/unfollow thread + in_array($item['verb'], ['Follow', 'Ignore', ACTIVITY_FOLLOW, ACTIVITY_UNFOLLOW]) || + str_contains($item['postopts'], 'nodeliver') || + // actor not fetchable + (isset($item['author']['xchan_network']) && in_array($item['author']['xchan_network'], ['rss', 'anon', 'token'])) + + ) { + return false; + } + + return true; +} + function item_normal_search() { return " and item.item_hidden = 0 and item.item_type in (0,3,6,7) and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 @@ -437,7 +459,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true, $channel $ret = array('success' => false); $is_comment = false; - if((($arr['parent']) && $arr['parent'] != $arr['id']) || (($arr['parent_mid']) && $arr['parent_mid'] != $arr['mid'])) + if((isset($arr['parent'], $arr['id']) && intval($arr['parent']) !== intval($arr['id'])) || (isset($arr['parent_mid'], $arr['mid']) && $arr['parent_mid'] !== $arr['mid'])) $is_comment = true; if(! array_key_exists('item_origin',$arr)) @@ -455,8 +477,8 @@ function post_activity_item($arr, $allow_code = false, $deliver = true, $channel $observer = App::get_observer(); } - $arr['aid'] = ((x($arr,'aid')) ? $arr['aid'] : $channel['channel_account_id']); - $arr['uid'] = ((x($arr,'uid')) ? $arr['uid'] : $channel['channel_id']); + $arr['aid'] = ((!empty($arr['aid'])) ? $arr['aid'] : $channel['channel_account_id']); + $arr['uid'] = ((!empty($arr['uid'])) ? $arr['uid'] : $channel['channel_id']); if(! perm_is_allowed($arr['uid'],$observer['xchan_hash'],(($is_comment) ? 'post_comments' : 'post_wall'))) { $ret['message'] = t('Permission denied'); @@ -472,18 +494,18 @@ function post_activity_item($arr, $allow_code = false, $deliver = true, $channel $arr['mimetype'] = 'text/bbcode'; - if(! $arr['mid']) { - $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : new_uuid()); + if(empty($arr['mid'])) { + $arr['uuid'] = ((!empty($arr['uuid'])) ? $arr['uuid'] : new_uuid()); } - $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : z_root() . '/item/' . $arr['uuid']); - $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); - $arr['thr_parent'] = ((x($arr,'thr_parent')) ? $arr['thr_parent'] : $arr['mid']); + $arr['mid'] = ((!empty($arr['mid'])) ? $arr['mid'] : z_root() . '/item/' . $arr['uuid']); + $arr['parent_mid'] = ((!empty($arr['parent_mid'])) ? $arr['parent_mid'] : $arr['mid']); + $arr['thr_parent'] = ((!empty($arr['thr_parent'])) ? $arr['thr_parent'] : $arr['mid']); - $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? $arr['owner_xchan'] : $channel['channel_hash']); - $arr['author_xchan'] = ((x($arr,'author_xchan')) ? $arr['author_xchan'] : $observer['xchan_hash']); + $arr['owner_xchan'] = ((!empty($arr['owner_xchan'])) ? $arr['owner_xchan'] : $channel['channel_hash']); + $arr['author_xchan'] = ((!empty($arr['author_xchan'])) ? $arr['author_xchan'] : $observer['xchan_hash']); - $arr['verb'] = ((x($arr,'verb')) ? $arr['verb'] : 'Create'); - $arr['obj_type'] = ((x($arr,'obj_type')) ? $arr['obj_type'] : 'Note'); + $arr['verb'] = ((!empty($arr['verb'])) ? $arr['verb'] : 'Create'); + $arr['obj_type'] = ((!empty($arr['obj_type'])) ? $arr['obj_type'] : 'Note'); if(! ( array_key_exists('allow_cid',$arr) || array_key_exists('allow_gid',$arr) || array_key_exists('deny_cid',$arr) || array_key_exists('deny_gid',$arr))) { @@ -495,7 +517,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true, $channel $arr['comment_policy'] = map_scope(PermissionLimits::Get($channel['channel_id'],'post_comments')); - if ((! $arr['plink']) && (intval($arr['item_thread_top']))) { + if (empty($arr['plink']) && (intval($arr['item_thread_top']))) { $arr['plink'] = $arr['mid']; } @@ -518,7 +540,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true, $channel */ call_hooks('post_local', $arr); - if(x($arr, 'cancel')) { + if (!empty($arr['cancel'])) { logger('Post cancelled by plugin.'); return $ret; } @@ -708,14 +730,14 @@ function get_item_elements($x,$allow_code = false) { if($arr['edited'] > datetime_convert()) $arr['edited'] = datetime_convert(); - $arr['expires'] = ((x($x,'expires') && $x['expires']) + $arr['expires'] = ((!empty($x['expires']) && $x['expires']) ? datetime_convert('UTC','UTC',$x['expires']) : NULL_DATE); - $arr['commented'] = ((x($x,'commented') && $x['commented']) + $arr['commented'] = ((!empty($x['commented']) && $x['commented']) ? datetime_convert('UTC','UTC',$x['commented']) : $arr['created']); - $arr['comments_closed'] = ((x($x,'comments_closed') && $x['comments_closed']) + $arr['comments_closed'] = ((!empty($x['comments_closed']) && $x['comments_closed']) ? datetime_convert('UTC','UTC',$x['comments_closed']) : NULL_DATE); @@ -1660,7 +1682,7 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr if(array_key_exists('parent',$arr)) unset($arr['parent']); - $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); + $arr['mimetype'] = ((!empty($arr['mimetype'])) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); if(($arr['mimetype'] == 'application/x-php') && (! $allow_exec)) { logger('item_store: php mimetype but allow_exec is denied.'); @@ -1672,19 +1694,19 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr $arr['summary'] = ((array_key_exists('summary',$arr) && $arr['summary']) ? trim($arr['summary']) : ''); $arr['body'] = ((array_key_exists('body',$arr) && $arr['body']) ? trim($arr['body']) : ''); - $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : ''); - $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : ''); - $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); - $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); - $arr['postopts'] = ((x($arr,'postopts')) ? trim($arr['postopts']) : ''); - $arr['route'] = ((x($arr,'route')) ? trim($arr['route']) : ''); - $arr['uuid'] = ((x($arr,'uuid')) ? trim($arr['uuid']) : ''); - $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : 0 ); - $arr['item_wall'] = ((x($arr,'item_wall')) ? intval($arr['item_wall']) : 0 ); - $arr['item_type'] = ((x($arr,'item_type')) ? intval($arr['item_type']) : 0 ); + $arr['allow_cid'] = ((!empty($arr['allow_cid'])) ? trim($arr['allow_cid']) : ''); + $arr['allow_gid'] = ((!empty($arr['allow_gid'])) ? trim($arr['allow_gid']) : ''); + $arr['deny_cid'] = ((!empty($arr['deny_cid'])) ? trim($arr['deny_cid']) : ''); + $arr['deny_gid'] = ((!empty($arr['deny_gid'])) ? trim($arr['deny_gid']) : ''); + $arr['postopts'] = ((!empty($arr['postopts'])) ? trim($arr['postopts']) : ''); + $arr['route'] = ((!empty($arr['route'])) ? trim($arr['route']) : ''); + $arr['uuid'] = ((!empty($arr['uuid'])) ? trim($arr['uuid']) : ''); + $arr['item_private'] = ((!empty($arr['item_private'])) ? intval($arr['item_private']) : 0 ); + $arr['item_wall'] = ((!empty($arr['item_wall'])) ? intval($arr['item_wall']) : 0 ); + $arr['item_type'] = ((!empty($arr['item_type'])) ? intval($arr['item_type']) : 0 ); // obsolete, but needed so as not to throw not-null constraints on some database driveres - $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); + $arr['item_flags'] = ((!empty($arr['item_flags'])) ? intval($arr['item_flags']) : 0 ); $arr['lang'] = detect_language($arr['body']); @@ -1725,32 +1747,32 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr $arr = $translate['item']; } - if((x($arr,'obj')) && is_array($arr['obj'])) { + if((!empty($arr['obj'])) && is_array($arr['obj'])) { activity_sanitise($arr['obj']); $arr['obj'] = json_encode($arr['obj']); } - if((x($arr,'target')) && is_array($arr['target'])) { + if((!empty($arr['target'])) && is_array($arr['target'])) { activity_sanitise($arr['target']); $arr['target'] = json_encode($arr['target']); } - if((x($arr,'attach')) && is_array($arr['attach'])) { + if((!empty($arr['attach'])) && is_array($arr['attach'])) { activity_sanitise($arr['attach']); $arr['attach'] = json_encode($arr['attach']); } - $arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0); - $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); - $arr['revision'] = ((x($arr,'revision') && intval($arr['revision']) > 0) ? intval($arr['revision']) : 0); + $arr['aid'] = ((!empty($arr['aid'])) ? intval($arr['aid']) : 0); + $arr['mid'] = ((!empty($arr['mid'])) ? notags(trim($arr['mid'])) : random_string()); + $arr['revision'] = ((!empty($arr['revision']) && intval($arr['revision']) > 0) ? intval($arr['revision']) : 0); - $arr['author_xchan'] = ((x($arr,'author_xchan')) ? notags(trim($arr['author_xchan'])) : ''); - $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : ''); - $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert()); - $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); - $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : NULL_DATE); - $arr['commented'] = ((x($arr,'commented') !== false) ? datetime_convert('UTC','UTC',$arr['commented']) : datetime_convert()); - $arr['comments_closed'] = ((x($arr,'comments_closed') !== false) ? datetime_convert('UTC','UTC',$arr['comments_closed']) : NULL_DATE); + $arr['author_xchan'] = ((!empty($arr['author_xchan'])) ? notags(trim($arr['author_xchan'])) : ''); + $arr['owner_xchan'] = ((!empty($arr['owner_xchan'])) ? notags(trim($arr['owner_xchan'])) : ''); + $arr['created'] = ((!empty($arr['created']) !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert()); + $arr['edited'] = ((!empty($arr['edited']) !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); + $arr['expires'] = ((!empty($arr['expires']) !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : NULL_DATE); + $arr['commented'] = ((!empty($arr['commented']) !== false) ? datetime_convert('UTC','UTC',$arr['commented']) : datetime_convert()); + $arr['comments_closed'] = ((!empty($arr['comments_closed']) !== false) ? datetime_convert('UTC','UTC',$arr['comments_closed']) : NULL_DATE); $arr['html'] = ((array_key_exists('html',$arr)) ? $arr['html'] : ''); if($deliver) { @@ -1764,26 +1786,26 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr // will still take place through backdoor methods. Since these fields are rarely used // otherwise, just preserve the original timestamp. - $arr['received'] = ((x($arr,'received') !== false) ? datetime_convert('UTC','UTC',$arr['received']) : datetime_convert()); - $arr['changed'] = ((x($arr,'changed') !== false) ? datetime_convert('UTC','UTC',$arr['changed']) : datetime_convert()); + $arr['received'] = ((!empty($arr['received']) !== false) ? datetime_convert('UTC','UTC',$arr['received']) : datetime_convert()); + $arr['changed'] = ((!empty($arr['changed']) !== false) ? datetime_convert('UTC','UTC',$arr['changed']) : datetime_convert()); } - $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); - $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); - $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); - $arr['thr_parent'] = ((x($arr,'thr_parent')) ? notags(trim($arr['thr_parent'])) : $arr['parent_mid']); - $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : 'Create'); - $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : 'Note'); - $arr['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : ''); - $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : ''); - $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : ''); - $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : ''); - $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : ''); - $arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : ''); + $arr['location'] = ((!empty($arr['location'])) ? notags(trim($arr['location'])) : ''); + $arr['coord'] = ((!empty($arr['coord'])) ? notags(trim($arr['coord'])) : ''); + $arr['parent_mid'] = ((!empty($arr['parent_mid'])) ? notags(trim($arr['parent_mid'])) : ''); + $arr['thr_parent'] = ((!empty($arr['thr_parent'])) ? notags(trim($arr['thr_parent'])) : $arr['parent_mid']); + $arr['verb'] = ((!empty($arr['verb'])) ? notags(trim($arr['verb'])) : 'Create'); + $arr['obj_type'] = ((!empty($arr['obj_type'])) ? notags(trim($arr['obj_type'])) : 'Note'); + $arr['obj'] = ((!empty($arr['obj'])) ? trim($arr['obj']) : ''); + $arr['tgt_type'] = ((!empty($arr['tgt_type'])) ? notags(trim($arr['tgt_type'])) : ''); + $arr['target'] = ((!empty($arr['target'])) ? trim($arr['target']) : ''); + $arr['plink'] = ((!empty($arr['plink'])) ? notags(trim($arr['plink'])) : ''); + $arr['attach'] = ((!empty($arr['attach'])) ? notags(trim($arr['attach'])) : ''); + $arr['app'] = ((!empty($arr['app'])) ? notags(trim($arr['app'])) : ''); - $arr['public_policy'] = ((x($arr,'public_policy')) ? notags(trim($arr['public_policy'])) : '' ); + $arr['public_policy'] = ((!empty($arr['public_policy'])) ? notags(trim($arr['public_policy'])) : '' ); - $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : 'contacts' ); + $arr['comment_policy'] = ((!empty($arr['comment_policy'])) ? notags(trim($arr['comment_policy'])) : 'contacts' ); if(! array_key_exists('item_unseen',$arr)) $arr['item_unseen'] = 1; @@ -1791,16 +1813,6 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr if((! array_key_exists('item_nocomment',$arr)) && ($arr['comment_policy'] == 'none')) $arr['item_nocomment'] = 1; - // handle time travelers - // Allow a bit of fudge in case somebody just has a slightly slow/fast clock - - $d1 = new DateTime('now +10 minutes', new DateTimeZone('UTC')); - $d2 = new DateTime($arr['created'] . '+00:00'); - - if($d2 > $d1) { - $arr['item_delayed'] = 1; - } - if(empty($arr['llink'])) { $arr['llink'] = z_root() . '/display/' . $arr['uuid']; } @@ -1842,7 +1854,7 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr ); } - if(comments_are_now_closed($r[0])) { + if(comments_are_now_closed($r[0]) && !in_array($arr['verb'], ['Add', 'Remove'])) { logger('item_store: comments closed'); $ret['message'] = 'Comments closed.'; return $ret; @@ -1937,7 +1949,7 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr */ call_hooks('post_remote', $arr); - if(x($arr, 'cancel')) { + if(!empty($arr['cancel'])) { logger('Post cancelled by plugin.'); $ret['message'] = 'cancelled.'; return $ret; @@ -1980,7 +1992,9 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr // find the item we just created - $r = q("SELECT * FROM item WHERE mid = '%s' AND uid = %d and revision = %d ORDER BY id ASC ", + $r = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid + WHERE item.mid = '%s' AND item.uid = %d and item.revision = %d ORDER BY item.id ASC ", dbesc($arr['mid']), intval($arr['uid']), intval($arr['revision']) @@ -2062,9 +2076,8 @@ function item_store($arr, $allow_exec = false, $deliver = true, $addAndSync = tr item_update_parent_commented($arr); - - if(strpos($arr['body'],'[embed]') !== false) { - Master::Summon([ 'Cache_embeds', $current_post ]); + if (str_contains($arr['body'], '[/embed]') || str_contains($arr['body'], '[/img]') || str_contains($arr['body'], '[/zmg]')) { + Master::Summon(['Cache_embeds', $arr['uuid']]); } $ret['success'] = true; @@ -2154,7 +2167,7 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy if(array_key_exists('edit',$arr)) unset($arr['edit']); - $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); + $arr['mimetype'] = ((!empty($arr['mimetype'])) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); if(($arr['mimetype'] == 'application/x-php') && (! $allow_exec)) { logger('item_store: php mimetype but allow_exec is denied.'); @@ -2225,12 +2238,12 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy unset($arr['thr_parent']); unset($arr['llink']); - $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); - $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : $orig[0]['expires']); + $arr['edited'] = ((!empty($arr['edited'])) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); + $arr['expires'] = ((!empty($arr['expires'])) ? datetime_convert('UTC','UTC',$arr['expires']) : $orig[0]['expires']); - $arr['revision'] = ((x($arr,'revision') && $arr['revision'] > 0) ? intval($arr['revision']) : 0); + $arr['revision'] = ((!empty($arr['revision'])) ? intval($arr['revision']) : 0); - if(array_key_exists('comments_closed',$arr) && $arr['comments_closed'] > NULL_DATE) + if(array_key_exists('comments_closed',$arr)) $arr['comments_closed'] = datetime_convert('UTC','UTC',$arr['comments_closed']); else $arr['comments_closed'] = $orig[0]['comments_closed']; @@ -2242,15 +2255,15 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy $arr['route'] = ((array_key_exists('route',$arr)) ? trim($arr['route']) : $orig[0]['route']); - $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']); - $arr['uuid'] = ((x($arr,'uuid')) ? notags(trim($arr['uuid'])) : $orig[0]['uuid']); - $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']); - $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']); - $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : $orig[0]['obj_type']); - $arr['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : $orig[0]['obj']); - $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : $orig[0]['tgt_type']); - $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : $orig[0]['target']); - $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : $orig[0]['plink']); + $arr['location'] = ((!empty($arr['location'])) ? notags(trim($arr['location'])) : $orig[0]['location']); + $arr['uuid'] = ((!empty($arr['uuid'])) ? notags(trim($arr['uuid'])) : $orig[0]['uuid']); + $arr['coord'] = ((!empty($arr['coord'])) ? notags(trim($arr['coord'])) : $orig[0]['coord']); + $arr['verb'] = ((!empty($arr['verb'])) ? notags(trim($arr['verb'])) : $orig[0]['verb']); + $arr['obj_type'] = ((!empty($arr['obj_type'])) ? notags(trim($arr['obj_type'])) : $orig[0]['obj_type']); + $arr['obj'] = ((!empty($arr['obj'])) ? trim($arr['obj']) : $orig[0]['obj']); + $arr['tgt_type'] = ((!empty($arr['tgt_type'])) ? notags(trim($arr['tgt_type'])) : $orig[0]['tgt_type']); + $arr['target'] = ((!empty($arr['target'])) ? trim($arr['target']) : $orig[0]['target']); + $arr['plink'] = ((!empty($arr['plink'])) ? notags(trim($arr['plink'])) : $orig[0]['plink']); $arr['allow_cid'] = ((array_key_exists('allow_cid',$arr)) ? trim($arr['allow_cid']) : $orig[0]['allow_cid']); $arr['allow_gid'] = ((array_key_exists('allow_gid',$arr)) ? trim($arr['allow_gid']) : $orig[0]['allow_gid']); @@ -2289,11 +2302,11 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy $arr['item_pending_remove'] = ((array_key_exists('item_pending_remove',$arr)) ? intval($arr['item_pending_remove']) : $orig[0]['item_pending_remove'] ); $arr['item_blocked'] = ((array_key_exists('item_blocked',$arr)) ? intval($arr['item_blocked']) : $orig[0]['item_blocked'] ); - $arr['sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); + $arr['sig'] = ((!empty($arr['sig'])) ? $arr['sig'] : ''); $arr['layout_mid'] = ((array_key_exists('layout_mid',$arr)) ? dbesc($arr['layout_mid']) : $orig[0]['layout_mid'] ); - $arr['public_policy'] = ((x($arr,'public_policy')) ? notags(trim($arr['public_policy'])) : $orig[0]['public_policy'] ); - $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : $orig[0]['comment_policy'] ); + $arr['public_policy'] = ((!empty($arr['public_policy'])) ? notags(trim($arr['public_policy'])) : $orig[0]['public_policy'] ); + $arr['comment_policy'] = ((!empty($arr['comment_policy'])) ? notags(trim($arr['comment_policy'])) : $orig[0]['comment_policy'] ); /** * @hooks post_remote_update @@ -2301,7 +2314,7 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy */ call_hooks('post_remote_update', $arr); - if(x($arr, 'cancel')) { + if(!empty($arr['cancel'])) { logger('Post cancelled by plugin.'); $ret['message'] = 'cancelled.'; return $ret; @@ -2353,7 +2366,9 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy // fetch an unescaped complete copy of the stored item - $r = q("select * from item where id = %d", + $r = q("SELECT item.*, tp.uuid AS thr_parent_uuid FROM item + LEFT JOIN item tp ON item.thr_parent = tp.mid AND item.uid = tp.uid + WHERE item.id = %d", intval($orig_post_id) ); if($r) @@ -2367,14 +2382,15 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy if(is_array($terms)) { foreach($terms as $t) { - q("insert into term (uid,oid,otype,ttype,term,url) - values(%d,%d,%d,%d,'%s','%s') ", + q("insert into term (uid, oid, otype, ttype, term, url, imgurl) + values (%d, %d, %d, %d, '%s', '%s', '%s')", intval($uid), intval($orig_post_id), intval(TERM_OBJ_POST), intval($t['ttype']), dbesc($t['term']), - dbesc($t['url']) + dbesc($t['url']), + dbesc($t['imgurl'] ?? ''), ); } $arr['term'] = $terms; @@ -2405,8 +2421,8 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $addAndSy */ call_hooks('item_stored_update',$arr); - if(strpos($arr['body'],'[embed]') !== false) { - Master::Summon([ 'Cache_embeds', $orig_post_id ]); + if (str_contains($arr['body'], '[/embed]') || str_contains($arr['body'], '[/img]') || str_contains($arr['body'], '[/zmg]')) { + Master::Summon(['Cache_embeds', $arr['uuid']]); } $ret['success'] = true; @@ -2469,21 +2485,23 @@ function send_status_notifications($post_id,$item) { $parent = 0; $is_reaction = false; - $thr_parent_id = 0; + $thr_parent_id = null; + $thr_parent_uuid = null; $type = ((intval($item['item_private']) === 2) ? NOTIFY_MAIL : NOTIFY_COMMENT); - if(array_key_exists('verb',$item) && activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + if(array_key_exists('verb',$item) && activity_match($item['verb'], ['Like', 'Dislike', ACTIVITY_LIKE, ACTIVITY_DISLIKE, 'Announce'])) { $type = NOTIFY_LIKE; - $r = q("select id from item where mid = '%s' and uid = %d limit 1", + $r = q("select id, uuid from item where mid = '%s' and uid = %d limit 1", dbesc($item['thr_parent']), intval($item['uid']) ); if ($r) { $thr_parent_id = $r[0]['id']; + $thr_parent_uuid = $r[0]['uuid']; } } @@ -2508,6 +2526,7 @@ function send_status_notifications($post_id,$item) { dbesc($item['parent_mid']), intval($item['uid']) ); + if($x) { foreach($x as $xx) { if($xx['author_xchan'] === $r[0]['channel_hash']) { @@ -2552,7 +2571,7 @@ function send_status_notifications($post_id,$item) { 'link' => $link, 'verb' => $item['verb'], 'otype' => 'item', - 'parent' => $thr_parent_id ? $thr_parent_id : $parent, + 'parent' => $thr_parent_id ?? $parent, 'parent_mid' => $thr_parent_id ? $item['thr_parent'] : $item['parent_mid'] )); } @@ -2829,8 +2848,8 @@ function tag_deliver($uid, $item_id) { 'from_xchan' => $item['author_xchan'], 'type' => NOTIFY_TAGSELF, 'item' => $item, - 'link' => $i[0]['llink'], - 'verb' => ACTIVITY_TAG, + 'link' => $item['llink'], + 'verb' => $item['verb'], 'otype' => 'item' )); @@ -3172,22 +3191,15 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $item['parent_mid'] = $item['mid']; $item['thr_parent'] = $item['mid']; $item['llink'] = z_root() . '/display/' . $item['uuid']; + $item['target'] = json_encode([ + 'id' => str_replace('/item/', '/conversation/', $item['mid']), + 'type' => 'Collection', + 'attributedTo' => z_root() . '/channel/' . $channel['channel_address'] + ]); + $item['tgt_type'] = 'Collection'; } -/* - $r = q("UPDATE item SET author_xchan = '%s', mid = '%s', parent_mid = '%s', thr_parent = '%s', llink = '%s' WHERE id = %d", - dbesc($item['author_xchan']), - dbesc($item['mid']), - dbesc($item['parent_mid']), - dbesc($item['thr_parent']), - dbesc($item['llink']), - intval($item_id) - ); -*/ } - hz_syslog('gothere'); - // sourced - $private = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0); @@ -3231,7 +3243,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false $r = q("update item set item_uplink = %d, item_nocomment = %d, item_flags = %d, owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d, public_policy = '%s', comment_policy = '%s', title = '%s', body = '%s', item_wall = %d, item_origin = %d, - author_xchan = '%s', mid = '%s', parent_mid = '%s', thr_parent = '%s', llink = '%s' where id = %d", + author_xchan = '%s', mid = '%s', parent_mid = '%s', thr_parent = '%s', llink = '%s', target = '%s', tgt_type = '%s' where id = %d", intval($item_uplink), intval($item_nocomment), intval($flag_bits), @@ -3252,6 +3264,8 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false dbesc($item['parent_mid']), dbesc($item['thr_parent']), dbesc($item['llink']), + dbesc($item['target']), + dbesc($item['tgt_type']), intval($item_id) ); @@ -3324,10 +3338,13 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false } else { // To prevent duplicates from possible clones of the forum/group, - // will create a v5 UUID of the source item mid. - $arr['uuid'] = uuid_from_url($item['mid']); + // we will create a v5 UUID of the source item mid. + // Add some extra entropy to prevent duplicate UUIDs with items where we already + // created an UUID from the mid (activities which do not provide an UUID field). + $arr['uuid'] = uuid_from_url($item['mid'] . '#group_item'); $arr['mid'] = z_root() . '/item/' . $arr['uuid']; $arr['parent_mid'] = $arr['mid']; + $arr['plink'] = $arr['mid']; } $arr['aid'] = $channel['channel_account_id']; @@ -3855,7 +3872,7 @@ function item_expire($uid,$days,$comment_days = 7) { if ($r) { foreach ($r as $item) { - drop_item($item['id'], expire: true); + drop_item($item['id'], uid: $uid); } } @@ -3916,7 +3933,7 @@ function drop_item($id, $stage = DROPITEM_NORMAL, $force = false, $uid = 0, $obs $ok_to_delete = true; } - // remote delete when nobody is authenticated (called from Libzot) + // remote delete when nobody is authenticated (called from Libzot and Daemons) if ($uid && intval($uid) === intval($item['uid'])) { $ok_to_delete = true; } @@ -4785,19 +4802,19 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C return $items; } -function webpage_to_namespace($webpage) { +function item_type_to_namespace($item_type) { - if($webpage == ITEM_TYPE_WEBPAGE) + if($item_type == ITEM_TYPE_WEBPAGE) $page_type = 'WEBPAGE'; - elseif($webpage == ITEM_TYPE_BLOCK) + elseif($item_type == ITEM_TYPE_BLOCK) $page_type = 'BUILDBLOCK'; - elseif($webpage == ITEM_TYPE_PDL) + elseif($item_type == ITEM_TYPE_PDL) $page_type = 'PDL'; - elseif($webpage == ITEM_TYPE_CARD) + elseif($item_type == ITEM_TYPE_CARD) $page_type = 'CARD'; - elseif($webpage == ITEM_TYPE_ARTICLE) + elseif($item_type == ITEM_TYPE_ARTICLE) $page_type = 'ARTICLE'; - elseif($webpage == ITEM_TYPE_DOC) + elseif($item_type == ITEM_TYPE_DOC) $page_type = 'docfile'; else $page_type = 'unknown'; @@ -4806,12 +4823,12 @@ function webpage_to_namespace($webpage) { } -function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remote_id,$mid) { +function update_remote_id($channel,$post_id,$item_type,$pagetitle,$namespace,$remote_id,$mid) { if(! intval($post_id)) return; - $page_type = webpage_to_namespace($webpage); + $page_type = item_type_to_namespace($item_type); if($page_type == 'unknown' && $namespace && $remote_id) { $page_type = $namespace; @@ -5287,13 +5304,19 @@ function addToCollectionAndSync($ret) { } xchan_query($items); - $items = fetch_post_tags($items); + // TODO: fetch_post_tags() will add term and iconfig twice if called twice and it looks like they are already added here + // $items = fetch_post_tags($items); $sync_items = []; $sync_items[] = encode_item($items[0], true); if (!in_array($ret['item']['verb'], ['Add', 'Remove'])) { + $activity = Activity::encode_activity($items[0]); - $new_obj = Activity::build_packet(Activity::encode_activity($items[0]), $channel, false); + if (!$activity) { + return $ret; + } + + $new_obj = Activity::build_packet($activity, $channel, false); $approval = Activity::addToCollection($channel, $new_obj, $ret['item']['parent_mid'], $ret['item'], deliver: false); if ($approval['success']) { @@ -5337,3 +5360,416 @@ function set_activity_mid($string) { return str_replace(z_root() . '/item/', z_root() . '/activity/', $string); } +/** + * @brief returns an item by id and parent belonging to local_channel() + * including activity counts. + * @param int $id + * @param int $parent + */ + +function item_by_item_id(int $id, int $parent): array +{ + if (!$id && !$parent && !local_channel()) { + return []; + } + + $item_normal_sql = item_normal(); + + $reaction = item_reaction_sql($parent); + $reaction_cte_sql = $reaction['cte']; + $reaction_select_sql = $reaction['select']; + $reaction_join_sql = $reaction['join']; + + return q("WITH + $reaction_cte_sql + SELECT + *, + $reaction_select_sql + FROM item + $reaction_join_sql + WHERE + item.id = %d + AND item.uid = %d + AND item.verb IN ('Create', 'Update', 'EmojiReact') + $item_normal_sql", + intval($id), + intval(local_channel()) + ); +} + + +/** + * @brief returns an array of items by ids + * ATTENTION: no permissions for the parents are checked here!!! + * Permissions MUST be checked by the module which calls this function. + * @param array $parents + * @param null|array $thr_parents (optional) - thr_parent mids which will be included + * @param string $permission_sql (optional) - SQL as provided by item_permission_sql() from the calling module + * @param bool $blog_mode (optional) - if set to yes only the parent items will be returned + */ + +function items_by_parent_ids(array $parents, null|array $thr_parents = null, string $permission_sql = '', bool $blog_mode = false): array +{ + if (!$parents) { + return []; + } + + $ids = ids_to_querystr($parents, 'item_id'); + $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true)); + $item_normal_sql = item_normal(); + $limit = $thread_allow ? 3 : 1000; + + $thr_parent_sql = (($thread_allow) ? " AND item.thr_parent = item.parent_mid " : ''); + if ($thr_parents && $thread_allow) { + $limit = 300; + $thr_parent_str = stringify_array($thr_parents, true); + $thr_parent_sql = " AND item.thr_parent IN (" . protect_sprintf($thr_parent_str) . ") "; + } + + $reaction = item_reaction_sql($ids, $permission_sql, 'final_selection'); + $reaction_cte_sql = $reaction['cte']; + $reaction_select_sql = $reaction['select']; + $reaction_join_sql = $reaction['join']; + + if ($blog_mode) { + $q = <<<SQL + WITH + final_selection AS ( + SELECT + item.* + FROM + item + WHERE + item.id IN ($ids) + ), + + $reaction_cte_sql + + SELECT + final_selection.*, + $reaction_select_sql + FROM final_selection + $reaction_join_sql + SQL; + + return dbq(trim($q)); + } + + $q = <<<SQL + WITH + parent_items AS ( + SELECT + item.*, + 0 AS rn + FROM item + WHERE + item.id IN ($ids) + ), + + $reaction_cte_sql, + + all_comments AS ( + SELECT + item.*, + ROW_NUMBER() OVER (PARTITION BY item.parent ORDER BY item.created DESC) AS rn + FROM item + WHERE item.parent IN ($ids) + AND item.verb IN ('Create', 'Update', 'EmojiReact') + AND item.item_thread_top = 0 + $thr_parent_sql + $permission_sql + $item_normal_sql + ), + + final_selection AS ( + SELECT * FROM parent_items + UNION ALL + SELECT * FROM all_comments WHERE all_comments.rn <= $limit + ) + + SELECT + final_selection.*, + $reaction_select_sql + FROM final_selection + $reaction_join_sql + SQL; + + return dbq(trim($q)); +} + +/** + * @brief prepare reaction sql for items_by_parent_ids() + * ATTENTION: no permissions for the pa are checked here!!! + * Permissions MUST be checked by the function which returns the ids. + * @param string $ids + * @param string $permission_sql (optional) - SQL provided by item_permission_sql() + * @param string $join_prefix (optional) - prefix for the join part defaults to 'item' + */ + +function item_reaction_sql(string $ids, string $permission_sql = '', string $join_prefix = 'item'): array +{ + $item_normal_sql = item_normal(); + $observer = get_observer_hash(); + + $verbs = [ + 'like' => ['Like'], + 'dislike' => ['Dislike'], + 'announce' => ['Announce'], + 'accept' => ['Accept'], + 'reject' => ['Reject'], + 'tentativeaccept' => ['TentativeAccept'] + ]; + + $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true)); + + if ($thread_allow) { + $verbs['comment'] = ['Create', 'Update', 'EmojiReact']; + } + + $cte = ''; + $select = ''; + $join = ''; + + foreach($verbs as $k => $v) { + + $observer_sql = "0 AS observer_{$k}_count"; + if ($observer) { + $observer_sql = "COUNT(CASE WHEN item.author_xchan = '$observer' THEN 1 END) AS observer_{$k}_count"; + } + + $verbs_str = stringify_array($v); + + if ($cte) { + $cte .= ",\n"; + } + + $cte .= <<<SQL + reaction_{$k} AS ( + SELECT + item.thr_parent, + -- COUNT(DISTINCT item.author_xchan) AS {$k}_count, (should we prevent multiple reactions by the same author?) + COUNT(*) AS {$k}_count, + $observer_sql + FROM item + WHERE item.verb IN ($verbs_str) + AND item.item_thread_top = 0 + AND item.parent IN ($ids) + $item_normal_sql + $permission_sql + GROUP BY item.thr_parent + ) + SQL; + + if ($select) { + $select .= ",\n"; + } + + $select .= <<<SQL + COALESCE(reaction_{$k}.{$k}_count, 0) AS {$k}_count, + COALESCE(reaction_{$k}.observer_{$k}_count, 0) AS observer_{$k}_count + SQL; + + $join .= <<<SQL + LEFT JOIN reaction_{$k} ON reaction_{$k}.thr_parent = $join_prefix.mid + SQL; + + } + + $ret['cte'] = $cte; + $ret['select'] = $select; + $ret['join'] = $join; + + return $ret; +} + + + +/** + * @brief returns an array of items by thr_parent mid of a parent + + * @param string $mid + * @param int $parent + * @param int|null $offset + */ + +function items_by_thr_parent(string $mid, int $parent, int|null $offset = null): array +{ + if (!$mid && !$parent) { + return []; + } + + $parent_item = q("SELECT uid FROM item WHERE id = %d", + intval($parent) + ); + + $order_sql = "ORDER BY item.created"; + if (isset($offset)) { + $order_sql = "ORDER BY item.created DESC, item.received DESC LIMIT 3 OFFSET $offset"; + } + + $owner_uid = intval($parent_item[0]['uid']); + $item_normal_sql = item_normal($owner_uid); + + if (local_channel() === $owner_uid) { + $reaction = item_reaction_sql($parent); + $reaction_cte_sql = $reaction['cte']; + $reaction_select_sql = $reaction['select']; + $reaction_join_sql = $reaction['join']; + + $ret = q("WITH + $reaction_cte_sql + SELECT + item.*, + $reaction_select_sql + FROM item + $reaction_join_sql + WHERE + item.thr_parent = '%s' + AND item.uid = %d + AND item.verb IN ('Create', 'Update', 'EmojiReact') + AND item.item_thread_top = 0 + $item_normal_sql + $order_sql", + dbesc($mid), + intval($owner_uid) + ); + } + else { + $observer_hash = get_observer_hash(); + $permission_sql = item_permissions_sql($owner_uid, $observer_hash); + + $reaction = item_reaction_sql($parent, $permission_sql); + $reaction_cte_sql = $reaction['cte']; + $reaction_select_sql = $reaction['select']; + $reaction_join_sql = $reaction['join']; + + $ret = q("WITH + $reaction_cte_sql + SELECT + item.*, + $reaction_select_sql + FROM item + $reaction_join_sql + WHERE + item.thr_parent = '%s' + AND item.uid = %d + AND item.verb IN ('Create', 'Update', 'EmojiReact') + AND item.item_thread_top = 0 + $permission_sql + $item_normal_sql + $order_sql", + dbesc($mid), + intval($owner_uid) + ); + } + + if (isset($offset)) { + $ret = array_reverse($ret); + } + + return $ret; +} + + +/** + * @brief returns an array of xchan entries (partly) for activities of an item by mid of a parent. + * Also checks if observer is allowed to add activities to the item. + * @param string $mid + * @param int $parent + * @param string $verb + */ + +function item_activity_xchans(string $mid, int $parent, string $verb): array +{ + if (!$mid && !$parent && !$verb) { + return []; + } + + $observer_hash = get_observer_hash(); + $parent_item = q("SELECT * FROM item WHERE id = %d", + intval($parent) + ); + + $owner_uid = intval($parent_item[0]['uid']); + $item_normal = item_normal($owner_uid); + + if (local_channel() === $owner_uid) { + $ret = q("SELECT item.id, item.item_blocked, xchan.xchan_hash, xchan.xchan_name as name, xchan.xchan_url as url, xchan.xchan_photo_s as photo FROM item + LEFT JOIN xchan ON item.author_xchan = xchan.xchan_hash + WHERE item.uid = %d + AND item.parent = %d + AND item.thr_parent = '%s' + AND item.verb = '%s' + AND item.item_thread_top = 0 + $item_normal + -- GROUP BY item.author_xchan (should we prevent multiple reactions by the same author?) + ORDER BY item.created", + intval(local_channel()), + intval($parent), + dbesc($mid), + dbesc($verb) + ); + } + else { + $sql_extra = item_permissions_sql($owner_uid, $observer_hash); + + $ret = q("SELECT item.id, item.item_blocked, xchan.xchan_hash, xchan.xchan_name as name, xchan.xchan_url as url, xchan.xchan_photo_s as photo FROM item + LEFT JOIN xchan ON item.author_xchan = xchan.xchan_hash + WHERE item.uid = %d + AND item.thr_parent = '%s' + AND item.verb = '%s' + AND item.item_thread_top = 0 + $sql_extra + $item_normal + -- GROUP BY item.author_xchan (should we prevent multiple reactions by the same author?) + ORDER BY item.created", + intval($owner_uid), + dbesc($mid), + dbesc($verb) + ); + } + + $ret['is_commentable'] = can_comment_on_post($observer_hash, $parent_item[0]); + + return $ret; +} + + +/** + * @brief find and return thr_parents we need to show when displaying a nested comment. + * TODO: can this be improved or maybe implemented differently in the UI? + * @param array $item + */ + +function get_recursive_thr_parents(array $item): array|null +{ + if ($item['id'] === $item['parent']) { + // This is a toplevel post, return null. + return null; + } + + $thr_parents[] = $item['thr_parent']; + + $mid = $item['thr_parent']; + $parent_mid = $item['parent_mid']; + $uid = $item['uid']; + $i = 0; + + while ($mid !== $item['parent_mid'] && $i < 100) { + $x = q("SELECT thr_parent, mid FROM item WHERE uid = %d AND mid = '%s'", + intval($uid), + dbesc($mid) + ); + + if (!$x) { + break; + } + + $mid = $x[0]['thr_parent']; + $thr_parents[] = $x[0]['thr_parent']; + + $i++; + } + + return $thr_parents; +} |