return false;
}
+ // To ensure the data integrity we do it in an transaction
+ dba::transaction();
+
+ // We cannot simply expand the condition to check for origin entries
+ // The condition needn't to be a simple array but could be a complex condition.
+ // And we have to execute this query before the update to ensure to fetch the same data.
+ $items = dba::select('item', ['id', 'origin'], $condition);
+
$success = dba::update('item', $fields, $condition);
if (!$success) {
+ dba::close($items);
+ dba::rollback();
return false;
}
$rows = dba::affected_rows();
- // We cannot simply expand the condition to check for origin entries
- // The condition needn't to be a simple array but could be a complex condition.
- $items = dba::select('item', ['id', 'origin'], $condition);
while ($item = dba::fetch($items)) {
Term::insertFromTagFieldByItemId($item['id']);
Term::insertFromFileFieldByItemId($item['id']);
self::updateThread($item['id']);
- // We only need to notfiy others when it is an original entry from us
- if ($item['origin']) {
+ // We only need to notfiy others when it is an original entry from us.
+ // Only call the notifier when the item has some content relevant change.
+ if ($item['origin'] && in_array('edited', array_keys($fields))) {
Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
}
}
+ dba::close($items);
+ dba::commit();
return $rows;
}
*
* @param array $condition The condition for finding the item entries
* @param integer $priority Priority for the notification
+ * @param integer $uid User who wants to delete the item
*/
- public static function delete($condition, $priority = PRIORITY_HIGH)
+ public static function delete($condition, $priority = PRIORITY_HIGH, $uid = 0)
{
$items = dba::select('item', ['id'], $condition);
while ($item = dba::fetch($items)) {
- self::deleteById($item['id'], $priority);
+ self::deleteById($item['id'], $priority, $uid);
}
dba::close($items);
}
*
* @param integer $item_id Item ID that should be delete
* @param integer $priority Priority for the notification
+ * @param integer $uid User who wants to delete the item
*
* @return boolean success
*/
- public static function deleteById($item_id, $priority = PRIORITY_HIGH)
+ public static function deleteById($item_id, $priority = PRIORITY_HIGH, $uid = 0)
{
// locate item to be deleted
- $fields = ['id', 'uid', 'parent', 'parent-uri', 'origin', 'deleted',
- 'file', 'resource-id', 'event-id', 'attach',
+ $fields = ['id', 'uri', 'uid', 'parent', 'parent-uri', 'origin',
+ 'deleted', 'file', 'resource-id', 'event-id', 'attach',
'verb', 'object-type', 'object', 'target', 'contact-id'];
$item = dba::selectFirst('item', $fields, ['id' => $item_id]);
if (!DBM::is_result($item)) {
+ logger('Item with ID ' . $item_id . " hasn't been found.", LOGGER_DEBUG);
return false;
}
if ($item['deleted']) {
+ logger('Item with ID ' . $item_id . ' has already been deleted.', LOGGER_DEBUG);
return false;
}
$parent = ['origin' => false];
}
- logger('delete item: ' . $item['id'], LOGGER_DEBUG);
+ // "Deleting" global items just means hiding them
+ if (($item['uid'] == 0) && ($uid != 0)) {
+ dba::update('user-item', ['hidden' => true], ['iid' => $item_id, 'uid' => $uid], true);
+ return true;
+ }
// clean up categories and tags so they don't end up as orphans
Term::insertFromFileFieldByItemId($item['id']);
self::deleteThread($item['id'], $item['parent-uri']);
+ if (!dba::exists('item', ["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) {
+ self::delete(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority);
+ }
+
// If it's the parent of a comment thread, kill all the kids
if ($item['id'] == $item['parent']) {
- self::delete(['parent' => $item['parent']], $priority);
+ self::delete(['parent' => $item['parent'], 'deleted' => false], $priority);
}
- // send the notification upstream/downstream
+ // Is it our comment and/or our thread?
if ($item['origin'] || $parent['origin']) {
+
+ // When we delete the original post we will delete all existing copies on the server as well
+ self::delete(['uri' => $item['uri'], 'deleted' => false], $priority);
+
+ // send the notification upstream/downstream
Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
+ } elseif ($item['uid'] != 0) {
+
+ // When we delete just our local user copy of an item, we have to set a marker to hide it
+ $global_item = dba::selectFirst('item', ['id'], ['uri' => $item['uri'], 'uid' => 0, 'deleted' => false]);
+ if (DBM::is_result($global_item)) {
+ dba::update('user-item', ['hidden' => true], ['iid' => $global_item['id'], 'uid' => $item['uid']], true);
+ }
}
+ logger('Item with ID ' . $item_id . " has been deleted.", LOGGER_DEBUG);
+
return true;
}
$item['origin'] = 1;
$item['network'] = NETWORK_DFRN;
$item['protocol'] = PROTOCOL_DFRN;
+
+ if (is_int($notify)) {
+ $priority = $notify;
+ } else {
+ $priority = PRIORITY_HIGH;
+ }
} else {
$item['network'] = trim(defaults($item, 'network', NETWORK_PHANTOM));
}
unset($item['dsprsig']);
}
+ if (!empty($item['diaspora_signed_text'])) {
+ $diaspora_signed_text = $item['diaspora_signed_text'];
+ unset($item['diaspora_signed_text']);
+ } else {
+ $diaspora_signed_text = '';
+ }
+
// Converting the plink
/// @TODO Check if this is really still needed
if ($item['network'] == NETWORK_OSTATUS) {
// The contact-id should be set before "self::insert" was called - but there seems to be issues sometimes
$item["contact-id"] = self::contactId($item);
- $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"]));
+ $default = ['url' => $item['author-link'], 'name' => $item['author-name'],
+ 'photo' => $item['author-avatar'], 'network' => $item['network']];
+
+ $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"], 0, false, $default));
if (Contact::isBlocked($item["author-id"])) {
logger('Contact '.$item["author-id"].' is blocked, item '.$item["uri"].' will not be stored');
return 0;
}
- $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"]));
+ $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
+ 'photo' => $item['owner-avatar'], 'network' => $item['network']];
+
+ $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"], 0, false, $default));
if (Contact::isBlocked($item["owner-id"])) {
logger('Contact '.$item["owner-id"].' is blocked, item '.$item["uri"].' will not be stored');
$fields = ['uri', 'parent-uri', 'id', 'deleted',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
- 'wall', 'private', 'forum_mode'];
+ 'wall', 'private', 'forum_mode', 'origin'];
$condition = ['uri' => $item['parent-uri'], 'uid' => $item['uid']];
$params = ['order' => ['id' => false]];
$parent = dba::selectFirst('item', $fields, $condition, $params);
'signature' => $dsprsig->signature, 'signer' => $dsprsig->signer]);
}
+ if (!empty($diaspora_signed_text)) {
+ // Formerly we stored the signed text, the signature and the author in different fields.
+ // We now store the raw data so that we are more flexible.
+ dba::insert('sign', ['iid' => $current_post, 'signed_text' => $diaspora_signed_text]);
+ }
+
$deleted = self::tagDeliver($item['uid'], $current_post);
/*
check_user_notification($current_post);
if ($notify) {
- Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
+ Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
+ } elseif (!empty($parent) && $parent['origin']) {
+ Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", "comment-import", $current_post);
}
return $current_post;
/**
* @brief Distributes public items to the receivers
*
- * @param integer $itemid Item ID that should be added
+ * @param integer $itemid Item ID that should be added
+ * @param string $signed_text Original text (for Diaspora signatures), JSON encoded.
*/
- public static function distribute($itemid)
+ public static function distribute($itemid, $signed_text = '')
{
$condition = ["`id` IN (SELECT `parent` FROM `item` WHERE `id` = ?)", $itemid];
$parent = dba::selectFirst('item', ['owner-id'], $condition);
unset($item['mention']);
unset($item['wall']);
unset($item['origin']);
- unset($item['global']);
unset($item['starred']);
unset($item['rendered-hash']);
unset($item['rendered-html']);
$users = [];
- $condition = ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `id` = ?) AND `uid` != 0 AND NOT `blocked` AND NOT `readonly` AND `rel` IN (?, ?)",
+ $condition = ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `id` = ?) AND `uid` != 0 AND NOT `blocked` AND `rel` IN (?, ?)",
$parent['owner-id'], CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
$contacts = dba::select('contact', ['uid'], $condition);
while ($contact = dba::fetch($contacts)) {
$users[$contact['uid']] = $contact['uid'];
}
+ $origin_uid = 0;
+
if ($item['uri'] != $item['parent-uri']) {
- $parents = dba::select('item', ['uid'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
+ $parents = dba::select('item', ['uid', 'origin'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
while ($parent = dba::fetch($parents)) {
$users[$parent['uid']] = $parent['uid'];
+ if ($parent['origin'] && !$item['origin']) {
+ $origin_uid = $parent['uid'];
+ }
}
}
foreach ($users as $uid) {
+ if ($origin_uid == $uid) {
+ $item['diaspora_signed_text'] = $signed_text;
+ }
self::storeForUser($itemid, $item, $uid);
}
}
/// @todo Handling of "event-id"
- $distributed = self::insert($item, false, false, true);
+ $notify = false;
+ if ($item['uri'] == $item['parent-uri']) {
+ $contact = dba::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]);
+ if (DBM::is_result($contact)) {
+ $notify = self::isRemoteSelf($contact, $item);
+ }
+ }
+
+ $distributed = self::insert($item, false, $notify, true);
if (!$distributed) {
logger("Distributed public item " . $itemid . " for user " . $uid . " wasn't stored", LOGGER_DEBUG);
if (!dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) {
// Preparing public shadow (removing user specific data)
- unset($item['id']);
$item['uid'] = 0;
- $item['origin'] = 0;
- $item['wall'] = 0;
+ unset($item['id']);
+ unset($item['parent']);
+ unset($item['wall']);
+ unset($item['mention']);
+ unset($item['origin']);
+ unset($item['starred']);
+ unset($item['rendered-hash']);
+ unset($item['rendered-html']);
if ($item['uri'] == $item['parent-uri']) {
$item['contact-id'] = Contact::getIdForURL($item['owner-link']);
} else {
return;
}
+ // Save "origin" and "parent" state
+ $origin = $item['origin'];
+ $parent = $item['parent'];
+
// Preparing public shadow (removing user specific data)
- unset($item['id']);
$item['uid'] = 0;
- $item['origin'] = 0;
- $item['wall'] = 0;
+ unset($item['id']);
+ unset($item['parent']);
+ unset($item['wall']);
+ unset($item['mention']);
+ unset($item['origin']);
+ unset($item['starred']);
+ unset($item['rendered-hash']);
+ unset($item['rendered-html']);
$item['contact-id'] = Contact::getIdForURL($item['author-link']);
if (in_array($item['type'], ["net-comment", "wall-comment"])) {
$public_shadow = self::insert($item, false, false, true);
logger("Stored public shadow for comment ".$item['uri']." under id ".$public_shadow, LOGGER_DEBUG);
+
+ // If this was a comment to a Diaspora post we don't get our comment back.
+ // This means that we have to distribute the comment by ourselves.
+ if ($origin) {
+ if (dba::exists('item', ['id' => $parent, 'network' => NETWORK_DIASPORA])) {
+ self::distribute($public_shadow);
+ }
+ }
}
/**
}
}
- private static function setHashtags(&$item)
+ public static function setHashtags(&$item)
{
$tags = get_tags($item["body"]);
// Prevent the forwarding of posts that are forwarded
if ($datarray["extid"] == NETWORK_DFRN) {
+ logger('Already forwarded', LOGGER_DEBUG);
return false;
}
// Prevent to forward already forwarded posts
if ($datarray["app"] == $a->get_hostname()) {
+ logger('Already forwarded (second test)', LOGGER_DEBUG);
return false;
}
// Only forward posts
if ($datarray["verb"] != ACTIVITY_POST) {
+ logger('No post', LOGGER_DEBUG);
return false;
}
if (($contact['network'] != NETWORK_FEED) && $datarray['private']) {
+ logger('Not public', LOGGER_DEBUG);
return false;
}
unset($datarray['created']);
unset($datarray['edited']);
+
+ unset($datarray['network']);
+ unset($datarray['owner-id']);
+ unset($datarray['author-id']);
}
if ($contact['network'] != NETWORK_FEED) {
unset($datarray["plink"]);
$datarray["uri"] = item_new_uri($a->get_hostname(), $contact['uid'], $datarray["guid"]);
$datarray["parent-uri"] = $datarray["uri"];
- $datarray["extid"] = $contact['network'];
+ $datarray["thr-parent"] = $datarray["uri"];
+ $datarray["extid"] = NETWORK_DFRN;
$urlpart = parse_url($datarray2['author-link']);
$datarray["app"] = $urlpart["host"];
} else {
if ($contact['network'] != NETWORK_FEED) {
// Store the original post
- $r = self::insert($datarray2, false, false);
- logger('remote-self post original item - Contact '.$contact['url'].' return '.$r.' Item '.print_r($datarray2, true), LOGGER_DEBUG);
+ $result = self::insert($datarray2, false, false);
+ logger('remote-self post original item - Contact '.$contact['url'].' return '.$result.' Item '.print_r($datarray2, true), LOGGER_DEBUG);
} else {
$datarray["app"] = "Feed";
+ $result = true;
}
// Trigger automatic reactions for addons
$_SESSION["authenticated"] = true;
$_SESSION["uid"] = $contact['uid'];
- return true;
+ return $result;
}
/**
{
$fields = ['uid', 'guid', 'title', 'body', 'created', 'edited', 'commented', 'received', 'changed',
'wall', 'private', 'pubmail', 'moderated', 'visible', 'spam', 'starred', 'bookmark', 'contact-id',
- 'deleted', 'origin', 'forum_mode', 'network', 'rendered-html', 'rendered-hash'];
+ 'deleted', 'origin', 'forum_mode', 'network', 'author-id', 'owner-id', 'rendered-html', 'rendered-hash'];
$condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid];
$item = dba::selectFirst('item', $fields, $condition);
$result = dba::update('thread', $fields, ['iid' => $itemid]);
- logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result." ".print_r($item, true), LOGGER_DEBUG);
+ logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result, LOGGER_DEBUG);
// Updating a shadow item entry
$items = dba::selectFirst('item', ['id'], ['guid' => $item['guid'], 'uid' => 0]);