3 namespace Friendica\Factory\Notification;
7 use Friendica\App\BaseURL;
8 use Friendica\BaseFactory;
9 use Friendica\Collection\Api\Notifications as ApiNotifications;
10 use Friendica\Content\Text\BBCode;
11 use Friendica\Core\L10n;
12 use Friendica\Core\PConfig\IPConfig;
13 use Friendica\Core\Protocol;
14 use Friendica\Core\Session\ISession;
15 use Friendica\Database\Database;
16 use Friendica\Model\Item;
17 use Friendica\Module\BaseNotifications;
18 use Friendica\Network\HTTPException\InternalServerErrorException;
19 use Friendica\Object\Api\Friendica\Notification as ApiNotification;
20 use Friendica\Protocol\Activity;
21 use Friendica\Repository;
22 use Friendica\Util\DateTimeFormat;
23 use Friendica\Util\Proxy;
24 use Friendica\Util\Temporal;
25 use Friendica\Util\XML;
26 use Psr\Log\LoggerInterface;
29 * Factory for creating notification objects based on items
30 * Currently, there are the following types of item based notifications:
36 class Notification extends BaseFactory
40 /** @var Repository\Notify */
41 private $notification;
49 public function __construct(LoggerInterface $logger, Database $dba, Repository\Notify $notification, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session)
51 parent::__construct($logger);
54 $this->notification = $notification;
55 $this->baseUrl = $baseUrl;
57 $this->nurl = $app->contact['nurl'] ?? '';
61 * Format the item query in an usable array
63 * @param array $item The item from the db query
65 * @return array The item, extended with the notification-specific information
67 * @throws InternalServerErrorException
70 private function formatItem(array $item)
72 $item['seen'] = ($item['unseen'] > 0 ? false : true);
74 // For feed items we use the user's contact, since the avatar is mostly self choosen.
75 if (!empty($item['network']) && $item['network'] == Protocol::FEED) {
76 $item['author-avatar'] = $item['contact-avatar'];
79 $item['label'] = (($item['id'] == $item['parent']) ? 'post' : 'comment');
80 $item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid'];
81 $item['image'] = Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO);
82 $item['url'] = $item['author-link'];
83 $item['text'] = (($item['id'] == $item['parent'])
84 ? $this->l10n->t("%s created a new post", $item['author-name'])
85 : $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']));
86 $item['when'] = DateTimeFormat::local($item['created'], 'r');
87 $item['ago'] = Temporal::getRelativeDate($item['created']);
95 * @return \Friendica\Object\Notification\Notification
97 * @throws InternalServerErrorException
99 private function createFromItem(array $item)
101 $item = $this->formatItem($item);
103 // Transform the different types of notification in an usable array
104 switch ($item['verb'] ?? '') {
106 return new \Friendica\Object\Notification\Notification([
108 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
109 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
110 'url' => $item['author-link'],
111 'text' => $this->l10n->t("%s liked %s's post", $item['author-name'], $item['parent-author-name']),
112 'when' => $item['when'],
113 'ago' => $item['ago'],
114 'seen' => $item['seen']]);
116 case Activity::DISLIKE:
117 return new \Friendica\Object\Notification\Notification([
118 'label' => 'dislike',
119 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
120 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
121 'url' => $item['author-link'],
122 'text' => $this->l10n->t("%s disliked %s's post", $item['author-name'], $item['parent-author-name']),
123 'when' => $item['when'],
124 'ago' => $item['ago'],
125 'seen' => $item['seen']]);
127 case Activity::ATTEND:
128 return new \Friendica\Object\Notification\Notification([
130 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
131 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
132 'url' => $item['author-link'],
133 'text' => $this->l10n->t("%s is attending %s's event", $item['author-name'], $item['parent-author-name']),
134 'when' => $item['when'],
135 'ago' => $item['ago'],
136 'seen' => $item['seen']]);
138 case Activity::ATTENDNO:
139 return new \Friendica\Object\Notification\Notification([
140 'label' => 'attendno',
141 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
142 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
143 'url' => $item['author-link'],
144 'text' => $this->l10n->t("%s is not attending %s's event", $item['author-name'], $item['parent-author-name']),
145 'when' => $item['when'],
146 'ago' => $item['ago'],
147 'seen' => $item['seen']]);
149 case Activity::ATTENDMAYBE:
150 return new \Friendica\Object\Notification\Notification([
151 'label' => 'attendmaybe',
152 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
153 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
154 'url' => $item['author-link'],
155 'text' => $this->l10n->t("%s may attending %s's event", $item['author-name'], $item['parent-author-name']),
156 'when' => $item['when'],
157 'ago' => $item['ago'],
158 'seen' => $item['seen']]);
160 case Activity::FRIEND:
161 if (!isset($item['object'])) {
162 return new \Friendica\Object\Notification\Notification([
164 'link' => $item['link'],
165 'image' => $item['image'],
166 'url' => $item['url'],
167 'text' => $item['text'],
168 'when' => $item['when'],
169 'ago' => $item['ago'],
170 'seen' => $item['seen']]);
173 $xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
174 $obj = XML::parseString($xmlHead . $item['object']);
175 $item['fname'] = $obj->title;
177 return new \Friendica\Object\Notification\Notification([
179 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
180 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
181 'url' => $item['author-link'],
182 'text' => $this->l10n->t("%s is now friends with %s", $item['author-name'], $item['fname']),
183 'when' => $item['when'],
184 'ago' => $item['ago'],
185 'seen' => $item['seen']]);
188 return new \Friendica\Object\Notification\Notification($item);
194 * Get system notifications
196 * @param bool $seen False => only include notifications into the query
197 * which aren't marked as "seen"
198 * @param int $start Start the query at this point
199 * @param int $limit Maximum number of query results
201 * @return \Friendica\Module\Notifications\Notification[]
203 public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
205 $conditions = ['uid' => local_user()];
208 $conditions['seen'] = false;
212 $params['order'] = ['date' => 'DESC'];
213 $params['limit'] = [$start, $limit];
215 $formattedNotifications = [];
217 $notifications = $this->notification->select($conditions, $params);
219 foreach ($notifications as $notification) {
220 $formattedNotifications[] = new \Friendica\Object\Notification\Notification([
221 'label' => 'notification',
222 'link' => $this->baseUrl->get(true) . '/notification/' . $notification->id,
223 'image' => Proxy::proxifyUrl($notification->photo, false, Proxy::SIZE_MICRO),
224 'url' => $notification->url,
225 'text' => strip_tags(BBCode::convert($notification->msg)),
226 'when' => DateTimeFormat::local($notification->date, 'r'),
227 'ago' => Temporal::getRelativeDate($notification->date),
228 'seen' => $notification->seen]);
230 } catch (Exception $e) {
231 $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
234 return $formattedNotifications;
238 * Get network notifications
240 * @param bool $seen False => only include notifications into the query
241 * which aren't marked as "seen"
242 * @param int $start Start the query at this point
243 * @param int $limit Maximum number of query results
245 * @return \Friendica\Object\Notification\Notification[]
247 public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
249 $conditions = ['wall' => false, 'uid' => local_user()];
252 $conditions['unseen'] = true;
255 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
256 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
257 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
259 $formattedNotifications = [];
262 $items = Item::selectForUser(local_user(), $fields, $conditions, $params);
264 while ($item = $this->dba->fetch($items)) {
265 $formattedNotifications[] = $this->createFromItem($item);
267 } catch (Exception $e) {
268 $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
271 return $formattedNotifications;
275 * Get personal notifications
277 * @param bool $seen False => only include notifications into the query
278 * which aren't marked as "seen"
279 * @param int $start Start the query at this point
280 * @param int $limit Maximum number of query results
282 * @return \Friendica\Object\Notification\Notification[]
284 public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
286 $myUrl = str_replace('http://', '', $this->nurl);
287 $diaspUrl = str_replace('/profile/', '/u/', $myUrl);
289 $condition = ["NOT `wall` AND `uid` = ? AND (`item`.`author-id` = ? OR `item`.`tag` REGEXP ? OR `item`.`tag` REGEXP ?)",
290 local_user(), public_contact(), $myUrl . '\\]', $diaspUrl . '\\]'];
293 $condition[0] .= " AND `unseen`";
296 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
297 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
298 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
300 $formattedNotifications = [];
303 $items = Item::selectForUser(local_user(), $fields, $condition, $params);
305 while ($item = $this->dba->fetch($items)) {
306 $formattedNotifications[] = $this->createFromItem($item);
308 } catch (Exception $e) {
309 $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
312 return $formattedNotifications;
316 * Get home notifications
318 * @param bool $seen False => only include notifications into the query
319 * which aren't marked as "seen"
320 * @param int $start Start the query at this point
321 * @param int $limit Maximum number of query results
323 * @return \Friendica\Object\Notification\Notification[]
325 public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
327 $condition = ['wall' => true, 'uid' => local_user()];
330 $condition['unseen'] = true;
333 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
334 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
335 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
337 $formattedNotifications = [];
340 $items = Item::selectForUser(local_user(), $fields, $condition, $params);
342 while ($item = $this->dba->fetch($items)) {
343 $item = $this->formatItem($item);
345 // Overwrite specific fields, not default item format
346 $item['label'] = 'comment';
347 $item['text'] = $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']);
349 $formattedNotifications[] = $this->createFromItem($item);
351 } catch (Exception $e) {
352 $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
355 return $formattedNotifications;
359 * @param int $uid The user id of the API call
360 * @param array $params Additional parameters
362 * @return ApiNotifications
366 public function getApiList(int $uid, array $params = ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50])
368 $notifies = $this->notification->select(['uid' => $uid], $params);
370 /** @var ApiNotification[] $notifications */
373 foreach ($notifies as $notify) {
374 $notifications[] = new ApiNotification($notify);
377 return new ApiNotifications($notifications);