diff --git a/src/Model/Item.php b/src/Model/Item.php index 98ffd89ff1..7545375181 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -6,28 +6,32 @@ namespace Friendica\Model; +use Friendica\BaseObject; use Friendica\Core\Addon; use Friendica\Core\Config; +use Friendica\Core\L10n; use Friendica\Core\PConfig; -use Friendica\Core\Worker; use Friendica\Core\System; +use Friendica\Core\Worker; +use Friendica\Database\DBM; use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\GContact; use Friendica\Model\Group; use Friendica\Model\Term; -use Friendica\Model\User; -use Friendica\Database\DBM; +use Friendica\Object\Image; +use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; use dba; use Text_LanguageDetect; +require_once 'boot.php'; require_once 'include/tags.php'; require_once 'include/threads.php'; require_once 'include/items.php'; require_once 'include/text.php'; -class Item +class Item extends BaseObject { /** * @brief Update existing item entries @@ -1642,4 +1646,230 @@ class Item } return false; } + + /** + * @brief add/remove activity to an item + * + * Toggle activities as like,dislike,attend of an item + * + * @param string $item_id + * @param string $verb + * Activity verb. One of + * like, unlike, dislike, undislike, attendyes, unattendyes, + * attendno, unattendno, attendmaybe, unattendmaybe + * @hook 'post_local_end' + * array $arr + * 'post_id' => ID of posted item + */ + public static function performLike($item_id, $verb) + { + if (!local_user() && !remote_user()) { + return false; + } + + switch ($verb) { + case 'like': + case 'unlike': + $bodyverb = L10n::t('%1$s likes %2$s\'s %3$s'); + $activity = ACTIVITY_LIKE; + break; + case 'dislike': + case 'undislike': + $bodyverb = L10n::t('%1$s doesn\'t like %2$s\'s %3$s'); + $activity = ACTIVITY_DISLIKE; + break; + case 'attendyes': + case 'unattendyes': + $bodyverb = L10n::t('%1$s is attending %2$s\'s %3$s'); + $activity = ACTIVITY_ATTEND; + break; + case 'attendno': + case 'unattendno': + $bodyverb = L10n::t('%1$s is not attending %2$s\'s %3$s'); + $activity = ACTIVITY_ATTENDNO; + break; + case 'attendmaybe': + case 'unattendmaybe': + $bodyverb = L10n::t('%1$s may attend %2$s\'s %3$s'); + $activity = ACTIVITY_ATTENDMAYBE; + break; + default: + logger('like: unknown verb ' . $verb . ' for item ' . $item_id); + return false; + } + + // Enable activity toggling instead of on/off + $event_verb_flag = $activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE; + + logger('like: verb ' . $verb . ' item ' . $item_id); + + $item = dba::selectFirst('item', [], ['`id` = ? OR `uri` = ?', $item_id, $item_id]); + if (!DBM::is_result($item)) { + logger('like: unknown item ' . $item_id); + return false; + } + + $uid = $item['uid']; + if (($uid == 0) && local_user()) { + $uid = local_user(); + } + + if (!can_write_wall($uid)) { + logger('like: unable to write on wall ' . $uid); + return false; + } + + // Retrieves the local post owner + $owner_self_contact = dba::selectFirst('contact', [], ['uid' => $uid, 'self' => true]); + if (!DBM::is_result($owner_self_contact)) { + logger('like: unknown owner ' . $uid); + return false; + } + + // Retrieve the current logged in user's public contact + $author_id = public_contact(); + + $author_contact = dba::selectFirst('contact', [], ['id' => $author_id]); + if (!DBM::is_result($author_contact)) { + logger('like: unknown author ' . $author_id); + return false; + } + + // Contact-id is the uid-dependant author contact + if (local_user() == $uid) { + $item_contact_id = $owner_self_contact['id']; + $item_contact = $owner_self_contact; + } else { + $item_contact_id = Contact::getIdForURL($author_contact['url'], $uid); + $item_contact = dba::selectFirst('contact', [], ['id' => $item_contact_id]); + if (!DBM::is_result($item_contact)) { + logger('like: unknown item contact ' . $item_contact_id); + return false; + } + } + + // Look for an existing verb row + // 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) . "'"; + } else { + $verbs = "'".dbesc($activity)."'"; + } + + $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']) + ); + + // If it exists, mark it as deleted + if (DBM::is_result($existing_like)) { + $like_item = $existing_like[0]; + + // Already voted, undo it + $fields = ['deleted' => true, 'unseen' => true, 'changed' => datetime_convert()]; + dba::update('item', $fields, ['id' => $like_item['id']]); + + // Clean up the Diaspora signatures for this like + // Go ahead and do it even if Diaspora support is disabled. We still want to clean up + // if it had been enabled in the past + dba::delete('sign', ['iid' => $like_item['id']]); + + $like_item_id = $like_item['id']; + Worker::add(PRIORITY_HIGH, "Notifier", "like", $like_item_id); + + if (!$event_verb_flag || $like_item['verb'] == $activity) { + return true; + } + } + + // Verb is "un-something", just trying to delete existing entries + if (strpos($verb, 'un') === 0) { + return true; + } + + // Else or if event verb different from existing row, create a new item row + $post_type = (($item['resource-id']) ? L10n::t('photo') : L10n::t('status')); + if ($item['object-type'] === ACTIVITY_OBJ_EVENT) { + $post_type = L10n::t('event'); + } + $objtype = $item['resource-id'] ? ACTIVITY_OBJ_IMAGE : ACTIVITY_OBJ_NOTE ; + $link = xmlify('' . "\n") ; + $body = $item['body']; + + $obj = <<< EOT + + + $objtype + 1 + {$item['uri']} + $link + + $body + +EOT; + + $ulink = '[url=' . $author_contact['url'] . ']' . $author_contact['name'] . '[/url]'; + $alink = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]'; + $plink = '[url=' . System::baseUrl() . '/display/' . $owner_self_contact['nick'] . '/' . $item['id'] . ']' . $post_type . '[/url]'; + + $new_item = [ + 'guid' => get_guid(32), + 'uri' => item_new_uri(self::getApp()->get_hostname(), $item['uid']), + 'uid' => $item['uid'], + 'contact-id' => $item_contact_id, + 'type' => 'activity', + 'wall' => $item['wall'], + 'origin' => 1, + 'gravity' => GRAVITY_LIKE, + 'parent' => $item['id'], + 'parent-uri' => $item['uri'], + 'thr-parent' => $item['uri'], + 'owner-id' => $item['owner-id'], + 'owner-name' => $item['owner-name'], + 'owner-link' => $item['owner-link'], + 'owner-avatar' => $item['owner-avatar'], + 'author-id' => $author_contact['id'], + 'author-name' => $author_contact['name'], + 'author-link' => $author_contact['url'], + 'author-avatar' => $author_contact['thumb'], + 'body' => sprintf($bodyverb, $ulink, $alink, $plink), + 'verb' => $activity, + 'object-type' => $objtype, + 'object' => $obj, + 'allow_cid' => $item['allow_cid'], + 'allow_gid' => $item['allow_gid'], + 'deny_cid' => $item['deny_cid'], + 'deny_gid' => $item['deny_gid'], + 'visible' => 1, + 'unseen' => 1, + ]; + + $new_item_id = Item::insert($new_item); + + // @todo: Explain this block + if (! $item['visible']) { + q("UPDATE `item` SET `visible` = 1 WHERE `id` = %d", + intval($item['id']) + ); + } + + // Save the author information for the like in case we need to relay to Diaspora + Diaspora::storeLikeSignature($item_contact, $new_item_id); + + $new_item['id'] = $new_item_id; + + Addon::callHooks('post_local_end', $new_item); + + Worker::add(PRIORITY_HIGH, "Notifier", "like", $new_item_id); + + return true; + } }