From 0481901b593f861366f9a766e0f020cce7a31be7 Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Fri, 8 Nov 2024 20:18:20 +0100 Subject: refactor drop_item[s]() for containers --- Zotlabs/Daemon/Channel_purge.php | 2 +- Zotlabs/Daemon/Cron.php | 3 +- Zotlabs/Daemon/Expire.php | 2 +- Zotlabs/Daemon/Importdoc.php | 18 +++- Zotlabs/Lib/Activity.php | 4 +- Zotlabs/Lib/Libzot.php | 19 ++-- Zotlabs/Module/Channel_calendar.php | 6 +- Zotlabs/Module/Item.php | 7 +- Zotlabs/Module/Like.php | 9 +- Zotlabs/Module/Moderate.php | 2 +- include/attach.php | 9 +- include/connections.php | 4 +- include/feedutils.php | 6 +- include/items.php | 169 +++++++++++++++++++++++++----------- 14 files changed, 175 insertions(+), 85 deletions(-) diff --git a/Zotlabs/Daemon/Channel_purge.php b/Zotlabs/Daemon/Channel_purge.php index 9fceb0fb9..d25edf8ba 100644 --- a/Zotlabs/Daemon/Channel_purge.php +++ b/Zotlabs/Daemon/Channel_purge.php @@ -24,7 +24,7 @@ class Channel_purge { ); if ($r) { foreach ($r as $rv) { - drop_item($rv['id'], false); + drop_item($rv['id']); } } } while ($r); diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 3f5ce28eb..186f3efcf 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -76,7 +76,8 @@ class Cron { if ($r) { require_once('include/items.php'); foreach ($r as $rr) { - drop_item($rr['id'], false, (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + drop_item($rr['id'], (($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL), uid: intval($rr['uid'])); + if ($rr['item_wall']) { // The notifier isn't normally invoked unless item_drop is interactive. Master::Summon(['Notifier', 'drop', $rr['id']]); diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php index ad52a6b71..3ac3dee3a 100644 --- a/Zotlabs/Daemon/Expire.php +++ b/Zotlabs/Daemon/Expire.php @@ -29,7 +29,7 @@ class Expire { ); if ($r) { foreach ($r as $rr) { - drop_item($rr['id'], false, DROPITEM_PHASE2); + drop_item($rr['id'], DROPITEM_PHASE2); } } diff --git a/Zotlabs/Daemon/Importdoc.php b/Zotlabs/Daemon/Importdoc.php index c5a81e50c..8f04e05f8 100644 --- a/Zotlabs/Daemon/Importdoc.php +++ b/Zotlabs/Daemon/Importdoc.php @@ -18,9 +18,13 @@ class Importdoc { static public function update_docs_dir($s) { $f = basename($s); $d = dirname($s); - if ($s === 'doc/html') + + if ($s === 'doc/html') { return; + } + $files = glob("$d/$f"); + if ($files) { foreach ($files as $fi) { if ($fi === 'doc/html') { @@ -37,6 +41,18 @@ class Importdoc { } } } + + // remove old files that weren't updated (indicates they were most likely deleted). + $i = q("select * from item where item_type = 5 and edited < %s - %s", + db_utcnow(), + db_quoteinterval('14 DAY', true) + ); + + if ($i) { + foreach ($i as $iv) { + drop_item($iv['id'], DROPITEM_NORMAL, true); + } + } } } diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index b7f40f632..9ba145bfa 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -1721,9 +1721,9 @@ class Activity { } if (in_array($observer, [$r[0]['author_xchan'], $r[0]['owner_xchan']])) { - drop_item($r[0]['id'], false, (($r[0]['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + drop_item($r[0]['id'], (($r[0]['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL), observer_hash: $observer); } elseif (in_array($act->actor['id'], [$r[0]['author_xchan'], $r[0]['owner_xchan']])) { - drop_item($r[0]['id'], false, (($r[0]['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); + drop_item($r[0]['id'], (($r[0]['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL)); } sync_an_item($channel['channel_id'], $r[0]['id']); diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index a9b24974d..7eabc074f 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -2423,21 +2423,20 @@ class Libzot { ); } } else { - if ($stored['id'] !== $stored['parent']) { - q( - "update item set commented = '%s', changed = '%s' where id = %d", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($stored['parent']) - ); - } - } + if ($stored['id'] !== $stored['parent']) { + q("update item set commented = '%s', changed = '%s' where id = %d", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($stored['parent']) + ); + } + } // Use phased deletion to set the deleted flag, call both tag_deliver and the notifier to notify downstream channels // and then clean up after ourselves with a cron job after several days to do the delete_item_lowlevel() (DROPITEM_PHASE2). - drop_item($post_id, false, DROPITEM_PHASE1); + drop_item($post_id, DROPITEM_PHASE1, uid: $uid); tag_deliver($uid, $post_id); } diff --git a/Zotlabs/Module/Channel_calendar.php b/Zotlabs/Module/Channel_calendar.php index d41c464a6..30683404b 100644 --- a/Zotlabs/Module/Channel_calendar.php +++ b/Zotlabs/Module/Channel_calendar.php @@ -458,13 +458,14 @@ class Channel_calendar extends Controller { } else { // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'], true, DROPITEM_PHASE1); + drop_item($i[0]['id'], DROPITEM_PHASE1); $complex = true; } $ii = q("select * from item where id = %d", intval($i[0]['id']) ); + if ($ii) { xchan_query($ii); $sync_item = fetch_post_tags($ii); @@ -473,6 +474,9 @@ class Channel_calendar extends Controller { if ($complex) { tag_deliver($i[0]['uid'], $i[0]['id']); + if (intval($i[0]['item_wall'])) { + Master::Summon(['Notifier', 'drop', $i[0]['id']]); + } } } } diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index e67566db9..cb9925c01 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -1494,7 +1494,6 @@ class Item extends Controller { $can_delete = true; } - if (!($can_delete || $local_delete)) { notice(t('Permission denied.') . EOL); return; @@ -1510,13 +1509,14 @@ class Item extends Controller { } else { // complex deletion that needs to propagate and be performed in phases - drop_item($i[0]['id'], true, DROPITEM_PHASE1); + drop_item($i[0]['id'], DROPITEM_PHASE1); $complex = true; } $r = q("select * from item where id = %d", intval($i[0]['id']) ); + if ($r) { xchan_query($r); $sync_item = fetch_post_tags($r); @@ -1525,6 +1525,9 @@ class Item extends Controller { if ($complex) { tag_deliver($i[0]['uid'], $i[0]['id']); + if (intval($i[0]['item_wall']) || $i[0]['mid'] !== $i[0]['parent_mid']) { + Master::Summon(['Notifier', 'drop', $dropped_item['id']]); + } } } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index eb92645ba..2fb3fab83 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -284,7 +284,7 @@ class Like extends Controller { intval($ch[0]['channel_id']) ); if ($r) - drop_item($r[0]['id'], false); + drop_item($r[0]['id']); if ($interactive) { notice(t('Previous action reversed.') . EOL); return $o; @@ -387,17 +387,20 @@ class Like extends Controller { // already liked it. Drop that item. require_once('include/items.php'); foreach ($r as $rr) { - drop_item($rr['id'], false, DROPITEM_PHASE1); + drop_item($rr['id'], DROPITEM_PHASE1); + // set the changed timestamp on the parent so we'll see the update without a page reload q("update item set changed = '%s' where id = %d and uid = %d", dbesc(datetime_convert()), intval($rr['parent']), intval($rr['uid']) ); + // Prior activity was a duplicate of the one we're submitting, just undo it; // don't fall through and create another - if (activity_match($rr['verb'], $activity)) + if (activity_match($rr['verb'], $activity)) { $multi_undo = false; + } $d = q("select * from item where id = %d", intval($rr['id']) diff --git a/Zotlabs/Module/Moderate.php b/Zotlabs/Module/Moderate.php index ed2a1e4f9..2103684ab 100644 --- a/Zotlabs/Module/Moderate.php +++ b/Zotlabs/Module/Moderate.php @@ -74,7 +74,7 @@ class Moderate extends \Zotlabs\Web\Controller { // let the sender know we received their comment but we don't permit spam here. // Activity::send_rejection_activity(App::get_channel(), $item['author_xchan'], $item); - drop_item($post_id,false); + drop_item($post_id); notice( t('Item deleted') . EOL); } diff --git a/include/attach.php b/include/attach.php index 4abcb3787..c8fde0cb7 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1606,8 +1606,7 @@ function attach_drop_photo($channel_id,$resource) { if($x) { $stage = (($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1); - $interactive = (($x[0]['item_hidden']) ? false : true); - drop_item($x[0]['id'], $interactive, $stage); + drop_item($x[0]['id'], $stage); } $r = q("SELECT content FROM photo WHERE resource_id = '%s' AND uid = %d AND os_storage = 1", @@ -1636,8 +1635,7 @@ function attach_drop_item($channel_id,$resource) { if($x) { $stage = (($x[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1); - $interactive = (($x[0]['item_hidden']) ? false : true); - drop_item($x[0]['id'], $interactive, $stage); + drop_item($x[0]['id'], $stage); } } @@ -1918,8 +1916,7 @@ function attach_store_item($channel, $observer, $file) { } $stage = (($r[0]['item_hidden']) ? DROPITEM_NORMAL : DROPITEM_PHASE1); - $interactive = (($r[0]['item_hidden']) ? false : true); - drop_item($r[0]['id'], $interactive, $stage); + drop_item($r[0]['id'], $stage); } diff --git a/include/connections.php b/include/connections.php index efc531171..7500647b4 100644 --- a/include/connections.php +++ b/include/connections.php @@ -342,7 +342,7 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) { if($r) { foreach($r as $rr) { - drop_item($rr['id'],false); + drop_item($rr['id']); } } @@ -513,7 +513,7 @@ function remove_abook_items($channel_id, $xchan_hash) { continue; } - drop_item($rr['id'],false); + drop_item($rr['id']); } } diff --git a/include/feedutils.php b/include/feedutils.php index a50214746..cc57d106c 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -998,7 +998,7 @@ function process_feed_tombstones($feed,$importer,$contact,$pass) { if(! intval($item['item_deleted'])) { logger('deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG); - drop_item($item['id'],false); + drop_item($item['id']); } } } @@ -1181,7 +1181,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); - drop_item($r[0]['id'],false); + drop_item($r[0]['id']); } continue; } @@ -1449,7 +1449,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { && isset($datarray['author_xchan']) && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); - drop_item($r[0]['id'],false); + drop_item($r[0]['id']); } continue; } diff --git a/include/items.php b/include/items.php index 2aed53b3c..8ca4b5ca0 100644 --- a/include/items.php +++ b/include/items.php @@ -3206,8 +3206,8 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false if ($r) { if (intval($item['item_deleted'])) { - drop_item($r[0]['id'], false, DROPITEM_PHASE1); - Master::Summon([ 'Notifier', 'drop', $r[0]['id'] ]); + drop_item($r[0]['id'], DROPITEM_PHASE1, uid: $r[0]['uid']); + Master::Summon(['Notifier', 'drop' ,$r[0]['id']]); return; } $arr['id'] = intval($r[0]['id']); @@ -3345,8 +3345,8 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $group = false if ($edit) { if (intval($item['item_deleted'])) { - drop_item($item['id'],false,DROPITEM_PHASE1); - Master::Summon([ 'Notifier','drop',$item['id'] ]); + drop_item($item['id'], DROPITEM_PHASE1, uid: $item['uid']); + Master::Summon(['Notifier', 'drop', $item['id']]); return; } return; @@ -3828,7 +3828,7 @@ function item_expire($uid,$days,$comment_days = 7) { if ($r) { foreach ($r as $item) { - drop_item($item['id'], false); + drop_item($item['id'], expire: true); } } @@ -3841,25 +3841,12 @@ function retain_item($id) { ); } -function drop_items($items,$interactive = false,$stage = DROPITEM_NORMAL) { - $uid = 0; - - if(! local_channel() && ! remote_channel()) - return; - - if(count($items)) { +function drop_items($items, $stage = DROPITEM_NORMAL, $force = false, $expire = false) { + if ($items) { foreach($items as $item) { - $owner = drop_item($item,$interactive,$stage); - if($owner && ! $uid) - $uid = $owner; + drop_item($item, $stage, $force, expire: $expire); } } - - // multiple threads may have been deleted, send an expire notification - - if($uid) { - Master::Summon([ 'Notifier','expire',$uid ]); - } } @@ -3872,7 +3859,7 @@ function drop_items($items,$interactive = false,$stage = DROPITEM_NORMAL) { // $stage = 1 => set deleted flag on the item and perform intial notifications // $stage = 2 => perform low level delete at a later stage -function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) { +function drop_item($id, $stage = DROPITEM_NORMAL, $force = false, $uid = 0, $observer_hash = '', $expire = false, $recurse = false) { // locate item to be deleted @@ -3880,33 +3867,48 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) { intval($id) ); - if((! $r) || (intval($r[0]['item_deleted']) && ($stage === DROPITEM_NORMAL))) { - if(! $interactive) - return 0; - notice( t('Item not found.') . EOL); - //goaway(z_root() . '/' . $_SESSION['return_url']); + if(!$r || (intval($r[0]['item_deleted']) && $stage === DROPITEM_NORMAL)) { + return false; } $item = $r[0]; - $ok_to_delete = false; + if(!$recurse) { + drop_related($item, $stage, $force, $uid, $observer_hash, $expire); + } - // system deletion - if(! $interactive) - $ok_to_delete = true; + $ok_to_delete = false; // admin deletion - if(is_site_admin()) + if(is_site_admin()) { $ok_to_delete = true; + } // owner deletion - if(local_channel() && local_channel() == $item['uid']) + if(local_channel() && local_channel() == $item['uid']) { $ok_to_delete = true; + } + + // remote delete when nobody is authenticated (called from Libzot) + if ($uid && intval($uid) === intval($item['uid'])) { + $ok_to_delete = true; + } // author deletion - $observer = App::get_observer(); - if($observer && $observer['xchan_hash'] && ($observer['xchan_hash'] === $item['author_xchan'])) + if ($observer_hash) { + $observer = ['xchan_hash' => $observer_hash]; + } + else { + $observer = App::get_observer(); + } + + if (isset($observer['xchan_hash']) && $observer['xchan_hash'] === $item['author_xchan']) { $ok_to_delete = true; + } + + if (isset($observer['xchan_hash']) && $observer['xchan_hash'] === $item['owner_xchan']) { + $ok_to_delete = true; + } if($ok_to_delete) { @@ -3919,9 +3921,9 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) { $arr = [ 'item' => $item, - 'interactive' => $interactive, 'stage' => $stage ]; + /** * @hooks drop_item * Called when an 'item' is removed. @@ -3944,30 +3946,95 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL) { delete_item_lowlevel($item, $stage); } - if(! $interactive) - return 1; + return true; + } + else { + return false; + } +} + - // send the notification upstream/downstream as the case may be - // only send notifications to others if this is the owner's wall item. +// If somebody deletes a 'Create' activity, find any associated 'Add/Collection' +// activity and delete it. And vice versa. + +function drop_related($item, $stage = DROPITEM_NORMAL, $force = false, $uid = 0, $observer_hash = '', $expire = false, $recurse = false) { + $allRelated = q("select * from item where parent_mid = '%s' and uid = %d", + dbesc($item['parent_mid']), + intval($item['uid']) + ); + if (! $allRelated) { + return; + } + if ($item['verb'] === 'Add' && $item['tgt_type'] === 'Collection') { + if (is_array($item['obj'])) { + $thisItem = $item['obj']; + } + else { + $thisItem = json_decode($item['obj'], true); + } + if (isset($thisItem['object']['id'])) { + $targetMid = $thisItem['object']['id']; + } + if (!$targetMid) { + return; + } + foreach ($allRelated as $related) { + if ($related['mid'] === $targetMid) { + drop_item($related['id'], $stage, $force, $uid, $observer_hash, $expire, recurse: true); + break; + } + } + } + else { + foreach ($allRelated as $related) { + if ($related['verb'] === 'Add' && str_contains($related['tgt_type'], 'Collection')) { + $thisItem = json_decode($related['obj'], true); + if (isset($thisItem['id']) && $thisItem['id'] === str_replace('/item/', '/activity/', $item['mid'])) { + drop_item($related['id'], $stage, $force, $uid, $observer_hash, $expire, recurse: true); + break; + } + } + } + } +} - // This isn't optimal. We somehow need to pass to this function whether or not - // to call the notifier, or we need to call the notifier from the calling function. - // We'll rely on the undocumented behaviour that DROPITEM_PHASE1 is (hopefully) only - // set if we know we're going to send delete notifications out to others. - if((intval($item['item_wall']) && ($stage != DROPITEM_PHASE2)) || ($stage == DROPITEM_PHASE1)) { - Master::Summon([ 'Notifier','drop',$notify_id ]); +function find_related($item) { + $allRelated = q("select * from item where parent_mid = '%s' and uid = %d", + dbesc($item['parent_mid']), + intval($item['uid']) + ); + if (! $allRelated) { + return false; + } + if ($item['verb'] === 'Add' && $item['tgt_type'] === 'Collection') { + $thisItem = json_decode($item['obj'],true); + if (is_array($thisItem)) { + $targetMid = $thisItem['object']['id']; + } + if (!$targetMid) { + return false; + } + foreach ($allRelated as $related) { + if ($related['mid'] === $targetMid) { + return $related; + } } - //goaway(z_root() . '/' . $_SESSION['return_url']); } else { - if(! $interactive) - return 0; - notice( t('Permission denied.') . EOL); - //goaway(z_root() . '/' . $_SESSION['return_url']); + foreach ($allRelated as $related) { + if ($related['verb'] === 'Add' && str_contains($related['tgt_type'], 'Collection')) { + $thisItem = json_decode($related['obj'], true); + if (isset($thisItem['object']['id']) && $thisItem['object']['id'] === $item['mid']) { + return $related; + } + } + } } + return false; } + /** * @warning This function does not check for permission and does not send * notifications and does not check recursion. -- cgit v1.2.3