From 1b4e3564a5b4e44bd2d4991bc36900753b819542 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 28 Aug 2021 21:55:04 -0400 Subject: [PATCH 01/12] Add new paradigm classes for notify - Create BaseDepository class - Create Entity, Collection, Factory and Depository classes - Create FormattedNotification Entity, Collection and Factory to remove business logic from Notify repository - Create new NotificationCreationIntercepted exception to allow addons to cancel notification creation - Remove unused frio notifications/notify.tpl template --- src/BaseCollection.php | 15 +- src/BaseDepository.php | 101 +++++ src/Capabilities/ICanCreateFromTableRow.php | 16 + .../Collection/FormattedNotifications.php | 36 ++ .../Notifications/Collection/Notifies.php | 43 ++ .../Notifications/Depository/Notify.php | 141 +++++++ .../Notifications/Entity/Notify.php | 128 ++++++ ...tificationCreationInterceptedException.php | 7 + .../Factory/FormattedNotification.php | 376 ++++++++++++++++++ .../Notifications/Factory/Notify.php | 58 +++ .../ValueObject/FormattedNotification.php | 65 +++ 11 files changed, 983 insertions(+), 3 deletions(-) create mode 100644 src/BaseDepository.php create mode 100644 src/Capabilities/ICanCreateFromTableRow.php create mode 100644 src/Navigation/Notifications/Collection/FormattedNotifications.php create mode 100644 src/Navigation/Notifications/Collection/Notifies.php create mode 100644 src/Navigation/Notifications/Depository/Notify.php create mode 100644 src/Navigation/Notifications/Entity/Notify.php create mode 100644 src/Navigation/Notifications/Exception/NotificationCreationInterceptedException.php create mode 100644 src/Navigation/Notifications/Factory/FormattedNotification.php create mode 100644 src/Navigation/Notifications/Factory/Notify.php create mode 100644 src/Navigation/Notifications/ValueObject/FormattedNotification.php diff --git a/src/BaseCollection.php b/src/BaseCollection.php index c4d637a36..1aa13ae96 100644 --- a/src/BaseCollection.php +++ b/src/BaseCollection.php @@ -22,12 +22,11 @@ namespace Friendica; /** - * The Collection classes inheriting from this abstract class are meant to represent a list of database record. - * The associated model class has to be provided in the child classes. + * The Collection classes inheriting from this class are meant to represent a list of structured objects of a single type. * * Collections can be used with foreach(), accessed like an array and counted. */ -abstract class BaseCollection extends \ArrayIterator +class BaseCollection extends \ArrayIterator { /** * This property is used with paginated results to hold the total number of items satisfying the paginated request. @@ -115,4 +114,14 @@ abstract class BaseCollection extends \ArrayIterator { return new static(array_filter($this->getArrayCopy(), $callback, $flag)); } + + /** + * Reverse the orders of the elements in the collection + * + * @return $this + */ + public function reverse(): BaseCollection + { + return new static(array_reverse($this->getArrayCopy()), $this->getTotalCount()); + } } diff --git a/src/BaseDepository.php b/src/BaseDepository.php new file mode 100644 index 000000000..00d3bcfdf --- /dev/null +++ b/src/BaseDepository.php @@ -0,0 +1,101 @@ +db = $database; + $this->logger = $logger; + $this->factory = $factory; + } + + + /** + * @param array $condition + * @param array $params + * @return BaseCollection + * @throws Exception + */ + protected function _select(array $condition, array $params = []): BaseCollection + { + $rows = $this->db->selectToArray(static::$table_name, [], $condition, $params); + + $Entities = new BaseCollection(); + foreach ($rows as $fields) { + $Entities[] = $this->factory->createFromTableRow($fields); + } + + return $Entities; + } + + /** + * @param array $condition + * @param array $params + * @return BaseEntity + * @throws NotFoundException + */ + protected function _selectOne(array $condition, array $params = []): BaseEntity + { + $fields = $this->db->selectFirst(static::$table_name, [], $condition, $params); + if (!$this->db->isResult($fields)) { + throw new NotFoundException(); + } + + return $this->factory->createFromTableRow($fields); + } + + /** + * @param array $condition + * @param array $params + * @return int + * @throws Exception + */ + public function count(array $condition, array $params = []): int + { + return $this->db->count(static::$table_name, $condition, $params); + } + + /** + * @param array $condition + * @return bool + * @throws Exception + */ + public function exists(array $condition): bool + { + return $this->db->exists(static::$table_name, $condition); + } +} diff --git a/src/Capabilities/ICanCreateFromTableRow.php b/src/Capabilities/ICanCreateFromTableRow.php new file mode 100644 index 000000000..bdb6d662d --- /dev/null +++ b/src/Capabilities/ICanCreateFromTableRow.php @@ -0,0 +1,16 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\Collection; + +use Friendica\BaseCollection; +use Friendica\Navigation\Notifications\ValueObject; + +class FormattedNotifications extends BaseCollection +{ + /** + * @return ValueObject\FormattedNotification + */ + public function current(): ValueObject\FormattedNotification + { + return parent::current(); + } +} diff --git a/src/Navigation/Notifications/Collection/Notifies.php b/src/Navigation/Notifications/Collection/Notifies.php new file mode 100644 index 000000000..47fac8d0a --- /dev/null +++ b/src/Navigation/Notifications/Collection/Notifies.php @@ -0,0 +1,43 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\Collection; + +use Friendica\BaseCollection; +use Friendica\Navigation\Notifications\Entity; + +class Notifies extends BaseCollection +{ + /** + * @return Entity\Notify + */ + public function current(): Entity\Notify + { + return parent::current(); + } + + public function setSeen(): Notifies + { + return $this->map(function (Entity\Notify $Notify) { + $Notify->setSeen(); + }); + } +} diff --git a/src/Navigation/Notifications/Depository/Notify.php b/src/Navigation/Notifications/Depository/Notify.php new file mode 100644 index 000000000..c35f50a73 --- /dev/null +++ b/src/Navigation/Notifications/Depository/Notify.php @@ -0,0 +1,141 @@ +getArrayCopy()); + } + + public function countForUser($uid, array $condition, array $params = []): int + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->count($condition, $params); + } + + public function existsForUser($uid, array $condition): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->exists($condition); + } + + /** + * @param int $id + * @return Entity\Notify + * @throws HTTPException\NotFoundException + */ + public function selectOneById(int $id): Entity\Notify + { + return $this->selectOne(['id' => $id]); + } + + public function selectForUser(int $uid, array $condition, array $params): Collection\Notifies + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->select($condition, $params); + } + + public function selectAllForUser(int $uid, array $params = []): Collection\Notifies + { + return $this->selectForUser($uid, [], $params); + } + + public function setAllSeenForUser(int $uid, array $condition = []): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } + + /** + * @param Entity\Notify $Notify + * @return Entity\Notify + * @throws HTTPException\NotFoundException + * @throws HTTPException\InternalServerErrorException + * @throws Exception\NotificationCreationInterceptedException + */ + public function save(Entity\Notify $Notify): Entity\Notify + { + $fields = [ + 'type' => $Notify->type, + 'name' => $Notify->name, + 'url' => $Notify->url, + 'photo' => $Notify->photo, + 'msg' => $Notify->msg, + 'uid' => $Notify->uid, + 'link' => $Notify->link, + 'iid' => $Notify->itemId, + 'parent' => $Notify->parent, + 'seen' => $Notify->seen, + 'verb' => $Notify->verb, + 'otype' => $Notify->otype, + 'name_cache' => $Notify->name_cache, + 'msg_cache' => $Notify->msg_cache, + 'uri-id' => $Notify->uriId, + 'parent-uri-id' => $Notify->parentUriId, + ]; + + if ($Notify->id) { + $this->db->update(self::$table_name, $fields, ['id' => $Notify->id]); + } else { + $fields['date'] = DateTimeFormat::utcNow(); + Hook::callAll('enotify_store', $fields); + + $this->db->insert(self::$table_name, $fields); + + $Notify = $this->selectOneById($this->db->lastInsertId()); + } + + return $Notify; + } + + public function setAllSeenForRelatedNotify(Entity\Notify $Notify): bool + { + $condition = [ + '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?', + $Notify->link, + $Notify->parent, + $Notify->otype, + $Notify->uid + ]; + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } +} diff --git a/src/Navigation/Notifications/Entity/Notify.php b/src/Navigation/Notifications/Entity/Notify.php new file mode 100644 index 000000000..88cd8ab36 --- /dev/null +++ b/src/Navigation/Notifications/Entity/Notify.php @@ -0,0 +1,128 @@ +type = $type; + $this->name = $name; + $this->url = $url; + $this->photo = $photo; + $this->date = $date; + $this->msg = $msg; + $this->uid = $uid; + $this->link = $link; + $this->itemId = $itemId; + $this->parent = $parent; + $this->seen = $seen; + $this->verb = $verb; + $this->otype = $otype; + $this->name_cache = $name_cache; + $this->msg_cache = $msg_cache; + $this->uriId = $uriId; + $this->parentUriId = $parentUriId; + $this->id = $id; + } + + public function setSeen() + { + $this->seen = true; + } + + public function updateMsgFromPreamble($epreamble) + { + $this->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $this->link->__toString()]); + $this->msg_cache = self::formatMessage($this->name_cache, strip_tags(BBCode::convert($this->msg))); + } + + /** + * Formats a notification message with the notification author + * + * Replace the name with {0} but ensure to make that only once. The {0} is used + * later and prints the name in bold. + * + * @param string $name + * @param string $message + * + * @return string Formatted message + */ + public static function formatMessage(string $name, string $message): string + { + if ($name != '') { + $pos = strpos($message, $name); + } else { + $pos = false; + } + + if ($pos !== false) { + $message = substr_replace($message, '{0}', $pos, strlen($name)); + } + + return $message; + } +} diff --git a/src/Navigation/Notifications/Exception/NotificationCreationInterceptedException.php b/src/Navigation/Notifications/Exception/NotificationCreationInterceptedException.php new file mode 100644 index 000000000..5dd789028 --- /dev/null +++ b/src/Navigation/Notifications/Exception/NotificationCreationInterceptedException.php @@ -0,0 +1,7 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\Factory; + +use Exception; +use Friendica\App\BaseURL; +use Friendica\BaseFactory; +use Friendica\Content\Text\BBCode; +use Friendica\Core\L10n; +use Friendica\Core\Protocol; +use Friendica\Database\Database; +use Friendica\Model\Contact; +use Friendica\Model\Post; +use Friendica\Module\BaseNotifications; +use Friendica\Navigation\Notifications\Collection\FormattedNotifications; +use Friendica\Navigation\Notifications\Depository; +use Friendica\Navigation\Notifications\ValueObject; +use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Protocol\Activity; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Proxy; +use Friendica\Util\Temporal; +use Friendica\Util\XML; +use Psr\Log\LoggerInterface; + +/** + * Factory for creating notification objects based on items + * Currently, there are the following types of item based notifications: + * - network + * - system + * - home + * - personal + */ +class FormattedNotification extends BaseFactory +{ + /** @var Database */ + private $dba; + /** @var Depository\Notify */ + private $notify; + /** @var BaseURL */ + private $baseUrl; + /** @var L10n */ + private $l10n; + + public function __construct(LoggerInterface $logger, Database $dba, Depository\Notify $notify, BaseURL $baseUrl, L10n $l10n) + { + parent::__construct($logger); + + $this->dba = $dba; + $this->notify = $notify; + $this->baseUrl = $baseUrl; + $this->l10n = $l10n; + } + + /** + * @param array $formattedItem The return of $this->formatItem + * + * @return ValueObject\FormattedNotification + */ + private function createFromFormattedItem(array $formattedItem): ValueObject\FormattedNotification + { + // Transform the different types of notification in a usable array + switch ($formattedItem['verb'] ?? '') { + case Activity::LIKE: + return new ValueObject\FormattedNotification( + 'like', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s liked %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + case Activity::DISLIKE: + return new ValueObject\FormattedNotification( + 'dislike', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s disliked %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + case Activity::ATTEND: + return new ValueObject\FormattedNotification( + 'attend', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s is attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + case Activity::ATTENDNO: + return new ValueObject\FormattedNotification( + 'attendno', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s is not attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + case Activity::ATTENDMAYBE: + return new ValueObject\FormattedNotification( + 'attendmaybe', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s may attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + case Activity::FRIEND: + if (!isset($formattedItem['object'])) { + return new ValueObject\FormattedNotification( + 'friend', + $formattedItem['link'], + $formattedItem['image'], + $formattedItem['url'], + $formattedItem['text'], + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + } + + $xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; + $obj = XML::parseString($xmlHead . $formattedItem['object']); + + $formattedItem['fname'] = $obj->title; + + return new ValueObject\FormattedNotification( + 'friend', + $this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'], + $formattedItem['author-avatar'], + $formattedItem['author-link'], + $this->l10n->t("%s is now friends with %s", $formattedItem['author-name'], $formattedItem['fname']), + $formattedItem['when'], + $formattedItem['ago'], + $formattedItem['seen'] + ); + + default: + return new ValueObject\FormattedNotification( + $formattedItem['label'] ?? '', + $formattedItem['link'] ?? '', + $formattedItem['image'] ?? '', + $formattedItem['url'] ?? '', + $formattedItem['text'] ?? '', + $formattedItem['when'] ?? '', + $formattedItem['ago'] ?? '', + $formattedItem['seen'] ?? false + ); + } + } + + /** + * Get system notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return FormattedNotifications + */ + public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications + { + $conditions = []; + if (!$seen) { + $conditions['seen'] = false; + } + + $params = []; + $params['order'] = ['date' => 'DESC']; + $params['limit'] = [$start, $limit]; + + $formattedNotifications = new FormattedNotifications(); + try { + $Notifies = $this->notify->selectForUser(local_user(), $conditions, $params); + + foreach ($Notifies as $Notify) { + $formattedNotifications[] = new ValueObject\FormattedNotification( + 'notification', + $this->baseUrl->get(true) . '/notification/' . $Notify->id, + Contact::getAvatarUrlForUrl($Notify->url, $Notify->uid, Proxy::SIZE_MICRO), + $Notify->url, + strip_tags(BBCode::toPlaintext($Notify->msg)), + DateTimeFormat::local($Notify->date->format(DateTimeFormat::MYSQL), 'r'), + Temporal::getRelativeDate($Notify->date->format(DateTimeFormat::MYSQL)), + $Notify->seen + ); + } + } catch (Exception $e) { + $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]); + } + + return $formattedNotifications; + } + + /** + * Get network notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return FormattedNotifications + */ + public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications + { + $condition = ['wall' => false, 'uid' => local_user()]; + + if (!$seen) { + $condition['unseen'] = true; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $formattedNotifications = new FormattedNotifications(); + + try { + $userPosts = Post::selectForUser(local_user(), $fields, $condition, $params); + while ($userPost = $this->dba->fetch($userPosts)) { + $formattedNotifications[] = $this->createFromFormattedItem($this->formatItem($userPost)); + } + } catch (Exception $e) { + $this->logger->warning('Select failed.', ['condition' => $condition, 'exception' => $e]); + } + + return $formattedNotifications; + } + + /** + * Get personal notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return FormattedNotifications + */ + public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications + { + $condition = ['wall' => false, 'uid' => local_user(), 'author-id' => public_contact()]; + + if (!$seen) { + $condition['unseen'] = true; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $formattedNotifications = new FormattedNotifications(); + + try { + $userPosts = Post::selectForUser(local_user(), $fields, $condition, $params); + while ($userPost = $this->dba->fetch($userPosts)) { + $formattedNotifications[] = $this->createFromFormattedItem($this->formatItem($userPost)); + } + } catch (Exception $e) { + $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]); + } + + return $formattedNotifications; + } + + /** + * Get home notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return FormattedNotifications + */ + public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications + { + $condition = ['wall' => true, 'uid' => local_user()]; + + if (!$seen) { + $condition['unseen'] = true; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $formattedNotifications = new FormattedNotifications(); + + try { + $userPosts = Post::selectForUser(local_user(), $fields, $condition, $params); + while ($userPost = $this->dba->fetch($userPosts)) { + $formattedItem = $this->formatItem($userPost); + + // Overwrite specific fields, not default item format + $formattedItem['label'] = 'comment'; + $formattedItem['text'] = $this->l10n->t("%s commented on %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']); + + $formattedNotifications[] = $this->createFromFormattedItem($formattedItem); + } + } catch (Exception $e) { + $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]); + } + + return $formattedNotifications; + } + + /** + * Format the item query in a usable array + * + * @param array $item The item from the db query + * + * @return array The item, extended with the notification-specific information + * + * @throws InternalServerErrorException + * @throws Exception + */ + private function formatItem(array $item): array + { + $item['seen'] = !($item['unseen'] > 0); + + // For feed items we use the user's contact, since the avatar is mostly self choosen. + if (!empty($item['network']) && $item['network'] == Protocol::FEED) { + $item['author-avatar'] = $item['contact-avatar']; + } + + $item['label'] = (($item['gravity'] == GRAVITY_PARENT) ? 'post' : 'comment'); + $item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid']; + $item['image'] = $item['author-avatar']; + $item['url'] = $item['author-link']; + $item['when'] = DateTimeFormat::local($item['created'], 'r'); + $item['ago'] = Temporal::getRelativeDate($item['created']); + $item['text'] = (($item['gravity'] == GRAVITY_PARENT) + ? $this->l10n->t("%s created a new post", $item['author-name']) + : $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name'])); + + return $item; + } +} diff --git a/src/Navigation/Notifications/Factory/Notify.php b/src/Navigation/Notifications/Factory/Notify.php new file mode 100644 index 000000000..87ba7d2fe --- /dev/null +++ b/src/Navigation/Notifications/Factory/Notify.php @@ -0,0 +1,58 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\ValueObject; + +use Friendica\BaseDataTransferObject; + +/** + * A view-only object for printing item notifications to the frontend + */ +class FormattedNotification extends BaseDataTransferObject +{ + const SYSTEM = 'system'; + const PERSONAL = 'personal'; + const NETWORK = 'network'; + const INTRO = 'intro'; + const HOME = 'home'; + + /** @var string */ + protected $label = ''; + /** @var string */ + protected $link = ''; + /** @var string */ + protected $image = ''; + /** @var string */ + protected $url = ''; + /** @var string */ + protected $text = ''; + /** @var string */ + protected $when = ''; + /** @var string */ + protected $ago = ''; + /** @var boolean */ + protected $seen = false; + + public function __construct(string $label, string $link, string $image, string $url, string $text, string $when, string $ago, bool $seen) + { + $this->label = $label ?? ''; + $this->link = $link ?? ''; + $this->image = $image ?? ''; + $this->url = $url ?? ''; + $this->text = $text ?? ''; + $this->when = $when ?? ''; + $this->ago = $ago ?? ''; + $this->seen = $seen ?? false; + } +} From 47acb6a2786ea5854228501160dd22199e6c2748 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 18 Sep 2021 01:08:29 -0400 Subject: [PATCH 02/12] Move notify to the new paradigm - Remove unused frion notify template - Update API test --- include/api.php | 53 ++- include/enotify.php | 51 +-- mod/display.php | 2 +- mod/ping.php | 2 +- src/DI.php | 15 +- src/Factory/Notification/Notification.php | 392 ------------------ src/Model/Notification.php | 92 ---- src/Module/BaseNotifications.php | 32 +- src/Module/Delegation.php | 4 +- src/Module/Notifications/Notification.php | 32 +- src/Module/Notifications/Notifications.php | 50 +-- src/Object/Api/Friendica/Notification.php | 40 +- src/Object/Notification/Notification.php | 143 ------- src/Repository/Notification.php | 130 ------ tests/datasets/api.fixture.php | 2 +- tests/legacy/ApiTest.php | 7 +- view/templates/notifications/attend_item.tpl | 2 +- .../templates/notifications/comments_item.tpl | 2 +- .../templates/notifications/dislikes_item.tpl | 2 +- view/templates/notifications/friends_item.tpl | 2 +- view/templates/notifications/likes_item.tpl | 2 +- view/templates/notifications/network_item.tpl | 2 +- view/templates/notifications/notification.tpl | 2 +- view/templates/notifications/posts_item.tpl | 2 +- .../frio/templates/notifications/notify.tpl | 12 - 25 files changed, 150 insertions(+), 925 deletions(-) delete mode 100644 src/Factory/Notification/Notification.php delete mode 100644 src/Object/Notification/Notification.php delete mode 100644 src/Repository/Notification.php delete mode 100644 view/theme/frio/templates/notifications/notify.tpl diff --git a/include/api.php b/include/api.php index af9fe7736..52f04d954 100644 --- a/include/api.php +++ b/include/api.php @@ -24,15 +24,14 @@ */ use Friendica\App; +use Friendica\Collection\Api\Notifications as ApiNotifications; use Friendica\Content\ContactSelector; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\Session; use Friendica\Core\System; -use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; @@ -54,6 +53,7 @@ use Friendica\Network\HTTPException\MethodNotAllowedException; use Friendica\Network\HTTPException\NotFoundException; use Friendica\Network\HTTPException\TooManyRequestsException; use Friendica\Network\HTTPException\UnauthorizedException; +use Friendica\Object\Api\Friendica\Notification as ApiNotification; use Friendica\Object\Image; use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; @@ -5580,23 +5580,24 @@ api_register_func('api/friendica/activity/unattendmaybe', 'api_friendica_activit */ function api_friendica_notification($type) { - $a = DI::app(); - if (api_user() === false) { throw new ForbiddenException(); } if (DI::args()->getArgc()!==3) { - throw new BadRequestException("Invalid argument count"); + throw new BadRequestException('Invalid argument count'); } - $notifications = DI::notification()->getApiList(local_user()); + $Notifies = DI::notify()->selectAllForUser(local_user(), ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50]); - if ($type == "xml") { - $xmlnotes = false; - if (!empty($notifications)) { - foreach ($notifications as $notification) { - $xmlnotes[] = ["@attributes" => $notification->toArray()]; - } + $notifications = new ApiNotifications(); + foreach ($Notifies as $Notify) { + $notifications[] = new ApiNotification($Notify); + } + + if ($type == 'xml') { + $xmlnotes = []; + foreach ($notifications as $notification) { + $xmlnotes[] = ['@attributes' => $notification->toArray()]; } $result = $xmlnotes; @@ -5606,7 +5607,7 @@ function api_friendica_notification($type) $result = false; } - return api_format_data("notes", $type, ['note' => $result]); + return api_format_data('notes', $type, ['note' => $result]); } /** @@ -5631,26 +5632,36 @@ function api_friendica_notification_seen($type) throw new ForbiddenException(); } if (DI::args()->getArgc() !== 4) { - throw new BadRequestException("Invalid argument count"); + throw new BadRequestException('Invalid argument count'); } - $id = (!empty($_REQUEST['id']) ? intval($_REQUEST['id']) : 0); + $id = intval($_REQUEST['id'] ?? 0); try { - $notify = DI::notify()->getByID($id, api_user()); - DI::notify()->setSeen(true, $notify); + $Notify = DI::notify()->selectOneById($id); + if ($Notify->uid !== api_user()) { + throw new NotFoundException(); + } - if ($notify->otype === Notification\ObjectType::ITEM) { - $item = Post::selectFirstForUser(api_user(), [], ['id' => $notify->iid, 'uid' => api_user()]); + if ($Notify->uriId) { + DI::dba()->update('notification', ['seen' => true], ['uid' => $Notify->uid, 'target-uri-id' => $Notify->uriId]); + } + + $Notify->setSeen(); + DI::notify()->save($Notify); + + if ($Notify->otype === Notification\ObjectType::ITEM) { + $item = Post::selectFirstForUser(api_user(), [], ['id' => $Notify->iid, 'uid' => api_user()]); if (DBA::isResult($item)) { // we found the item, return it to the user $ret = api_format_items([$item], $user_info, false, $type); $data = ['status' => $ret]; - return api_format_data("status", $type, $data); + return api_format_data('status', $type, $data); } // the item can't be found, but we set the notification as seen, so we count this as a success } - return api_format_data('result', $type, ['result' => "success"]); + + return api_format_data('result', $type, ['result' => 'success']); } catch (NotFoundException $e) { throw new BadRequestException('Invalid argument', $e); } catch (Exception $e) { diff --git a/include/enotify.php b/include/enotify.php index 84b46edc0..c3d5e4a40 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -19,11 +19,9 @@ * */ -use Friendica\Content\Text\BBCode; use Friendica\Content\Text\Plaintext; use Friendica\Core\Hook; use Friendica\Core\Logger; -use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; @@ -33,6 +31,7 @@ use Friendica\Model\Notification; use Friendica\Model\Post; use Friendica\Model\User; use Friendica\Model\Verb; +use Friendica\Navigation\Notifications; use Friendica\Protocol\Activity; /** @@ -397,42 +396,22 @@ function notification_store_and_send($params, $sitelink, $tsitelink, $hsitelink, $notify_id = 0; if ($show_in_notification_page) { - $fields = [ - 'name' => $params['source_name'] ?? '', - 'name_cache' => substr(strip_tags(BBCode::convertForUriId($uri_id, $params['source_name'])), 0, 255), - 'url' => $params['source_link'] ?? '', - 'photo' => $params['source_photo'] ?? '', - 'link' => $itemlink ?? '', - 'uid' => $params['uid'] ?? 0, - 'type' => $params['type'] ?? '', - 'verb' => $params['verb'] ?? '', - 'otype' => $params['otype'] ?? '', - ]; - if (!empty($item_id)) { - $fields['iid'] = $item_id; - } - if (!empty($uri_id)) { - $fields['uri-id'] = $uri_id; - } - if (!empty($parent_id)) { - $fields['parent'] = $parent_id; - } - if (!empty($parent_uri_id)) { - $fields['parent-uri-id'] = $parent_uri_id; - } - $notification = DI::notify()->insert($fields); + /** @var $factory Notifications\Factory\Notify */ + $factory = DI::getDice()->create(Notifications\Factory\Notify::class); - // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook - if (!$notification) { + $Notify = $factory->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id); + try { + $Notify = DI::notify()->save($Notify); + } catch (Notifications\Exception\NotificationCreationInterceptedException $e) { + // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook return false; } - $notification->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $notification->link]); + $Notify->updateMsgFromPreamble($epreamble); + $Notify = DI::notify()->save($Notify); - DI::notify()->update($notification); - - $itemlink = DI::baseUrl() . '/notification/' . $notification->id; - $notify_id = $notification->id; + $itemlink = DI::baseUrl() . '/notification/' . $Notify->id; + $notify_id = $Notify->id; } // send email notification if notification preferences permit @@ -566,9 +545,9 @@ function notification_from_array(array $notification) // Check to see if there was already a tag notify or comment notify for this post. // If so don't create a second notification $condition = ['type' => [Notification\Type::TAG_SELF, Notification\Type::COMMENT, Notification\Type::SHARE], - 'link' => $params['link'], 'verb' => Activity::POST, 'uid' => $notification['uid']]; - if (DBA::exists('notify', $condition)) { - Logger::info('Duplicate found, quitting', $condition); + 'link' => $params['link'], 'verb' => Activity::POST]; + if (DI::notify()->existsForUser($notification['uid'], $condition)) { + Logger::info('Duplicate found, quitting', $condition + ['uid' => $notification['uid']]); return false; } diff --git a/mod/display.php b/mod/display.php index 87775126e..26f6eae36 100644 --- a/mod/display.php +++ b/mod/display.php @@ -223,7 +223,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()]); + DI::notify()->setAllSeenForUser(local_user(), ['parent-uri-id' => $item['parent-uri-id']]); } // We are displaying an "alternate" link if that post was public. See issue 2864 diff --git a/mod/ping.php b/mod/ping.php index 69aef1bf3..2036b8ec4 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -445,7 +445,7 @@ function ping_get_notifications($uid) $notification["message"] = $notification["msg_cache"]; } else { $notification["name"] = strip_tags(BBCode::convert($notification["name"])); - $notification["message"] = Notification::formatMessage($notification["name"], strip_tags(BBCode::convert($notification["msg"]))); + $notification["message"] = \Friendica\Navigation\Notifications\Entity\Notify::formatMessage($notification["name"], BBCode::toPlaintext($notification["msg"])); q( "UPDATE `notify` SET `name_cache` = '%s', `msg_cache` = '%s' WHERE `id` = %d", diff --git a/src/DI.php b/src/DI.php index ed67efe00..a54f26c07 100644 --- a/src/DI.php +++ b/src/DI.php @@ -370,14 +370,6 @@ abstract class DI return self::$dice->create(Factory\Api\Twitter\User::class); } - /** - * @return Factory\Notification\Notification - */ - public static function notification() - { - return self::$dice->create(Factory\Notification\Notification::class); - } - /** * @return Factory\Notification\Introduction */ @@ -469,12 +461,9 @@ abstract class DI return self::$dice->create(Repository\ProfileField::class); } - /** - * @return Repository\Notification - */ - public static function notify() + public static function notify(): Navigation\Notifications\Depository\Notify { - return self::$dice->create(Repository\Notification::class); + return self::$dice->create(Navigation\Notifications\Depository\Notify::class); } // diff --git a/src/Factory/Notification/Notification.php b/src/Factory/Notification/Notification.php deleted file mode 100644 index 9f833130a..000000000 --- a/src/Factory/Notification/Notification.php +++ /dev/null @@ -1,392 +0,0 @@ -. - * - */ - -namespace Friendica\Factory\Notification; - -use Exception; -use Friendica\App; -use Friendica\App\BaseURL; -use Friendica\BaseFactory; -use Friendica\Collection\Api\Notifications as ApiNotifications; -use Friendica\Content\Text\BBCode; -use Friendica\Core\L10n; -use Friendica\Core\PConfig\IPConfig; -use Friendica\Core\Protocol; -use Friendica\Core\Session\ISession; -use Friendica\Database\Database; -use Friendica\Model\Contact; -use Friendica\Model\Post; -use Friendica\Module\BaseNotifications; -use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Object\Api\Friendica\Notification as ApiNotification; -use Friendica\Protocol\Activity; -use Friendica\Repository; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy; -use Friendica\Util\Temporal; -use Friendica\Util\XML; -use Psr\Log\LoggerInterface; - -/** - * Factory for creating notification objects based on items - * Currently, there are the following types of item based notifications: - * - network - * - system - * - home - * - personal - */ -class Notification extends BaseFactory -{ - /** @var Database */ - private $dba; - /** @var Repository\Notification */ - private $notification; - /** @var BaseURL */ - private $baseUrl; - /** @var L10n */ - private $l10n; - - public function __construct(LoggerInterface $logger, Database $dba, Repository\Notification $notification, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session) - { - parent::__construct($logger); - - $this->dba = $dba; - $this->notification = $notification; - $this->baseUrl = $baseUrl; - $this->l10n = $l10n; - } - - /** - * Format the item query in an usable array - * - * @param array $item The item from the db query - * - * @return array The item, extended with the notification-specific information - * - * @throws InternalServerErrorException - * @throws Exception - */ - private function formatItem(array $item) - { - $item['seen'] = ($item['unseen'] > 0 ? false : true); - - // For feed items we use the user's contact, since the avatar is mostly self choosen. - if (!empty($item['network']) && $item['network'] == Protocol::FEED) { - $item['author-avatar'] = $item['contact-avatar']; - } - - $item['label'] = (($item['gravity'] == GRAVITY_PARENT) ? 'post' : 'comment'); - $item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid']; - $item['image'] = $item['author-avatar']; - $item['url'] = $item['author-link']; - $item['text'] = (($item['gravity'] == GRAVITY_PARENT) - ? $this->l10n->t("%s created a new post", $item['author-name']) - : $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name'])); - $item['when'] = DateTimeFormat::local($item['created'], 'r'); - $item['ago'] = Temporal::getRelativeDate($item['created']); - - return $item; - } - - /** - * @param array $item - * - * @return \Friendica\Object\Notification\Notification - * - * @throws InternalServerErrorException - */ - private function createFromItem(array $item) - { - $item = $this->formatItem($item); - - // Transform the different types of notification in an usable array - switch ($item['verb'] ?? '') { - case Activity::LIKE: - return new \Friendica\Object\Notification\Notification([ - 'label' => 'like', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s liked %s's post", $item['author-name'], $item['parent-author-name']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - case Activity::DISLIKE: - return new \Friendica\Object\Notification\Notification([ - 'label' => 'dislike', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s disliked %s's post", $item['author-name'], $item['parent-author-name']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - case Activity::ATTEND: - return new \Friendica\Object\Notification\Notification([ - 'label' => 'attend', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s is attending %s's event", $item['author-name'], $item['parent-author-name']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - case Activity::ATTENDNO: - return new \Friendica\Object\Notification\Notification([ - 'label' => 'attendno', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s is not attending %s's event", $item['author-name'], $item['parent-author-name']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - case Activity::ATTENDMAYBE: - return new \Friendica\Object\Notification\Notification([ - 'label' => 'attendmaybe', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s may attending %s's event", $item['author-name'], $item['parent-author-name']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - case Activity::FRIEND: - if (!isset($item['object'])) { - return new \Friendica\Object\Notification\Notification([ - 'label' => 'friend', - 'link' => $item['link'], - 'image' => $item['image'], - 'url' => $item['url'], - 'text' => $item['text'], - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - } - - $xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; - $obj = XML::parseString($xmlHead . $item['object']); - $item['fname'] = $obj->title; - - return new \Friendica\Object\Notification\Notification([ - 'label' => 'friend', - 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => $item['author-avatar'], - 'url' => $item['author-link'], - 'text' => $this->l10n->t("%s is now friends with %s", $item['author-name'], $item['fname']), - 'when' => $item['when'], - 'ago' => $item['ago'], - 'seen' => $item['seen']]); - - default: - return new \Friendica\Object\Notification\Notification($item); - break; - } - } - - /** - * Get system notifications - * - * @param bool $seen False => only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return \Friendica\Module\Notifications\Notification[] - */ - public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT) - { - $conditions = ['uid' => local_user()]; - - if (!$seen) { - $conditions['seen'] = false; - } - - $params = []; - $params['order'] = ['date' => 'DESC']; - $params['limit'] = [$start, $limit]; - - $formattedNotifications = []; - try { - $notifications = $this->notification->select($conditions, $params); - - foreach ($notifications as $notification) { - $formattedNotifications[] = new \Friendica\Object\Notification\Notification([ - 'label' => 'notification', - 'link' => $this->baseUrl->get(true) . '/notification/' . $notification->id, - 'image' => Contact::getAvatarUrlForUrl($notification->url, $notification->uid, Proxy::SIZE_MICRO), - 'url' => $notification->url, - 'text' => strip_tags(BBCode::convert($notification->msg)), - 'when' => DateTimeFormat::local($notification->date, 'r'), - 'ago' => Temporal::getRelativeDate($notification->date), - 'seen' => $notification->seen]); - } - } catch (Exception $e) { - $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]); - } - - return $formattedNotifications; - } - - /** - * Get network notifications - * - * @param bool $seen False => only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return \Friendica\Object\Notification\Notification[] - */ - public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT) - { - $conditions = ['wall' => false, 'uid' => local_user()]; - - if (!$seen) { - $conditions['unseen'] = true; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - - $formattedNotifications = []; - - try { - $items = Post::selectForUser(local_user(), $fields, $conditions, $params); - - while ($item = $this->dba->fetch($items)) { - $formattedNotifications[] = $this->createFromItem($item); - } - } catch (Exception $e) { - $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]); - } - - return $formattedNotifications; - } - - /** - * Get personal notifications - * - * @param bool $seen False => only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return \Friendica\Object\Notification\Notification[] - */ - public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT) - { - $condition = ["NOT `wall` AND `uid` = ? AND `author-id` = ?", local_user(), public_contact()]; - - if (!$seen) { - $condition[0] .= " AND `unseen`"; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - - $formattedNotifications = []; - - try { - $items = Post::selectForUser(local_user(), $fields, $condition, $params); - - while ($item = $this->dba->fetch($items)) { - $formattedNotifications[] = $this->createFromItem($item); - } - } catch (Exception $e) { - $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]); - } - - return $formattedNotifications; - } - - /** - * Get home notifications - * - * @param bool $seen False => only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return \Friendica\Object\Notification\Notification[] - */ - public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT) - { - $condition = ['wall' => true, 'uid' => local_user()]; - - if (!$seen) { - $condition['unseen'] = true; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - - $formattedNotifications = []; - - try { - $items = Post::selectForUser(local_user(), $fields, $condition, $params); - - while ($item = $this->dba->fetch($items)) { - $item = $this->formatItem($item); - - // Overwrite specific fields, not default item format - $item['label'] = 'comment'; - $item['text'] = $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']); - - $formattedNotifications[] = $this->createFromItem($item); - } - } catch (Exception $e) { - $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]); - } - - return $formattedNotifications; - } - - /** - * @param int $uid The user id of the API call - * @param array $params Additional parameters - * - * @return ApiNotifications - * - * @throws Exception - */ - public function getApiList(int $uid, array $params = ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50]) - { - $notifies = $this->notification->select(['uid' => $uid], $params); - - /** @var ApiNotification[] $notifications */ - $notifications = []; - - foreach ($notifies as $notify) { - $notifications[] = new ApiNotification($notify); - } - - return new ApiNotifications($notifications); - } -} diff --git a/src/Model/Notification.php b/src/Model/Notification.php index 1df420845..e9f9ff41c 100644 --- a/src/Model/Notification.php +++ b/src/Model/Notification.php @@ -33,101 +33,9 @@ use Psr\Log\LoggerInterface; /** * Model for an entry in the notify table - * - * @property string hash - * @property integer type - * @property string name Full name of the contact subject - * @property string url Profile page URL of the contact subject - * @property string photo Profile photo URL of the contact subject - * @property string date YYYY-MM-DD hh:mm:ss local server time - * @property string msg - * @property integer uid Owner User Id - * @property string link Notification URL - * @property integer iid Item Id - * @property integer parent Parent Item Id - * @property boolean seen Whether the notification was read or not. - * @property string verb Verb URL (@see http://activitystrea.ms) - * @property string otype Subject type ('item', 'intro' or 'mail') - * - * @property-read string name_cache Full name of the contact subject - * @property-read string msg_cache Plaintext version of the notification text with a placeholder (`{0}`) for the subject contact's name. */ class Notification extends BaseModel { - /** @var \Friendica\Repository\Notification */ - private $repo; - - public function __construct(Database $dba, LoggerInterface $logger, \Friendica\Repository\Notification $repo, array $data = []) - { - parent::__construct($dba, $logger, $data); - - $this->repo = $repo; - - $this->setNameCache(); - $this->setMsgCache(); - } - - /** - * Sets the pre-formatted name (caching) - */ - private function setNameCache() - { - try { - $this->name_cache = strip_tags(BBCode::convert($this->source_name)); - } catch (InternalServerErrorException $e) { - } - } - - /** - * Sets the pre-formatted msg (caching) - */ - private function setMsgCache() - { - try { - $this->msg_cache = self::formatMessage($this->name_cache, strip_tags(BBCode::convert($this->msg))); - } catch (InternalServerErrorException $e) { - } - } - - public function __set($name, $value) - { - parent::__set($name, $value); - - if ($name == 'msg') { - $this->setMsgCache(); - } - - if ($name == 'source_name') { - $this->setNameCache(); - } - } - - /** - * Formats a notification message with the notification author - * - * Replace the name with {0} but ensure to make that only once. The {0} is used - * later and prints the name in bold. - * - * @param string $name - * @param string $message - * - * @return string Formatted message - */ - public static function formatMessage($name, $message) - { - if ($name != '') { - $pos = strpos($message, $name); - } else { - $pos = false; - } - - if ($pos !== false) { - $message = substr_replace($message, '{0}', $pos, strlen($name)); - } - - return $message; - } - /** * Fetch the notification type for the given notification * diff --git a/src/Module/BaseNotifications.php b/src/Module/BaseNotifications.php index a472c520a..e7f9bdabe 100644 --- a/src/Module/BaseNotifications.php +++ b/src/Module/BaseNotifications.php @@ -27,8 +27,8 @@ use Friendica\Content\Pager; use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\DI; +use Friendica\Navigation\Notifications\ValueObject\FormattedNotification; use Friendica\Network\HTTPException\ForbiddenException; -use Friendica\Object\Notification\Notification; /** * Base Module for each tab of the notification display @@ -39,29 +39,29 @@ abstract class BaseNotifications extends BaseModule { /** @var array Array of URL parameters */ const URL_TYPES = [ - Notification::NETWORK => 'network', - Notification::SYSTEM => 'system', - Notification::HOME => 'home', - Notification::PERSONAL => 'personal', - Notification::INTRO => 'intros', + FormattedNotification::NETWORK => 'network', + FormattedNotification::SYSTEM => 'system', + FormattedNotification::HOME => 'home', + FormattedNotification::PERSONAL => 'personal', + FormattedNotification::INTRO => 'intros', ]; /** @var array Array of the allowed notifications and their printable name */ const PRINT_TYPES = [ - Notification::NETWORK => 'Network', - Notification::SYSTEM => 'System', - Notification::HOME => 'Home', - Notification::PERSONAL => 'Personal', - Notification::INTRO => 'Introductions', + FormattedNotification::NETWORK => 'Network', + FormattedNotification::SYSTEM => 'System', + FormattedNotification::HOME => 'Home', + FormattedNotification::PERSONAL => 'Personal', + FormattedNotification::INTRO => 'Introductions', ]; /** @var array The array of access keys for notification pages */ const ACCESS_KEYS = [ - Notification::NETWORK => 'w', - Notification::SYSTEM => 'y', - Notification::HOME => 'h', - Notification::PERSONAL => 'r', - Notification::INTRO => 'i', + FormattedNotification::NETWORK => 'w', + FormattedNotification::SYSTEM => 'y', + FormattedNotification::HOME => 'h', + FormattedNotification::PERSONAL => 'r', + FormattedNotification::INTRO => 'i', ]; /** @var int The default count of items per page */ diff --git a/src/Module/Delegation.php b/src/Module/Delegation.php index 54f868881..279c694b1 100644 --- a/src/Module/Delegation.php +++ b/src/Module/Delegation.php @@ -126,9 +126,9 @@ class Delegation extends BaseModule $identities[$key]['selected'] = ($identity['nickname'] === DI::app()->getLoggedInUserNickname()); - $condition = ["`uid` = ? AND `msg` != '' AND NOT (`type` IN (?, ?)) AND NOT `seen`", $identity['uid'], Notification\Type::INTRO, Notification\Type::MAIL]; + $condition = ["`msg` != '' AND NOT (`type` IN (?, ?)) AND NOT `seen`", Notification\Type::INTRO, Notification\Type::MAIL]; $params = ['distinct' => true, 'expression' => 'parent']; - $notifications = DBA::count('notify', $condition, $params); + $notifications = DI::notify()->countForUser($identity['uid'], $condition, $params); $params = ['distinct' => true, 'expression' => 'convid']; $notifications += DBA::count('mail', ['uid' => $identity['uid'], 'seen' => false], $params); diff --git a/src/Module/Notifications/Notification.php b/src/Module/Notifications/Notification.php index 14e58c217..6f032772c 100644 --- a/src/Module/Notifications/Notification.php +++ b/src/Module/Notifications/Notification.php @@ -78,7 +78,8 @@ class Notification extends BaseModule if (DI::args()->get(1) === 'mark' && DI::args()->get(2) === 'all') { try { - $success = DI::notify()->setSeen(); + DI::dba()->update('notification', ['seen' => true], ['uid' => local_user()]); + $success = DI::notify()->setAllSeenForUser(local_user()); } catch (\Exception $e) { DI::logger()->warning('set all seen failed.', ['exception' => $e]); $success = false; @@ -97,7 +98,7 @@ class Notification extends BaseModule * @throws HTTPException\InternalServerErrorException * @throws \Exception */ - public static function content(array $parameters = []) + public static function content(array $parameters = []): string { if (!local_user()) { notice(DI::l10n()->t('You must be logged in to show this page.')); @@ -107,17 +108,24 @@ class Notification extends BaseModule $request_id = $parameters['id'] ?? false; if ($request_id) { - $notify = DI::notify()->getByID($request_id, local_user()); - - if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) { - $notify->seen = true; - DI::notify()->update($notify); - } else { - DI::notify()->setSeen(true, $notify); + $Notify = DI::notify()->selectOneById($request_id); + if ($Notify->uid !== local_user()) { + throw new HTTPException\ForbiddenException(); } - if (!empty($notify->link)) { - System::externalRedirect($notify->link); + if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) { + $Notify->setSeen(); + DI::notify()->save($Notify); + } else { + if ($Notify->uriId) { + DI::dba()->update('notification', ['seen' => true], ['uid' => $Notify->uid, 'target-uri-id' => $Notify->uriId]); + } + + DI::notify()->setAllSeenForRelatedNotify($Notify); + } + + if ($Notify->link) { + System::externalRedirect($Notify->link); } DI::baseUrl()->redirect(); @@ -125,6 +133,6 @@ class Notification extends BaseModule DI::baseUrl()->redirect('notifications/system'); - throw new HTTPException\InternalServerErrorException('Invalid situation.'); + return ''; } } diff --git a/src/Module/Notifications/Notifications.php b/src/Module/Notifications/Notifications.php index 06b5ca80d..79f1596f9 100644 --- a/src/Module/Notifications/Notifications.php +++ b/src/Module/Notifications/Notifications.php @@ -25,7 +25,9 @@ use Friendica\Content\Nav; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Module\BaseNotifications; -use Friendica\Object\Notification\Notification; +use Friendica\Navigation\Notifications\Collection\FormattedNotifications; +use Friendica\Navigation\Notifications\ValueObject\FormattedNotification; +use Friendica\Network\HTTPException\InternalServerErrorException; /** * Prints all notification types except introduction: @@ -42,41 +44,35 @@ class Notifications extends BaseNotifications public static function getNotifications() { $notificationHeader = ''; - /** @var Notification[] $notifications */ $notifications = []; - // Get the network notifications + /** @var \Friendica\Navigation\Notifications\Factory\FormattedNotification $factory */ + $factory = DI::getDice()->create(\Friendica\Navigation\Notifications\Factory\FormattedNotification::class); + if ((DI::args()->get(1) == 'network')) { $notificationHeader = DI::l10n()->t('Network Notifications'); $notifications = [ - 'ident' => Notification::NETWORK, - 'notifications' => DI::notification()->getNetworkList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), + 'ident' => FormattedNotification::NETWORK, + 'notifications' => $factory->getNetworkList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), ]; - - // Get the system notifications } elseif ((DI::args()->get(1) == 'system')) { $notificationHeader = DI::l10n()->t('System Notifications'); $notifications = [ - 'ident' => Notification::SYSTEM, - 'notifications' => DI::notification()->getSystemList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), + 'ident' => FormattedNotification::SYSTEM, + 'notifications' => $factory->getSystemList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), ]; - - // Get the personal notifications } elseif ((DI::args()->get(1) == 'personal')) { $notificationHeader = DI::l10n()->t('Personal Notifications'); $notifications = [ - 'ident' => Notification::PERSONAL, - 'notifications' => DI::notification()->getPersonalList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), + 'ident' => FormattedNotification::PERSONAL, + 'notifications' => $factory->getPersonalList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), ]; - - // Get the home notifications } elseif ((DI::args()->get(1) == 'home')) { $notificationHeader = DI::l10n()->t('Home Notifications'); $notifications = [ - 'ident' => Notification::HOME, - 'notifications' => DI::notification()->getHomeList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), + 'ident' => FormattedNotification::HOME, + 'notifications' => $factory->getHomeList(self::$showAll, self::$firstItemNum, self::ITEMS_PER_PAGE), ]; - // fallback - redirect to main page } else { DI::baseUrl()->redirect('notifications'); } @@ -98,11 +94,10 @@ class Notifications extends BaseNotifications $notifications = $notificationResult['notifications'] ?? []; $notificationHeader = $notificationResult['header'] ?? ''; - if (!empty($notifications['notifications'])) { // Loop trough ever notification This creates an array with the output html for each // notification and apply the correct template according to the notificationtype (label). - /** @var Notification $notification */ + /** @var FormattedNotification $notification */ foreach ($notifications['notifications'] as $notification) { $notification_templates = [ 'like' => 'notifications/likes_item.tpl', @@ -116,21 +111,16 @@ class Notifications extends BaseNotifications 'notification' => 'notifications/notification.tpl', ]; - $notificationTemplate = Renderer::getMarkupTemplate($notification_templates[$notification->getLabel()]); + $notificationArray = $notification->toArray(); + + $notificationTemplate = Renderer::getMarkupTemplate($notification_templates[$notificationArray['label']]); $notificationContent[] = Renderer::replaceMacros($notificationTemplate, [ - '$item_label' => $notification->getLabel(), - '$item_link' => $notification->getLink(), - '$item_image' => $notification->getImage(), - '$item_url' => $notification->getUrl(), - '$item_text' => $notification->getText(), - '$item_when' => $notification->getWhen(), - '$item_ago' => $notification->getAgo(), - '$item_seen' => $notification->isSeen(), + '$notification' => $notificationArray ]); } } else { - $notificationNoContent = DI::l10n()->t('No more %s notifications.', $notifications['ident']); + $notificationNoContent = DI::l10n()->t('No more %s notifications.', $notificationResult['ident']); } $notificationShowLink = [ diff --git a/src/Object/Api/Friendica/Notification.php b/src/Object/Api/Friendica/Notification.php index e2ff3f677..1bec7cfed 100644 --- a/src/Object/Api/Friendica/Notification.php +++ b/src/Object/Api/Friendica/Notification.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2021, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -24,7 +24,7 @@ namespace Friendica\Object\Api\Friendica; use Friendica\BaseDataTransferObject; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; -use Friendica\Model\Notification as NotificationModel; +use Friendica\Navigation\Notifications\Entity\Notify; use Friendica\Util\DateTimeFormat; use Friendica\Util\Temporal; @@ -37,8 +37,6 @@ class Notification extends BaseDataTransferObject { /** @var integer */ protected $id; - /** @var string */ - protected $hash; /** @var integer */ protected $type; /** @var string Full name of the contact subject */ @@ -78,21 +76,37 @@ class Notification extends BaseDataTransferObject /** @var string Message (Plaintext) */ protected $msg_plain; - public function __construct(NotificationModel $notify) + public function __construct(Notify $Notify) { - // map each notify attribute to the entity - foreach ($notify->toArray() as $key => $value) { - $this->{$key} = $value; + $this->id = $Notify->id; + $this->type = $Notify->type; + $this->name = $Notify->name; + $this->url = $Notify->url->__toString(); + $this->photo = $Notify->photo->__toString(); + $this->date = DateTimeFormat::local($Notify->date->format(DateTimeFormat::MYSQL)); + $this->msg = $Notify->msg; + $this->uid = $Notify->uid; + $this->link = $Notify->link->__toString(); + $this->iid = $Notify->itemId; + $this->parent = $Notify->parent; + $this->seen = $Notify->seen; + $this->verb = $Notify->verb; + $this->otype = $Notify->otype; + $this->name_cache = $Notify->name_cache; + $this->msg_cache = $Notify->msg_cache; + $this->timestamp = $Notify->date->format('U'); + $this->date_rel = Temporal::getRelativeDate($this->date); + + try { + $this->msg_html = BBCode::convert($this->msg, false); + } catch (\Exception $e) { + $this->msg_html = ''; } - // add additional attributes for the API try { - $this->timestamp = strtotime(DateTimeFormat::local($this->date)); - $this->msg_html = BBCode::convert($this->msg, false); $this->msg_plain = explode("\n", trim(HTML::toPlaintext($this->msg_html, 0)))[0]; } catch (\Exception $e) { + $this->msg_plain = ''; } - - $this->date_rel = Temporal::getRelativeDate($this->date); } } diff --git a/src/Object/Notification/Notification.php b/src/Object/Notification/Notification.php deleted file mode 100644 index 164e7cc03..000000000 --- a/src/Object/Notification/Notification.php +++ /dev/null @@ -1,143 +0,0 @@ -. - * - */ - -namespace Friendica\Object\Notification; - -/** - * A view-only object for printing item notifications to the frontend - */ -class Notification implements \JsonSerializable -{ - const SYSTEM = 'system'; - const PERSONAL = 'personal'; - const NETWORK = 'network'; - const INTRO = 'intro'; - const HOME = 'home'; - - /** @var string */ - private $label = ''; - /** @var string */ - private $link = ''; - /** @var string */ - private $image = ''; - /** @var string */ - private $url = ''; - /** @var string */ - private $text = ''; - /** @var string */ - private $when = ''; - /** @var string */ - private $ago = ''; - /** @var boolean */ - private $seen = false; - - /** - * @return string - */ - public function getLabel() - { - return $this->label; - } - - /** - * @return string - */ - public function getLink() - { - return $this->link; - } - - /** - * @return string - */ - public function getImage() - { - return $this->image; - } - - /** - * @return string - */ - public function getUrl() - { - return $this->url; - } - - /** - * @return string - */ - public function getText() - { - return $this->text; - } - - /** - * @return string - */ - public function getWhen() - { - return $this->when; - } - - /** - * @return string - */ - public function getAgo() - { - return $this->ago; - } - - /** - * @return bool - */ - public function isSeen() - { - return $this->seen; - } - - public function __construct(array $data) - { - $this->label = $data['label'] ?? ''; - $this->link = $data['link'] ?? ''; - $this->image = $data['image'] ?? ''; - $this->url = $data['url'] ?? ''; - $this->text = $data['text'] ?? ''; - $this->when = $data['when'] ?? ''; - $this->ago = $data['ago'] ?? ''; - $this->seen = $data['seen'] ?? false; - } - - /** - * @inheritDoc - */ - public function jsonSerialize() - { - return get_object_vars($this); - } - - /** - * @return array - */ - public function toArray() - { - return get_object_vars($this); - } -} diff --git a/src/Repository/Notification.php b/src/Repository/Notification.php deleted file mode 100644 index 1748759b6..000000000 --- a/src/Repository/Notification.php +++ /dev/null @@ -1,130 +0,0 @@ -. - * - */ - -namespace Friendica\Repository; - -use Exception; -use Friendica\BaseRepository; -use Friendica\Collection; -use Friendica\Core\Hook; -use Friendica\Model; -use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Network\HTTPException\NotFoundException; -use Friendica\Util\DateTimeFormat; - -class Notification extends BaseRepository -{ - protected static $table_name = 'notify'; - - protected static $model_class = Model\Notification::class; - - protected static $collection_class = Collection\Notifications::class; - - /** - * {@inheritDoc} - * - * @return Model\Notification - */ - protected function create(array $data) - { - return new Model\Notification($this->dba, $this->logger, $this, $data); - } - - /** - * {@inheritDoc} - * - * @return Collection\Notifications - */ - public function select(array $condition = [], array $params = []) - { - $params['order'] = $params['order'] ?? ['date' => 'DESC']; - - return parent::select($condition, $params); - } - - /** - * Return one notify instance based on ID / UID - * - * @param int $id The ID of the notify instance - * @param int $uid The user ID, bound to this notify instance (= security check) - * - * @return Model\Notification - * @throws NotFoundException - */ - public function getByID(int $id, int $uid) - { - return $this->selectFirst(['id' => $id, 'uid' => $uid]); - } - - /** - * Set seen state of notifications of the local_user() - * - * @param bool $seen optional true or false. default true - * @param Model\Notification $notify optional a notify, which should be set seen (including his parents) - * - * @return bool true on success, false on error - * - * @throws Exception - */ - 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, - $notify->otype, - local_user()]; - } - - return $this->dba->update('notify', ['seen' => $seen], $conditions); - } - - /** - * @param array $fields - * - * @return Model\Notification|false - * - * @throws InternalServerErrorException - * @throws Exception - */ - public function insert(array $fields) - { - $fields['date'] = DateTimeFormat::utcNow(); - - Hook::callAll('enotify_store', $fields); - - if (empty($fields)) { - $this->logger->debug('Abort adding notification entry'); - return false; - } - - $this->logger->debug('adding notification entry', ['fields' => $fields]); - - return parent::insert($fields); - } -} diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index 2c6512f8c..86b2bd564 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -732,7 +732,7 @@ return [ 'link' => 'http://localhost/notification/1', 'iid' => 4, 'seen' => 0, - 'verb' => '', + 'verb' => \Friendica\Protocol\Activity::POST, 'otype' => Notification\ObjectType::ITEM, 'name_cache' => 'Reply to', 'msg_cache' => 'A test reply from an item', diff --git a/tests/legacy/ApiTest.php b/tests/legacy/ApiTest.php index 8e0edaeef..553eef2a0 100644 --- a/tests/legacy/ApiTest.php +++ b/tests/legacy/ApiTest.php @@ -12,6 +12,7 @@ use Friendica\Core\Protocol; use Friendica\DI; use Friendica\Network\HTTPException; use Friendica\Test\FixtureTest; +use Friendica\Util\DateTimeFormat; use Friendica\Util\Temporal; use Monolog\Handler\TestHandler; @@ -3714,11 +3715,13 @@ class ApiTest extends FixtureTest { DI::args()->setArgv(['api', 'friendica', 'notification']); $result = api_friendica_notification('xml'); - $dateRel = Temporal::getRelativeDate('2020-01-01 12:12:02'); + $date = DateTimeFormat::local('2020-01-01 12:12:02'); + $dateRel = Temporal::getRelativeDate('2020-01-01 07:12:02'); + $assertXml=<< - + XML; self::assertXmlStringEqualsXmlString($assertXml, $result); diff --git a/view/templates/notifications/attend_item.tpl b/view/templates/notifications/attend_item.tpl index a8de3c793..07c770fb5 100644 --- a/view/templates/notifications/attend_item.tpl +++ b/view/templates/notifications/attend_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/templates/notifications/comments_item.tpl b/view/templates/notifications/comments_item.tpl index a8de3c793..07c770fb5 100644 --- a/view/templates/notifications/comments_item.tpl +++ b/view/templates/notifications/comments_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/templates/notifications/dislikes_item.tpl b/view/templates/notifications/dislikes_item.tpl index a8de3c793..07c770fb5 100644 --- a/view/templates/notifications/dislikes_item.tpl +++ b/view/templates/notifications/dislikes_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/templates/notifications/friends_item.tpl b/view/templates/notifications/friends_item.tpl index 9df155465..8e0158a0e 100644 --- a/view/templates/notifications/friends_item.tpl +++ b/view/templates/notifications/friends_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/templates/notifications/likes_item.tpl b/view/templates/notifications/likes_item.tpl index a8de3c793..07c770fb5 100644 --- a/view/templates/notifications/likes_item.tpl +++ b/view/templates/notifications/likes_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/templates/notifications/network_item.tpl b/view/templates/notifications/network_item.tpl index e2c0e249d..36b7dd424 100644 --- a/view/templates/notifications/network_item.tpl +++ b/view/templates/notifications/network_item.tpl @@ -1,4 +1,4 @@ diff --git a/view/templates/notifications/notification.tpl b/view/templates/notifications/notification.tpl index 4ff7bc7a8..b5c684cd8 100644 --- a/view/templates/notifications/notification.tpl +++ b/view/templates/notifications/notification.tpl @@ -1,4 +1,4 @@ diff --git a/view/templates/notifications/posts_item.tpl b/view/templates/notifications/posts_item.tpl index a8de3c793..07c770fb5 100644 --- a/view/templates/notifications/posts_item.tpl +++ b/view/templates/notifications/posts_item.tpl @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/view/theme/frio/templates/notifications/notify.tpl b/view/theme/frio/templates/notifications/notify.tpl deleted file mode 100644 index 58f3b0da9..000000000 --- a/view/theme/frio/templates/notifications/notify.tpl +++ /dev/null @@ -1,12 +0,0 @@ - - From bf81f21001cead41585ecd549a98afe4e2797840 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 22:14:32 -0400 Subject: [PATCH 03/12] Rename Model\Post\UserNotifications type constants --- include/api.php | 13 ++++-- include/enotify.php | 6 +-- src/Model/Notification.php | 54 +++++++++++---------- src/Model/Post/UserNotification.php | 57 +++++++++++------------ src/Module/Api/Mastodon/Notifications.php | 27 ++++++----- 5 files changed, 84 insertions(+), 73 deletions(-) diff --git a/include/api.php b/include/api.php index 52f04d954..cd096c187 100644 --- a/include/api.php +++ b/include/api.php @@ -2193,11 +2193,14 @@ function api_statuses_mentions($type) (SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`) AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?"; - $condition = [GRAVITY_PARENT, GRAVITY_COMMENT, api_user(), - Post\UserNotification::NOTIF_EXPLICIT_TAGGED | Post\UserNotification::NOTIF_IMPLICIT_TAGGED | - Post\UserNotification::NOTIF_THREAD_COMMENT | Post\UserNotification::NOTIF_DIRECT_COMMENT | - Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, - api_user(), $since_id]; + $condition = [ + GRAVITY_PARENT, GRAVITY_COMMENT, + api_user(), + Post\UserNotification::TYPE_EXPLICIT_TAGGED | Post\UserNotification::TYPE_IMPLICIT_TAGGED | + Post\UserNotification::TYPE_THREAD_COMMENT | Post\UserNotification::TYPE_DIRECT_COMMENT | + Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, + api_user(), $since_id, + ]; if ($max_id > 0) { $query .= " AND `id` <= ?"; diff --git a/include/enotify.php b/include/enotify.php index c3d5e4a40..623347b8b 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -495,7 +495,7 @@ function notification_from_array(array $notification) { Logger::info('Start', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); - if ($notification['type'] == Post\UserNotification::NOTIF_NONE) { + if ($notification['type'] == Post\UserNotification::TYPE_NONE) { Logger::info('Not an item based notification, quitting', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); return false; } @@ -562,10 +562,10 @@ function notification_from_array(array $notification) // So, we cannot have different subjects for notifications of the same thread. // Before this we have the name of the replier on the subject rendering // different subjects for messages on the same thread. - if ($notification['type'] == Post\UserNotification::NOTIF_EXPLICIT_TAGGED) { + if ($notification['type'] == Post\UserNotification::TYPE_EXPLICIT_TAGGED) { $params['type'] = Notification\Type::TAG_SELF; $subject = $l10n->t('%s %s tagged you', $subjectPrefix, $contact['name']); - } elseif ($notification['type'] == Post\UserNotification::NOTIF_SHARED) { + } elseif ($notification['type'] == Post\UserNotification::TYPE_SHARED) { $params['type'] = Notification\Type::SHARE; $subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $contact['name']); } else { diff --git a/src/Model/Notification.php b/src/Model/Notification.php index e9f9ff41c..8411ed77d 100644 --- a/src/Model/Notification.php +++ b/src/Model/Notification.php @@ -44,20 +44,24 @@ class Notification extends BaseModel */ public static function getType(array $notification): string { - if (($notification['vid'] == Verb::getID(Activity::FOLLOW)) && ($notification['type'] == Post\UserNotification::NOTIF_NONE)) { + if (($notification['vid'] == Verb::getID(Activity::FOLLOW)) && ($notification['type'] == Post\UserNotification::TYPE_NONE)) { $contact = Contact::getById($notification['actor-id'], ['pending']); $type = $contact['pending'] ? 'follow_request' : 'follow'; } elseif (($notification['vid'] == Verb::getID(Activity::ANNOUNCE)) && - in_array($notification['type'], [Post\UserNotification::NOTIF_DIRECT_COMMENT, Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT])) { + in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_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])) { + in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { $type = 'favourite'; - } elseif ($notification['type'] == Post\UserNotification::NOTIF_SHARED) { + } elseif ($notification['type'] == Post\UserNotification::TYPE_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])) { + } elseif (in_array($notification['type'], [ + Post\UserNotification::TYPE_EXPLICIT_TAGGED, + Post\UserNotification::TYPE_IMPLICIT_TAGGED, + Post\UserNotification::TYPE_DIRECT_COMMENT, + Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, + Post\UserNotification::TYPE_THREAD_COMMENT + ])) { $type = 'mention'; } else { return ''; @@ -90,7 +94,7 @@ class Notification extends BaseModel return $message; } - if ($notification['type'] == Post\UserNotification::NOTIF_NONE) { + if ($notification['type'] == Post\UserNotification::TYPE_NONE) { if ($contact['pending']) { $msg = $l10n->t('%1$s wants to follow you'); } else { @@ -108,7 +112,7 @@ class Notification extends BaseModel $announce = Verb::getID(Activity::ANNOUNCE); $post = Verb::getID(Activity::POST); - if (in_array($notification['type'], [Post\UserNotification::NOTIF_THREAD_COMMENT, Post\UserNotification::NOTIF_COMMENT_PARTICIPATION, Post\UserNotification::NOTIF_ACTIVITY_PARTICIPATION, Post\UserNotification::NOTIF_EXPLICIT_TAGGED])) { + if (in_array($notification['type'], [Post\UserNotification::TYPE_THREAD_COMMENT, Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION, Post\UserNotification::TYPE_EXPLICIT_TAGGED])) { $item = Post::selectFirst([], ['uri-id' => $notification['parent-uri-id'], 'uid' => [0, $notification['uid']]], ['order' => ['uid' => true]]); if (empty($item)) { Logger::info('Parent post not found', ['uri-id' => $notification['parent-uri-id']]); @@ -137,13 +141,13 @@ class Notification extends BaseModel $cid = $item['causer-id']; } - if (($notification['type'] == Post\UserNotification::NOTIF_SHARED) && !empty($cid)) { + if (($notification['type'] == Post\UserNotification::TYPE_SHARED) && !empty($cid)) { $causer = Contact::getById($cid, ['id', 'name', 'url']); if (empty($contact)) { Logger::info('Causer not found', ['causer' => $cid]); return $message; } - } elseif (in_array($notification['type'], [Post\UserNotification::NOTIF_COMMENT_PARTICIPATION, Post\UserNotification::NOTIF_ACTIVITY_PARTICIPATION])) { + } elseif (in_array($notification['type'], [Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION])) { $contact = Contact::getById($item['author-id'], ['id', 'name', 'url']); if (empty($contact)) { Logger::info('Author not found', ['author' => $item['author-id']]); @@ -163,54 +167,54 @@ class Notification extends BaseModel switch ($notification['vid']) { case $like: switch ($notification['type']) { - case Post\UserNotification::NOTIF_DIRECT_COMMENT: + case Post\UserNotification::TYPE_DIRECT_COMMENT: $msg = $l10n->t('%1$s liked your comment %2$s'); break; - case Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT: + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: $msg = $l10n->t('%1$s liked your post %2$s'); break; } break; case $dislike: switch ($notification['type']) { - case Post\UserNotification::NOTIF_DIRECT_COMMENT: + case Post\UserNotification::TYPE_DIRECT_COMMENT: $msg = $l10n->t('%1$s disliked your comment %2$s'); break; - case Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT: + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: $msg = $l10n->t('%1$s disliked your post %2$s'); break; } break; case $announce: switch ($notification['type']) { - case Post\UserNotification::NOTIF_DIRECT_COMMENT: + case Post\UserNotification::TYPE_DIRECT_COMMENT: $msg = $l10n->t('%1$s shared your comment %2$s'); break; - case Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT: + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: $msg = $l10n->t('%1$s shared your post %2$s'); break; } break; case $post: switch ($notification['type']) { - case Post\UserNotification::NOTIF_EXPLICIT_TAGGED: + case Post\UserNotification::TYPE_EXPLICIT_TAGGED: $msg = $l10n->t('%1$s tagged you on %2$s'); break; - case Post\UserNotification::NOTIF_IMPLICIT_TAGGED: + case Post\UserNotification::TYPE_IMPLICIT_TAGGED: $msg = $l10n->t('%1$s replied to you on %2$s'); break; - case Post\UserNotification::NOTIF_THREAD_COMMENT: + case Post\UserNotification::TYPE_THREAD_COMMENT: $msg = $l10n->t('%1$s commented in your thread %2$s'); break; - case Post\UserNotification::NOTIF_DIRECT_COMMENT: + case Post\UserNotification::TYPE_DIRECT_COMMENT: $msg = $l10n->t('%1$s commented on your comment %2$s'); break; - case Post\UserNotification::NOTIF_COMMENT_PARTICIPATION: - case Post\UserNotification::NOTIF_ACTIVITY_PARTICIPATION: + case Post\UserNotification::TYPE_COMMENT_PARTICIPATION: + case Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION: if (($causer['id'] == $contact['id']) && ($title != '')) { $msg = $l10n->t('%1$s commented in their thread %2$s'); } elseif ($causer['id'] == $contact['id']) { @@ -222,11 +226,11 @@ class Notification extends BaseModel } break; - case Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT: + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: $msg = $l10n->t('%1$s commented on your thread %2$s'); break; - case Post\UserNotification::NOTIF_SHARED: + case Post\UserNotification::TYPE_SHARED: if (($causer['id'] != $contact['id']) && ($title != '')) { $msg = $l10n->t('%1$s shared the post %2$s from %3$s'); } elseif ($causer['id'] != $contact['id']) { diff --git a/src/Model/Post/UserNotification.php b/src/Model/Post/UserNotification.php index f4bcb3e40..3f053e0f5 100644 --- a/src/Model/Post/UserNotification.php +++ b/src/Model/Post/UserNotification.php @@ -39,16 +39,15 @@ use Friendica\Util\DateTimeFormat; class UserNotification { // Notification types - const NOTIF_NONE = 0; - const NOTIF_EXPLICIT_TAGGED = 1; - const NOTIF_IMPLICIT_TAGGED = 2; - const NOTIF_THREAD_COMMENT = 4; - const NOTIF_DIRECT_COMMENT = 8; - const NOTIF_COMMENT_PARTICIPATION = 16; - const NOTIF_ACTIVITY_PARTICIPATION = 32; - const NOTIF_DIRECT_THREAD_COMMENT = 64; - const NOTIF_SHARED = 128; - + const TYPE_NONE = 0; + const TYPE_EXPLICIT_TAGGED = 1; + const TYPE_IMPLICIT_TAGGED = 2; + const TYPE_THREAD_COMMENT = 4; + const TYPE_DIRECT_COMMENT = 8; + const TYPE_COMMENT_PARTICIPATION = 16; + const TYPE_ACTIVITY_PARTICIPATION = 32; + const TYPE_DIRECT_THREAD_COMMENT = 64; + const TYPE_SHARED = 128; /** * Insert a new user notification entry @@ -174,11 +173,11 @@ class UserNotification return; } - $notification_type = self::NOTIF_NONE; + $notification_type = self::TYPE_NONE; if (self::checkShared($item, $uid)) { - $notification_type = $notification_type | self::NOTIF_SHARED; - self::insertNoticationByItem(self::NOTIF_SHARED, $uid, $item); + $notification_type = $notification_type | self::TYPE_SHARED; + self::insertNoticationByItem(self::TYPE_SHARED, $uid, $item); $notified = true; } else { $notified = false; @@ -200,57 +199,57 @@ class UserNotification } if (self::checkExplicitMention($item, $profiles)) { - $notification_type = $notification_type | self::NOTIF_EXPLICIT_TAGGED; + $notification_type = $notification_type | self::TYPE_EXPLICIT_TAGGED; if (!$notified) { - self::insertNoticationByItem( self::NOTIF_EXPLICIT_TAGGED, $uid, $item); + self::insertNoticationByItem( self::TYPE_EXPLICIT_TAGGED, $uid, $item); $notified = true; } } if (self::checkImplicitMention($item, $profiles)) { - $notification_type = $notification_type | self::NOTIF_IMPLICIT_TAGGED; + $notification_type = $notification_type | self::TYPE_IMPLICIT_TAGGED; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_IMPLICIT_TAGGED, $uid, $item); + self::insertNoticationByItem(self::TYPE_IMPLICIT_TAGGED, $uid, $item); $notified = true; } } if (self::checkDirectComment($item, $contacts)) { - $notification_type = $notification_type | self::NOTIF_DIRECT_COMMENT; + $notification_type = $notification_type | self::TYPE_DIRECT_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_DIRECT_COMMENT, $uid, $item); + self::insertNoticationByItem(self::TYPE_DIRECT_COMMENT, $uid, $item); $notified = true; } } if (self::checkDirectCommentedThread($item, $contacts)) { - $notification_type = $notification_type | self::NOTIF_DIRECT_THREAD_COMMENT; + $notification_type = $notification_type | self::TYPE_DIRECT_THREAD_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_DIRECT_THREAD_COMMENT, $uid, $item); + self::insertNoticationByItem(self::TYPE_DIRECT_THREAD_COMMENT, $uid, $item); $notified = true; } } if (self::checkCommentedThread($item, $contacts)) { - $notification_type = $notification_type | self::NOTIF_THREAD_COMMENT; + $notification_type = $notification_type | self::TYPE_THREAD_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_THREAD_COMMENT, $uid, $item); + self::insertNoticationByItem(self::TYPE_THREAD_COMMENT, $uid, $item); $notified = true; } } if (self::checkCommentedParticipation($item, $contacts)) { - $notification_type = $notification_type | self::NOTIF_COMMENT_PARTICIPATION; + $notification_type = $notification_type | self::TYPE_COMMENT_PARTICIPATION; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_COMMENT_PARTICIPATION, $uid, $item); + self::insertNoticationByItem(self::TYPE_COMMENT_PARTICIPATION, $uid, $item); $notified = true; } } if (self::checkActivityParticipation($item, $contacts)) { - $notification_type = $notification_type | self::NOTIF_ACTIVITY_PARTICIPATION; + $notification_type = $notification_type | self::TYPE_ACTIVITY_PARTICIPATION; if (!$notified) { - self::insertNoticationByItem(self::NOTIF_ACTIVITY_PARTICIPATION, $uid, $item); + self::insertNoticationByItem(self::TYPE_ACTIVITY_PARTICIPATION, $uid, $item); $notified = true; } } @@ -278,7 +277,7 @@ class UserNotification private static function insertNoticationByItem(int $type, int $uid, array $item) { if (($item['gravity'] == GRAVITY_ACTIVITY) && - !in_array($type, [self::NOTIF_DIRECT_COMMENT, self::NOTIF_DIRECT_THREAD_COMMENT])) { + !in_array($type, [self::TYPE_DIRECT_COMMENT, self::TYPE_DIRECT_THREAD_COMMENT])) { // Activities are only stored when performed on the user's post or comment return; } @@ -321,7 +320,7 @@ class UserNotification $fields = [ 'uid' => $uid, 'vid' => $vid, - 'type' => self::NOTIF_NONE, + 'type' => self::TYPE_NONE, 'actor-id' => $actor, 'created' => DateTimeFormat::utcNow(), ]; diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index e02823dea..763a768f5 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -77,37 +77,42 @@ class Notifications extends BaseApi if (in_array('follow_request', $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, ["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND `pending`))", - Verb::getID(Activity::FOLLOW), Post\UserNotification::NOTIF_NONE]); + Verb::getID(Activity::FOLLOW), Post\UserNotification::TYPE_NONE]); } if (in_array('follow', $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, ["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND NOT `pending`))", - Verb::getID(Activity::FOLLOW), Post\UserNotification::NOTIF_NONE]); + Verb::getID(Activity::FOLLOW), Post\UserNotification::TYPE_NONE]); } if (in_array('favourite', $request['exclude_types'])) { - $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?, ?) OR NOT `type` IN (?, ?))", + $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]); + Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_THREAD_COMMENT + ]); } if (in_array('reblog', $request['exclude_types'])) { - $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?, ?))", + $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]); + Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_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]); + $condition = DBA::mergeConditions($condition, [ + "(NOT `vid` IN (?) OR NOT `type` IN (?, ?, ?, ?, ?))", + Verb::getID(Activity::POST), Post\UserNotification::TYPE_EXPLICIT_TAGGED, + Post\UserNotification::TYPE_IMPLICIT_TAGGED, Post\UserNotification::TYPE_DIRECT_COMMENT, + Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, Post\UserNotification::TYPE_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]); + Verb::getID(Activity::POST), Post\UserNotification::TYPE_SHARED]); } if (!empty($request['max_id'])) { From dae5703e5cb8eced3dbc7196f2313528dca05507 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 23:36:03 -0400 Subject: [PATCH 04/12] Improve accuracy of accepted parameter types in phpDoc of Database->fetch - Suppresses an unwarranted type error raised by static analyzers --- src/Database/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 28283b1c8..2341e435c 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -909,7 +909,7 @@ class Database /** * Fetch a single row * - * @param PDOStatement|mysqli_stmt $stmt statement object + * @param bool|PDOStatement|mysqli_stmt $stmt statement object * * @return array|false current row */ From ea6f7aba40e4a99661d6fb4d4367e2762a27306e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 23:25:17 -0400 Subject: [PATCH 05/12] Fix method name typos in Model\Post\UserNotification - Reformat file, improve type-hinting --- src/Model/Contact.php | 4 +- src/Model/Post/UserNotification.php | 121 ++++++++++++++++------------ 2 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index faad2adc7..18e041ab9 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2705,7 +2705,7 @@ class Contact // Ensure to always have the correct network type, independent from the connection request method self::updateFromProbe($contact['id']); - Post\UserNotification::insertNotication($contact['id'], Verb::getID(Activity::FOLLOW), $importer['uid']); + Post\UserNotification::insertNotification($contact['id'], Verb::getID(Activity::FOLLOW), $importer['uid']); return true; } else { @@ -2736,7 +2736,7 @@ class Contact self::updateAvatar($contact_id, $photo, true); - Post\UserNotification::insertNotication($contact_id, Verb::getID(Activity::FOLLOW), $importer['uid']); + Post\UserNotification::insertNotification($contact_id, Verb::getID(Activity::FOLLOW), $importer['uid']); $contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]); diff --git a/src/Model/Post/UserNotification.php b/src/Model/Post/UserNotification.php index 3f053e0f5..065cba44c 100644 --- a/src/Model/Post/UserNotification.php +++ b/src/Model/Post/UserNotification.php @@ -21,9 +21,10 @@ namespace Friendica\Model\Post; -use \BadMethodCallException; -use Friendica\Core\Logger; +use BadMethodCallException; +use Exception; use Friendica\Core\Hook; +use Friendica\Core\Logger; use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\Database\DBStructure; @@ -31,10 +32,11 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Post; use Friendica\Model\Subscription; -use Friendica\Util\Strings; use Friendica\Model\Tag; +use Friendica\Network\HTTPException; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Strings; class UserNotification { @@ -54,11 +56,11 @@ class UserNotification * * @param integer $uri_id * @param integer $uid - * @param array $fields + * @param array $data * @return bool success - * @throws \Exception + * @throws Exception */ - public static function insert(int $uri_id, int $uid, array $data = []) + public static function insert(int $uri_id, int $uid, array $data = []): bool { if (empty($uri_id)) { throw new BadMethodCallException('Empty URI_id'); @@ -66,9 +68,8 @@ class UserNotification $fields = DBStructure::getFieldsForTable('post-user-notification', $data); - // Additionally assign the key fields $fields['uri-id'] = $uri_id; - $fields['uid'] = $uid; + $fields['uid'] = $uid; return DBA::insert('post-user-notification', $fields, Database::INSERT_IGNORE); } @@ -81,9 +82,9 @@ class UserNotification * @param array $data * @param bool $insert_if_missing * @return bool - * @throws \Exception + * @throws Exception */ - public static function update(int $uri_id, int $uid, array $data = [], bool $insert_if_missing = false) + public static function update(int $uri_id, int $uid, array $data = [], bool $insert_if_missing = false): bool { if (empty($uri_id)) { throw new BadMethodCallException('Empty URI_id'); @@ -105,32 +106,33 @@ class UserNotification /** * Delete a row from the post-user-notification table * - * @param array $conditions Field condition(s) - * @param array $options - * - cascade: If true we delete records in other tables that depend on the one we're deleting through + * @param array $conditions Field condition(s) + * @param array $options - cascade: If true we delete records in other tables that depend on the one we're deleting through * relations (default: true) * - * @return boolean was the delete successful? - * @throws \Exception + * @return boolean was the deletion successful? + * @throws Exception */ - public static function delete(array $conditions, array $options = []) + public static function delete(array $conditions, array $options = []): bool { return DBA::delete('post-user-notification', $conditions, $options); } /** * Checks an item for notifications and sets the "notification-type" field + * * @ToDo: * - Check for mentions in posts with "uid=0" where the user hadn't interacted before * * @param int $uri_id URI ID * @param int $uid user ID + * @throws Exception */ public static function setNotification(int $uri_id, int $uid) { $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]); + '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; } @@ -166,6 +168,7 @@ class UserNotification * * @param array $item Item array * @param int $uid User ID + * @throws HTTPException\InternalServerErrorException */ private static function setNotificationForUser(array $item, int $uid) { @@ -177,7 +180,7 @@ class UserNotification if (self::checkShared($item, $uid)) { $notification_type = $notification_type | self::TYPE_SHARED; - self::insertNoticationByItem(self::TYPE_SHARED, $uid, $item); + self::insertNotificationByItem(self::TYPE_SHARED, $uid, $item); $notified = true; } else { $notified = false; @@ -187,6 +190,7 @@ class UserNotification // Fetch all contacts for the given profiles $contacts = []; + $ret = DBA::select('contact', ['id'], ['uid' => 0, 'nurl' => $profiles]); while ($contact = DBA::fetch($ret)) { $contacts[] = $contact['id']; @@ -201,7 +205,7 @@ class UserNotification if (self::checkExplicitMention($item, $profiles)) { $notification_type = $notification_type | self::TYPE_EXPLICIT_TAGGED; if (!$notified) { - self::insertNoticationByItem( self::TYPE_EXPLICIT_TAGGED, $uid, $item); + self::insertNotificationByItem(self::TYPE_EXPLICIT_TAGGED, $uid, $item); $notified = true; } } @@ -209,7 +213,7 @@ class UserNotification if (self::checkImplicitMention($item, $profiles)) { $notification_type = $notification_type | self::TYPE_IMPLICIT_TAGGED; if (!$notified) { - self::insertNoticationByItem(self::TYPE_IMPLICIT_TAGGED, $uid, $item); + self::insertNotificationByItem(self::TYPE_IMPLICIT_TAGGED, $uid, $item); $notified = true; } } @@ -217,7 +221,7 @@ class UserNotification if (self::checkDirectComment($item, $contacts)) { $notification_type = $notification_type | self::TYPE_DIRECT_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::TYPE_DIRECT_COMMENT, $uid, $item); + self::insertNotificationByItem(self::TYPE_DIRECT_COMMENT, $uid, $item); $notified = true; } } @@ -225,7 +229,7 @@ class UserNotification if (self::checkDirectCommentedThread($item, $contacts)) { $notification_type = $notification_type | self::TYPE_DIRECT_THREAD_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::TYPE_DIRECT_THREAD_COMMENT, $uid, $item); + self::insertNotificationByItem(self::TYPE_DIRECT_THREAD_COMMENT, $uid, $item); $notified = true; } } @@ -233,7 +237,7 @@ class UserNotification if (self::checkCommentedThread($item, $contacts)) { $notification_type = $notification_type | self::TYPE_THREAD_COMMENT; if (!$notified) { - self::insertNoticationByItem(self::TYPE_THREAD_COMMENT, $uid, $item); + self::insertNotificationByItem(self::TYPE_THREAD_COMMENT, $uid, $item); $notified = true; } } @@ -241,7 +245,7 @@ class UserNotification if (self::checkCommentedParticipation($item, $contacts)) { $notification_type = $notification_type | self::TYPE_COMMENT_PARTICIPATION; if (!$notified) { - self::insertNoticationByItem(self::TYPE_COMMENT_PARTICIPATION, $uid, $item); + self::insertNotificationByItem(self::TYPE_COMMENT_PARTICIPATION, $uid, $item); $notified = true; } } @@ -249,8 +253,7 @@ class UserNotification if (self::checkActivityParticipation($item, $contacts)) { $notification_type = $notification_type | self::TYPE_ACTIVITY_PARTICIPATION; if (!$notified) { - self::insertNoticationByItem(self::TYPE_ACTIVITY_PARTICIPATION, $uid, $item); - $notified = true; + self::insertNotificationByItem(self::TYPE_ACTIVITY_PARTICIPATION, $uid, $item); } } @@ -269,12 +272,13 @@ class UserNotification /** * Add a notification entry for a given item array * - * @param int $type User notification type - * @param int $uid User ID + * @param int $type User notification type + * @param int $uid User ID * @param array $item Item array - * @return boolean + * @return void + * @throws Exception */ - private static function insertNoticationByItem(int $type, int $uid, array $item) + private static function insertNotificationByItem(int $type, int $uid, array $item): void { if (($item['gravity'] == GRAVITY_ACTIVITY) && !in_array($type, [self::TYPE_DIRECT_COMMENT, self::TYPE_DIRECT_THREAD_COMMENT])) { @@ -297,14 +301,12 @@ class UserNotification $fields['target-uri-id'] = $item['uri-id']; } - $ret = DBA::insert('notification', $fields, Database::INSERT_IGNORE); - if ($ret) { + if (DBA::insert('notification', $fields, Database::INSERT_IGNORE)) { $id = DBA::lastInsertId(); if (!empty($id)) { Subscription::pushByNotificationId($id); } } - return $ret; } /** @@ -314,8 +316,9 @@ class UserNotification * @param int $vid Verb ID * @param int $uid User ID * @return boolean + * @throws Exception */ - public static function insertNotication(int $actor, int $vid, int $uid) + public static function insertNotification(int $actor, int $vid, int $uid): bool { $fields = [ 'uid' => $uid, @@ -337,11 +340,13 @@ class UserNotification /** * Fetch all profiles (contact URL) of a given user + * * @param int $uid User ID * * @return array Profile links + * @throws HTTPException\InternalServerErrorException */ - private static function getProfileForUser(int $uid) + private static function getProfileForUser(int $uid): array { $notification_data = ['uid' => $uid, 'profiles' => []]; Hook::callAll('check_item_notification', $notification_data); @@ -364,11 +369,11 @@ class UserNotification // Now the alias $profiles[] = $owner['alias']; - // Notifications from Diaspora are often with an URL in the Diaspora format + // Notifications from Diaspora often have a URL in the Diaspora format $profiles[] = DI::baseUrl() . '/u/' . $user['nickname']; // Validate and add profile links - foreach ($profiles AS $key => $profile) { + foreach ($profiles as $key => $profile) { // Check for invalid profile urls (without scheme, host or path) and remove them if (empty(parse_url($profile, PHP_URL_SCHEME)) || empty(parse_url($profile, PHP_URL_HOST)) || empty(parse_url($profile, PHP_URL_PATH))) { unset($profiles[$key]); @@ -376,11 +381,11 @@ class UserNotification } // Add the normalized form - $profile = Strings::normaliseLink($profile); + $profile = Strings::normaliseLink($profile); $profiles[] = $profile; // Add the SSL form - $profile = str_replace('http://', 'https://', $profile); + $profile = str_replace('http://', 'https://', $profile); $profiles[] = $profile; } @@ -389,11 +394,13 @@ class UserNotification /** * Check for a "shared" notification for every new post of contacts from the given user + * * @param array $item - * @param int $uid User ID + * @param int $uid User ID * @return bool A contact had shared something + * @throws Exception */ - private static function checkShared(array $item, int $uid) + private static function checkShared(array $item, int $uid): bool { // Only check on original posts and reshare ("announce") activities, otherwise return if (($item['gravity'] != GRAVITY_PARENT) && ($item['verb'] != Activity::ANNOUNCE)) { @@ -424,12 +431,14 @@ class UserNotification } /** - * Check for an implicit mention (only tag, no body) of the given user + * Check for an implicit mention (only in tags, not in body) of the given user + * * @param array $item * @param array $profiles Profile links * @return bool The user is mentioned + * @throws Exception */ - private static function checkImplicitMention(array $item, array $profiles) + private static function checkImplicitMention(array $item, array $profiles): bool { $mentions = Tag::getByURIId($item['uri-id'], [Tag::IMPLICIT_MENTION]); foreach ($mentions as $mention) { @@ -445,11 +454,13 @@ class UserNotification /** * Check for an explicit mention (tag and body) of the given user + * * @param array $item * @param array $profiles Profile links * @return bool The user is mentioned + * @throws Exception */ - private static function checkExplicitMention(array $item, array $profiles) + private static function checkExplicitMention(array $item, array $profiles): bool { $mentions = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]); foreach ($mentions as $mention) { @@ -465,11 +476,13 @@ class UserNotification /** * Check if the given user had created this thread + * * @param array $item * @param array $contacts Array of contact IDs * @return bool The user had created this thread + * @throws Exception */ - private static function checkCommentedThread(array $item, array $contacts) + private static function checkCommentedThread(array $item, array $contacts): bool { $condition = ['parent' => $item['parent'], 'author-id' => $contacts, 'deleted' => false, 'gravity' => GRAVITY_PARENT]; return Post::exists($condition); @@ -477,11 +490,13 @@ class UserNotification /** * Check for a direct comment to a post of the given user + * * @param array $item * @param array $contacts Array of contact IDs * @return bool The item is a direct comment to a user comment + * @throws Exception */ - private static function checkDirectComment(array $item, array $contacts) + private static function checkDirectComment(array $item, array $contacts): bool { $condition = ['uri' => $item['thr-parent'], 'uid' => $item['uid'], 'author-id' => $contacts, 'deleted' => false, 'gravity' => GRAVITY_COMMENT]; return Post::exists($condition); @@ -489,11 +504,13 @@ class UserNotification /** * Check for a direct comment to the starting post of the given user + * * @param array $item * @param array $contacts Array of contact IDs * @return bool The user had created this thread + * @throws Exception */ - private static function checkDirectCommentedThread(array $item, array $contacts) + private static function checkDirectCommentedThread(array $item, array $contacts): bool { $condition = ['uri' => $item['thr-parent'], 'uid' => $item['uid'], 'author-id' => $contacts, 'deleted' => false, 'gravity' => GRAVITY_PARENT]; return Post::exists($condition); @@ -501,11 +518,13 @@ class UserNotification /** * Check if the user had commented in this thread + * * @param array $item * @param array $contacts Array of contact IDs * @return bool The user had commented in the thread + * @throws Exception */ - private static function checkCommentedParticipation(array $item, array $contacts) + private static function checkCommentedParticipation(array $item, array $contacts): bool { $condition = ['parent' => $item['parent'], 'author-id' => $contacts, 'deleted' => false, 'gravity' => GRAVITY_COMMENT]; return Post::exists($condition); @@ -513,11 +532,13 @@ class UserNotification /** * Check if the user had interacted in this thread (Like, Dislike, ...) + * * @param array $item * @param array $contacts Array of contact IDs * @return bool The user had interacted in the thread + * @throws Exception */ - private static function checkActivityParticipation(array $item, array $contacts) + private static function checkActivityParticipation(array $item, array $contacts): bool { $condition = ['parent' => $item['parent'], 'author-id' => $contacts, 'deleted' => false, 'gravity' => GRAVITY_ACTIVITY]; return Post::exists($condition); From 3e6fea30f2f3bc2352b6e16e1d48e204cde3696b Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 23:15:23 -0400 Subject: [PATCH 06/12] Create constants for Mastodon notification types --- src/Factory/Api/Mastodon/Notification.php | 9 ----- src/Model/Notification.php | 11 +++--- src/Model/Subscription.php | 3 +- src/Module/Api/Mastodon/Notifications.php | 12 +++--- src/Module/Api/Mastodon/PushSubscription.php | 39 ++++++++++---------- src/Object/Api/Mastodon/Notification.php | 19 +++++++++- src/Object/Api/Mastodon/Subscription.php | 15 ++++---- 7 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php index 82ef67d61..03d102b50 100644 --- a/src/Factory/Api/Mastodon/Notification.php +++ b/src/Factory/Api/Mastodon/Notification.php @@ -49,15 +49,6 @@ class Notification extends BaseFactory if (!$this->dba->isResult($notification)) { return null; } - /* - follow = Someone followed you - follow_request = Someone requested to follow you - mention = Someone mentioned you in their status - reblog = Someone boosted one of your statuses - favourite = Someone favourited one of your statuses - poll = A poll you have voted in or created has ended - status = Someone you enabled notifications for has posted a status - */ $type = ModelNotification::getType($notification); if (empty($type)) { diff --git a/src/Model/Notification.php b/src/Model/Notification.php index 8411ed77d..284b3e7a8 100644 --- a/src/Model/Notification.php +++ b/src/Model/Notification.php @@ -28,6 +28,7 @@ use Friendica\Core\Logger; use Friendica\Database\Database; use Friendica\DI; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Object\Api\Mastodon\Notification as MstdnNotification; use Friendica\Protocol\Activity; use Psr\Log\LoggerInterface; @@ -46,15 +47,15 @@ class Notification extends BaseModel { if (($notification['vid'] == Verb::getID(Activity::FOLLOW)) && ($notification['type'] == Post\UserNotification::TYPE_NONE)) { $contact = Contact::getById($notification['actor-id'], ['pending']); - $type = $contact['pending'] ? 'follow_request' : 'follow'; + $type = $contact['pending'] ? MstdnNotification::TYPE_INTRODUCTION : MstdnNotification::TYPE_FOLLOW; } elseif (($notification['vid'] == Verb::getID(Activity::ANNOUNCE)) && in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { - $type = 'reblog'; + $type = MstdnNotification::TYPE_RESHARE; } elseif (in_array($notification['vid'], [Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE)]) && in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { - $type = 'favourite'; + $type = MstdnNotification::TYPE_LIKE; } elseif ($notification['type'] == Post\UserNotification::TYPE_SHARED) { - $type = 'status'; + $type = MstdnNotification::TYPE_POST; } elseif (in_array($notification['type'], [ Post\UserNotification::TYPE_EXPLICIT_TAGGED, Post\UserNotification::TYPE_IMPLICIT_TAGGED, @@ -62,7 +63,7 @@ class Notification extends BaseModel Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, Post\UserNotification::TYPE_THREAD_COMMENT ])) { - $type = 'mention'; + $type = MstdnNotification::TYPE_MENTION; } else { return ''; } diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index 59e71307f..f9498af98 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -25,6 +25,7 @@ use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Object\Api\Mastodon\Notification; use Minishlink\WebPush\VAPID; class Subscription @@ -142,8 +143,8 @@ class Subscription $notification = DBA::selectFirst('notification', [], ['id' => $nid]); $type = Notification::getType($notification); + $desktop_notification = !in_array($type, [Notification::TYPE_RESHARE, Notification::TYPE_LIKE]); - $desktop_notification = !in_array($type, ['reblog', 'favourite']); if (DI::pConfig()->get($notification['uid'], 'system', 'notify_like') && ($type == 'favourite')) { $desktop_notification = true; diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index 763a768f5..a04a9ae69 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -74,19 +74,19 @@ class Notifications extends BaseApi } } - if (in_array('follow_request', $request['exclude_types'])) { + if (in_array(Notification::TYPE_INTRODUCTION, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, ["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND `pending`))", Verb::getID(Activity::FOLLOW), Post\UserNotification::TYPE_NONE]); } - if (in_array('follow', $request['exclude_types'])) { + if (in_array(Notification::TYPE_FOLLOW, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, ["(`vid` != ? OR `type` != ? OR NOT EXISTS (SELECT `id` FROM `contact` WHERE `id` = `actor-id` AND NOT `pending`))", Verb::getID(Activity::FOLLOW), Post\UserNotification::TYPE_NONE]); } - if (in_array('favourite', $request['exclude_types'])) { + if (in_array(Notification::TYPE_LIKE, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, [ "(NOT `vid` IN (?, ?) OR NOT `type` IN (?, ?))", Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE), @@ -94,7 +94,7 @@ class Notifications extends BaseApi ]); } - if (in_array('reblog', $request['exclude_types'])) { + if (in_array(Notification::TYPE_RESHARE, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, [ "(NOT `vid` IN (?) OR NOT `type` IN (?, ?))", Verb::getID(Activity::ANNOUNCE), @@ -102,7 +102,7 @@ class Notifications extends BaseApi ]); } - if (in_array('mention', $request['exclude_types'])) { + if (in_array(Notification::TYPE_MENTION, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, [ "(NOT `vid` IN (?) OR NOT `type` IN (?, ?, ?, ?, ?))", Verb::getID(Activity::POST), Post\UserNotification::TYPE_EXPLICIT_TAGGED, @@ -110,7 +110,7 @@ class Notifications extends BaseApi Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, Post\UserNotification::TYPE_THREAD_COMMENT]); } - if (in_array('status', $request['exclude_types'])) { + if (in_array(Notification::TYPE_POST, $request['exclude_types'])) { $condition = DBA::mergeConditions($condition, ["(NOT `vid` IN (?) OR NOT `type` IN (?))", Verb::getID(Activity::POST), Post\UserNotification::TYPE_SHARED]); } diff --git a/src/Module/Api/Mastodon/PushSubscription.php b/src/Module/Api/Mastodon/PushSubscription.php index 411d5391f..3bc77aeb6 100644 --- a/src/Module/Api/Mastodon/PushSubscription.php +++ b/src/Module/Api/Mastodon/PushSubscription.php @@ -26,6 +26,7 @@ use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Subscription; use Friendica\Module\BaseApi; +use Friendica\Object\Api\Mastodon\Notification; /** * @see https://docs.joinmastodon.org/methods/notifications/push/ @@ -44,18 +45,18 @@ class PushSubscription extends BaseApi ]); $subscription = [ - 'application-id' => $application['id'], - 'uid' => $uid, - 'endpoint' => $request['subscription']['endpoint'] ?? '', - 'pubkey' => $request['subscription']['keys']['p256dh'] ?? '', - 'secret' => $request['subscription']['keys']['auth'] ?? '', - 'follow' => $request['data']['alerts']['follow'] ?? false, - 'favourite' => $request['data']['alerts']['favourite'] ?? false, - 'reblog' => $request['data']['alerts']['reblog'] ?? false, - 'mention' => $request['data']['alerts']['mention'] ?? false, - 'poll' => $request['data']['alerts']['poll'] ?? false, - 'follow_request' => $request['data']['alerts']['follow_request'] ?? false, - 'status' => $request['data']['alerts']['status'] ?? false, + 'application-id' => $application['id'], + 'uid' => $uid, + 'endpoint' => $request['subscription']['endpoint'] ?? '', + 'pubkey' => $request['subscription']['keys']['p256dh'] ?? '', + 'secret' => $request['subscription']['keys']['auth'] ?? '', + Notification::TYPE_FOLLOW => $request['data']['alerts'][Notification::TYPE_FOLLOW] ?? false, + Notification::TYPE_LIKE => $request['data']['alerts'][Notification::TYPE_LIKE] ?? false, + Notification::TYPE_RESHARE => $request['data']['alerts'][Notification::TYPE_RESHARE] ?? false, + Notification::TYPE_MENTION => $request['data']['alerts'][Notification::TYPE_MENTION] ?? false, + Notification::TYPE_POLL => $request['data']['alerts'][Notification::TYPE_POLL] ?? false, + Notification::TYPE_INTRODUCTION => $request['data']['alerts'][Notification::TYPE_INTRODUCTION] ?? false, + Notification::TYPE_POST => $request['data']['alerts'][Notification::TYPE_POST] ?? false, ]; $ret = Subscription::replace($subscription); @@ -82,13 +83,13 @@ class PushSubscription extends BaseApi } $fields = [ - 'follow' => $request['data']['alerts']['follow'] ?? false, - 'favourite' => $request['data']['alerts']['favourite'] ?? false, - 'reblog' => $request['data']['alerts']['reblog'] ?? false, - 'mention' => $request['data']['alerts']['mention'] ?? false, - 'poll' => $request['data']['alerts']['poll'] ?? false, - 'follow_request' => $request['data']['alerts']['follow_request'] ?? false, - 'status' => $request['data']['alerts']['status'] ?? false, + Notification::TYPE_FOLLOW => $request['data']['alerts'][Notification::TYPE_FOLLOW] ?? false, + Notification::TYPE_LIKE => $request['data']['alerts'][Notification::TYPE_LIKE] ?? false, + Notification::TYPE_RESHARE => $request['data']['alerts'][Notification::TYPE_RESHARE] ?? false, + Notification::TYPE_MENTION => $request['data']['alerts'][Notification::TYPE_MENTION] ?? false, + Notification::TYPE_POLL => $request['data']['alerts'][Notification::TYPE_POLL] ?? false, + Notification::TYPE_INTRODUCTION => $request['data']['alerts'][Notification::TYPE_INTRODUCTION] ?? false, + Notification::TYPE_POST => $request['data']['alerts'][Notification::TYPE_POST] ?? false, ]; $ret = Subscription::update($application['id'], $uid, $fields); diff --git a/src/Object/Api/Mastodon/Notification.php b/src/Object/Api/Mastodon/Notification.php index 5f39c503e..d660baa08 100644 --- a/src/Object/Api/Mastodon/Notification.php +++ b/src/Object/Api/Mastodon/Notification.php @@ -33,9 +33,26 @@ use Friendica\Util\DateTimeFormat; */ class Notification extends BaseDataTransferObject { + /* From the Mastodon documentation: + * - follow = Someone followed you + * - follow_request = Someone requested to follow you + * - mention = Someone mentioned you in their status + * - reblog = Someone boosted one of your statuses + * - favourite = Someone favourited one of your statuses + * - poll = A poll you have voted in or created has ended + * - status = Someone you enabled notifications for has posted a status + */ + public const TYPE_FOLLOW = 'follow'; + public const TYPE_INTRODUCTION = 'follow_request'; + public const TYPE_MENTION = 'mention'; + public const TYPE_RESHARE = 'reblog'; + public const TYPE_LIKE = 'favourite'; + public const TYPE_POLL = 'poll'; + public const TYPE_POST = 'status'; + /** @var string */ protected $id; - /** @var string (Enumerable oneOf) */ + /** @var string One of the TYPE_* constant values */ protected $type; /** @var string (Datetime) */ protected $created_at; diff --git a/src/Object/Api/Mastodon/Subscription.php b/src/Object/Api/Mastodon/Subscription.php index dcd0a6982..ddc6f7d6e 100644 --- a/src/Object/Api/Mastodon/Subscription.php +++ b/src/Object/Api/Mastodon/Subscription.php @@ -42,20 +42,19 @@ class Subscription extends BaseDataTransferObject /** * Creates a subscription record from an item record. * - * @param array $subscription - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @param array $subscription + * @param string $vapid */ public function __construct(array $subscription, string $vapid) { $this->id = (string)$subscription['id']; $this->endpoint = $subscription['endpoint']; $this->alerts = [ - 'follow' => $subscription['follow'], - 'favourite' => $subscription['favourite'], - 'reblog' => $subscription['reblog'], - 'mention' => $subscription['mention'], - 'mention' => $subscription['mention'], - 'poll' => $subscription['poll'], + Notification::TYPE_FOLLOW => $subscription[Notification::TYPE_FOLLOW], + Notification::TYPE_LIKE => $subscription[Notification::TYPE_LIKE], + Notification::TYPE_RESHARE => $subscription[Notification::TYPE_RESHARE], + Notification::TYPE_MENTION => $subscription[Notification::TYPE_MENTION], + Notification::TYPE_POLL => $subscription[Notification::TYPE_POLL], ]; $this->server_key = $vapid; From 43e5b317edb17d555bf930cb20dd7d6d5d63c8ab Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 23:37:41 -0400 Subject: [PATCH 07/12] Add new paradigm classes for notification and introduction notifications - Add support for bounded select in BaseDepository --- src/BaseDepository.php | 62 +++++ .../Collection/Notifications.php | 43 ++++ .../Notifications/Depository/Notification.php | 141 ++++++++++ .../Notifications/Entity/Notification.php | 74 ++++++ .../UnexpectedNotificationTypeException.php | 7 + .../Notifications/Factory/Introduction.php | 187 ++++++++++++++ .../Notifications/Factory/Notification.php | 235 +++++++++++++++++ .../ValueObject/Introduction.php | 241 ++++++++++++++++++ 8 files changed, 990 insertions(+) create mode 100644 src/Navigation/Notifications/Collection/Notifications.php create mode 100644 src/Navigation/Notifications/Depository/Notification.php create mode 100644 src/Navigation/Notifications/Entity/Notification.php create mode 100644 src/Navigation/Notifications/Exception/UnexpectedNotificationTypeException.php create mode 100644 src/Navigation/Notifications/Factory/Introduction.php create mode 100644 src/Navigation/Notifications/Factory/Notification.php create mode 100644 src/Navigation/Notifications/ValueObject/Introduction.php diff --git a/src/BaseDepository.php b/src/BaseDepository.php index 00d3bcfdf..18cca9d30 100644 --- a/src/BaseDepository.php +++ b/src/BaseDepository.php @@ -5,6 +5,7 @@ namespace Friendica; use Exception; use Friendica\Capabilities\ICanCreateFromTableRow; use Friendica\Database\Database; +use Friendica\Database\DBA; use Friendica\Network\HTTPException\NotFoundException; use Psr\Log\LoggerInterface; @@ -43,6 +44,67 @@ abstract class BaseDepository $this->factory = $factory; } + /** + * Populates the collection according to the condition. Retrieves a limited subset of entities depending on the + * boundaries and the limit. The total count of rows matching the condition is stored in the collection. + * + * Depends on the corresponding table featuring a numerical auto incremented column called `id`. + * + * max_id and min_id are susceptible to the query order: + * - min_id alone only reliably works with ASC order + * - max_id alone only reliably works with DESC order + * If the wrong order is detected in either case, we reverse the query order and the entity list order after the query + * + * Chainable. + * + * @param array $condition + * @param array $params + * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible + * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible + * @param int $limit + * @return BaseCollection + * @throws \Exception + */ + protected function _selectByBoundaries( + array $condition = [], + array $params = [], + int $min_id = null, + int $max_id = null, + int $limit = self::LIMIT + ): BaseCollection { + $totalCount = $this->count($condition); + + $boundCondition = $condition; + + $reverseOrder = false; + + if (isset($min_id)) { + $boundCondition = DBA::mergeConditions($boundCondition, ['`id` > ?', $min_id]); + if (!isset($max_id) && isset($params['order']['id']) && ($params['order']['id'] === true || $params['order']['id'] === 'DESC')) { + $reverseOrder = true; + + $params['order']['id'] = 'ASC'; + } + } + + if (isset($max_id)) { + $boundCondition = DBA::mergeConditions($boundCondition, ['`id` < ?', $max_id]); + if (!isset($min_id) && (!isset($params['order']['id']) || $params['order']['id'] === false || $params['order']['id'] === 'ASC')) { + $reverseOrder = true; + + $params['order']['id'] = 'DESC'; + } + } + + $params['limit'] = $limit; + + $Entities = $this->_select($boundCondition, $params); + if ($reverseOrder) { + $Entities->reverse(); + } + + return new BaseCollection($Entities->getArrayCopy(), $totalCount); + } /** * @param array $condition diff --git a/src/Navigation/Notifications/Collection/Notifications.php b/src/Navigation/Notifications/Collection/Notifications.php new file mode 100644 index 000000000..f383b4ccb --- /dev/null +++ b/src/Navigation/Notifications/Collection/Notifications.php @@ -0,0 +1,43 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\Collection; + +use Friendica\BaseCollection; +use Friendica\Navigation\Notifications\Entity; + +class Notifications extends BaseCollection +{ + /** + * @return Entity\Notification + */ + public function current(): Entity\Notification + { + return parent::current(); + } + + public function setSeen(): Notifications + { + return $this->map(function (Entity\Notification $Notification) { + $Notification->setSeen(); + }); + } +} diff --git a/src/Navigation/Notifications/Depository/Notification.php b/src/Navigation/Notifications/Depository/Notification.php new file mode 100644 index 000000000..a93bce665 --- /dev/null +++ b/src/Navigation/Notifications/Depository/Notification.php @@ -0,0 +1,141 @@ +getArrayCopy()); + } + + public function countForUser($uid, array $condition, array $params = []): int + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->count($condition, $params); + } + + public function existsForUser($uid, array $condition): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->exists($condition); + } + + /** + * @param int $id + * @return Entity\Notification + * @throws NotFoundException + */ + public function selectOneById(int $id): Entity\Notification + { + return $this->selectOne(['id' => $id]); + } + + public function selectOneForUser(int $uid, array $condition, array $params = []): Entity\Notification + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->selectOne($condition, $params); + } + + public function selectForUser(int $uid, array $condition = [], array $params = []): Collection\Notifications + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->select($condition, $params); + } + + public function selectAllForUser(int $uid): Collection\Notifications + { + return $this->selectForUser($uid); + } + + /** + * @param array $condition + * @param array $params + * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible + * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible + * @param int $limit + * @return BaseCollection + * @throws Exception + * @see _selectByBoundaries + */ + public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT) + { + $BaseCollection = parent::_selectByBoundaries($condition, $params, $min_id, $max_id, $limit); + + return new Collection\Notifications($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount()); + } + + public function setAllSeenForUser(int $uid, array $condition = []): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } + + /** + * @param Entity\Notification $Notification + * @return Entity\Notification + * @throws Exception + */ + public function save(Entity\Notification $Notification): Entity\Notification + { + $fields = [ + 'uid' => $Notification->uid, + 'vid' => Verb::getID($Notification->verb), + 'type' => $Notification->type, + 'actor-id' => $Notification->actorId, + 'target-uri-id' => $Notification->targetUriId, + 'parent-uri-id' => $Notification->parentUriId, + 'seen' => $Notification->seen, + ]; + + if ($Notification->id) { + $this->db->update(self::$table_name, $fields, ['id' => $Notification->id]); + } else { + $fields['created'] = DateTimeFormat::utcNow(); + $this->db->insert(self::$table_name, $fields); + + $Notification = $this->selectOneById($this->db->lastInsertId()); + } + + return $Notification; + } +} diff --git a/src/Navigation/Notifications/Entity/Notification.php b/src/Navigation/Notifications/Entity/Notification.php new file mode 100644 index 000000000..3f491f98c --- /dev/null +++ b/src/Navigation/Notifications/Entity/Notification.php @@ -0,0 +1,74 @@ +uid = $uid; + $this->verb = $verb; + $this->type = $type; + $this->actorId = $actorId; + $this->targetUriId = $targetUriId; + $this->parentUriId = $parentUriId ?: $targetUriId; + $this->created = $created; + $this->seen = $seen; + $this->id = $id; + } + + public function setSeen() + { + $this->seen = true; + } +} diff --git a/src/Navigation/Notifications/Exception/UnexpectedNotificationTypeException.php b/src/Navigation/Notifications/Exception/UnexpectedNotificationTypeException.php new file mode 100644 index 000000000..e370608af --- /dev/null +++ b/src/Navigation/Notifications/Exception/UnexpectedNotificationTypeException.php @@ -0,0 +1,7 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\Factory; + +use Exception; +use Friendica\App; +use Friendica\App\BaseURL; +use Friendica\BaseFactory; +use Friendica\Content\Text\BBCode; +use Friendica\Core\L10n; +use Friendica\Core\PConfig\IPConfig; +use Friendica\Core\Protocol; +use Friendica\Core\Session\ISession; +use Friendica\Database\Database; +use Friendica\Model\Contact; +use Friendica\Module\BaseNotifications; +use Friendica\Navigation\Notifications\ValueObject; +use Friendica\Util\Proxy; +use Psr\Log\LoggerInterface; + +/** + * Factory for creating notification objects based on introductions + * Currently, there are two main types of introduction based notifications: + * - Friend suggestion + * - Friend/Follower request + */ +class Introduction extends BaseFactory +{ + /** @var Database */ + private $dba; + /** @var BaseURL */ + private $baseUrl; + /** @var L10n */ + private $l10n; + /** @var IPConfig */ + private $pConfig; + /** @var ISession */ + private $session; + /** @var string */ + private $nick; + + public function __construct(LoggerInterface $logger, Database $dba, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session) + { + parent::__construct($logger); + + $this->dba = $dba; + $this->baseUrl = $baseUrl; + $this->l10n = $l10n; + $this->pConfig = $pConfig; + $this->session = $session; + $this->nick = $app->getLoggedInUserNickname() ?? ''; + } + + /** + * Get introductions + * + * @param bool $all If false only include introductions into the query + * which aren't marked as ignored + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * @param int $id When set, only the introduction with this id is displayed + * + * @return ValueObject\Introduction[] + */ + public function getList(bool $all = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT, int $id = 0): array + { + $sql_extra = ""; + + if (empty($id)) { + if (!$all) { + $sql_extra = " AND NOT `ignore` "; + } + + $sql_extra .= " AND NOT `intro`.`blocked` "; + } else { + $sql_extra = sprintf(" AND `intro`.`id` = %d ", $id); + } + + $formattedIntroductions = []; + + try { + /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact and fcontact + $stmtNotifications = $this->dba->p( + "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, + `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, + `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest` + FROM `intro` + LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` + LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` + WHERE `intro`.`uid` = ? $sql_extra + LIMIT ?, ?", + $_SESSION['uid'], + $start, + $limit + ); + + while ($intro = $this->dba->fetch($stmtNotifications)) { + if (empty($intro['url'])) { + continue; + } + + // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests. + // We have to distinguish between these two because they use different data. + // Contact suggestions + if ($intro['fid'] ?? '') { + if (empty($intro['furl'])) { + continue; + } + $return_addr = bin2hex($this->nick . '@' . + $this->baseUrl->getHostname() . + (($this->baseUrl->getUrlPath()) ? '/' . $this->baseUrl->getUrlPath() : '')); + + $formattedIntroductions[] = new ValueObject\Introduction([ + 'label' => 'friend_suggestion', + 'str_type' => $this->l10n->t('Friend Suggestion'), + 'intro_id' => $intro['intro_id'], + 'madeby' => $intro['name'], + 'madeby_url' => $intro['url'], + 'madeby_zrl' => Contact::magicLink($intro['url']), + 'madeby_addr' => $intro['addr'], + 'contact_id' => $intro['contact-id'], + 'photo' => Contact::getAvatarUrlForUrl($intro['furl'], 0, Proxy::SIZE_SMALL), + 'name' => $intro['fname'], + 'url' => $intro['furl'], + 'zrl' => Contact::magicLink($intro['furl']), + 'hidden' => $intro['hidden'] == 1, + 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), + 'note' => $intro['note'], + 'request' => $intro['frequest'] . '?addr=' . $return_addr]); + + // Normal connection requests + } else { + // Don't show these data until you are connected. Diaspora is doing the same. + if ($intro['network'] === Protocol::DIASPORA) { + $intro['location'] = ""; + $intro['about'] = ""; + } + + $formattedIntroductions[] = new ValueObject\Introduction([ + 'label' => (($intro['network'] !== Protocol::OSTATUS) ? 'friend_request' : 'follower'), + 'str_type' => (($intro['network'] !== Protocol::OSTATUS) ? $this->l10n->t('Friend/Connect Request') : $this->l10n->t('New Follower')), + 'dfrn_id' => $intro['issued-id'], + 'uid' => $this->session->get('uid'), + 'intro_id' => $intro['intro_id'], + 'contact_id' => $intro['contact-id'], + 'photo' => Contact::getPhoto($intro), + 'name' => $intro['name'], + 'location' => BBCode::convert($intro['location'], false), + 'about' => BBCode::convert($intro['about'], false), + 'keywords' => $intro['keywords'], + 'hidden' => $intro['hidden'] == 1, + 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), + 'url' => $intro['url'], + 'zrl' => Contact::magicLink($intro['url']), + 'addr' => $intro['addr'], + 'network' => $intro['network'], + 'knowyou' => $intro['knowyou'], + 'note' => $intro['note'], + ]); + } + } + } catch (Exception $e) { + $this->logger->warning('Select failed.', ['uid' => $_SESSION['uid'], 'exception' => $e]); + } + + return $formattedIntroductions; + } +} diff --git a/src/Navigation/Notifications/Factory/Notification.php b/src/Navigation/Notifications/Factory/Notification.php new file mode 100644 index 000000000..e8ff9ea30 --- /dev/null +++ b/src/Navigation/Notifications/Factory/Notification.php @@ -0,0 +1,235 @@ +actorId, ['id', 'name', 'url', 'pending']); + if (empty($causer)) { + $this->logger->info('Causer not found', ['contact' => $Notification->actorId]); + return $message; + } + + if ($Notification->type === Post\UserNotification::TYPE_NONE) { + if ($causer['pending']) { + $msg = $userL10n->t('%1$s wants to follow you'); + } else { + $msg = $userL10n->t('%1$s had started following you'); + } + $title = $causer['name']; + $link = $baseUrl . '/contact/' . $causer['id']; + } else { + if (!$Notification->targetUriId) { + return $message; + } + + if (in_array($Notification->type, [Post\UserNotification::TYPE_THREAD_COMMENT, Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION, Post\UserNotification::TYPE_EXPLICIT_TAGGED])) { + $item = Post::selectFirst([], ['uri-id' => $Notification->parentUriId, 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]); + if (empty($item)) { + $this->logger->info('Parent post not found', ['uri-id' => $Notification->parentUriId]); + return $message; + } + } else { + $item = Post::selectFirst([], ['uri-id' => $Notification->targetUriId, 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]); + if (empty($item)) { + $this->logger->info('Post not found', ['uri-id' => $Notification->targetUriId]); + return $message; + } + + if ($Notification->verb == Activity::POST) { + $item = Post::selectFirst([], ['uri-id' => $item['thr-parent-id'], 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]); + if (empty($item)) { + $this->logger->info('Thread parent post not found', ['uri-id' => $item['thr-parent-id']]); + return $message; + } + } + } + + if ($item['owner-id'] != $item['author-id']) { + $cid = $item['owner-id']; + } + if (!empty($item['causer-id']) && ($item['causer-id'] != $item['author-id'])) { + $cid = $item['causer-id']; + } + + if (($Notification->type === Post\UserNotification::TYPE_SHARED) && !empty($cid)) { + $causer = Contact::getById($cid, ['id', 'name', 'url']); + if (empty($causer)) { + $this->logger->info('Causer not found', ['causer' => $cid]); + return $message; + } + } elseif (in_array($Notification->type, [Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION])) { + $author = Contact::getById($item['author-id'], ['id', 'name', 'url']); + if (empty($author)) { + $this->logger->info('Author not found', ['author' => $item['author-id']]); + return $message; + } + } + + $link = $baseUrl . '/display/' . urlencode($item['guid']); + + $content = Plaintext::getPost($item, 70); + if (!empty($content['text'])) { + $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; + } else { + $title = ''; + } + + switch ($Notification->verb) { + case Activity::LIKE: + switch ($Notification->type) { + case Post\UserNotification::TYPE_DIRECT_COMMENT: + $msg = $userL10n->t('%1$s liked your comment %2$s'); + break; + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: + $msg = $userL10n->t('%1$s liked your post %2$s'); + break; + } + break; + case Activity::DISLIKE: + switch ($Notification->type) { + case Post\UserNotification::TYPE_DIRECT_COMMENT: + $msg = $userL10n->t('%1$s disliked your comment %2$s'); + break; + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: + $msg = $userL10n->t('%1$s disliked your post %2$s'); + break; + } + break; + case Activity::ANNOUNCE: + switch ($Notification->type) { + case Post\UserNotification::TYPE_DIRECT_COMMENT: + $msg = $userL10n->t('%1$s shared your comment %2$s'); + break; + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: + $msg = $userL10n->t('%1$s shared your post %2$s'); + break; + } + break; + case Activity::POST: + switch ($Notification->type) { + case Post\UserNotification::TYPE_EXPLICIT_TAGGED: + $msg = $userL10n->t('%1$s tagged you on %2$s'); + break; + + case Post\UserNotification::TYPE_IMPLICIT_TAGGED: + $msg = $userL10n->t('%1$s replied to you on %2$s'); + break; + + case Post\UserNotification::TYPE_THREAD_COMMENT: + $msg = $userL10n->t('%1$s commented in your thread %2$s'); + break; + + case Post\UserNotification::TYPE_DIRECT_COMMENT: + $msg = $userL10n->t('%1$s commented on your comment %2$s'); + break; + + case Post\UserNotification::TYPE_COMMENT_PARTICIPATION: + case Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION: + if (($causer['id'] == $author['id']) && ($title != '')) { + $msg = $userL10n->t('%1$s commented in their thread %2$s'); + } elseif ($causer['id'] == $author['id']) { + $msg = $userL10n->t('%1$s commented in their thread'); + } elseif ($title != '') { + $msg = $userL10n->t('%1$s commented in the thread %2$s from %3$s'); + } else { + $msg = $userL10n->t('%1$s commented in the thread from %3$s'); + } + break; + + case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: + $msg = $userL10n->t('%1$s commented on your thread %2$s'); + break; + + case Post\UserNotification::TYPE_SHARED: + if (($causer['id'] != $author['id']) && ($title != '')) { + $msg = $userL10n->t('%1$s shared the post %2$s from %3$s'); + } elseif ($causer['id'] != $author['id']) { + $msg = $userL10n->t('%1$s shared a post from %3$s'); + } elseif ($title != '') { + $msg = $userL10n->t('%1$s shared the post %2$s'); + } else { + $msg = $userL10n->t('%1$s shared a post'); + } + break; + } + break; + } + } + + if (!empty($msg)) { + // Name of the notification's causer + $message['causer'] = $causer['name']; + // Format for the "ping" mechanism + $message['notification'] = sprintf($msg, '{0}', $title, $author['name']); + // Plain text for the web push api + $message['plain'] = sprintf($msg, $causer['name'], $title, $author['name']); + // Rich text for other purposes + $message['rich'] = sprintf($msg, + '[url=' . $causer['url'] . ']' . $causer['name'] . '[/url]', + '[url=' . $link . ']' . $title . '[/url]', + '[url=' . $author['url'] . ']' . $author['name'] . '[/url]'); + } + + return $message; + } +} diff --git a/src/Navigation/Notifications/ValueObject/Introduction.php b/src/Navigation/Notifications/ValueObject/Introduction.php new file mode 100644 index 000000000..332f6ccf6 --- /dev/null +++ b/src/Navigation/Notifications/ValueObject/Introduction.php @@ -0,0 +1,241 @@ +. + * + */ + +namespace Friendica\Navigation\Notifications\ValueObject; + +/** + * A view-only object for printing introduction notifications to the frontend + */ +class Introduction implements \JsonSerializable +{ + /** @var string */ + private $label; + /** @var string */ + private $type; + /** @var int */ + private $intro_id; + /** @var string */ + private $madeBy; + /** @var string */ + private $madeByUrl; + /** @var string */ + private $madeByZrl; + /** @var string */ + private $madeByAddr; + /** @var int */ + private $contactId; + /** @var string */ + private $photo; + /** @var string */ + private $name; + /** @var string */ + private $url; + /** @var string */ + private $zrl; + /** @var boolean */ + private $hidden; + /** @var int */ + private $postNewFriend; + /** @var boolean */ + private $knowYou; + /** @var string */ + private $note; + /** @var string */ + private $request; + /** @var int */ + private $dfrnId; + /** @var string */ + private $addr; + /** @var string */ + private $network; + /** @var int */ + private $uid; + /** @var string */ + private $keywords; + /** @var string */ + private $location; + /** @var string */ + private $about; + + public function getLabel(): string + { + return $this->label; + } + + public function getType(): string + { + return $this->type; + } + + public function getIntroId(): int + { + return $this->intro_id; + } + + public function getMadeBy(): string + { + return $this->madeBy; + } + + public function getMadeByUrl(): string + { + return $this->madeByUrl; + } + + public function getMadeByZrl(): string + { + return $this->madeByZrl; + } + + public function getMadeByAddr(): string + { + return $this->madeByAddr; + } + + public function getContactId(): int + { + return $this->contactId; + } + + public function getPhoto(): string + { + return $this->photo; + } + + public function getName(): string + { + return $this->name; + } + + public function getUrl(): string + { + return $this->url; + } + + public function getZrl(): string + { + return $this->zrl; + } + + public function isHidden(): bool + { + return $this->hidden; + } + + public function getPostNewFriend(): int + { + return $this->postNewFriend; + } + + public function getKnowYou(): string + { + return $this->knowYou; + } + + public function getNote(): string + { + return $this->note; + } + + public function getRequest(): string + { + return $this->request; + } + + public function getDfrnId(): int + { + return $this->dfrnId; + } + + public function getAddr(): string + { + return $this->addr; + } + + public function getNetwork(): string + { + return $this->network; + } + + public function getUid(): int + { + return $this->uid; + } + + public function getKeywords(): string + { + return $this->keywords; + } + + public function getLocation(): string + { + return $this->location; + } + + public function getAbout(): string + { + return $this->about; + } + + public function __construct(array $data = []) + { + $this->label = $data['label'] ?? ''; + $this->type = $data['str_type'] ?? ''; + $this->intro_id = $data['intro_id'] ?? -1; + $this->madeBy = $data['madeBy'] ?? ''; + $this->madeByUrl = $data['madeByUrl'] ?? ''; + $this->madeByZrl = $data['madeByZrl'] ?? ''; + $this->madeByAddr = $data['madeByAddr'] ?? ''; + $this->contactId = $data['contactId'] ?? -1; + $this->photo = $data['photo'] ?? ''; + $this->name = $data['name'] ?? ''; + $this->url = $data['url'] ?? ''; + $this->zrl = $data['zrl'] ?? ''; + $this->hidden = $data['hidden'] ?? false; + $this->postNewFriend = $data['postNewFriend'] ?? ''; + $this->knowYou = $data['knowYou'] ?? false; + $this->note = $data['note'] ?? ''; + $this->request = $data['request'] ?? ''; + $this->dfrnId = -1; + $this->addr = $data['addr'] ?? ''; + $this->network = $data['network'] ?? ''; + $this->uid = $data['uid'] ?? -1; + $this->keywords = $data['keywords'] ?? ''; + $this->location = $data['location'] ?? ''; + $this->about = $data['about'] ?? ''; + } + + /** + * @inheritDoc + */ + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * @return array + */ + public function toArray(): array + { + return get_object_vars($this); + } +} From bc0734e0f19fb6dbb488b3e63a67e41f8fea1ede Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Sep 2021 23:41:02 -0400 Subject: [PATCH 08/12] Move notification introductions to the new paradigm --- src/DI.php | 7 +- src/Factory/Notification/Introduction.php | 188 ------------- src/Module/Notifications/Introductions.php | 87 +++--- src/Object/Notification/Introduction.php | 313 --------------------- 4 files changed, 45 insertions(+), 550 deletions(-) delete mode 100644 src/Factory/Notification/Introduction.php delete mode 100644 src/Object/Notification/Introduction.php diff --git a/src/DI.php b/src/DI.php index a54f26c07..ed73ab767 100644 --- a/src/DI.php +++ b/src/DI.php @@ -370,12 +370,9 @@ abstract class DI return self::$dice->create(Factory\Api\Twitter\User::class); } - /** - * @return Factory\Notification\Introduction - */ - public static function notificationIntro() + public static function notificationIntro(): Navigation\Notifications\Factory\Introduction { - return self::$dice->create(Factory\Notification\Introduction::class); + return self::$dice->create(Navigation\Notifications\Factory\Introduction::class); } // diff --git a/src/Factory/Notification/Introduction.php b/src/Factory/Notification/Introduction.php deleted file mode 100644 index 2b5aecdb0..000000000 --- a/src/Factory/Notification/Introduction.php +++ /dev/null @@ -1,188 +0,0 @@ -. - * - */ - -namespace Friendica\Factory\Notification; - -use Exception; -use Friendica\App; -use Friendica\App\BaseURL; -use Friendica\BaseFactory; -use Friendica\Content\Text\BBCode; -use Friendica\Core\L10n; -use Friendica\Core\PConfig\IPConfig; -use Friendica\Core\Protocol; -use Friendica\Core\Session\ISession; -use Friendica\Database\Database; -use Friendica\Model\Contact; -use Friendica\Module\BaseNotifications; -use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Object\Notification; -use Friendica\Util\Proxy; -use Psr\Log\LoggerInterface; - -/** - * Factory for creating notification objects based on introductions - * Currently, there are two main types of introduction based notifications: - * - Friend suggestion - * - Friend/Follower request - */ -class Introduction extends BaseFactory -{ - /** @var Database */ - private $dba; - /** @var BaseURL */ - private $baseUrl; - /** @var L10n */ - private $l10n; - /** @var IPConfig */ - private $pConfig; - /** @var ISession */ - private $session; - /** @var string */ - private $nick; - - public function __construct(LoggerInterface $logger, Database $dba, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session) - { - parent::__construct($logger); - - $this->dba = $dba; - $this->baseUrl = $baseUrl; - $this->l10n = $l10n; - $this->pConfig = $pConfig; - $this->session = $session; - $this->nick = $app->getLoggedInUserNickname() ?? ''; - } - - /** - * Get introductions - * - * @param bool $all If false only include introductions into the query - * which aren't marked as ignored - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * @param int $id When set, only the introduction with this id is displayed - * - * @return Notification\Introduction[] - */ - public function getList(bool $all = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT, int $id = 0) - { - $sql_extra = ""; - - if (empty($id)) { - if (!$all) { - $sql_extra = " AND NOT `ignore` "; - } - - $sql_extra .= " AND NOT `intro`.`blocked` "; - } else { - $sql_extra = sprintf(" AND `intro`.`id` = %d ", intval($id)); - } - - $formattedNotifications = []; - - try { - /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact and fcontact - $stmtNotifications = $this->dba->p( - "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, - `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, - `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest` - FROM `intro` - LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` - LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` - WHERE `intro`.`uid` = ? $sql_extra - LIMIT ?, ?", - $_SESSION['uid'], - $start, - $limit - ); - - while ($notification = $this->dba->fetch($stmtNotifications)) { - if (empty($notification['url'])) { - continue; - } - - // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests. - // We have to distinguish between these two because they use different data. - // Contact suggestions - if ($notification['fid'] ?? '') { - if (empty($notification['furl'])) { - continue; - } - $return_addr = bin2hex($this->nick . '@' . - $this->baseUrl->getHostName() . - (($this->baseUrl->getURLPath()) ? '/' . $this->baseUrl->getURLPath() : '')); - - $formattedNotifications[] = new Notification\Introduction([ - 'label' => 'friend_suggestion', - 'str_type' => $this->l10n->t('Friend Suggestion'), - 'intro_id' => $notification['intro_id'], - 'madeby' => $notification['name'], - 'madeby_url' => $notification['url'], - 'madeby_zrl' => Contact::magicLink($notification['url']), - 'madeby_addr' => $notification['addr'], - 'contact_id' => $notification['contact-id'], - 'photo' => Contact::getAvatarUrlForUrl($notification['furl'], 0, Proxy::SIZE_SMALL), - 'name' => $notification['fname'], - 'url' => $notification['furl'], - 'zrl' => Contact::magicLink($notification['furl']), - 'hidden' => $notification['hidden'] == 1, - 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), - 'note' => $notification['note'], - 'request' => $notification['frequest'] . '?addr=' . $return_addr]); - - // Normal connection requests - } else { - // Don't show these data until you are connected. Diaspora is doing the same. - if ($notification['network'] === Protocol::DIASPORA) { - $notification['location'] = ""; - $notification['about'] = ""; - } - - $formattedNotifications[] = new Notification\Introduction([ - 'label' => (($notification['network'] !== Protocol::OSTATUS) ? 'friend_request' : 'follower'), - 'str_type' => (($notification['network'] !== Protocol::OSTATUS) ? $this->l10n->t('Friend/Connect Request') : $this->l10n->t('New Follower')), - 'dfrn_id' => $notification['issued-id'], - 'uid' => $this->session->get('uid'), - 'intro_id' => $notification['intro_id'], - 'contact_id' => $notification['contact-id'], - 'photo' => Contact::getPhoto($notification), - 'name' => $notification['name'], - 'location' => BBCode::convert($notification['location'], false), - 'about' => BBCode::convert($notification['about'], false), - 'keywords' => $notification['keywords'], - 'hidden' => $notification['hidden'] == 1, - 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), - 'url' => $notification['url'], - 'zrl' => Contact::magicLink($notification['url']), - 'addr' => $notification['addr'], - 'network' => $notification['network'], - 'knowyou' => $notification['knowyou'], - 'note' => $notification['note'], - ]); - } - } - } catch (Exception $e) { - $this->logger->warning('Select failed.', ['uid' => $_SESSION['uid'], 'exception' => $e]); - } - - return $formattedNotifications; - } -} diff --git a/src/Module/Notifications/Introductions.php b/src/Module/Notifications/Introductions.php index 878195931..9bc9f40b0 100644 --- a/src/Module/Notifications/Introductions.php +++ b/src/Module/Notifications/Introductions.php @@ -26,11 +26,10 @@ use Friendica\Content\Nav; use Friendica\Content\Text\BBCode; use Friendica\Core\Protocol; use Friendica\Core\Renderer; -use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\User; use Friendica\Module\BaseNotifications; -use Friendica\Object\Notification\Introduction; +use Friendica\Navigation\Notifications\ValueObject\Introduction; /** * Prints notifications about introduction @@ -82,34 +81,34 @@ class Introductions extends BaseNotifications // Loop through all introduction notifications.This creates an array with the output html for each // introduction - /** @var Introduction $notification */ - foreach ($notifications['notifications'] as $notification) { + /** @var Introduction $Introduction */ + foreach ($notifications['notifications'] as $Introduction) { // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests. // We have to distinguish between these two because they use different data. - switch ($notification->getLabel()) { + switch ($Introduction->getLabel()) { case 'friend_suggestion': $notificationContent[] = Renderer::replaceMacros($notificationSuggestions, [ - '$type' => $notification->getLabel(), + '$type' => $Introduction->getLabel(), '$str_notification_type' => DI::l10n()->t('Notification type:'), - '$str_type' => $notification->getType(), - '$intro_id' => $notification->getIntroId(), + '$str_type' => $Introduction->getType(), + '$intro_id' => $Introduction->getIntroId(), '$lbl_madeby' => DI::l10n()->t('Suggested by:'), - '$madeby' => $notification->getMadeBy(), - '$madeby_url' => $notification->getMadeByUrl(), - '$madeby_zrl' => $notification->getMadeByZrl(), - '$madeby_addr' => $notification->getMadeByAddr(), - '$contact_id' => $notification->getContactId(), - '$photo' => $notification->getPhoto(), - '$fullname' => $notification->getName(), + '$madeby' => $Introduction->getMadeBy(), + '$madeby_url' => $Introduction->getMadeByUrl(), + '$madeby_zrl' => $Introduction->getMadeByZrl(), + '$madeby_addr' => $Introduction->getMadeByAddr(), + '$contact_id' => $Introduction->getContactId(), + '$photo' => $Introduction->getPhoto(), + '$fullname' => $Introduction->getName(), '$dfrn_url' => $owner['url'], - '$url' => $notification->getUrl(), - '$zrl' => $notification->getZrl(), + '$url' => $Introduction->getUrl(), + '$zrl' => $Introduction->getZrl(), '$lbl_url' => DI::l10n()->t('Profile URL'), - '$addr' => $notification->getAddr(), + '$addr' => $Introduction->getAddr(), '$action' => 'follow', '$approve' => DI::l10n()->t('Approve'), - '$note' => $notification->getNote(), + '$note' => $Introduction->getNote(), '$ignore' => DI::l10n()->t('Ignore'), '$discard' => DI::l10n()->t('Discard'), '$is_mobile' => DI::mode()->isMobile(), @@ -118,15 +117,15 @@ class Introductions extends BaseNotifications // Normal connection requests default: - if ($notification->getNetwork() === Protocol::DFRN) { + if ($Introduction->getNetwork() === Protocol::DFRN) { $lbl_knowyou = DI::l10n()->t('Claims to be known to you: '); - $knowyou = ($notification->getKnowYou() ? DI::l10n()->t('Yes') : DI::l10n()->t('No')); + $knowyou = ($Introduction->getKnowYou() ? DI::l10n()->t('Yes') : DI::l10n()->t('No')); } else { $lbl_knowyou = ''; $knowyou = ''; } - $convertedName = BBCode::convert($notification->getName()); + $convertedName = BBCode::convert($Introduction->getName()); $helptext = DI::l10n()->t('Shall your connection be bidirectional or not?'); $helptext2 = DI::l10n()->t('Accepting %s as a friend allows %s to subscribe to your posts, and you will also receive updates from them in your news feed.', $convertedName, $convertedName); @@ -137,51 +136,51 @@ class Introductions extends BaseNotifications $action = 'follow_confirm'; - $header = $notification->getName(); + $header = $Introduction->getName(); - if ($notification->getAddr() != '') { - $header .= ' <' . $notification->getAddr() . '>'; + if ($Introduction->getAddr() != '') { + $header .= ' <' . $Introduction->getAddr() . '>'; } - $header .= ' (' . ContactSelector::networkToName($notification->getNetwork(), $notification->getUrl()) . ')'; + $header .= ' (' . ContactSelector::networkToName($Introduction->getNetwork(), $Introduction->getUrl()) . ')'; - if ($notification->getNetwork() != Protocol::DIASPORA) { + if ($Introduction->getNetwork() != Protocol::DIASPORA) { $discard = DI::l10n()->t('Discard'); } else { $discard = ''; } $notificationContent[] = Renderer::replaceMacros($notificationTemplate, [ - '$type' => $notification->getLabel(), + '$type' => $Introduction->getLabel(), '$header' => $header, '$str_notification_type' => DI::l10n()->t('Notification type:'), - '$str_type' => $notification->getType(), - '$dfrn_id' => $notification->getDfrnId(), - '$uid' => $notification->getUid(), - '$intro_id' => $notification->getIntroId(), - '$contact_id' => $notification->getContactId(), - '$photo' => $notification->getPhoto(), - '$fullname' => $notification->getName(), - '$location' => $notification->getLocation(), + '$str_type' => $Introduction->getType(), + '$dfrn_id' => $Introduction->getDfrnId(), + '$uid' => $Introduction->getUid(), + '$intro_id' => $Introduction->getIntroId(), + '$contact_id' => $Introduction->getContactId(), + '$photo' => $Introduction->getPhoto(), + '$fullname' => $Introduction->getName(), + '$location' => $Introduction->getLocation(), '$lbl_location' => DI::l10n()->t('Location:'), - '$about' => $notification->getAbout(), + '$about' => $Introduction->getAbout(), '$lbl_about' => DI::l10n()->t('About:'), - '$keywords' => $notification->getKeywords(), + '$keywords' => $Introduction->getKeywords(), '$lbl_keywords' => DI::l10n()->t('Tags:'), - '$hidden' => ['hidden', DI::l10n()->t('Hide this contact from others'), $notification->isHidden(), ''], + '$hidden' => ['hidden', DI::l10n()->t('Hide this contact from others'), $Introduction->isHidden(), ''], '$lbl_connection_type' => $helptext, '$friend' => $friend, '$follower' => $follower, - '$url' => $notification->getUrl(), - '$zrl' => $notification->getZrl(), + '$url' => $Introduction->getUrl(), + '$zrl' => $Introduction->getZrl(), '$lbl_url' => DI::l10n()->t('Profile URL'), - '$addr' => $notification->getAddr(), + '$addr' => $Introduction->getAddr(), '$lbl_knowyou' => $lbl_knowyou, '$lbl_network' => DI::l10n()->t('Network:'), - '$network' => ContactSelector::networkToName($notification->getNetwork(), $notification->getUrl()), + '$network' => ContactSelector::networkToName($Introduction->getNetwork(), $Introduction->getUrl()), '$knowyou' => $knowyou, '$approve' => DI::l10n()->t('Approve'), - '$note' => $notification->getNote(), + '$note' => $Introduction->getNote(), '$ignore' => DI::l10n()->t('Ignore'), '$discard' => $discard, '$action' => $action, diff --git a/src/Object/Notification/Introduction.php b/src/Object/Notification/Introduction.php deleted file mode 100644 index 2a2d90cda..000000000 --- a/src/Object/Notification/Introduction.php +++ /dev/null @@ -1,313 +0,0 @@ -. - * - */ - -namespace Friendica\Object\Notification; - -/** - * A view-only object for printing introduction notifications to the frontend - */ -class Introduction implements \JsonSerializable -{ - /** @var string */ - private $label = ''; - /** @var string */ - private $type = ''; - /** @var integer */ - private $intro_id = -1; - /** @var string */ - private $madeBy = ''; - /** @var string */ - private $madeByUrl = ''; - /** @var string */ - private $madeByZrl = ''; - /** @var string */ - private $madeByAddr = ''; - /** @var integer */ - private $contactId = -1; - /** @var string */ - private $photo = ''; - /** @var string */ - private $name = ''; - /** @var string */ - private $url = ''; - /** @var string */ - private $zrl = ''; - /** @var boolean */ - private $hidden = false; - /** @var integer */ - private $postNewFriend = -1; - /** @var boolean */ - private $knowYou = false; - /** @var string */ - private $note = ''; - /** @var string */ - private $request = ''; - /** @var int */ - private $dfrnId = -1; - /** @var string */ - private $addr = ''; - /** @var string */ - private $network = ''; - /** @var int */ - private $uid = -1; - /** @var string */ - private $keywords = ''; - /** @var string */ - private $location = ''; - /** @var string */ - private $about = ''; - - /** - * @return string - */ - public function getLabel() - { - return $this->label; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - - /** - * @return int - */ - public function getIntroId() - { - return $this->intro_id; - } - - /** - * @return string - */ - public function getMadeBy() - { - return $this->madeBy; - } - - /** - * @return string - */ - public function getMadeByUrl() - { - return $this->madeByUrl; - } - - /** - * @return string - */ - public function getMadeByZrl() - { - return $this->madeByZrl; - } - - /** - * @return string - */ - public function getMadeByAddr() - { - return $this->madeByAddr; - } - - /** - * @return int - */ - public function getContactId() - { - return $this->contactId; - } - - /** - * @return string - */ - public function getPhoto() - { - return $this->photo; - } - - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * @return string - */ - public function getUrl() - { - return $this->url; - } - - /** - * @return string - */ - public function getZrl() - { - return $this->zrl; - } - - /** - * @return bool - */ - public function isHidden() - { - return $this->hidden; - } - - /** - * @return int - */ - public function getPostNewFriend() - { - return $this->postNewFriend; - } - - /** - * @return string - */ - public function getKnowYou() - { - return $this->knowYou; - } - - /** - * @return string - */ - public function getNote() - { - return $this->note; - } - - /** - * @return string - */ - public function getRequest() - { - return $this->request; - } - - /** - * @return string - */ - public function getDfrnId() - { - return $this->dfrnId; - } - - /** - * @return string - */ - public function getAddr() - { - return $this->addr; - } - - /** - * @return string - */ - public function getNetwork() - { - return $this->network; - } - - /** - * @return int - */ - public function getUid() - { - return $this->uid; - } - - /** - * @return string - */ - public function getKeywords() - { - return $this->keywords; - } - - /** - * @return string - */ - public function getLocation() - { - return $this->location; - } - - /** - * @return string - */ - public function getAbout() - { - return $this->about; - } - - public function __construct(array $data = []) - { - $this->label = $data['label'] ?? ''; - $this->type = $data['str_type'] ?? ''; - $this->intro_id = $data['intro_id'] ?? -1; - $this->madeBy = $data['madeBy'] ?? ''; - $this->madeByUrl = $data['madeByUrl'] ?? ''; - $this->madeByZrl = $data['madeByZrl'] ?? ''; - $this->madeByAddr = $data['madeByAddr'] ?? ''; - $this->contactId = $data['contactId'] ?? ''; - $this->photo = $data['photo'] ?? ''; - $this->name = $data['name'] ?? ''; - $this->url = $data['url'] ?? ''; - $this->zrl = $data['zrl'] ?? ''; - $this->hidden = $data['hidden'] ?? false; - $this->postNewFriend = $data['postNewFriend'] ?? ''; - $this->knowYou = $data['knowYou'] ?? false; - $this->note = $data['note'] ?? ''; - $this->request = $data['request'] ?? ''; - $this->dfrnId = -1; - $this->addr = $data['addr'] ?? ''; - $this->network = $data['network'] ?? ''; - $this->uid = $data['uid'] ?? -1; - $this->keywords = $data['keywords'] ?? ''; - $this->location = $data['location'] ?? ''; - $this->about = $data['about'] ?? ''; - } - - /** - * @inheritDoc - */ - public function jsonSerialize() - { - return $this->toArray(); - } - - /** - * @return array - */ - public function toArray() - { - return get_object_vars($this); - } -} From 7a2d5f6a8eea1babea3450af0f62f3f692f91348 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 18 Sep 2021 00:03:32 -0400 Subject: [PATCH 09/12] Move notification to the new paradigm --- include/api.php | 2 +- include/enotify.php | 41 ++- mod/display.php | 2 +- src/DI.php | 5 + src/Factory/Api/Mastodon/Notification.php | 68 +++-- src/Model/Contact.php | 4 +- src/Model/Notification.php | 266 ------------------ src/Model/Post/UserNotification.php | 66 ++--- src/Model/Subscription.php | 17 +- src/Module/Api/Mastodon/Notifications.php | 50 ++-- .../Api/Mastodon/Notifications/Clear.php | 4 +- .../Api/Mastodon/Notifications/Dismiss.php | 5 +- src/Module/Notifications/Notification.php | 4 +- src/Module/Notifications/Notifications.php | 31 +- src/Object/Api/Mastodon/Notification.php | 4 +- src/Worker/PushSubscription.php | 26 +- 16 files changed, 175 insertions(+), 420 deletions(-) delete mode 100644 src/Model/Notification.php diff --git a/include/api.php b/include/api.php index cd096c187..11950f52f 100644 --- a/include/api.php +++ b/include/api.php @@ -5647,7 +5647,7 @@ function api_friendica_notification_seen($type) } if ($Notify->uriId) { - DI::dba()->update('notification', ['seen' => true], ['uid' => $Notify->uid, 'target-uri-id' => $Notify->uriId]); + DI::notification()->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]); } $Notify->setSeen(); diff --git a/include/enotify.php b/include/enotify.php index 623347b8b..90a17ad25 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -491,22 +491,21 @@ function notification_store_and_send($params, $sitelink, $tsitelink, $hsitelink, return false; } -function notification_from_array(array $notification) +function notification_from_array(Notifications\Entity\Notification $Notification) { - Logger::info('Start', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); + Logger::info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); - if ($notification['type'] == Post\UserNotification::TYPE_NONE) { - Logger::info('Not an item based notification, quitting', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); + if ($Notification->type === Post\UserNotification::TYPE_NONE) { + Logger::info('Not an item based notification, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); return false; } $params = []; - $params['verb'] = Verb::getByID($notification['vid']); - - $params['uid'] = $notification['uid']; + $params['verb'] = $Notification->verb; + $params['uid'] = $Notification->uid; $params['otype'] = Notification\ObjectType::ITEM; - $user = User::getById($notification['uid']); + $user = User::getById($Notification->uid); $params['notify_flags'] = $user['notify-flags']; $params['language'] = $user['language']; @@ -516,18 +515,18 @@ function notification_from_array(array $notification) // from here on everything is in the recipients language $l10n = DI::l10n()->withLang($user['language']); - $contact = Contact::getById($notification['actor-id'], ['url', 'name', 'photo']); + $contact = Contact::getById($Notification->actorId, ['url', 'name', 'photo']); if (DBA::isResult($contact)) { $params['source_link'] = $contact['url']; $params['source_name'] = $contact['name']; $params['source_photo'] = $contact['photo']; } - $item = Post::selectFirstForUser($notification['uid'], Item::ITEM_FIELDLIST, - ['uid' => [0, $notification['uid']], 'uri-id' => $notification['target-uri-id'], 'deleted' => false], + $item = Post::selectFirstForUser($Notification->uid, Item::ITEM_FIELDLIST, + ['uid' => [0, $Notification->uid], 'uri-id' => $Notification->targetUriId, 'deleted' => false], ['order' => ['uid' => true]]); if (empty($item)) { - Logger::info('Item not found', ['uri-id' => $notification['target-uri-id'], 'type' => $notification['type']]); + Logger::info('Item not found', ['uri-id' => $Notification->targetUriId, 'type' => $Notification->type]); return false; } @@ -537,8 +536,8 @@ function notification_from_array(array $notification) $subjectPrefix = $l10n->t('[Friendica:Notify]'); - if (Post\ThreadUser::getIgnored($notification['parent-uri-id'], $notification['uid'])) { - Logger::info('Thread is ignored', ['parent-uri-id' => $notification['parent-uri-id'], 'type' => $notification['type']]); + if (Post\ThreadUser::getIgnored($Notification->parentUriId, $Notification->uid)) { + Logger::info('Thread is ignored', ['parent-uri-id' => $Notification->parentUriId, 'type' => $Notification->type]); return false; } @@ -546,8 +545,8 @@ function notification_from_array(array $notification) // If so don't create a second notification $condition = ['type' => [Notification\Type::TAG_SELF, Notification\Type::COMMENT, Notification\Type::SHARE], 'link' => $params['link'], 'verb' => Activity::POST]; - if (DI::notify()->existsForUser($notification['uid'], $condition)) { - Logger::info('Duplicate found, quitting', $condition + ['uid' => $notification['uid']]); + if (DI::notify()->existsForUser($Notification->uid, $condition)) { + Logger::info('Duplicate found, quitting', $condition + ['uid' => $Notification->uid]); return false; } @@ -562,10 +561,10 @@ function notification_from_array(array $notification) // So, we cannot have different subjects for notifications of the same thread. // Before this we have the name of the replier on the subject rendering // different subjects for messages on the same thread. - if ($notification['type'] == Post\UserNotification::TYPE_EXPLICIT_TAGGED) { + if ($Notification->type === Post\UserNotification::TYPE_EXPLICIT_TAGGED) { $params['type'] = Notification\Type::TAG_SELF; $subject = $l10n->t('%s %s tagged you', $subjectPrefix, $contact['name']); - } elseif ($notification['type'] == Post\UserNotification::TYPE_SHARED) { + } elseif ($Notification->type === Post\UserNotification::TYPE_SHARED) { $params['type'] = Notification\Type::SHARE; $subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $contact['name']); } else { @@ -573,9 +572,9 @@ function notification_from_array(array $notification) $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']); } - $msg = Notification::getMessage($notification); + $msg = (new Notifications\Factory\Notification(DI::logger()))->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); if (empty($msg)) { - Logger::info('No notification message, quitting', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); + Logger::info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); return false; } @@ -590,7 +589,7 @@ function notification_from_array(array $notification) $hsitelink = sprintf($sitelink, '' . $sitename . ''); $itemlink = $params['link']; - Logger::info('Perform notification', ['uid' => $notification['uid'], 'id' => $notification['id'], 'type' => $notification['type']]); + Logger::info('Perform notification', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); return notification_store_and_send($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $item['body'], $itemlink, true); } diff --git a/mod/display.php b/mod/display.php index 26f6eae36..a349a552c 100644 --- a/mod/display.php +++ b/mod/display.php @@ -222,7 +222,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()]); + DI::notification()->setAllSeenForUser(local_user(), ['parent-uri-id' => $item['parent-uri-id']]); DI::notify()->setAllSeenForUser(local_user(), ['parent-uri-id' => $item['parent-uri-id']]); } diff --git a/src/DI.php b/src/DI.php index ed73ab767..4168997d4 100644 --- a/src/DI.php +++ b/src/DI.php @@ -458,6 +458,11 @@ abstract class DI return self::$dice->create(Repository\ProfileField::class); } + public static function notification(): Navigation\Notifications\Depository\Notification + { + return self::$dice->create(Navigation\Notifications\Depository\Notification::class); + } + public static function notify(): Navigation\Notifications\Depository\Notify { return self::$dice->create(Navigation\Notifications\Depository\Notify::class); diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php index 03d102b50..bdc5717f8 100644 --- a/src/Factory/Api/Mastodon/Notification.php +++ b/src/Factory/Api/Mastodon/Notification.php @@ -22,44 +22,41 @@ namespace Friendica\Factory\Api\Mastodon; use Friendica\BaseFactory; -use Friendica\Database\Database; -use Friendica\Model\Notification as ModelNotification; +use Friendica\Model\Contact; +use Friendica\Navigation\Notifications; +use Friendica\Navigation\Notifications\Exception\UnexpectedNotificationTypeException; +use Friendica\Object\Api\Mastodon\Notification as MstdnNotification; +use Friendica\Protocol\Activity; use Psr\Log\LoggerInterface; +use Friendica\Navigation\Notifications\Entity; +use Friendica\Model\Post; class Notification extends BaseFactory { - /** @var Database */ - private $dba; /** @var Account */ private $mstdnAccountFactory; /** @var Status */ private $mstdnStatusFactory; - public function __construct(LoggerInterface $logger, Database $dba, Account $mstdnAccountFactory, Status $mstdnStatusFactoryFactory) + public function __construct(LoggerInterface $logger, Account $mstdnAccountFactory, Status $mstdnStatusFactoryFactory) { parent::__construct($logger); - $this->dba = $dba; $this->mstdnAccountFactory = $mstdnAccountFactory; $this->mstdnStatusFactory = $mstdnStatusFactoryFactory; } - public function createFromNotificationId(int $id) + public function createFromNotification(Notifications\Entity\Notification $Notification): MstdnNotification { - $notification = $this->dba->selectFirst('notification', [], ['id' => $id]); - if (!$this->dba->isResult($notification)) { - return null; - } - - $type = ModelNotification::getType($notification); + $type = self::getType($Notification); if (empty($type)) { - return null; + throw new UnexpectedNotificationTypeException(); } - $account = $this->mstdnAccountFactory->createFromContactId($notification['actor-id'], $notification['uid']); + $account = $this->mstdnAccountFactory->createFromContactId($Notification->actorId, $Notification->uid); - if (!empty($notification['target-uri-id'])) { + if ($Notification->targetUriId) { try { - $status = $this->mstdnStatusFactory->createFromUriId($notification['target-uri-id'], $notification['uid']); + $status = $this->mstdnStatusFactory->createFromUriId($Notification->targetUriId, $Notification->uid); } catch (\Throwable $th) { $status = null; } @@ -67,6 +64,41 @@ class Notification extends BaseFactory $status = null; } - return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['created'], $account, $status); + return new MstdnNotification($Notification->id, $type, $Notification->created, $account, $status); + } + + /** + * Computes the Mastodon notification type from the given local notification + * + * @param Entity\Notification $Notification + * @return string + * @throws \Exception + */ + public static function getType(Entity\Notification $Notification): string + { + if (($Notification->verb == Activity::FOLLOW) && ($Notification->type === Post\UserNotification::TYPE_NONE)) { + $contact = Contact::getById($Notification->actorId, ['pending']); + $type = $contact['pending'] ? MstdnNotification::TYPE_INTRODUCTION : MstdnNotification::TYPE_FOLLOW; + } elseif (($Notification->verb == Activity::ANNOUNCE) && + in_array($Notification->type, [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { + $type = MstdnNotification::TYPE_RESHARE; + } elseif (in_array($Notification->verb, [Activity::LIKE, Activity::DISLIKE]) && + in_array($Notification->type, [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { + $type = MstdnNotification::TYPE_LIKE; + } elseif ($Notification->type === Post\UserNotification::TYPE_SHARED) { + $type = MstdnNotification::TYPE_POST; + } elseif (in_array($Notification->type, [ + Post\UserNotification::TYPE_EXPLICIT_TAGGED, + Post\UserNotification::TYPE_IMPLICIT_TAGGED, + Post\UserNotification::TYPE_DIRECT_COMMENT, + Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, + Post\UserNotification::TYPE_THREAD_COMMENT + ])) { + $type = MstdnNotification::TYPE_MENTION; + } else { + return ''; + } + + return $type; } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 18e041ab9..ddf541b70 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2705,7 +2705,7 @@ class Contact // Ensure to always have the correct network type, independent from the connection request method self::updateFromProbe($contact['id']); - Post\UserNotification::insertNotification($contact['id'], Verb::getID(Activity::FOLLOW), $importer['uid']); + Post\UserNotification::insertNotification($contact['id'], Activity::FOLLOW, $importer['uid']); return true; } else { @@ -2736,7 +2736,7 @@ class Contact self::updateAvatar($contact_id, $photo, true); - Post\UserNotification::insertNotification($contact_id, Verb::getID(Activity::FOLLOW), $importer['uid']); + Post\UserNotification::insertNotification($contact_id, Activity::FOLLOW, $importer['uid']); $contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]); diff --git a/src/Model/Notification.php b/src/Model/Notification.php deleted file mode 100644 index 284b3e7a8..000000000 --- a/src/Model/Notification.php +++ /dev/null @@ -1,266 +0,0 @@ -. - * - */ - -namespace Friendica\Model; - -use Friendica\BaseModel; -use Friendica\Content\Text\BBCode; -use Friendica\Content\Text\Plaintext; -use Friendica\Core\Logger; -use Friendica\Database\Database; -use Friendica\DI; -use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Object\Api\Mastodon\Notification as MstdnNotification; -use Friendica\Protocol\Activity; -use Psr\Log\LoggerInterface; - -/** - * Model for an entry in the notify table - */ -class Notification extends BaseModel -{ - /** - * Fetch the notification type for the given notification - * - * @param array $notification - * @return string - */ - public static function getType(array $notification): string - { - if (($notification['vid'] == Verb::getID(Activity::FOLLOW)) && ($notification['type'] == Post\UserNotification::TYPE_NONE)) { - $contact = Contact::getById($notification['actor-id'], ['pending']); - $type = $contact['pending'] ? MstdnNotification::TYPE_INTRODUCTION : MstdnNotification::TYPE_FOLLOW; - } elseif (($notification['vid'] == Verb::getID(Activity::ANNOUNCE)) && - in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { - $type = MstdnNotification::TYPE_RESHARE; - } elseif (in_array($notification['vid'], [Verb::getID(Activity::LIKE), Verb::getID(Activity::DISLIKE)]) && - in_array($notification['type'], [Post\UserNotification::TYPE_DIRECT_COMMENT, Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT])) { - $type = MstdnNotification::TYPE_LIKE; - } elseif ($notification['type'] == Post\UserNotification::TYPE_SHARED) { - $type = MstdnNotification::TYPE_POST; - } elseif (in_array($notification['type'], [ - Post\UserNotification::TYPE_EXPLICIT_TAGGED, - Post\UserNotification::TYPE_IMPLICIT_TAGGED, - Post\UserNotification::TYPE_DIRECT_COMMENT, - Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT, - Post\UserNotification::TYPE_THREAD_COMMENT - ])) { - $type = MstdnNotification::TYPE_MENTION; - } else { - return ''; - } - - return $type; - } - - /** - * Create a notification message for the given notification - * - * @param array $notification - * @return array with the elements "causer", "notification", "plain" and "rich" - */ - public static function getMessage(array $notification) - { - $message = []; - - $user = User::getById($notification['uid']); - if (empty($user)) { - Logger::info('User not found', ['application' => $notification['uid']]); - return $message; - } - - $l10n = DI::l10n()->withLang($user['language']); - - $causer = $contact = Contact::getById($notification['actor-id'], ['id', 'name', 'url', 'pending']); - if (empty($contact)) { - Logger::info('Contact not found', ['contact' => $notification['actor-id']]); - return $message; - } - - if ($notification['type'] == Post\UserNotification::TYPE_NONE) { - if ($contact['pending']) { - $msg = $l10n->t('%1$s wants to follow you'); - } else { - $msg = $l10n->t('%1$s had started following you'); - } - $title = $contact['name']; - $link = DI::baseUrl() . '/contact/' . $contact['id']; - } else { - if (empty($notification['target-uri-id'])) { - return $message; - } - - $like = Verb::getID(Activity::LIKE); - $dislike = Verb::getID(Activity::DISLIKE); - $announce = Verb::getID(Activity::ANNOUNCE); - $post = Verb::getID(Activity::POST); - - if (in_array($notification['type'], [Post\UserNotification::TYPE_THREAD_COMMENT, Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION, Post\UserNotification::TYPE_EXPLICIT_TAGGED])) { - $item = Post::selectFirst([], ['uri-id' => $notification['parent-uri-id'], 'uid' => [0, $notification['uid']]], ['order' => ['uid' => true]]); - if (empty($item)) { - Logger::info('Parent post not found', ['uri-id' => $notification['parent-uri-id']]); - return $message; - } - } else { - $item = Post::selectFirst([], ['uri-id' => $notification['target-uri-id'], 'uid' => [0, $notification['uid']]], ['order' => ['uid' => true]]); - if (empty($item)) { - Logger::info('Post not found', ['uri-id' => $notification['target-uri-id']]); - return $message; - } - - if ($notification['vid'] == $post) { - $item = Post::selectFirst([], ['uri-id' => $item['thr-parent-id'], 'uid' => [0, $notification['uid']]], ['order' => ['uid' => true]]); - if (empty($item)) { - Logger::info('Thread parent post not found', ['uri-id' => $item['thr-parent-id']]); - return $message; - } - } - } - - if ($item['owner-id'] != $item['author-id']) { - $cid = $item['owner-id']; - } - if (!empty($item['causer-id']) && ($item['causer-id'] != $item['author-id'])) { - $cid = $item['causer-id']; - } - - if (($notification['type'] == Post\UserNotification::TYPE_SHARED) && !empty($cid)) { - $causer = Contact::getById($cid, ['id', 'name', 'url']); - if (empty($contact)) { - Logger::info('Causer not found', ['causer' => $cid]); - return $message; - } - } elseif (in_array($notification['type'], [Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION])) { - $contact = Contact::getById($item['author-id'], ['id', 'name', 'url']); - if (empty($contact)) { - Logger::info('Author not found', ['author' => $item['author-id']]); - return $message; - } - } - - $link = DI::baseUrl() . '/display/' . urlencode($item['guid']); - - $content = Plaintext::getPost($item, 70); - if (!empty($content['text'])) { - $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; - } else { - $title = ''; - } - - switch ($notification['vid']) { - case $like: - switch ($notification['type']) { - case Post\UserNotification::TYPE_DIRECT_COMMENT: - $msg = $l10n->t('%1$s liked your comment %2$s'); - break; - case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: - $msg = $l10n->t('%1$s liked your post %2$s'); - break; - } - break; - case $dislike: - switch ($notification['type']) { - case Post\UserNotification::TYPE_DIRECT_COMMENT: - $msg = $l10n->t('%1$s disliked your comment %2$s'); - break; - case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: - $msg = $l10n->t('%1$s disliked your post %2$s'); - break; - } - break; - case $announce: - switch ($notification['type']) { - case Post\UserNotification::TYPE_DIRECT_COMMENT: - $msg = $l10n->t('%1$s shared your comment %2$s'); - break; - case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: - $msg = $l10n->t('%1$s shared your post %2$s'); - break; - } - break; - case $post: - switch ($notification['type']) { - case Post\UserNotification::TYPE_EXPLICIT_TAGGED: - $msg = $l10n->t('%1$s tagged you on %2$s'); - break; - - case Post\UserNotification::TYPE_IMPLICIT_TAGGED: - $msg = $l10n->t('%1$s replied to you on %2$s'); - break; - - case Post\UserNotification::TYPE_THREAD_COMMENT: - $msg = $l10n->t('%1$s commented in your thread %2$s'); - break; - - case Post\UserNotification::TYPE_DIRECT_COMMENT: - $msg = $l10n->t('%1$s commented on your comment %2$s'); - break; - - case Post\UserNotification::TYPE_COMMENT_PARTICIPATION: - case Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION: - if (($causer['id'] == $contact['id']) && ($title != '')) { - $msg = $l10n->t('%1$s commented in their thread %2$s'); - } elseif ($causer['id'] == $contact['id']) { - $msg = $l10n->t('%1$s commented in their thread'); - } elseif ($title != '') { - $msg = $l10n->t('%1$s commented in the thread %2$s from %3$s'); - } else { - $msg = $l10n->t('%1$s commented in the thread from %3$s'); - } - break; - - case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT: - $msg = $l10n->t('%1$s commented on your thread %2$s'); - break; - - case Post\UserNotification::TYPE_SHARED: - if (($causer['id'] != $contact['id']) && ($title != '')) { - $msg = $l10n->t('%1$s shared the post %2$s from %3$s'); - } elseif ($causer['id'] != $contact['id']) { - $msg = $l10n->t('%1$s shared a post from %3$s'); - } elseif ($title != '') { - $msg = $l10n->t('%1$s shared the post %2$s'); - } else { - $msg = $l10n->t('%1$s shared a post'); - } - break; - } - break; - } - } - - if (!empty($msg)) { - // Name of the notification's causer - $message['causer'] = $causer['name']; - // Format for the "ping" mechanism - $message['notification'] = sprintf($msg, '{0}', $title, $contact['name']); - // Plain text for the web push api - $message['plain'] = sprintf($msg, $causer['name'], $title, $contact['name']); - // Rich text for other purposes - $message['rich'] = sprintf($msg, - '[url=' . $causer['url'] . ']' . $causer['name'] . '[/url]', - '[url=' . $link . ']' . $title . '[/url]', - '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'); - } - - return $message; - } -} diff --git a/src/Model/Post/UserNotification.php b/src/Model/Post/UserNotification.php index 065cba44c..d14eb7f3b 100644 --- a/src/Model/Post/UserNotification.php +++ b/src/Model/Post/UserNotification.php @@ -33,9 +33,9 @@ use Friendica\Model\Contact; use Friendica\Model\Post; use Friendica\Model\Subscription; use Friendica\Model\Tag; +use Friendica\Navigation\Notifications; use Friendica\Network\HTTPException; use Friendica\Protocol\Activity; -use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; class UserNotification @@ -286,56 +286,46 @@ class UserNotification return; } - $fields = [ - 'uid' => $uid, - 'vid' => $item['vid'], - 'type' => $type, - 'actor-id' => $item['author-id'], - 'parent-uri-id' => $item['parent-uri-id'], - 'created' => DateTimeFormat::utcNow(), - ]; + $notification = (new Notifications\Factory\Notification(DI::logger()))->createForUser( + $uid, + $item['vid'], + $type, + $item['author-id'], + $item['gravity'] == GRAVITY_ACTIVITY ? $item['thr-parent-id'] : $item['uri-id'], + $item['parent-uri-id'] + ); - if ($item['gravity'] == GRAVITY_ACTIVITY) { - $fields['target-uri-id'] = $item['thr-parent-id']; - } else { - $fields['target-uri-id'] = $item['uri-id']; - } + try { + $notification = DI::notification()->save($notification); + Subscription::pushByNotification($notification); + } catch (Exception $e) { - if (DBA::insert('notification', $fields, Database::INSERT_IGNORE)) { - $id = DBA::lastInsertId(); - if (!empty($id)) { - Subscription::pushByNotificationId($id); - } } } /** * Add a notification entry * - * @param int $actor Contact ID of the actor - * @param int $vid Verb ID - * @param int $uid User ID + * @param int $actor Contact ID of the actor + * @param string $verb One of the Activity verb constant values + * @param int $uid User ID * @return boolean * @throws Exception */ - public static function insertNotification(int $actor, int $vid, int $uid): bool + public static function insertNotification(int $actor, string $verb, int $uid): bool { - $fields = [ - 'uid' => $uid, - 'vid' => $vid, - 'type' => self::TYPE_NONE, - 'actor-id' => $actor, - 'created' => DateTimeFormat::utcNow(), - ]; - - $ret = DBA::insert('notification', $fields, Database::INSERT_IGNORE); - if ($ret) { - $id = DBA::lastInsertId(); - if (!empty($id)) { - Subscription::pushByNotificationId($id); - } + $notification = (new Notifications\Factory\Notification(DI::logger()))->createForRelationship( + $uid, + $actor, + $verb + ); + try { + $notification = DI::notification()->save($notification); + Subscription::pushByNotification($notification); + return true; + } catch (Exception $e) { + return false; } - return $ret; } /** diff --git a/src/Model/Subscription.php b/src/Model/Subscription.php index f9498af98..c6c57c732 100644 --- a/src/Model/Subscription.php +++ b/src/Model/Subscription.php @@ -25,6 +25,7 @@ use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Navigation\Notifications\Entity; use Friendica\Object\Api\Mastodon\Notification; use Minishlink\WebPush\VAPID; @@ -138,34 +139,32 @@ class Subscription * @param int $nid * @return void */ - public static function pushByNotificationId(int $nid) + public static function pushByNotification(Entity\Notification $Notification) { - $notification = DBA::selectFirst('notification', [], ['id' => $nid]); + $type = \Friendica\Factory\Api\Mastodon\Notification::getType($Notification); - $type = Notification::getType($notification); $desktop_notification = !in_array($type, [Notification::TYPE_RESHARE, Notification::TYPE_LIKE]); - - if (DI::pConfig()->get($notification['uid'], 'system', 'notify_like') && ($type == 'favourite')) { + if (DI::pConfig()->get($Notification->uid, 'system', 'notify_like') && ($type == Notification::TYPE_LIKE)) { $desktop_notification = true; } - if (DI::pConfig()->get($notification['uid'], 'system', 'notify_announce') && ($type == 'reblog')) { + if (DI::pConfig()->get($Notification->uid, 'system', 'notify_announce') && ($type == Notification::TYPE_RESHARE)) { $desktop_notification = true; } if ($desktop_notification) { - notification_from_array($notification); + notification_from_array($Notification); } if (empty($type)) { return; } - $subscriptions = DBA::select('subscription', [], ['uid' => $notification['uid'], $type => true]); + $subscriptions = DBA::select('subscription', [], ['uid' => $Notification->uid, $type => true]); while ($subscription = DBA::fetch($subscriptions)) { Logger::info('Push notification', ['id' => $subscription['id'], 'uid' => $subscription['uid'], 'type' => $type]); - Worker::add(PRIORITY_HIGH, 'PushSubscription', $subscription['id'], $nid); + Worker::add(PRIORITY_HIGH, 'PushSubscription', $subscription['id'], $Notification->id); } DBA::close($subscriptions); } diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index a04a9ae69..28166e6ed 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -28,6 +28,8 @@ use Friendica\Model\Contact; use Friendica\Model\Post; use Friendica\Model\Verb; use Friendica\Module\BaseApi; +use Friendica\Navigation\Notifications\Entity; +use Friendica\Object\Api\Mastodon\Notification; use Friendica\Protocol\Activity; /** @@ -46,10 +48,12 @@ class Notifications extends BaseApi if (!empty($parameters['id'])) { $id = $parameters['id']; - if (!DBA::exists('notification', ['id' => $id, 'uid' => $uid])) { + try { + $notification = DI::notification()->selectOneForUser($uid, ['id' => $id]); + System::jsonExit(DI::mstdnNotification()->createFromNotification($notification)); + } catch (\Exception $e) { DI::mstdnError()->RecordNotFound(); } - System::jsonExit(DI::mstdnNotification()->createFromNotificationId($id)); } $request = self::getRequest([ @@ -63,7 +67,7 @@ class Notifications extends BaseApi 'count' => 0, // Unknown parameter ]); - $params = ['order' => ['id' => true], 'limit' => $request['limit']]; + $params = ['order' => ['id' => true]]; $condition = ['uid' => $uid, 'seen' => false]; @@ -115,36 +119,26 @@ class Notifications extends BaseApi Verb::getID(Activity::POST), Post\UserNotification::TYPE_SHARED]); } - if (!empty($request['max_id'])) { - $condition = DBA::mergeConditions($condition, ["`id` < ?", $request['max_id']]); - } + $mstdnNotifications = []; - if (!empty($request['since_id'])) { - $condition = DBA::mergeConditions($condition, ["`id` > ?", $request['since_id']]); - } + $Notifications = DI::notification()->selectByBoundaries( + $condition, + $params, + $request['min_id'] ?? null, + $request['min_id'] ?? $request['since_id'] ?? null, + $request['limit'] + ); - if (!empty($request['min_id'])) { - $condition = DBA::mergeConditions($condition, ["`id` > ?", $request['min_id']]); - - $params['order'] = ['id']; - } - - $notifications = []; - - $notify = DBA::select('notification', ['id'], $condition, $params); - while ($notification = DBA::fetch($notify)) { - self::setBoundaries($notification['id']); - $entry = DI::mstdnNotification()->createFromNotificationId($notification['id']); - if (!empty($entry)) { - $notifications[] = $entry; + foreach($Notifications as $Notification) { + try { + $mstdnNotifications[] = DI::mstdnNotification()->createFromNotification($Notification); + self::setBoundaries($Notification->id); + } catch (\Exception $e) { + // Skip this notification } } - if (!empty($request['min_id'])) { - array_reverse($notifications); - } - self::setLinkHeader(); - System::jsonExit($notifications); + System::jsonExit($mstdnNotifications); } } diff --git a/src/Module/Api/Mastodon/Notifications/Clear.php b/src/Module/Api/Mastodon/Notifications/Clear.php index 5471dc24e..9dca0bf65 100644 --- a/src/Module/Api/Mastodon/Notifications/Clear.php +++ b/src/Module/Api/Mastodon/Notifications/Clear.php @@ -22,7 +22,7 @@ namespace Friendica\Module\Api\Mastodon\Notifications; use Friendica\Core\System; -use Friendica\Database\DBA; +use Friendica\DI; use Friendica\Module\BaseApi; /** @@ -35,7 +35,7 @@ class Clear extends BaseApi self::checkAllowedScope(self::SCOPE_WRITE); $uid = self::getCurrentUserID(); - DBA::update('notification', ['seen' => true], ['uid' => $uid]); + DI::notification()->setAllSeenForUser($uid); System::jsonExit([]); } diff --git a/src/Module/Api/Mastodon/Notifications/Dismiss.php b/src/Module/Api/Mastodon/Notifications/Dismiss.php index 8900a2d43..b615c5e89 100644 --- a/src/Module/Api/Mastodon/Notifications/Dismiss.php +++ b/src/Module/Api/Mastodon/Notifications/Dismiss.php @@ -25,6 +25,7 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; +use Friendica\Network\HTTPException\ForbiddenException; /** * @see https://docs.joinmastodon.org/methods/notifications/ @@ -40,7 +41,9 @@ class Dismiss extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - DBA::update('notification', ['seen' => true], ['uid' => $uid, 'id' => $parameters['id']]); + $Notification = DI::notification()->selectOneForUser($uid, $parameters['id']); + $Notification->setSeen(); + DI::notification()->save($Notification); System::jsonExit([]); } diff --git a/src/Module/Notifications/Notification.php b/src/Module/Notifications/Notification.php index 6f032772c..2480e63b0 100644 --- a/src/Module/Notifications/Notification.php +++ b/src/Module/Notifications/Notification.php @@ -78,7 +78,7 @@ class Notification extends BaseModule if (DI::args()->get(1) === 'mark' && DI::args()->get(2) === 'all') { try { - DI::dba()->update('notification', ['seen' => true], ['uid' => local_user()]); + DI::notification()->setAllSeenForUser(local_user()); $success = DI::notify()->setAllSeenForUser(local_user()); } catch (\Exception $e) { DI::logger()->warning('set all seen failed.', ['exception' => $e]); @@ -118,7 +118,7 @@ class Notification extends BaseModule DI::notify()->save($Notify); } else { if ($Notify->uriId) { - DI::dba()->update('notification', ['seen' => true], ['uid' => $Notify->uid, 'target-uri-id' => $Notify->uriId]); + DI::notification()->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]); } DI::notify()->setAllSeenForRelatedNotify($Notify); diff --git a/src/Module/Notifications/Notifications.php b/src/Module/Notifications/Notifications.php index 79f1596f9..af8b14512 100644 --- a/src/Module/Notifications/Notifications.php +++ b/src/Module/Notifications/Notifications.php @@ -95,25 +95,24 @@ class Notifications extends BaseNotifications $notificationHeader = $notificationResult['header'] ?? ''; if (!empty($notifications['notifications'])) { + $notificationTemplates = [ + 'like' => 'notifications/likes_item.tpl', + 'dislike' => 'notifications/dislikes_item.tpl', + 'attend' => 'notifications/attend_item.tpl', + 'attendno' => 'notifications/attend_item.tpl', + 'attendmaybe' => 'notifications/attend_item.tpl', + 'friend' => 'notifications/friends_item.tpl', + 'comment' => 'notifications/comments_item.tpl', + 'post' => 'notifications/posts_item.tpl', + 'notification' => 'notifications/notification.tpl', + ]; // Loop trough ever notification This creates an array with the output html for each // notification and apply the correct template according to the notificationtype (label). - /** @var FormattedNotification $notification */ - foreach ($notifications['notifications'] as $notification) { - $notification_templates = [ - 'like' => 'notifications/likes_item.tpl', - 'dislike' => 'notifications/dislikes_item.tpl', - 'attend' => 'notifications/attend_item.tpl', - 'attendno' => 'notifications/attend_item.tpl', - 'attendmaybe' => 'notifications/attend_item.tpl', - 'friend' => 'notifications/friends_item.tpl', - 'comment' => 'notifications/comments_item.tpl', - 'post' => 'notifications/posts_item.tpl', - 'notification' => 'notifications/notification.tpl', - ]; + /** @var FormattedNotification $Notification */ + foreach ($notifications['notifications'] as $Notification) { + $notificationArray = $Notification->toArray(); - $notificationArray = $notification->toArray(); - - $notificationTemplate = Renderer::getMarkupTemplate($notification_templates[$notificationArray['label']]); + $notificationTemplate = Renderer::getMarkupTemplate($notificationTemplates[$notificationArray['label']]); $notificationContent[] = Renderer::replaceMacros($notificationTemplate, [ '$notification' => $notificationArray diff --git a/src/Object/Api/Mastodon/Notification.php b/src/Object/Api/Mastodon/Notification.php index d660baa08..d912dc894 100644 --- a/src/Object/Api/Mastodon/Notification.php +++ b/src/Object/Api/Mastodon/Notification.php @@ -66,11 +66,11 @@ class Notification extends BaseDataTransferObject * * @throws HttpException\InternalServerErrorException|Exception */ - public function __construct(int $id, string $type, string $created_at, Account $account = null, Status $status = null) + public function __construct(int $id, string $type, \DateTime $created_at, Account $account = null, Status $status = null) { $this->id = (string)$id; $this->type = $type; - $this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::JSON); + $this->created_at = $created_at->format(DateTimeFormat::JSON); $this->account = $account->toArray(); if (!empty($status)) { diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index eaf2adb32..ae34abc87 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -27,10 +27,11 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\Notification; use Friendica\Model\Post; use Friendica\Model\Subscription as ModelSubscription; use Friendica\Model\User; +use Friendica\Navigation\Notifications; +use Friendica\Network\HTTPException\NotFoundException; use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; @@ -46,8 +47,9 @@ class PushSubscription return; } - $notification = DBA::selectFirst('notification', [], ['id' => $nid]); - if (empty($notification)) { + try { + $Notification = DI::notification()->selectOneById($nid); + } catch (NotFoundException $e) { Logger::info('Notification not found', ['notification' => $nid]); return; } @@ -58,7 +60,7 @@ class PushSubscription return; } - $user = User::getById($notification['uid']); + $user = User::getById($Notification->uid); if (empty($user)) { Logger::info('User not found', ['application' => $subscription['uid']]); return; @@ -66,23 +68,21 @@ class PushSubscription $l10n = DI::l10n()->withLang($user['language']); - $type = Notification::getType($notification); - - if (!empty($notification['actor-id'])) { - $actor = Contact::getById($notification['actor-id']); + if ($Notification->actorId) { + $actor = Contact::getById($Notification->actorId); } $body = ''; - if (!empty($notification['target-uri-id'])) { - $post = Post::selectFirst([], ['uri-id' => $notification['target-uri-id'], 'uid' => [0, $notification['uid']]]); + if ($Notification->targetUriId) { + $post = Post::selectFirst([], ['uri-id' => $Notification->targetUriId, 'uid' => [0, $Notification->uid]]); if (!empty($post['body'])) { $body = BBCode::toPlaintext($post['body'], false); - $body = Plaintext::shorten($body, 160, $notification['uid']); + $body = Plaintext::shorten($body, 160, $Notification->uid); } } - $message = Notification::getMessage($notification); + $message = (new Notifications\Factory\Notification(DI::logger()))->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); $title = $message['plain'] ?: ''; $push = Subscription::create([ @@ -98,7 +98,7 @@ class PushSubscription 'access_token' => $application_token['access_token'], 'preferred_locale' => $user['language'], 'notification_id' => $nid, - 'notification_type' => $type, + 'notification_type' => \Friendica\Factory\Api\Mastodon\Notification::getType($Notification), 'icon' => $actor['thumb'] ?? '', 'title' => $title ?: $l10n->t('Notification from Friendica'), 'body' => $body ?: $l10n->t('Empty Post'), From 8a1a2f658cc52db2cfebba346574ad2feb605df8 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 19 Sep 2021 12:56:24 -0400 Subject: [PATCH 10/12] Create new DI shorthands for notification-related factories --- include/enotify.php | 8 ++------ src/DI.php | 15 +++++++++++++++ src/Module/Notifications/Notifications.php | 3 +-- src/Worker/PushSubscription.php | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/enotify.php b/include/enotify.php index 90a17ad25..c30b6de86 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -30,7 +30,6 @@ use Friendica\Model\Item; use Friendica\Model\Notification; use Friendica\Model\Post; use Friendica\Model\User; -use Friendica\Model\Verb; use Friendica\Navigation\Notifications; use Friendica\Protocol\Activity; @@ -396,10 +395,7 @@ function notification_store_and_send($params, $sitelink, $tsitelink, $hsitelink, $notify_id = 0; if ($show_in_notification_page) { - /** @var $factory Notifications\Factory\Notify */ - $factory = DI::getDice()->create(Notifications\Factory\Notify::class); - - $Notify = $factory->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id); + $Notify = DI::notifyFactory()->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id); try { $Notify = DI::notify()->save($Notify); } catch (Notifications\Exception\NotificationCreationInterceptedException $e) { @@ -572,7 +568,7 @@ function notification_from_array(Notifications\Entity\Notification $Notification $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']); } - $msg = (new Notifications\Factory\Notification(DI::logger()))->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); + $msg = DI::notificationFactory()->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); if (empty($msg)) { Logger::info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); return false; diff --git a/src/DI.php b/src/DI.php index 4168997d4..ea7310fee 100644 --- a/src/DI.php +++ b/src/DI.php @@ -463,11 +463,26 @@ abstract class DI return self::$dice->create(Navigation\Notifications\Depository\Notification::class); } + public static function notificationFactory(): Navigation\Notifications\Factory\Notification + { + return self::$dice->create(Navigation\Notifications\Factory\Notification::class); + } + public static function notify(): Navigation\Notifications\Depository\Notify { return self::$dice->create(Navigation\Notifications\Depository\Notify::class); } + public static function notifyFactory(): Navigation\Notifications\Factory\Notify + { + return self::$dice->create(Navigation\Notifications\Factory\Notify::class); + } + + public static function formattedNotificationFactory(): Navigation\Notifications\Factory\FormattedNotification + { + return self::$dice->create(Navigation\Notifications\Factory\FormattedNotification::class); + } + // // "Protocol" namespace instances // diff --git a/src/Module/Notifications/Notifications.php b/src/Module/Notifications/Notifications.php index af8b14512..a0e40719c 100644 --- a/src/Module/Notifications/Notifications.php +++ b/src/Module/Notifications/Notifications.php @@ -46,8 +46,7 @@ class Notifications extends BaseNotifications $notificationHeader = ''; $notifications = []; - /** @var \Friendica\Navigation\Notifications\Factory\FormattedNotification $factory */ - $factory = DI::getDice()->create(\Friendica\Navigation\Notifications\Factory\FormattedNotification::class); + $factory = DI::formattedNotificationFactory(); if ((DI::args()->get(1) == 'network')) { $notificationHeader = DI::l10n()->t('Network Notifications'); diff --git a/src/Worker/PushSubscription.php b/src/Worker/PushSubscription.php index ae34abc87..f07b77319 100644 --- a/src/Worker/PushSubscription.php +++ b/src/Worker/PushSubscription.php @@ -82,7 +82,7 @@ class PushSubscription } } - $message = (new Notifications\Factory\Notification(DI::logger()))->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); + $message = DI::notificationFactory()->getMessageFromNotification($Notification, DI::baseUrl(), $l10n); $title = $message['plain'] ?: ''; $push = Subscription::create([ From 4f42522ebcefd85cb3c54b7e4dbba79a30518864 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 19 Sep 2021 12:57:16 -0400 Subject: [PATCH 11/12] Simplify Depository\Notify->selectAllForUser parameters - Shifts database structure knowledge from the controller to the depository where it should be --- include/api.php | 2 +- src/Navigation/Notifications/Depository/Notify.php | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/api.php b/include/api.php index 11950f52f..41d1e2f1e 100644 --- a/include/api.php +++ b/include/api.php @@ -5590,7 +5590,7 @@ function api_friendica_notification($type) throw new BadRequestException('Invalid argument count'); } - $Notifies = DI::notify()->selectAllForUser(local_user(), ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50]); + $Notifies = DI::notify()->selectAllForUser(local_user(), 50); $notifications = new ApiNotifications(); foreach ($Notifies as $Notify) { diff --git a/src/Navigation/Notifications/Depository/Notify.php b/src/Navigation/Notifications/Depository/Notify.php index c35f50a73..4c7a1ef33 100644 --- a/src/Navigation/Notifications/Depository/Notify.php +++ b/src/Navigation/Notifications/Depository/Notify.php @@ -73,9 +73,16 @@ class Notify extends BaseDepository return $this->select($condition, $params); } - public function selectAllForUser(int $uid, array $params = []): Collection\Notifies + /** + * Returns notifications for the user, unread first, ordered in descending chronological order. + * + * @param int $uid + * @param int $limit + * @return Collection\Notifies + */ + public function selectAllForUser(int $uid, int $limit): Collection\Notifies { - return $this->selectForUser($uid, [], $params); + return $this->selectForUser($uid, [], ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => $limit]); } public function setAllSeenForUser(int $uid, array $condition = []): bool From d441c8b25fcf86d4a9f1d2ca451fca01da00ef37 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 18 Sep 2021 01:22:52 -0400 Subject: [PATCH 12/12] Update main translation file after strings have moved --- view/lang/C/messages.po | 492 ++++++++++++++++++++-------------------- 1 file changed, 246 insertions(+), 246 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 3e2421d04..dc445b732 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2021.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-02 16:56-0400\n" +"POT-Creation-Date: 2021-10-02 17:52-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -37,7 +37,7 @@ msgstr[1] "" msgid "Monthly posting limit of %d post reached. The post was rejected." msgstr "" -#: include/api.php:4428 mod/photos.php:89 mod/photos.php:198 mod/photos.php:621 +#: include/api.php:4431 mod/photos.php:89 mod/photos.php:198 mod/photos.php:621 #: mod/photos.php:1032 mod/photos.php:1049 mod/photos.php:1598 #: src/Model/User.php:1169 src/Model/User.php:1177 src/Model/User.php:1185 #: src/Module/Settings/Profile/Photo/Crop.php:101 @@ -49,195 +49,195 @@ msgstr "" msgid "Profile Photos" msgstr "" -#: include/enotify.php:52 include/enotify.php:559 +#: include/enotify.php:50 include/enotify.php:533 msgid "[Friendica:Notify]" msgstr "" -#: include/enotify.php:116 +#: include/enotify.php:114 #, php-format msgid "%s New mail received at %s" msgstr "" -#: include/enotify.php:118 +#: include/enotify.php:116 #, php-format msgid "%1$s sent you a new private message at %2$s." msgstr "" -#: include/enotify.php:119 +#: include/enotify.php:117 msgid "a private message" msgstr "" -#: include/enotify.php:119 +#: include/enotify.php:117 #, php-format msgid "%1$s sent you %2$s." msgstr "" -#: include/enotify.php:121 +#: include/enotify.php:119 #, php-format msgid "Please visit %s to view and/or reply to your private messages." msgstr "" -#: include/enotify.php:152 +#: include/enotify.php:150 #, php-format msgid "%1$s commented on %2$s's %3$s %4$s" msgstr "" -#: include/enotify.php:157 +#: include/enotify.php:155 #, php-format msgid "%1$s commented on your %2$s %3$s" msgstr "" -#: include/enotify.php:161 +#: include/enotify.php:159 #, php-format msgid "%1$s commented on their %2$s %3$s" msgstr "" -#: include/enotify.php:165 include/enotify.php:594 +#: include/enotify.php:163 include/enotify.php:568 #, php-format msgid "%1$s Comment to conversation #%2$d by %3$s" msgstr "" -#: include/enotify.php:167 +#: include/enotify.php:165 #, php-format msgid "%s commented on an item/conversation you have been following." msgstr "" -#: include/enotify.php:171 include/enotify.php:186 include/enotify.php:205 -#: include/enotify.php:609 +#: include/enotify.php:169 include/enotify.php:184 include/enotify.php:203 +#: include/enotify.php:583 #, php-format msgid "Please visit %s to view and/or reply to the conversation." msgstr "" -#: include/enotify.php:178 +#: include/enotify.php:176 #, php-format msgid "%s %s posted to your profile wall" msgstr "" -#: include/enotify.php:180 +#: include/enotify.php:178 #, php-format msgid "%1$s posted to your profile wall at %2$s" msgstr "" -#: include/enotify.php:181 +#: include/enotify.php:179 #, php-format msgid "%1$s posted to [url=%2$s]your wall[/url]" msgstr "" -#: include/enotify.php:193 +#: include/enotify.php:191 #, php-format msgid "%1$s %2$s poked you" msgstr "" -#: include/enotify.php:195 +#: include/enotify.php:193 #, php-format msgid "%1$s poked you at %2$s" msgstr "" -#: include/enotify.php:196 +#: include/enotify.php:194 #, php-format msgid "%1$s [url=%2$s]poked you[/url]." msgstr "" -#: include/enotify.php:213 +#: include/enotify.php:211 #, php-format msgid "%s Introduction received" msgstr "" -#: include/enotify.php:215 +#: include/enotify.php:213 #, php-format msgid "You've received an introduction from '%1$s' at %2$s" msgstr "" -#: include/enotify.php:216 +#: include/enotify.php:214 #, php-format msgid "You've received [url=%1$s]an introduction[/url] from %2$s." msgstr "" -#: include/enotify.php:221 include/enotify.php:267 +#: include/enotify.php:219 include/enotify.php:265 #, php-format msgid "You may visit their profile at %s" msgstr "" -#: include/enotify.php:223 +#: include/enotify.php:221 #, php-format msgid "Please visit %s to approve or reject the introduction." msgstr "" -#: include/enotify.php:230 +#: include/enotify.php:228 #, php-format msgid "%s A new person is sharing with you" msgstr "" -#: include/enotify.php:232 include/enotify.php:233 +#: include/enotify.php:230 include/enotify.php:231 #, php-format msgid "%1$s is sharing with you at %2$s" msgstr "" -#: include/enotify.php:240 +#: include/enotify.php:238 #, php-format msgid "%s You have a new follower" msgstr "" -#: include/enotify.php:242 include/enotify.php:243 +#: include/enotify.php:240 include/enotify.php:241 #, php-format msgid "You have a new follower at %2$s : %1$s" msgstr "" -#: include/enotify.php:256 +#: include/enotify.php:254 #, php-format msgid "%s Friend suggestion received" msgstr "" -#: include/enotify.php:258 +#: include/enotify.php:256 #, php-format msgid "You've received a friend suggestion from '%1$s' at %2$s" msgstr "" -#: include/enotify.php:259 +#: include/enotify.php:257 #, php-format msgid "You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s." msgstr "" -#: include/enotify.php:265 +#: include/enotify.php:263 msgid "Name:" msgstr "" -#: include/enotify.php:266 +#: include/enotify.php:264 msgid "Photo:" msgstr "" -#: include/enotify.php:269 +#: include/enotify.php:267 #, php-format msgid "Please visit %s to approve or reject the suggestion." msgstr "" -#: include/enotify.php:277 include/enotify.php:292 +#: include/enotify.php:275 include/enotify.php:290 #, php-format msgid "%s Connection accepted" msgstr "" -#: include/enotify.php:279 include/enotify.php:294 +#: include/enotify.php:277 include/enotify.php:292 #, php-format msgid "'%1$s' has accepted your connection request at %2$s" msgstr "" -#: include/enotify.php:280 include/enotify.php:295 +#: include/enotify.php:278 include/enotify.php:293 #, php-format msgid "%2$s has accepted your [url=%1$s]connection request[/url]." msgstr "" -#: include/enotify.php:285 +#: include/enotify.php:283 msgid "" "You are now mutual friends and may exchange status updates, photos, and " "email without restriction." msgstr "" -#: include/enotify.php:287 +#: include/enotify.php:285 #, php-format msgid "Please visit %s if you wish to make any changes to this relationship." msgstr "" -#: include/enotify.php:300 +#: include/enotify.php:298 #, php-format msgid "" "'%1$s' has chosen to accept you a fan, which restricts some forms of " @@ -246,37 +246,37 @@ msgid "" "automatically." msgstr "" -#: include/enotify.php:302 +#: include/enotify.php:300 #, php-format msgid "" "'%1$s' may choose to extend this into a two-way or more permissive " "relationship in the future." msgstr "" -#: include/enotify.php:304 +#: include/enotify.php:302 #, php-format msgid "Please visit %s if you wish to make any changes to this relationship." msgstr "" -#: include/enotify.php:314 mod/removeme.php:63 +#: include/enotify.php:312 mod/removeme.php:63 msgid "[Friendica System Notify]" msgstr "" -#: include/enotify.php:314 +#: include/enotify.php:312 msgid "registration request" msgstr "" -#: include/enotify.php:316 +#: include/enotify.php:314 #, php-format msgid "You've received a registration request from '%1$s' at %2$s" msgstr "" -#: include/enotify.php:317 +#: include/enotify.php:315 #, php-format msgid "You've received a [url=%1$s]registration request[/url] from %2$s." msgstr "" -#: include/enotify.php:322 +#: include/enotify.php:320 #, php-format msgid "" "Full Name:\t%s\n" @@ -284,17 +284,17 @@ msgid "" "Login Name:\t%s (%s)" msgstr "" -#: include/enotify.php:328 +#: include/enotify.php:326 #, php-format msgid "Please visit %s to approve or reject the request." msgstr "" -#: include/enotify.php:588 +#: include/enotify.php:562 #, php-format msgid "%s %s tagged you" msgstr "" -#: include/enotify.php:591 +#: include/enotify.php:565 #, php-format msgid "%s %s shared a new post" msgstr "" @@ -638,7 +638,7 @@ msgstr "" #: mod/events.php:556 src/Content/Widget/VCard.php:98 src/Model/Event.php:86 #: src/Model/Event.php:113 src/Model/Event.php:483 src/Model/Event.php:969 #: src/Model/Profile.php:367 src/Module/Contact.php:565 -#: src/Module/Directory.php:150 src/Module/Notifications/Introductions.php:166 +#: src/Module/Directory.php:150 src/Module/Notifications/Introductions.php:165 #: src/Module/Profile/Profile.php:194 msgid "Location:" msgstr "" @@ -732,13 +732,13 @@ msgstr "" #: mod/follow.php:141 mod/unfollow.php:100 #: src/Module/Admin/Blocklist/Contact.php:100 src/Module/Contact.php:561 -#: src/Module/Notifications/Introductions.php:108 -#: src/Module/Notifications/Introductions.php:177 +#: src/Module/Notifications/Introductions.php:107 +#: src/Module/Notifications/Introductions.php:176 msgid "Profile URL" msgstr "" #: mod/follow.php:142 src/Module/Contact.php:573 -#: src/Module/Notifications/Introductions.php:170 +#: src/Module/Notifications/Introductions.php:169 #: src/Module/Profile/Profile.php:207 msgid "Tags:" msgstr "" @@ -957,8 +957,8 @@ msgstr "" msgid "Message collection failure." msgstr "" -#: mod/message.php:120 src/Module/Notifications/Introductions.php:114 -#: src/Module/Notifications/Introductions.php:149 +#: mod/message.php:120 src/Module/Notifications/Introductions.php:113 +#: src/Module/Notifications/Introductions.php:148 #: src/Module/Notifications/Notification.php:56 msgid "Discard" msgstr "" @@ -3039,8 +3039,8 @@ msgstr "" #: src/Content/Item.php:450 src/Module/Contact.php:545 #: src/Module/Contact.php:805 src/Module/Contact.php:1089 -#: src/Module/Notifications/Introductions.php:113 -#: src/Module/Notifications/Introductions.php:185 +#: src/Module/Notifications/Introductions.php:112 +#: src/Module/Notifications/Introductions.php:184 #: src/Module/Notifications/Notification.php:59 msgid "Ignore" msgstr "" @@ -3246,7 +3246,7 @@ msgid "Friend Requests" msgstr "" #: src/Content/Nav.php:278 src/Module/BaseNotifications.php:139 -#: src/Module/Notifications/Introductions.php:54 +#: src/Module/Notifications/Introductions.php:53 msgid "Notifications" msgstr "" @@ -3564,7 +3564,7 @@ msgid "Matrix:" msgstr "" #: src/Content/Widget/VCard.php:101 src/Model/Profile.php:465 -#: src/Module/Notifications/Introductions.php:180 +#: src/Module/Notifications/Introductions.php:179 msgid "Network:" msgstr "" @@ -4305,59 +4305,6 @@ msgstr "" msgid "Internal Server Error" msgstr "" -#: src/Factory/Notification/Introduction.php:135 -msgid "Friend Suggestion" -msgstr "" - -#: src/Factory/Notification/Introduction.php:161 -msgid "Friend/Connect Request" -msgstr "" - -#: src/Factory/Notification/Introduction.php:161 -msgid "New Follower" -msgstr "" - -#: src/Factory/Notification/Notification.php:101 -#, php-format -msgid "%s created a new post" -msgstr "" - -#: src/Factory/Notification/Notification.php:102 -#: src/Factory/Notification/Notification.php:360 -#, php-format -msgid "%s commented on %s's post" -msgstr "" - -#: src/Factory/Notification/Notification.php:128 -#, php-format -msgid "%s liked %s's post" -msgstr "" - -#: src/Factory/Notification/Notification.php:139 -#, php-format -msgid "%s disliked %s's post" -msgstr "" - -#: src/Factory/Notification/Notification.php:150 -#, php-format -msgid "%s is attending %s's event" -msgstr "" - -#: src/Factory/Notification/Notification.php:161 -#, php-format -msgid "%s is not attending %s's event" -msgstr "" - -#: src/Factory/Notification/Notification.php:172 -#, php-format -msgid "%s may attending %s's event" -msgstr "" - -#: src/Factory/Notification/Notification.php:199 -#, php-format -msgid "%s is now friends with %s" -msgstr "" - #: src/LegacyModule.php:49 #, php-format msgid "Legacy module file not found: %s" @@ -4368,8 +4315,8 @@ msgid "UnFollow" msgstr "" #: src/Model/Contact.php:1090 src/Module/Admin/Users/Pending.php:107 -#: src/Module/Notifications/Introductions.php:111 -#: src/Module/Notifications/Introductions.php:183 +#: src/Module/Notifications/Introductions.php:110 +#: src/Module/Notifications/Introductions.php:182 msgid "Approve" msgstr "" @@ -4588,111 +4535,6 @@ msgstr "" msgid "[no subject]" msgstr "" -#: src/Model/Notification.php:187 -#, php-format -msgid "%1$s wants to follow you" -msgstr "" - -#: src/Model/Notification.php:189 -#, php-format -msgid "%1$s had started following you" -msgstr "" - -#: src/Model/Notification.php:259 -#, php-format -msgid "%1$s liked your comment %2$s" -msgstr "" - -#: src/Model/Notification.php:262 -#, php-format -msgid "%1$s liked your post %2$s" -msgstr "" - -#: src/Model/Notification.php:269 -#, php-format -msgid "%1$s disliked your comment %2$s" -msgstr "" - -#: src/Model/Notification.php:272 -#, php-format -msgid "%1$s disliked your post %2$s" -msgstr "" - -#: src/Model/Notification.php:279 -#, php-format -msgid "%1$s shared your comment %2$s" -msgstr "" - -#: src/Model/Notification.php:282 -#, php-format -msgid "%1$s shared your post %2$s" -msgstr "" - -#: src/Model/Notification.php:289 -#, php-format -msgid "%1$s tagged you on %2$s" -msgstr "" - -#: src/Model/Notification.php:293 -#, php-format -msgid "%1$s replied to you on %2$s" -msgstr "" - -#: src/Model/Notification.php:297 -#, php-format -msgid "%1$s commented in your thread %2$s" -msgstr "" - -#: src/Model/Notification.php:301 -#, php-format -msgid "%1$s commented on your comment %2$s" -msgstr "" - -#: src/Model/Notification.php:307 -#, php-format -msgid "%1$s commented in their thread %2$s" -msgstr "" - -#: src/Model/Notification.php:309 -#, php-format -msgid "%1$s commented in their thread" -msgstr "" - -#: src/Model/Notification.php:311 -#, php-format -msgid "%1$s commented in the thread %2$s from %3$s" -msgstr "" - -#: src/Model/Notification.php:313 -#, php-format -msgid "%1$s commented in the thread from %3$s" -msgstr "" - -#: src/Model/Notification.php:318 -#, php-format -msgid "%1$s commented on your thread %2$s" -msgstr "" - -#: src/Model/Notification.php:323 -#, php-format -msgid "%1$s shared the post %2$s from %3$s" -msgstr "" - -#: src/Model/Notification.php:325 -#, php-format -msgid "%1$s shared a post from %3$s" -msgstr "" - -#: src/Model/Notification.php:327 -#, php-format -msgid "%1$s shared the post %2$s" -msgstr "" - -#: src/Model/Notification.php:329 -#, php-format -msgid "%1$s shared a post" -msgstr "" - #: src/Model/Profile.php:355 src/Module/Profile/Profile.php:256 #: src/Module/Profile/Profile.php:258 msgid "Edit profile" @@ -4708,7 +4550,7 @@ msgid "Homepage:" msgstr "" #: src/Model/Profile.php:371 src/Module/Contact.php:571 -#: src/Module/Notifications/Introductions.php:168 +#: src/Module/Notifications/Introductions.php:167 msgid "About:" msgstr "" @@ -5561,7 +5403,7 @@ msgid "Search in logs" msgstr "" #: src/Module/Admin/Logs/View.php:88 -#: src/Module/Notifications/Notifications.php:138 +#: src/Module/Notifications/Notifications.php:126 msgid "Show all" msgstr "" @@ -7481,7 +7323,7 @@ msgstr "" msgid "Awaiting connection acknowledge" msgstr "" -#: src/Module/Contact.php:553 src/Module/Notifications/Introductions.php:171 +#: src/Module/Contact.php:553 src/Module/Notifications/Introductions.php:170 msgid "Hide this contact from others" msgstr "" @@ -7813,7 +7655,7 @@ msgid "" msgstr "" #: src/Module/Contact/Revoke.php:98 -#: src/Module/Notifications/Introductions.php:123 +#: src/Module/Notifications/Introductions.php:122 #: src/Module/OAuth/Acknowledge.php:47 src/Module/Register.php:117 msgid "Yes" msgstr "" @@ -8696,73 +8538,73 @@ msgstr "" msgid "A Decentralized Social Network" msgstr "" -#: src/Module/Notifications/Introductions.php:78 +#: src/Module/Notifications/Introductions.php:77 msgid "Show Ignored Requests" msgstr "" -#: src/Module/Notifications/Introductions.php:78 +#: src/Module/Notifications/Introductions.php:77 msgid "Hide Ignored Requests" msgstr "" -#: src/Module/Notifications/Introductions.php:94 -#: src/Module/Notifications/Introductions.php:157 +#: src/Module/Notifications/Introductions.php:93 +#: src/Module/Notifications/Introductions.php:156 msgid "Notification type:" msgstr "" -#: src/Module/Notifications/Introductions.php:97 +#: src/Module/Notifications/Introductions.php:96 msgid "Suggested by:" msgstr "" -#: src/Module/Notifications/Introductions.php:122 +#: src/Module/Notifications/Introductions.php:121 msgid "Claims to be known to you: " msgstr "" -#: src/Module/Notifications/Introductions.php:123 +#: src/Module/Notifications/Introductions.php:122 #: src/Module/OAuth/Acknowledge.php:48 src/Module/Register.php:118 msgid "No" msgstr "" -#: src/Module/Notifications/Introductions.php:131 +#: src/Module/Notifications/Introductions.php:130 msgid "Shall your connection be bidirectional or not?" msgstr "" -#: src/Module/Notifications/Introductions.php:132 +#: src/Module/Notifications/Introductions.php:131 #, php-format msgid "" "Accepting %s as a friend allows %s to subscribe to your posts, and you will " "also receive updates from them in your news feed." msgstr "" -#: src/Module/Notifications/Introductions.php:133 +#: src/Module/Notifications/Introductions.php:132 #, php-format msgid "" "Accepting %s as a subscriber allows them to subscribe to your posts, but you " "will not receive updates from them in your news feed." msgstr "" -#: src/Module/Notifications/Introductions.php:135 +#: src/Module/Notifications/Introductions.php:134 msgid "Friend" msgstr "" -#: src/Module/Notifications/Introductions.php:136 +#: src/Module/Notifications/Introductions.php:135 msgid "Subscriber" msgstr "" -#: src/Module/Notifications/Introductions.php:195 +#: src/Module/Notifications/Introductions.php:194 msgid "No introductions." msgstr "" -#: src/Module/Notifications/Introductions.php:196 -#: src/Module/Notifications/Notifications.php:133 +#: src/Module/Notifications/Introductions.php:195 +#: src/Module/Notifications/Notifications.php:121 #, php-format msgid "No more %s notifications." msgstr "" -#: src/Module/Notifications/Notification.php:103 +#: src/Module/Notifications/Notification.php:104 msgid "You must be logged in to show this page." msgstr "" -#: src/Module/Notifications/Notifications.php:50 +#: src/Module/Notifications/Notifications.php:52 msgid "Network Notifications" msgstr "" @@ -8770,15 +8612,15 @@ msgstr "" msgid "System Notifications" msgstr "" -#: src/Module/Notifications/Notifications.php:66 +#: src/Module/Notifications/Notifications.php:64 msgid "Personal Notifications" msgstr "" -#: src/Module/Notifications/Notifications.php:74 +#: src/Module/Notifications/Notifications.php:70 msgid "Home Notifications" msgstr "" -#: src/Module/Notifications/Notifications.php:138 +#: src/Module/Notifications/Notifications.php:126 msgid "Show unread" msgstr "" @@ -10239,6 +10081,164 @@ msgid "" "features and resources." msgstr "" +#: src/Navigation/Notifications/Factory/FormattedNotification.php:89 +#, php-format +msgid "%s liked %s's post" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:101 +#, php-format +msgid "%s disliked %s's post" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:113 +#, php-format +msgid "%s is attending %s's event" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:125 +#, php-format +msgid "%s is not attending %s's event" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:137 +#, php-format +msgid "%s may attending %s's event" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:167 +#, php-format +msgid "%s is now friends with %s" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:334 +#: src/Navigation/Notifications/Factory/FormattedNotification.php:372 +#, php-format +msgid "%s commented on %s's post" +msgstr "" + +#: src/Navigation/Notifications/Factory/FormattedNotification.php:371 +#, php-format +msgid "%s created a new post" +msgstr "" + +#: src/Navigation/Notifications/Factory/Introduction.php:134 +msgid "Friend Suggestion" +msgstr "" + +#: src/Navigation/Notifications/Factory/Introduction.php:160 +msgid "Friend/Connect Request" +msgstr "" + +#: src/Navigation/Notifications/Factory/Introduction.php:160 +msgid "New Follower" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:74 +#, php-format +msgid "%1$s wants to follow you" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:76 +#, php-format +msgid "%1$s had started following you" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:141 +#, php-format +msgid "%1$s liked your comment %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:144 +#, php-format +msgid "%1$s liked your post %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:151 +#, php-format +msgid "%1$s disliked your comment %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:154 +#, php-format +msgid "%1$s disliked your post %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:161 +#, php-format +msgid "%1$s shared your comment %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:164 +#, php-format +msgid "%1$s shared your post %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:171 +#, php-format +msgid "%1$s tagged you on %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:175 +#, php-format +msgid "%1$s replied to you on %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:179 +#, php-format +msgid "%1$s commented in your thread %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:183 +#, php-format +msgid "%1$s commented on your comment %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:189 +#, php-format +msgid "%1$s commented in their thread %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:191 +#, php-format +msgid "%1$s commented in their thread" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:193 +#, php-format +msgid "%1$s commented in the thread %2$s from %3$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:195 +#, php-format +msgid "%1$s commented in the thread from %3$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:200 +#, php-format +msgid "%1$s commented on your thread %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:205 +#, php-format +msgid "%1$s shared the post %2$s from %3$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:207 +#, php-format +msgid "%1$s shared a post from %3$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:209 +#, php-format +msgid "%1$s shared the post %2$s" +msgstr "" + +#: src/Navigation/Notifications/Factory/Notification.php:211 +#, php-format +msgid "%1$s shared a post" +msgstr "" + #: src/Object/EMail/ItemCCEMail.php:42 #, php-format msgid ""