]> git.mxchange.org Git - friendica.git/blob - src/Factory/Notification/Notification.php
Merge branch 'stable' into develop
[friendica.git] / src / Factory / Notification / Notification.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Factory\Notification;
23
24 use Exception;
25 use Friendica\App;
26 use Friendica\App\BaseURL;
27 use Friendica\BaseFactory;
28 use Friendica\Collection\Api\Notifications as ApiNotifications;
29 use Friendica\Content\Text\BBCode;
30 use Friendica\Core\L10n;
31 use Friendica\Core\PConfig\IPConfig;
32 use Friendica\Core\Protocol;
33 use Friendica\Core\Session\ISession;
34 use Friendica\Database\Database;
35 use Friendica\Model\Contact;
36 use Friendica\Model\Post;
37 use Friendica\Module\BaseNotifications;
38 use Friendica\Network\HTTPException\InternalServerErrorException;
39 use Friendica\Object\Api\Friendica\Notification as ApiNotification;
40 use Friendica\Protocol\Activity;
41 use Friendica\Repository;
42 use Friendica\Util\DateTimeFormat;
43 use Friendica\Util\Proxy;
44 use Friendica\Util\Temporal;
45 use Friendica\Util\XML;
46 use Psr\Log\LoggerInterface;
47
48 /**
49  * Factory for creating notification objects based on items
50  * Currently, there are the following types of item based notifications:
51  * - network
52  * - system
53  * - home
54  * - personal
55  */
56 class Notification extends BaseFactory
57 {
58         /** @var Database */
59         private $dba;
60         /** @var Repository\Notification */
61         private $notification;
62         /** @var BaseURL */
63         private $baseUrl;
64         /** @var L10n */
65         private $l10n;
66         /** @var string */
67         private $nurl;
68
69         public function __construct(LoggerInterface $logger, Database $dba, Repository\Notification $notification, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session)
70         {
71                 parent::__construct($logger);
72
73                 $this->dba          = $dba;
74                 $this->notification = $notification;
75                 $this->baseUrl      = $baseUrl;
76                 $this->l10n         = $l10n;
77                 $this->nurl         = $app->contact['nurl'] ?? '';
78         }
79
80         /**
81          * Format the item query in an usable array
82          *
83          * @param array $item The item from the db query
84          *
85          * @return array The item, extended with the notification-specific information
86          *
87          * @throws InternalServerErrorException
88          * @throws Exception
89          */
90         private function formatItem(array $item)
91         {
92                 $item['seen'] = ($item['unseen'] > 0 ? false : true);
93
94                 // For feed items we use the user's contact, since the avatar is mostly self choosen.
95                 if (!empty($item['network']) && $item['network'] == Protocol::FEED) {
96                         $item['author-avatar'] = $item['contact-avatar'];
97                 }
98
99                 $item['label'] = (($item['gravity'] == GRAVITY_PARENT) ? 'post' : 'comment');
100                 $item['link']  = $this->baseUrl->get(true) . '/display/' . $item['parent-guid'];
101                 $item['image'] = $item['author-avatar'];
102                 $item['url']   = $item['author-link'];
103                 $item['text']  = (($item['gravity'] == GRAVITY_PARENT)
104                         ? $this->l10n->t("%s created a new post", $item['author-name'])
105                         : $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']));
106                 $item['when']  = DateTimeFormat::local($item['created'], 'r');
107                 $item['ago']   = Temporal::getRelativeDate($item['created']);
108
109                 return $item;
110         }
111
112         /**
113          * @param array $item
114          *
115          * @return \Friendica\Object\Notification\Notification
116          *
117          * @throws InternalServerErrorException
118          */
119         private function createFromItem(array $item)
120         {
121                 $item = $this->formatItem($item);
122
123                 // Transform the different types of notification in an usable array
124                 switch ($item['verb'] ?? '') {
125                         case Activity::LIKE:
126                                 return new \Friendica\Object\Notification\Notification([
127                                         'label' => 'like',
128                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
129                                         'image' => $item['author-avatar'],
130                                         'url'   => $item['author-link'],
131                                         'text'  => $this->l10n->t("%s liked %s's post", $item['author-name'], $item['parent-author-name']),
132                                         'when'  => $item['when'],
133                                         'ago'   => $item['ago'],
134                                         'seen'  => $item['seen']]);
135
136                         case Activity::DISLIKE:
137                                 return new \Friendica\Object\Notification\Notification([
138                                         'label' => 'dislike',
139                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
140                                         'image' => $item['author-avatar'],
141                                         'url'   => $item['author-link'],
142                                         'text'  => $this->l10n->t("%s disliked %s's post", $item['author-name'], $item['parent-author-name']),
143                                         'when'  => $item['when'],
144                                         'ago'   => $item['ago'],
145                                         'seen'  => $item['seen']]);
146
147                         case Activity::ATTEND:
148                                 return new \Friendica\Object\Notification\Notification([
149                                         'label' => 'attend',
150                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
151                                         'image' => $item['author-avatar'],
152                                         'url'   => $item['author-link'],
153                                         'text'  => $this->l10n->t("%s is attending %s's event", $item['author-name'], $item['parent-author-name']),
154                                         'when'  => $item['when'],
155                                         'ago'   => $item['ago'],
156                                         'seen'  => $item['seen']]);
157
158                         case Activity::ATTENDNO:
159                                 return new \Friendica\Object\Notification\Notification([
160                                         'label' => 'attendno',
161                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
162                                         'image' => $item['author-avatar'],
163                                         'url'   => $item['author-link'],
164                                         'text'  => $this->l10n->t("%s is not attending %s's event", $item['author-name'], $item['parent-author-name']),
165                                         'when'  => $item['when'],
166                                         'ago'   => $item['ago'],
167                                         'seen'  => $item['seen']]);
168
169                         case Activity::ATTENDMAYBE:
170                                 return new \Friendica\Object\Notification\Notification([
171                                         'label' => 'attendmaybe',
172                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
173                                         'image' => $item['author-avatar'],
174                                         'url'   => $item['author-link'],
175                                         'text'  => $this->l10n->t("%s may attending %s's event", $item['author-name'], $item['parent-author-name']),
176                                         'when'  => $item['when'],
177                                         'ago'   => $item['ago'],
178                                         'seen'  => $item['seen']]);
179
180                         case Activity::FRIEND:
181                                 if (!isset($item['object'])) {
182                                         return new \Friendica\Object\Notification\Notification([
183                                                 'label' => 'friend',
184                                                 'link'  => $item['link'],
185                                                 'image' => $item['image'],
186                                                 'url'   => $item['url'],
187                                                 'text'  => $item['text'],
188                                                 'when'  => $item['when'],
189                                                 'ago'   => $item['ago'],
190                                                 'seen'  => $item['seen']]);
191                                 }
192
193                                 $xmlHead       = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
194                                 $obj           = XML::parseString($xmlHead . $item['object']);
195                                 $item['fname'] = $obj->title;
196
197                                 return new \Friendica\Object\Notification\Notification([
198                                         'label' => 'friend',
199                                         'link'  => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
200                                         'image' => $item['author-avatar'],
201                                         'url'   => $item['author-link'],
202                                         'text'  => $this->l10n->t("%s is now friends with %s", $item['author-name'], $item['fname']),
203                                         'when'  => $item['when'],
204                                         'ago'   => $item['ago'],
205                                         'seen'  => $item['seen']]);
206
207                         default:
208                                 return new \Friendica\Object\Notification\Notification($item);
209                                 break;
210                 }
211         }
212
213         /**
214          * Get system notifications
215          *
216          * @param bool $seen          False => only include notifications into the query
217          *                            which aren't marked as "seen"
218          * @param int  $start         Start the query at this point
219          * @param int  $limit         Maximum number of query results
220          *
221          * @return \Friendica\Module\Notifications\Notification[]
222          */
223         public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
224         {
225                 $conditions = ['uid' => local_user()];
226
227                 if (!$seen) {
228                         $conditions['seen'] = false;
229                 }
230
231                 $params          = [];
232                 $params['order'] = ['date' => 'DESC'];
233                 $params['limit'] = [$start, $limit];
234
235                 $formattedNotifications = [];
236                 try {
237                         $notifications = $this->notification->select($conditions, $params);
238
239                         foreach ($notifications as $notification) {
240                                 $formattedNotifications[] = new \Friendica\Object\Notification\Notification([
241                                         'label' => 'notification',
242                                         'link'  => $this->baseUrl->get(true) . '/notification/' . $notification->id,
243                                         'image' => Contact::getAvatarUrlForUrl($notification->url, $notification->uid, Proxy::SIZE_MICRO),
244                                         'url'   => $notification->url,
245                                         'text'  => strip_tags(BBCode::convert($notification->msg)),
246                                         'when'  => DateTimeFormat::local($notification->date, 'r'),
247                                         'ago'   => Temporal::getRelativeDate($notification->date),
248                                         'seen'  => $notification->seen]);
249                         }
250                 } catch (Exception $e) {
251                         $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
252                 }
253
254                 return $formattedNotifications;
255         }
256
257         /**
258          * Get network notifications
259          *
260          * @param bool $seen          False => only include notifications into the query
261          *                            which aren't marked as "seen"
262          * @param int  $start         Start the query at this point
263          * @param int  $limit         Maximum number of query results
264          *
265          * @return \Friendica\Object\Notification\Notification[]
266          */
267         public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
268         {
269                 $conditions = ['wall' => false, 'uid' => local_user()];
270
271                 if (!$seen) {
272                         $conditions['unseen'] = true;
273                 }
274
275                 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
276                         'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
277                 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
278
279                 $formattedNotifications = [];
280
281                 try {
282                         $items = Post::selectForUser(local_user(), $fields, $conditions, $params);
283
284                         while ($item = $this->dba->fetch($items)) {
285                                 $formattedNotifications[] = $this->createFromItem($item);
286                         }
287                 } catch (Exception $e) {
288                         $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
289                 }
290
291                 return $formattedNotifications;
292         }
293
294         /**
295          * Get personal notifications
296          *
297          * @param bool $seen          False => only include notifications into the query
298          *                            which aren't marked as "seen"
299          * @param int  $start         Start the query at this point
300          * @param int  $limit         Maximum number of query results
301          *
302          * @return \Friendica\Object\Notification\Notification[]
303          */
304         public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
305         {
306                 $condition = ["NOT `wall` AND `uid` = ? AND `author-id` = ?", local_user(), public_contact()];
307
308                 if (!$seen) {
309                         $condition[0] .= " AND `unseen`";
310                 }
311
312                 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
313                         'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
314                 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
315
316                 $formattedNotifications = [];
317
318                 try {
319                         $items = Post::selectForUser(local_user(), $fields, $condition, $params);
320
321                         while ($item = $this->dba->fetch($items)) {
322                                 $formattedNotifications[] = $this->createFromItem($item);
323                         }
324                 } catch (Exception $e) {
325                         $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
326                 }
327
328                 return $formattedNotifications;
329         }
330
331         /**
332          * Get home notifications
333          *
334          * @param bool $seen          False => only include notifications into the query
335          *                            which aren't marked as "seen"
336          * @param int  $start         Start the query at this point
337          * @param int  $limit         Maximum number of query results
338          *
339          * @return \Friendica\Object\Notification\Notification[]
340          */
341         public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
342         {
343                 $condition = ['wall' => true, 'uid' => local_user()];
344
345                 if (!$seen) {
346                         $condition['unseen'] = true;
347                 }
348
349                 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
350                         'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
351                 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
352
353                 $formattedNotifications = [];
354
355                 try {
356                         $items = Post::selectForUser(local_user(), $fields, $condition, $params);
357
358                         while ($item = $this->dba->fetch($items)) {
359                                 $item = $this->formatItem($item);
360
361                                 // Overwrite specific fields, not default item format
362                                 $item['label'] = 'comment';
363                                 $item['text']  = $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']);
364
365                                 $formattedNotifications[] = $this->createFromItem($item);
366                         }
367                 } catch (Exception $e) {
368                         $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
369                 }
370
371                 return $formattedNotifications;
372         }
373
374         /**
375          * @param int   $uid    The user id of the API call
376          * @param array $params Additional parameters
377          *
378          * @return ApiNotifications
379          *
380          * @throws Exception
381          */
382         public function getApiList(int $uid, array $params = ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50])
383         {
384                 $notifies = $this->notification->select(['uid' => $uid], $params);
385
386                 /** @var ApiNotification[] $notifications */
387                 $notifications = [];
388
389                 foreach ($notifies as $notify) {
390                         $notifications[] = new ApiNotification($notify);
391                 }
392
393                 return new ApiNotifications($notifications);
394         }
395 }