]> git.mxchange.org Git - friendica.git/commitdiff
API: Show activity notifications
authorMichael <heluecht@pirati.ca>
Tue, 1 Jun 2021 05:51:03 +0000 (05:51 +0000)
committerMichael <heluecht@pirati.ca>
Tue, 1 Jun 2021 05:51:03 +0000 (05:51 +0000)
database.sql
mod/display.php
src/Factory/Api/Mastodon/Notification.php
src/Model/Contact.php
src/Model/Post/UserNotification.php
src/Module/Api/Mastodon/Notifications.php
src/Module/Api/Mastodon/Notifications/Clear.php
src/Module/Api/Mastodon/Notifications/Dismiss.php
src/Repository/Notification.php
static/dbstructure.config.php

index 202d5365523d77b093bc95ef0ad8ff11ffe55683..cc2e4b790a487531dccd6b6ee090e902b4c72881 100644 (file)
@@ -1,6 +1,6 @@
 -- ------------------------------------------
 -- Friendica 2021.06-rc (Siberian Iris)
--- DB_UPDATE_VERSION 1420
+-- DB_UPDATE_VERSION 1421
 -- ------------------------------------------
 
 
@@ -819,6 +819,33 @@ CREATE TABLE IF NOT EXISTS `manage` (
        FOREIGN KEY (`mid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
 ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other';
 
+--
+-- TABLE notification
+--
+CREATE TABLE IF NOT EXISTS `notification` (
+       `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
+       `uid` mediumint unsigned COMMENT 'Owner User id',
+       `vid` smallint unsigned COMMENT 'Id of the verb table entry that contains the activity verbs',
+       `type` tinyint unsigned COMMENT '',
+       `actor-id` int unsigned COMMENT 'Link to the contact table with uid=0 of the actor that caused the notification',
+       `target-uri-id` int unsigned COMMENT 'Item-uri id of the related post',
+       `parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post',
+       `created` datetime COMMENT '',
+       `seen` boolean DEFAULT '0' COMMENT '',
+        PRIMARY KEY(`id`),
+        UNIQUE INDEX `uid_vid_type_actor-id_target-uri-id` (`uid`,`vid`,`type`,`actor-id`,`target-uri-id`),
+        INDEX `vid` (`vid`),
+        INDEX `actor-id` (`actor-id`),
+        INDEX `target-uri-id` (`target-uri-id`),
+        INDEX `parent-uri-id` (`parent-uri-id`),
+        INDEX `seen_uid` (`seen`,`uid`),
+       FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
+       FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
+       FOREIGN KEY (`actor-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
+       FOREIGN KEY (`target-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
+       FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
+) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications';
+
 --
 -- TABLE notify
 --
index 2750102d7b4ea10f4af6b7cb4e9c4392ac7a47aa..c9d39b1db08051549ab92cbd3b9f8d153fa94545 100644 (file)
@@ -236,6 +236,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
        }
 
        if (!DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
+               DBA::update('notification', ['seen' => true], ['parent-uri-id' => $item['parent-uri-id'], 'uid' => local_user()]);
                DBA::update('notify', ['seen' => true], ['parent-uri-id' => $item['parent-uri-id'], 'uid' => local_user()]);
        }
 
index db5e7d489824254b95a0ee91a24341f23fdffa15..58c1fee9dea75a5f784f2273d919a14f8843ff2b 100644 (file)
@@ -24,23 +24,18 @@ namespace Friendica\Factory\Api\Mastodon;
 use Friendica\BaseFactory;
 use Friendica\Database\DBA;
 use Friendica\DI;
-use Friendica\Model\Contact;
-use Friendica\Model\Notification as ModelNotification;
+use Friendica\Model\Post;
+use Friendica\Model\Verb;
+use Friendica\Protocol\Activity;
 
 class Notification extends BaseFactory
 {
        public function createFromNotifyId(int $id)
        {
-               $notification = DBA::selectFirst('notify', [], ['id' => $id]);
+               $notification = DBA::selectFirst('notification', [], ['id' => $id]);
                if (!DBA::isResult($notification)) {
                        return null;
                }
-
-               $cid = Contact::getIdForURL($notification['url'], 0, false);
-               if (empty($cid)) {
-                       return null;
-               }
-
                /*
                follow         = Someone followed you
                follow_request = Someone requested to follow you
@@ -51,32 +46,27 @@ class Notification extends BaseFactory
                status         = Someone you enabled notifications for has posted a status
                */
 
-               switch ($notification['type']) {
-                       case ModelNotification\Type::INTRO:
-                               $type = 'follow_request';
-                               break;
-
-                       case ModelNotification\Type::WALL:
-                       case ModelNotification\Type::COMMENT:
-                       case ModelNotification\Type::MAIL:
-                       case ModelNotification\Type::TAG_SELF:
-                       case ModelNotification\Type::POKE:
-                               $type = 'mention';
-                               break;
-
-                       case ModelNotification\Type::SHARE:
-                               $type = 'status';
-                               break;
-
-                       default:
-                               return null;
+               if (($notification['vid'] == Verb::getID(Activity::ANNOUNCE)) &&
+                       in_array($notification['type'], [Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT])) {
+                       $type = 'reblog';
+               } elseif (in_array($notification['vid'], [Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE)]) &&
+                       in_array($notification['type'], [Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT])) {
+                       $type = 'favourite';
+               } elseif ($notification['type'] == Post\UserNotification::NOTIF_SHARED) {
+                       $type = 'status';
+               } elseif (in_array($notification['type'], [Post\UserNotification::NOTIF_EXPLICIT_TAGGED,
+                       Post\UserNotification::NOTIF_IMPLICIT_TAGGED, Post\UserNotification::NOTIF_DIRECT_COMMENT,
+                       Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT])) {
+                       $type = 'mention';
+               } else {
+                       return null;
                }
 
-               $account = DI::mstdnAccount()->createFromContactId($cid);
+               $account = DI::mstdnAccount()->createFromContactId($notification['actor-id']);
 
-               if (!empty($notification['uri-id'])) {
+               if (!empty($notification['target-uri-id'])) {
                        try {
-                               $status = DI::mstdnStatus()->createFromUriId($notification['uri-id'], $notification['uid']);
+                               $status = DI::mstdnStatus()->createFromUriId($notification['target-uri-id'], $notification['uid']);
                        } catch (\Throwable $th) {
                                $status = null;
                        }
@@ -84,6 +74,6 @@ class Notification extends BaseFactory
                        $status = null;
                }
 
-               return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['date'], $account, $status);
+               return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['created'], $account, $status);
        }
 }
index cd4e608eae9a618d1d933d4c0de46a535dab97a4..916efe833051bf32008339bf1290358e435b7770 100644 (file)
@@ -715,6 +715,8 @@ class Contact
                        DBA::update('contact', $fields, ['id' => $self['id']]);
 
                        // Update the public contact as well
+                       $fields['prvkey'] = null;
+                       $fields['self']   = false;
                        DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $self['nurl']]);
 
                        // Update the profile
index ce7ca464e1238fcfdbdef924229d156c6c83e991..306584bbd6a03a378f0942105792671dd6471823 100644 (file)
@@ -33,7 +33,7 @@ use Friendica\Model\Post;
 use Friendica\Util\Strings;
 use Friendica\Model\Tag;
 use Friendica\Protocol\Activity;
-
+use Friendica\Util\DateTimeFormat;
 
 class UserNotification
 {
@@ -128,8 +128,8 @@ class UserNotification
         */
        public static function setNotification(int $uri_id, int $uid)
        {
-               $fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity',
-                       'private', 'contact-id', 'thr-parent', 'parent-uri-id', 'parent-uri', 'author-id', 'verb'];
+               $fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', 'vid', 'gravity',
+                       'private', 'contact-id', 'thr-parent', 'thr-parent-id', 'parent-uri-id', 'parent-uri', 'author-id', 'verb'];
                $item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]);
                if (!DBA::isResult($item)) {
                        return;
@@ -148,7 +148,7 @@ class UserNotification
                }
 
                // Add every user who participated so far in this thread
-               // This can only happen with participations on global items. (means: uid = 0) 
+               // This can only happen with participations on global items. (means: uid = 0)
                $users = DBA::p("SELECT DISTINCT(`contact-uid`) AS `uid` FROM `post-user-view`
                        WHERE `contact-uid` != 0 AND `parent-uri-id` = ? AND `uid` = ?", $item['parent-uri-id'], $uid);
                while ($user = DBA::fetch($users)) {
@@ -177,6 +177,10 @@ class UserNotification
 
                if (self::checkShared($item, $uid)) {
                        $notification_type = $notification_type | self::NOTIF_SHARED;
+                       self::insertNotication(self::NOTIF_SHARED, $uid, $item);
+                       $notified = true;
+               } else {
+                       $notified = false;
                }
 
                $profiles = self::getProfileForUser($uid);
@@ -194,38 +198,64 @@ class UserNotification
                        return;
                }
 
-               // Only create notifications for posts and comments, not for activities
-               if (in_array($item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT])) {
-                       if (self::checkImplicitMention($item, $profiles)) {
-                               $notification_type = $notification_type | self::NOTIF_IMPLICIT_TAGGED;
+               if (self::checkExplicitMention($item, $profiles)) {
+                       $notification_type = $notification_type | self::NOTIF_EXPLICIT_TAGGED;
+                       if (!$notified) {
+                               self::insertNotication( self::NOTIF_EXPLICIT_TAGGED, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkExplicitMention($item, $profiles)) {
-                               $notification_type = $notification_type | self::NOTIF_EXPLICIT_TAGGED;
+               if (self::checkImplicitMention($item, $profiles)) {
+                       $notification_type = $notification_type | self::NOTIF_IMPLICIT_TAGGED;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_IMPLICIT_TAGGED, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkCommentedThread($item, $contacts)) {
-                               $notification_type = $notification_type | self::NOTIF_THREAD_COMMENT;
+               if (self::checkDirectComment($item, $contacts)) {
+                       $notification_type = $notification_type | self::NOTIF_DIRECT_COMMENT;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_DIRECT_COMMENT, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkDirectComment($item, $contacts)) {
-                               $notification_type = $notification_type | self::NOTIF_DIRECT_COMMENT;
+               if (self::checkDirectCommentedThread($item, $contacts)) {
+                       $notification_type = $notification_type | self::NOTIF_DIRECT_THREAD_COMMENT;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_DIRECT_THREAD_COMMENT, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkDirectCommentedThread($item, $contacts)) {
-                               $notification_type = $notification_type | self::NOTIF_DIRECT_THREAD_COMMENT;
+               if (self::checkCommentedThread($item, $contacts)) {
+                       $notification_type = $notification_type | self::NOTIF_THREAD_COMMENT;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_THREAD_COMMENT, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkCommentedParticipation($item, $contacts)) {
-                               $notification_type = $notification_type | self::NOTIF_COMMENT_PARTICIPATION;
+               if (self::checkCommentedParticipation($item, $contacts)) {
+                       $notification_type = $notification_type | self::NOTIF_COMMENT_PARTICIPATION;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_COMMENT_PARTICIPATION, $uid, $item);
+                               $notified = true;
                        }
+               }
 
-                       if (self::checkActivityParticipation($item, $contacts)) {
-                               $notification_type = $notification_type | self::NOTIF_ACTIVITY_PARTICIPATION;
+               if (self::checkActivityParticipation($item, $contacts)) {
+                       $notification_type = $notification_type | self::NOTIF_ACTIVITY_PARTICIPATION;
+                       if (!$notified) {
+                               self::insertNotication(self::NOTIF_ACTIVITY_PARTICIPATION, $uid, $item);
+                               $notified = true;
                        }
                }
 
-               if (empty($notification_type)) {
+               // Only create notifications for posts and comments, not for activities
+               if (empty($notification_type) || !in_array($item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT])) {
                        return;
                }
 
@@ -236,6 +266,32 @@ class UserNotification
                self::update($item['uri-id'], $uid, $fields, true);
        }
 
+       private static function insertNotication(int $type, int $uid, array $item)
+       {
+               if (($item['gravity'] == GRAVITY_ACTIVITY) &&
+                       !in_array($type, [self::NOTIF_DIRECT_COMMENT, self::NOTIF_DIRECT_THREAD_COMMENT])) {
+                       // Activities are only stored when performed on the user's post or comment
+                       return;
+               }
+
+               $fields = [
+                       'uid' => $uid, 
+                       'vid' => $item['vid'], 
+                       'type' => $type,
+                       'actor-id' => $item['author-id'], 
+                       'parent-uri-id' => $item['parent-uri-id'],
+                       'created' => DateTimeFormat::utcNow(),
+               ];
+
+               if ($item['gravity'] == GRAVITY_ACTIVITY) {
+                       $fields['target-uri-id'] = $item['thr-parent-id'];
+               } else {
+                       $fields['target-uri-id'] = $item['uri-id'];
+               }
+
+               dba::insert('notification', $fields);
+       }
+
        /**
         * Fetch all profiles (contact URL) of a given user
         * @param int $uid User ID
index 9a606c2aa7c76e92e2056ad83e0f5d6ebe1e7642..4a25224d88fcf31b6139e86b1cf4afaf30834b76 100644 (file)
@@ -25,8 +25,10 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\DI;
 use Friendica\Model\Contact;
-use Friendica\Model\Notification;
+use Friendica\Model\Post;
+use Friendica\Model\Verb;
 use Friendica\Module\BaseApi;
+use Friendica\Protocol\Activity;
 
 /**
  * @see https://docs.joinmastodon.org/methods/notifications/
@@ -63,7 +65,7 @@ class Notifications extends BaseApi
 
                $params = ['order' => ['id' => true], 'limit' => $request['limit']];
 
-               $condition = ['uid' => $uid, 'seen' => false, 'type' => []];
+               $condition = ['uid' => $uid, 'seen' => false];
 
                if (!empty($request['account_id'])) {
                        $contact = Contact::getById($request['account_id'], ['url']);
@@ -72,17 +74,32 @@ class Notifications extends BaseApi
                        }
                }
 
-               if (!in_array('follow_request', $request['exclude_types'])) {
-                       $condition['type'] = array_merge($condition['type'], [Notification\Type::INTRO]);
+               if (in_array('follow_request', $request['exclude_types'])) {
+                       $condition = DBA::mergeConditions($condition, ["NOT `vid` IN (?)", Verb::getID(Activity::FOLLOW)]);
                }
 
-               if (!in_array('mention', $request['exclude_types'])) {
-                       $condition['type'] = array_merge($condition['type'],
-                               [Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, Notification\Type::TAG_SELF, Notification\Type::POKE]);
+               if (in_array('favourite', $request['exclude_types'])) {
+                       $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?, ?) OR NOT `type` IN (?, ?))",
+                               Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE),
+                               Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
                }
 
-               if (!in_array('status', $request['exclude_types'])) {
-                       $condition['type'] = array_merge($condition['type'], [Notification\Type::SHARE]);
+               if (in_array('reblog', $request['exclude_types'])) {
+                       $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?, ?))",
+                               Verb::getID(Activity::ANNOUNCE),
+                               Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
+               }
+
+               if (in_array('mention', $request['exclude_types'])) {
+                       $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?, ?, ?, ?, ?))",
+                               Verb::getID(Activity::POST), Post\UserNotification::NOTIF_EXPLICIT_TAGGED,
+                               Post\UserNotification::NOTIF_IMPLICIT_TAGGED, Post\UserNotification::NOTIF_DIRECT_COMMENT,
+                               Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, Post\UserNotification::NOTIF_THREAD_COMMENT]);
+               }
+
+               if (in_array('status', $request['exclude_types'])) {
+                       $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?))",
+                               Verb::getID(Activity::POST), Post\UserNotification::NOTIF_SHARED]);
                }
 
                if (!empty($request['max_id'])) {
@@ -101,9 +118,12 @@ class Notifications extends BaseApi
 
                $notifications = [];
 
-               $notify = DBA::select('notify', ['id'], $condition, $params);
+               $notify = DBA::select('notification', ['id'], $condition, $params);
                while ($notification = DBA::fetch($notify)) {
-                       $notifications[] = DI::mstdnNotification()->createFromNotifyId($notification['id']);
+                       $entry = DI::mstdnNotification()->createFromNotifyId($notification['id']);
+                       if (!empty($entry)) {
+                               $notifications[] = $entry;
+                       }
                }
 
                if (!empty($request['min_id'])) {
index c809ad2af99bcf2eabfbea57f8339cf1083cd7d5..b6961eb5320f408b99c75d1ed4f407fc576f2e11 100644 (file)
@@ -35,7 +35,7 @@ class Clear extends BaseApi
                self::login(self::SCOPE_WRITE);
                $uid = self::getCurrentUserID();
 
-               DBA::update('notify', ['seen' => true], ['uid' => $uid]);
+               DBA::update('notification', ['seen' => true], ['uid' => $uid]);
 
                System::jsonExit([]);
        }
index a0f57a40589cc029d13852bddf5aefb49cb8c162..4c8d3deb9a1bc3b7585ea7eb42d528140c67a816 100644 (file)
@@ -40,7 +40,7 @@ class Dismiss extends BaseApi
                        DI::mstdnError()->UnprocessableEntity();
                }
 
-               DBA::update('notify', ['seen' => true], ['uid' => $uid, 'id' => $parameters['id']]);
+               DBA::update('notification', ['seen' => true], ['uid' => $uid, 'id' => $parameters['id']]);
 
                System::jsonExit([]);
        }
index 39abdad199f5b8211c123cebe3a2d645720deb24..1748759b607a09f0f3c4e8ca72b1d1c30cc8de1f 100644 (file)
@@ -87,8 +87,13 @@ class Notification extends BaseRepository
        public function setSeen(bool $seen = true, Model\Notification $notify = null)
        {
                if (empty($notify)) {
+                       $this->dba->update('notification', ['seen' => $seen], ['uid' => local_user()]);
                        $conditions = ['uid' => local_user()];
                } else {
+                       if (!empty($notify->{'uri-id'})) {
+                               $this->dba->update('notification', ['seen' => $seen], ['uid' => local_user(), 'target-uri-id' => $notify->{'uri-id'}]);
+                       }
+
                        $conditions = ['(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?',
                                $notify->link,
                                $notify->parent,
index e3b1bea30c6b4695261025de68d8c9e22de9cb36..961321e0fa98b65fb2cbccdb762b66d47f631769 100644 (file)
@@ -55,7 +55,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1420);
+       define('DB_UPDATE_VERSION', 1421);
 }
 
 return [
@@ -882,6 +882,29 @@ return [
                        "mid" => ["mid"],
                ]
        ],
+       "notification" => [
+               "comment" => "notifications",
+               "fields" => [
+                       "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
+                       "uid" => ["type" => "mediumint unsigned", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
+                       "vid" => ["type" => "smallint unsigned", "foreign" => ["verb" => "id", "on delete" => "restrict"], "comment" => "Id of the verb table entry that contains the activity verbs"],
+                       "type" => ["type" => "tinyint unsigned", "comment" => ""],
+                       "actor-id" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Link to the contact table with uid=0 of the actor that caused the notification"],
+                       "target-uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Item-uri id of the related post"],
+                       "parent-uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"],
+                       "created" => ["type" => "datetime", "comment" => ""],
+                       "seen" => ["type" => "boolean", "default" => "0", "comment" => ""],
+               ],
+               "indexes" => [
+                       "PRIMARY" => ["id"],
+                       "uid_vid_type_actor-id_target-uri-id" => ["UNIQUE", "uid", "vid", "type", "actor-id", "target-uri-id"],
+                       "vid" => ["vid"],
+                       "actor-id" => ["actor-id"],
+                       "target-uri-id" => ["target-uri-id"],
+                       "parent-uri-id" => ["parent-uri-id"],
+                       "seen_uid" => ["seen", "uid"],
+               ]
+       ],
        "notify" => [
                "comment" => "notifications",
                "fields" => [