Merge pull request #4264 from annando/item-delete

There is now a new delete function in the item class
This commit is contained in:
Hypolite Petovan 2018-01-18 08:02:50 -05:00 committed by GitHub
commit c69d60644f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 195 deletions

View file

@ -19,6 +19,7 @@ use Friendica\Model\Group;
use Friendica\Model\Mail; use Friendica\Model\Mail;
use Friendica\Model\Photo; use Friendica\Model\Photo;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Model\Item;
use Friendica\Network\FKOAuth1; use Friendica\Network\FKOAuth1;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Network\HTTPException\BadRequestException; use Friendica\Network\HTTPException\BadRequestException;
@ -2214,7 +2215,7 @@ function api_statuses_destroy($type)
$ret = api_statuses_show($type); $ret = api_statuses_show($type);
drop_item($id, false); Item::delete($id);
return $ret; return $ret;
} }
@ -3987,7 +3988,7 @@ function api_fr_photoalbum_delete($type)
if (!DBM::is_result($photo_item)) { if (!DBM::is_result($photo_item)) {
throw new InternalServerErrorException("problem with deleting items occured"); 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 // 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 // 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) // 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.']; $answer = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.'];
return api_format_data("photo_delete", $type, ['$result' => $answer]); return api_format_data("photo_delete", $type, ['$result' => $answer]);

View file

@ -1980,7 +1980,7 @@ function item_getfeedtags($item) {
function item_expire($uid, $days, $network = "", $force = false) { function item_expire($uid, $days, $network = "", $force = false) {
if ((! $uid) || ($days < 1)) { if (!$uid || ($days < 1)) {
return; return;
} }
@ -1989,7 +1989,7 @@ function item_expire($uid, $days, $network = "", $force = false) {
* and just expire conversations started by others * and just expire conversations started by others
*/ */
$expire_network_only = PConfig::get($uid,'expire', 'network_only'); $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 != "") { if ($network != "") {
$sql_extra .= sprintf(" AND network = '%s' ", dbesc($network)); $sql_extra .= sprintf(" AND network = '%s' ", dbesc($network));
@ -2013,7 +2013,7 @@ function item_expire($uid, $days, $network = "", $force = false) {
intval($days) intval($days)
); );
if (! DBM::is_result($r)) { if (!DBM::is_result($r)) {
return; return;
} }
@ -2050,37 +2050,28 @@ function item_expire($uid, $days, $network = "", $force = false) {
continue; 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 /// @TODO type-hint is array
function drop_items($items) { function drop_items($items) {
$uid = 0; $uid = 0;
if (! local_user() && ! remote_user()) { if (!local_user() && !remote_user()) {
return; return;
} }
if (count($items)) { if (count($items)) {
foreach ($items as $item) { foreach ($items as $item) {
$owner = drop_item($item,false); $owner = Item::delete($item);
if ($owner && ! $uid) if ($owner && ! $uid)
$uid = $owner; $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) {
function drop_item($id, $interactive = true) {
$a = get_app(); $a = get_app();
@ -2090,11 +2081,8 @@ function drop_item($id, $interactive = true) {
intval($id) intval($id)
); );
if (! DBM::is_result($r)) { if (!DBM::is_result($r)) {
if (! $interactive) { notice(t('Item not found.') . EOL);
return 0;
}
notice( t('Item not found.') . EOL);
goaway(System::baseUrl() . '/' . $_SESSION['return_url']); goaway(System::baseUrl() . '/' . $_SESSION['return_url']);
} }
@ -2104,8 +2092,6 @@ function drop_item($id, $interactive = true) {
return 0; return 0;
} }
$owner = $item['uid'];
$contact_id = 0; $contact_id = 0;
// check if logged in user is either the author or owner of this item // 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) {
if ((local_user() == $item['uid']) || $contact_id || !$interactive) {
// Check if we should do HTML-based delete confirmation // Check if we should do HTML-based delete confirmation
if ($_REQUEST['confirm']) { if ($_REQUEST['confirm']) {
@ -2150,126 +2135,16 @@ function drop_item($id, $interactive = true) {
goaway(System::baseUrl() . '/' . $_SESSION['return_url']); goaway(System::baseUrl() . '/' . $_SESSION['return_url']);
} }
logger('delete item: ' . $item['id'], LOGGER_DEBUG);
// delete the item // delete the item
dba::update('item', ['deleted' => true, 'title' => '', 'body' => '', Item::delete($item['id']);
'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']);
// 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']); goaway(System::baseUrl() . '/' . $_SESSION['return_url']);
//NOTREACHED //NOTREACHED
} else { } else {
if (! $interactive) { notice(t('Permission denied.') . EOL);
return 0;
}
notice( t('Permission denied.') . EOL);
goaway(System::baseUrl() . '/' . $_SESSION['return_url']); goaway(System::baseUrl() . '/' . $_SESSION['return_url']);
//NOTREACHED //NOTREACHED
} }
} }
/// @todo: This query seems to be really slow /// @todo: This query seems to be really slow

View file

@ -15,6 +15,7 @@ use Friendica\Database\DBM;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Model\Item;
use Friendica\Module\Login; use Friendica\Module\Login;
require_once 'include/enotify.php'; require_once 'include/enotify.php';
@ -492,7 +493,7 @@ function admin_page_deleteitem_post(App $a)
// associated threads. // associated threads.
$r = dba::select('item', ['id'], ['guid' => $guid]); $r = dba::select('item', ['id'], ['guid' => $guid]);
while ($row = dba::fetch($r)) { while ($row = dba::fetch($r)) {
drop_item($row['id'], false); Item::delete($row['id']);
} }
dba::close($r); dba::close($r);
} }

View file

@ -10,6 +10,7 @@ use Friendica\Core\System;
use Friendica\Core\Worker; use Friendica\Core\Worker;
use Friendica\Database\DBM; use Friendica\Database\DBM;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\Item;
require_once 'include/bbcode.php'; require_once 'include/bbcode.php';
require_once 'include/datetime.php'; require_once 'include/datetime.php';
@ -547,7 +548,7 @@ function events_content(App $a) {
// Delete only real events (no birthdays) // Delete only real events (no birthdays)
if (DBM::is_result($ev) && $ev[0]['type'] == 'event') { 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) { if ($del == 0) {

View file

@ -972,7 +972,11 @@ function item_content(App $a) {
$o = ''; $o = '';
if (($a->argc == 3) && ($a->argv[1] === 'drop') && intval($a->argv[2])) { 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()) { if (is_ajax()) {
// ajax return: [<item id>, 0 (no perm) | <owner id>] // ajax return: [<item id>, 0 (no perm) | <owner id>]
echo json_encode([intval($a->argv[2]), intval($o)]); echo json_encode([intval($a->argv[2]), intval($o)]);

View file

@ -64,6 +64,102 @@ class Item
return $rows; 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 * @brief Add a shadow entry for a given item id that is a thread starter
* *

View file

@ -29,7 +29,6 @@ class Delivery {
logger('delivery: invoked: '.$cmd.': '.$item_id.' to '.$contact_id, LOGGER_DEBUG); logger('delivery: invoked: '.$cmd.': '.$item_id.' to '.$contact_id, LOGGER_DEBUG);
$expire = false;
$mail = false; $mail = false;
$fsuggest = false; $fsuggest = false;
$relocate = false; $relocate = false;
@ -54,18 +53,6 @@ class Delivery {
$uid = $message[0]['uid']; $uid = $message[0]['uid'];
$recipients[] = $message[0]['contact-id']; $recipients[] = $message[0]['contact-id'];
$item = $message[0]; $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') { } elseif ($cmd === 'suggest') {
$normal_mode = false; $normal_mode = false;
$fsuggest = true; $fsuggest = true;
@ -160,16 +147,6 @@ class Delivery {
// if $parent['wall'] == 1 we will already have the parent message in our array // if $parent['wall'] == 1 we will already have the parent message in our array
// and we will relay the whole lot. // 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(); $localhost = $a->get_hostname();
if (strpos($localhost,':')) { if (strpos($localhost,':')) {
$localhost = substr($localhost,0,strpos($localhost,':')); $localhost = substr($localhost,0,strpos($localhost,':'));
@ -184,7 +161,7 @@ class Delivery {
$relay_to_owner = false; $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; $relay_to_owner = true;
} }

View file

@ -41,7 +41,6 @@ require_once 'include/bbcode.php';
* drop (in diaspora.php, items.php, photos.php) * drop (in diaspora.php, items.php, photos.php)
* edit_post (in item.php) * edit_post (in item.php)
* event (in events.php) * event (in events.php)
* expire (in items.php)
* like (in like.php, poke.php) * like (in like.php, poke.php)
* mail (in message.php) * mail (in message.php)
* suggest (in fsuggest.php) * suggest (in fsuggest.php)
@ -60,7 +59,6 @@ class Notifier {
logger('notifier: invoked: '.$cmd.': '.$item_id, LOGGER_DEBUG); logger('notifier: invoked: '.$cmd.': '.$item_id, LOGGER_DEBUG);
$expire = false;
$mail = false; $mail = false;
$fsuggest = false; $fsuggest = false;
$relocate = false; $relocate = false;
@ -82,19 +80,6 @@ class Notifier {
$uid = $message[0]['uid']; $uid = $message[0]['uid'];
$recipients[] = $message[0]['contact-id']; $recipients[] = $message[0]['contact-id'];
$item = $message[0]; $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') { } elseif ($cmd === 'suggest') {
$normal_mode = false; $normal_mode = false;
$fsuggest = true; $fsuggest = true;
@ -213,18 +198,6 @@ class Notifier {
// if $parent['wall'] == 1 we will already have the parent message in our array // if $parent['wall'] == 1 we will already have the parent message in our array
// and we will relay the whole lot. // 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()); $localhost = str_replace('www.','',$a->get_hostname());
if (strpos($localhost,':')) { if (strpos($localhost,':')) {
$localhost = substr($localhost,0,strpos($localhost,':')); $localhost = substr($localhost,0,strpos($localhost,':'));
@ -239,7 +212,7 @@ class Notifier {
$relay_to_owner = false; $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; $relay_to_owner = true;
} }