diff --git a/include/api.php b/include/api.php index 21db40433..d4007564f 100644 --- a/include/api.php +++ b/include/api.php @@ -19,6 +19,7 @@ use Friendica\Model\Group; use Friendica\Model\Mail; use Friendica\Model\Photo; use Friendica\Model\User; +use Friendica\Model\Item; use Friendica\Network\FKOAuth1; use Friendica\Network\HTTPException; use Friendica\Network\HTTPException\BadRequestException; @@ -2214,7 +2215,7 @@ function api_statuses_destroy($type) $ret = api_statuses_show($type); - drop_item($id, false); + Item::delete($id); return $ret; } @@ -3987,7 +3988,7 @@ function api_fr_photoalbum_delete($type) if (!DBM::is_result($photo_item)) { throw new InternalServerErrorException("problem with deleting items occured"); } - drop_item($photo_item[0]['id'], false); + Item::delete($photo_item[0]['id']); } // now let's delete all photos from the album @@ -4290,7 +4291,7 @@ function api_fr_photo_delete($type) } // function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore // to the user and the contacts of the users (drop_items() do all the necessary magic to avoid orphans in database and federate deletion) - drop_item($photo_item[0]['id'], false); + Item::delete($photo_item[0]['id']); $answer = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.']; return api_format_data("photo_delete", $type, ['$result' => $answer]); diff --git a/include/items.php b/include/items.php index cbd3883db..6f93f78b3 100644 --- a/include/items.php +++ b/include/items.php @@ -1980,7 +1980,7 @@ function item_getfeedtags($item) { function item_expire($uid, $days, $network = "", $force = false) { - if ((! $uid) || ($days < 1)) { + if (!$uid || ($days < 1)) { return; } @@ -1989,7 +1989,7 @@ function item_expire($uid, $days, $network = "", $force = false) { * and just expire conversations started by others */ $expire_network_only = PConfig::get($uid,'expire', 'network_only'); - $sql_extra = ((intval($expire_network_only)) ? " AND wall = 0 " : ""); + $sql_extra = (intval($expire_network_only) ? " AND wall = 0 " : ""); if ($network != "") { $sql_extra .= sprintf(" AND network = '%s' ", dbesc($network)); @@ -2013,7 +2013,7 @@ function item_expire($uid, $days, $network = "", $force = false) { intval($days) ); - if (! DBM::is_result($r)) { + if (!DBM::is_result($r)) { return; } @@ -2050,37 +2050,28 @@ function item_expire($uid, $days, $network = "", $force = false) { continue; } - drop_item($item['id'], false); + Item::delete($item['id'], PRIORITY_LOW); } - - Worker::add(['priority' => PRIORITY_LOW, 'dont_fork' => true], "Notifier", "expire", $uid); } /// @TODO type-hint is array function drop_items($items) { $uid = 0; - if (! local_user() && ! remote_user()) { + if (!local_user() && !remote_user()) { return; } if (count($items)) { foreach ($items as $item) { - $owner = drop_item($item,false); + $owner = Item::delete($item); if ($owner && ! $uid) $uid = $owner; } } - - // multiple threads may have been deleted, send an expire notification - - if ($uid) { - Worker::add(['priority' => PRIORITY_LOW, 'dont_fork' => true], "Notifier", "expire", $uid); - } } - -function drop_item($id, $interactive = true) { +function drop_item($id) { $a = get_app(); @@ -2090,11 +2081,8 @@ function drop_item($id, $interactive = true) { intval($id) ); - if (! DBM::is_result($r)) { - if (! $interactive) { - return 0; - } - notice( t('Item not found.') . EOL); + if (!DBM::is_result($r)) { + notice(t('Item not found.') . EOL); goaway(System::baseUrl() . '/' . $_SESSION['return_url']); } @@ -2104,8 +2092,6 @@ function drop_item($id, $interactive = true) { return 0; } - $owner = $item['uid']; - $contact_id = 0; // check if logged in user is either the author or owner of this item @@ -2119,8 +2105,7 @@ function drop_item($id, $interactive = true) { } } - - if ((local_user() == $item['uid']) || $contact_id || !$interactive) { + if ((local_user() == $item['uid']) || $contact_id) { // Check if we should do HTML-based delete confirmation if ($_REQUEST['confirm']) { @@ -2150,126 +2135,16 @@ function drop_item($id, $interactive = true) { goaway(System::baseUrl() . '/' . $_SESSION['return_url']); } - logger('delete item: ' . $item['id'], LOGGER_DEBUG); - // delete the item - dba::update('item', ['deleted' => true, 'title' => '', 'body' => '', - 'edited' => datetime_convert(), 'changed' => datetime_convert()], - ['id' => $item['id']]); + Item::delete($item['id']); - create_tags_from_item($item['id']); - Term::createFromItem($item['id']); - delete_thread($item['id'], $item['parent-uri']); - - // clean up categories and tags so they don't end up as orphans - - $matches = false; - $cnt = preg_match_all('/<(.*?)>/', $item['file'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],true); - } - } - - $matches = false; - - $cnt = preg_match_all('/\[(.*?)\]/', $item['file'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],false); - } - } - - /* - * If item is a link to a photo resource, nuke all the associated photos - * (visitors will not have photo resources) - * This only applies to photos uploaded from the photos page. Photos inserted into a post do not - * generate a resource-id and therefore aren't intimately linked to the item. - */ - if (strlen($item['resource-id'])) { - dba::delete('photo', ['resource-id' => $item['resource-id'], 'uid' => $item['uid']]); - } - - // If item is a link to an event, nuke the event record. - if (intval($item['event-id'])) { - dba::delete('event', ['id' => $item['event-id'], 'uid' => $item['uid']]); - } - - // If item has attachments, drop them - foreach (explode(", ", $item['attach']) as $attach) { - preg_match("|attach/(\d+)|", $attach, $matches); - dba::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]); - } - - // The new code splits the queries since the mysql optimizer really has bad problems with subqueries - - // Creating list of parents - $r = q("SELECT `id` FROM `item` WHERE `parent` = %d AND `uid` = %d", - intval($item['id']), - intval($item['uid']) - ); - - $parentid = ""; - - foreach ($r as $row) { - if ($parentid != "") { - $parentid .= ", "; - } - - $parentid .= $row["id"]; - } - - // Now delete them - if ($parentid != "") { - q("DELETE FROM `sign` WHERE `iid` IN (%s)", dbesc($parentid)); - } - - // If it's the parent of a comment thread, kill all the kids - if ($item['uri'] == $item['parent-uri']) { - dba::update('item', ['deleted' => true, 'title' => '', 'body' => '', - 'edited' => datetime_convert(), 'changed' => datetime_convert()], - ['parent-uri' => $item['parent-uri'], 'uid' => $item['uid']]); - - create_tags_from_itemuri($item['parent-uri'], $item['uid']); - Term::createFromItemURI($item['parent-uri'], $item['uid']); - delete_thread_uri($item['parent-uri'], $item['uid']); - // ignore the result - } else { - // ensure that last-child is set in case the comment that had it just got wiped. - dba::update('item', ['last-child' => false, 'changed' => datetime_convert()], - ['parent-uri' => $item['parent-uri'], 'uid' => $item['uid']]); - - // who is the last child now? - $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d ORDER BY `edited` DESC LIMIT 1", - dbesc($item['parent-uri']), - intval($item['uid']) - ); - if (DBM::is_result($r)) { - dba::update('item', ['last-child' => true], ['id' => $r[0]['id']]); - } - } - - // send the notification upstream/downstream - // The priority depends on how the deletion is done. - $drop_id = intval($item['id']); - $priority = ($interactive ? PRIORITY_HIGH : PRIORITY_LOW); - - Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", $drop_id); - - if (! $interactive) { - return $owner; - } goaway(System::baseUrl() . '/' . $_SESSION['return_url']); //NOTREACHED } else { - if (! $interactive) { - return 0; - } - notice( t('Permission denied.') . EOL); + notice(t('Permission denied.') . EOL); goaway(System::baseUrl() . '/' . $_SESSION['return_url']); //NOTREACHED } - } /// @todo: This query seems to be really slow diff --git a/mod/admin.php b/mod/admin.php index fc3fb5819..eabbe36d3 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -15,6 +15,7 @@ use Friendica\Database\DBM; use Friendica\Database\DBStructure; use Friendica\Model\Contact; use Friendica\Model\User; +use Friendica\Model\Item; use Friendica\Module\Login; require_once 'include/enotify.php'; @@ -492,7 +493,7 @@ function admin_page_deleteitem_post(App $a) // associated threads. $r = dba::select('item', ['id'], ['guid' => $guid]); while ($row = dba::fetch($r)) { - drop_item($row['id'], false); + Item::delete($row['id']); } dba::close($r); } diff --git a/mod/events.php b/mod/events.php index 1c8c92a31..a1ffcfc1c 100644 --- a/mod/events.php +++ b/mod/events.php @@ -10,6 +10,7 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBM; use Friendica\Model\Profile; +use Friendica\Model\Item; require_once 'include/bbcode.php'; require_once 'include/datetime.php'; @@ -547,7 +548,7 @@ function events_content(App $a) { // Delete only real events (no birthdays) if (DBM::is_result($ev) && $ev[0]['type'] == 'event') { - $del = drop_item($ev[0]['itemid'], false); + $del = Item::delete($ev[0]['itemid']); } if ($del == 0) { diff --git a/mod/item.php b/mod/item.php index 0ff5eb4fe..19d56638c 100644 --- a/mod/item.php +++ b/mod/item.php @@ -972,7 +972,11 @@ function item_content(App $a) { $o = ''; if (($a->argc == 3) && ($a->argv[1] === 'drop') && intval($a->argv[2])) { - $o = drop_item($a->argv[2], !is_ajax()); + if (is_ajax()) { + $o = Item::delete($a->argv[2]); + } else { + $o = drop_item($a->argv[2]); + } if (is_ajax()) { // ajax return: [, 0 (no perm) | ] echo json_encode([intval($a->argv[2]), intval($o)]); diff --git a/src/Model/Item.php b/src/Model/Item.php index 0c8b8b549..765ab1780 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -64,6 +64,102 @@ class Item return $rows; } + /** + * @brief Delete an item and notify others about it - if it was ours + * + * @param integer $item_id Item ID that should be delete + * + * @return $boolean success + */ + public static function delete($item_id, $priority = PRIORITY_HIGH) + { + // locate item to be deleted + $fields = ['id', 'uid', 'parent', 'parent-uri', 'origin', 'deleted', 'file', 'resource-id', 'event-id', 'attach']; + $item = dba::selectFirst('item', $fields, ['id' => $item_id]); + if (!DBM::is_result($item)) { + return false; + } + + if ($item['deleted']) { + return false; + } + + $parent = dba::selectFirst('item', ['origin'], ['id' => $item['parent']]); + if (!DBM::is_result($parent)) { + $parent = ['origin' => false]; + } + + logger('delete item: ' . $item['id'], LOGGER_DEBUG); + + // clean up categories and tags so they don't end up as orphans + + $matches = false; + $cnt = preg_match_all('/<(.*?)>/', $item['file'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],true); + } + } + + $matches = false; + + $cnt = preg_match_all('/\[(.*?)\]/', $item['file'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],false); + } + } + + /* + * If item is a link to a photo resource, nuke all the associated photos + * (visitors will not have photo resources) + * This only applies to photos uploaded from the photos page. Photos inserted into a post do not + * generate a resource-id and therefore aren't intimately linked to the item. + */ + if (strlen($item['resource-id'])) { + dba::delete('photo', ['resource-id' => $item['resource-id'], 'uid' => $item['uid']]); + } + + // If item is a link to an event, nuke the event record. + if (intval($item['event-id'])) { + dba::delete('event', ['id' => $item['event-id'], 'uid' => $item['uid']]); + } + + // If item has attachments, drop them + foreach (explode(", ", $item['attach']) as $attach) { + preg_match("|attach/(\d+)|", $attach, $matches); + dba::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]); + } + + // When it is our item we don't delete it here, since we have to send delete messages + if ($item['origin'] || $parent['origin']) { + // Set the item to "deleted" + dba::update('item', ['deleted' => true, 'title' => '', 'body' => '', + 'edited' => datetime_convert(), 'changed' => datetime_convert()], + ['id' => $item['id']]); + + create_tags_from_item($item['id']); + Term::createFromItem($item['id']); + delete_thread($item['id'], $item['parent-uri']); + + // If it's the parent of a comment thread, kill all the kids + if ($item['id'] == $item['parent']) { + $items = dba::select('item', ['id'], ['parent' => $item['parent']]); + while ($row = dba::fetch($items)) { + self::delete($row['id'], $priority); + } + } + + // send the notification upstream/downstream + Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id'])); + } else { + // delete it immediately. All related children will be deleted as well. + dba::delete('item', ['id' => $item['id']]); + } + + return true; + } + /** * @brief Add a shadow entry for a given item id that is a thread starter * diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 324ca921f..bd7c5b817 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -29,7 +29,6 @@ class Delivery { logger('delivery: invoked: '.$cmd.': '.$item_id.' to '.$contact_id, LOGGER_DEBUG); - $expire = false; $mail = false; $fsuggest = false; $relocate = false; @@ -54,18 +53,6 @@ class Delivery { $uid = $message[0]['uid']; $recipients[] = $message[0]['contact-id']; $item = $message[0]; - } elseif ($cmd === 'expire') { - $normal_mode = false; - $expire = true; - $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 - AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 30 MINUTE", - intval($item_id) - ); - $uid = $item_id; - $item_id = 0; - if (!count($items)) { - return; - } } elseif ($cmd === 'suggest') { $normal_mode = false; $fsuggest = true; @@ -160,16 +147,6 @@ class Delivery { // if $parent['wall'] == 1 we will already have the parent message in our array // and we will relay the whole lot. - // expire sends an entire group of expire messages and cannot be forwarded. - // However the conversation owner will be a part of the conversation and will - // be notified during this run. - // Other DFRN conversation members will be alerted during polled updates. - - // Diaspora members currently are not notified of expirations, and other networks have - // either limited or no ability to process deletions. We should at least fix Diaspora - // by stringing togther an array of retractions and sending them onward. - - $localhost = $a->get_hostname(); if (strpos($localhost,':')) { $localhost = substr($localhost,0,strpos($localhost,':')); @@ -184,7 +161,7 @@ class Delivery { $relay_to_owner = false; - if (!$top_level && ($parent['wall'] == 0) && !$expire && stristr($target_item['uri'],$localhost)) { + if (!$top_level && ($parent['wall'] == 0) && stristr($target_item['uri'], $localhost)) { $relay_to_owner = true; } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 4b20da314..d69627d11 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -41,7 +41,6 @@ require_once 'include/bbcode.php'; * drop (in diaspora.php, items.php, photos.php) * edit_post (in item.php) * event (in events.php) - * expire (in items.php) * like (in like.php, poke.php) * mail (in message.php) * suggest (in fsuggest.php) @@ -60,7 +59,6 @@ class Notifier { logger('notifier: invoked: '.$cmd.': '.$item_id, LOGGER_DEBUG); - $expire = false; $mail = false; $fsuggest = false; $relocate = false; @@ -82,19 +80,6 @@ class Notifier { $uid = $message[0]['uid']; $recipients[] = $message[0]['contact-id']; $item = $message[0]; - - } elseif ($cmd === 'expire') { - $normal_mode = false; - $expire = true; - $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 - AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 10 MINUTE", - intval($item_id) - ); - $uid = $item_id; - $item_id = 0; - if (!count($items)) { - return; - } } elseif ($cmd === 'suggest') { $normal_mode = false; $fsuggest = true; @@ -213,18 +198,6 @@ class Notifier { // if $parent['wall'] == 1 we will already have the parent message in our array // and we will relay the whole lot. - // expire sends an entire group of expire messages and cannot be forwarded. - // However the conversation owner will be a part of the conversation and will - // be notified during this run. - // Other DFRN conversation members will be alerted during polled updates. - - - - // Diaspora members currently are not notified of expirations, and other networks have - // either limited or no ability to process deletions. We should at least fix Diaspora - // by stringing togther an array of retractions and sending them onward. - - $localhost = str_replace('www.','',$a->get_hostname()); if (strpos($localhost,':')) { $localhost = substr($localhost,0,strpos($localhost,':')); @@ -239,7 +212,7 @@ class Notifier { $relay_to_owner = false; - if (!$top_level && ($parent['wall'] == 0) && !$expire && (stristr($target_item['uri'],$localhost))) { + if (!$top_level && ($parent['wall'] == 0) && (stristr($target_item['uri'],$localhost))) { $relay_to_owner = true; }