From 028c9f4da5190f6d9290117fb6503d5934e1034a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Jun 2018 19:37:13 +0000 Subject: [PATCH] We now store the verb in the item-content as well --- boot.php | 2 +- include/api.php | 2 +- src/Database/DBStructure.php | 1 + src/Model/Item.php | 90 ++++++++++++++++++++++-------------- src/Protocol/DFRN.php | 10 ++-- src/Protocol/OStatus.php | 12 ++--- 6 files changed, 70 insertions(+), 47 deletions(-) diff --git a/boot.php b/boot.php index 64abdddca3..864e7804c9 100644 --- a/boot.php +++ b/boot.php @@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily'); define('FRIENDICA_VERSION', '2018.08-dev'); define('DFRN_PROTOCOL_VERSION', '2.23'); -define('DB_UPDATE_VERSION', 1271); +define('DB_UPDATE_VERSION', 1272); define('NEW_UPDATE_ROUTINE_VERSION', 1170); /** diff --git a/include/api.php b/include/api.php index 2eaabd8242..32fe6c651d 100644 --- a/include/api.php +++ b/include/api.php @@ -1626,7 +1626,7 @@ function api_statuses_home_timeline($type) } if (!empty($idarray)) { - $unseen = dba::exists('item', ['unseen' => true, 'id' => $idarray]); + $unseen = Item::exists(['unseen' => true, 'id' => $idarray]); if ($unseen) { Item::update(['unseen' => false], ['unseen' => true, 'id' => $idarray]); } diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index eaf54cee23..8fb2afddb2 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -1270,6 +1270,7 @@ class DBStructure "target-type" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => "ActivityStreams target type if applicable (URI)"], "target" => ["type" => "text", "comment" => "JSON encoded target structure if used"], "plink" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "permalink or URL to a displayable copy of the message at its source"], + "verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => "ActivityStreams verb"], ], "indexes" => [ "PRIMARY" => ["id"], diff --git a/src/Model/Item.php b/src/Model/Item.php index fe6dc5d1d4..2dac079b6f 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -58,7 +58,7 @@ class Item extends BaseObject // Field list for "item-content" table that is mixed with the item table const CONTENT_FIELDLIST = ['title', 'content-warning', 'body', 'location', - 'coord', 'app', 'rendered-hash', 'rendered-html', + 'coord', 'app', 'rendered-hash', 'rendered-html', 'verb', 'object-type', 'object', 'target-type', 'target']; // All fields in the item table @@ -143,6 +143,27 @@ class Item extends BaseObject return $data; } + /** + * @brief Check if item data exists + * + * @param array $condition array of fields for condition + * + * @return boolean Are there rows for that condition? + */ + public static function exists($condition) { + $stmt = self::select(['id'], $condition, ['limit' => 1]); + + if (is_bool($stmt)) { + $retval = $stmt; + } else { + $retval = (dba::num_rows($stmt) > 0); + } + + dba::close($stmt); + + return $retval; + } + /** * Retrieve a single record from the item table for a given user and returns it in an associative array * @@ -377,8 +398,8 @@ class Item extends BaseObject $fields['item'] = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', 'guid', 'contact-id', 'owner-id', 'author-id', 'type', 'wall', 'gravity', 'extid', - 'created', 'edited', 'commented', 'received', 'changed', 'verb', - 'postopts', 'plink', 'resource-id', 'event-id', 'tag', 'attach', 'inform', + 'created', 'edited', 'commented', 'received', 'changed', 'postopts', + 'plink', 'resource-id', 'event-id', 'tag', 'attach', 'inform', 'file', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'private', 'pubmail', 'moderated', 'visible', 'starred', 'bookmark', 'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', @@ -728,15 +749,14 @@ class Item extends BaseObject self::deleteTagsFromItem($item); // Set the item to "deleted" - dba::update('item', ['deleted' => true, 'title' => '', 'body' => '', - 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()], + dba::update('item', ['deleted' => true, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()], ['id' => $item['id']]); Term::insertFromTagFieldByItemId($item['id']); Term::insertFromFileFieldByItemId($item['id']); self::deleteThread($item['id'], $item['parent-uri']); - if (!dba::exists('item', ["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) { + if (!self::exists(["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) { self::delete(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority); } @@ -1120,7 +1140,7 @@ class Item extends BaseObject // Checking if there is already an item with the same guid logger('Checking for an item for user '.$item['uid'].' on network '.$item['network'].' with the guid '.$item['guid'], LOGGER_DEBUG); $condition = ['guid' => $item['guid'], 'network' => $item['network'], 'uid' => $item['uid']]; - if (dba::exists('item', $condition)) { + if (self::exists($condition)) { logger('found item with guid '.$item['guid'].' for user '.$item['uid'].' on network '.$item['network'], LOGGER_DEBUG); return 0; } @@ -1234,7 +1254,7 @@ class Item extends BaseObject $condition = ["`uri` = ? AND `network` IN (?, ?) AND `uid` = ?", $item['uri'], $item['network'], NETWORK_DFRN, $item['uid']]; - if (dba::exists('item', $condition)) { + if (self::exists($condition)) { logger('duplicated item with the same uri found. '.print_r($item,true)); return 0; } @@ -1242,7 +1262,7 @@ class Item extends BaseObject // On Friendica and Diaspora the GUID is unique if (in_array($item['network'], [NETWORK_DFRN, NETWORK_DIASPORA])) { $condition = ['guid' => $item['guid'], 'uid' => $item['uid']]; - if (dba::exists('item', $condition)) { + if (self::exists($condition)) { logger('duplicated item with the same guid found. '.print_r($item,true)); return 0; } @@ -1250,7 +1270,7 @@ class Item extends BaseObject // Check for an existing post with the same content. There seems to be a problem with OStatus. $condition = ["`body` = ? AND `network` = ? AND `created` = ? AND `contact-id` = ? AND `uid` = ?", $item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']]; - if (dba::exists('item', $condition)) { + if (self::exists($condition)) { logger('duplicated item with the same body found. '.print_r($item,true)); return 0; } @@ -1263,7 +1283,7 @@ class Item extends BaseObject // Set the global flag on all items if this was a global item entry dba::update('item', ['global' => true], ['uri' => $item["uri"]]); } else { - $item["global"] = dba::exists('item', ['uid' => 0, 'uri' => $item["uri"]]); + $item["global"] = self::exists(['uid' => 0, 'uri' => $item["uri"]]); } // ACL settings @@ -1304,7 +1324,7 @@ class Item extends BaseObject * An unique index would help - but the limitations of MySQL (maximum size of index values) prevent this. */ if ($item["uid"] == 0) { - if (dba::exists('item', ['uri' => trim($item['uri']), 'uid' => 0])) { + if (self::exists(['uri' => trim($item['uri']), 'uid' => 0])) { logger('Global item already stored. URI: '.$item['uri'].' on network '.$item['network'], LOGGER_DEBUG); return 0; } @@ -1671,7 +1691,7 @@ class Item extends BaseObject if (DBM::is_result($item) && ($item["allow_cid"] == '') && ($item["allow_gid"] == '') && ($item["deny_cid"] == '') && ($item["deny_gid"] == '')) { - if (!dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) { + if (!self::exists(['uri' => $item['uri'], 'uid' => 0])) { // Preparing public shadow (removing user specific data) $item['uid'] = 0; unset($item['id']); @@ -1725,12 +1745,12 @@ class Item extends BaseObject } // Is there a shadow parent? - if (!dba::exists('item', ['uri' => $item['parent-uri'], 'uid' => 0])) { + if (!self::exists(['uri' => $item['parent-uri'], 'uid' => 0])) { return; } // Is there already a shadow entry? - if (dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) { + if (self::exists(['uri' => $item['uri'], 'uid' => 0])) { return; } @@ -1760,7 +1780,7 @@ class Item extends BaseObject // 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 && dba::exists('item', ['id' => $parent, 'network' => NETWORK_DIASPORA])) { + if ($origin && self::exists(['id' => $parent, 'network' => NETWORK_DIASPORA])) { self::distribute($public_shadow); } } @@ -2570,30 +2590,32 @@ class Item extends BaseObject // event participation are essentially radio toggles. If you make a subsequent choice, // we need to eradicate your first choice. if ($event_verb_flag) { - $verbs = "'" . dbesc(ACTIVITY_ATTEND) . "', '" . dbesc(ACTIVITY_ATTENDNO) . "', '" . dbesc(ACTIVITY_ATTENDMAYBE) . "'"; + $verbs = [ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE]; } else { - $verbs = "'".dbesc($activity)."'"; + $verbs = $activity; } - /// @todo This query is expected to be a performance eater due to the "OR" - it has to be changed totally - $existing_like = q("SELECT `id`, `guid`, `verb` FROM `item` - WHERE `verb` IN ($verbs) - AND `deleted` = 0 - AND `author-id` = %d - AND `uid` = %d - AND (`parent` = '%s' OR `parent-uri` = '%s' OR `thr-parent` = '%s') - LIMIT 1", - intval($author_contact['id']), - intval($item['uid']), - dbesc($item_id), dbesc($item_id), dbesc($item['uri']) - ); + $base_condition = ['verb' => $verbs, 'deleted' => false, 'gravity' => GRAVITY_ACTIVITY, + 'author-id' => $author_contact['id'], 'uid' => item['uid']]; + + $condition = array_merge($base_condition, ['parent' => $item_id]); + $like_item = self::selectFirst(['id', 'guid', 'verb'], $condition); + + if (!DBM::is_result($like_item)) { + $condition = array_merge($base_condition, ['parent-uri' => $item_id]); + $like_item = self::selectFirst(['id', 'guid', 'verb'], $condition); + } + + if (!DBM::is_result($like_item)) { + $condition = array_merge($base_condition, ['thr-parent' => $item_id]); + $like_item = self::selectFirst(['id', 'guid', 'verb'], $condition); + } // If it exists, mark it as deleted - if (DBM::is_result($existing_like)) { - $like_item = $existing_like[0]; - + if (DBM::is_result($like_item)) { // Already voted, undo it $fields = ['deleted' => true, 'unseen' => true, 'changed' => DateTimeFormat::utcNow()]; + /// @todo Consider using self::update - but before doing so, check the side effects dba::update('item', $fields, ['id' => $like_item['id']]); // Clean up the Diaspora signatures for this like @@ -2757,7 +2779,7 @@ EOT; if ($itemuri != "") { $condition = ["`uri` = ? AND NOT `deleted` AND NOT (`uid` IN (?, 0))", $itemuri, $item["uid"]]; - if (!dba::exists('item', $condition)) { + if (!self::exists($condition)) { dba::delete('item', ['uri' => $itemuri, 'uid' => 0]); logger("deleteThread: Deleted shadow for item ".$itemuri, LOGGER_DEBUG); } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 0b8cb73827..c1e116f2f7 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -2313,15 +2313,15 @@ class DFRN $item["gravity"] = GRAVITY_ACTIVITY; // only one like or dislike per person // splitted into two queries for performance issues - $condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], + $condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], 'gravity' => GRAVITY_ACTIVITY, 'verb' => $item["verb"], 'parent-uri' => $item["parent-uri"]]; - if (dba::exists('item', $condition)) { + if (Item::exists($condition)) { return false; } - $condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], + $condition = ['uid' => $item["uid"], 'author-id' => $item["author-id"], 'gravity' => GRAVITY_ACTIVITY, 'verb' => $item["verb"], 'thr-parent' => $item["parent-uri"]]; - if (dba::exists('item', $condition)) { + if (Item::exists($condition)) { return false; } } else { @@ -2770,7 +2770,7 @@ class DFRN // Comments can be deleted by the thread owner or comment owner if (($item['id'] != $item['parent']) && ($item['contact-id'] != $importer["id"])) { $condition = ['id' => $item['parent'], 'contact-id' => $importer["id"]]; - if (!dba::exists('item', $condition)) { + if (!Item::exists($condition)) { logger("Item with uri " . $uri . " wasn't found or mustn't be deleted by contact " . $importer["id"] . " - ignoring deletion.", LOGGER_DEBUG); return; } diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 9aef0840c0..4538d36dfa 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -416,7 +416,7 @@ class OStatus } // Deletions come with the same uri, so we check for duplicates after processing deletions - if (dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item["uri"]])) { + if (Item::exists(['uid' => $importer["uid"], 'uri' => $item["uri"]])) { logger('Post with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.', LOGGER_DEBUG); continue; } else { @@ -489,7 +489,7 @@ class OStatus } } else { // But we will only import complete threads - $valid = dba::exists('item', ['uid' => $importer["uid"], 'uri' => self::$itemlist[0]['parent-uri']]); + $valid = Item::exists(['uid' => $importer["uid"], 'uri' => self::$itemlist[0]['parent-uri']]); if ($valid) { logger("Item with uri ".self::$itemlist[0]["uri"]." belongs to parent ".self::$itemlist[0]['parent-uri']." of user ".$importer["uid"].". It will be imported.", LOGGER_DEBUG); } @@ -506,7 +506,7 @@ class OStatus } } foreach (self::$itemlist as $item) { - $found = dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item["uri"]]); + $found = Item::exists(['uid' => $importer["uid"], 'uri' => $item["uri"]]); if ($found) { logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already exists.", LOGGER_DEBUG); } elseif ($item['contact-id'] < 0) { @@ -537,8 +537,8 @@ class OStatus */ private static function deleteNotice($item) { - $condition = ['uid' => $item['uid'], 'author-link' => $item['author-link'], 'uri' => $item['uri']]; - if (!dba::exists('item', $condition)) { + $condition = ['uid' => $item['uid'], 'author-id' => $item['author-id'], 'uri' => $item['uri']]; + if (!Item::exists($condition)) { logger('Item from '.$item['author-link'].' with uri '.$item['uri'].' for user '.$item['uid']." wasn't found. We don't delete it."); return; } @@ -674,7 +674,7 @@ class OStatus } if (isset($item["parent-uri"])) { - if (!dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item['parent-uri']])) { + if (!Item::exists(['uid' => $importer["uid"], 'uri' => $item['parent-uri']])) { if ($related != '') { self::fetchRelated($related, $item["parent-uri"], $importer); }