-- ------------------------------------------
-- Friendica 2021.06-rc (Siberian Iris)
--- DB_UPDATE_VERSION 1420
+-- DB_UPDATE_VERSION 1421
-- ------------------------------------------
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
--
}
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()]);
}
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
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;
}
$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);
}
}
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
use Friendica\Util\Strings;
use Friendica\Model\Tag;
use Friendica\Protocol\Activity;
-
+use Friendica\Util\DateTimeFormat;
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;
}
// 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)) {
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);
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;
}
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
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/
$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']);
}
}
- 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'])) {
$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'])) {
self::login(self::SCOPE_WRITE);
$uid = self::getCurrentUserID();
- DBA::update('notify', ['seen' => true], ['uid' => $uid]);
+ DBA::update('notification', ['seen' => true], ['uid' => $uid]);
System::jsonExit([]);
}
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([]);
}
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,
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
- define('DB_UPDATE_VERSION', 1420);
+ define('DB_UPDATE_VERSION', 1421);
}
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" => [