3 namespace Friendica\Factory\Notification;
7 use Friendica\App\BaseURL;
8 use Friendica\BaseFactory;
9 use Friendica\Content\Text\BBCode;
10 use Friendica\Core\L10n;
11 use Friendica\Core\PConfig\IPConfig;
12 use Friendica\Core\Protocol;
13 use Friendica\Core\Session\ISession;
14 use Friendica\Database\Database;
15 use Friendica\Model\Item;
16 use Friendica\Module\BaseNotifications;
17 use Friendica\Network\HTTPException\InternalServerErrorException;
18 use Friendica\Protocol\Activity;
19 use Friendica\Repository\Notification;
20 use Friendica\Util\DateTimeFormat;
21 use Friendica\Util\Proxy;
22 use Friendica\Util\Temporal;
23 use Friendica\Util\XML;
24 use Psr\Log\LoggerInterface;
26 class NotificationFactory extends BaseFactory
30 /** @var Notification */
31 private $notification;
39 public function __construct(LoggerInterface $logger, Database $dba, Notification $notification, BaseURL $baseUrl, L10n $l10n, App $app, IPConfig $pConfig, ISession $session)
41 parent::__construct($logger);
44 $this->notification = $notification;
45 $this->baseUrl = $baseUrl;
47 $this->nurl = $app->contact['nurl'] ?? '';
51 * Format the item query in an usable array
53 * @param array $item The item from the db query
55 * @return array The item, extended with the notification-specific information
57 * @throws InternalServerErrorException
60 private function formatItem(array $item)
62 $item['seen'] = ($item['unseen'] > 0 ? false : true);
64 // For feed items we use the user's contact, since the avatar is mostly self choosen.
65 if (!empty($item['network']) && $item['network'] == Protocol::FEED) {
66 $item['author-avatar'] = $item['contact-avatar'];
69 $item['label'] = (($item['id'] == $item['parent']) ? 'post' : 'comment');
70 $item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid'];
71 $item['image'] = Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO);
72 $item['url'] = $item['author-link'];
73 $item['text'] = (($item['id'] == $item['parent'])
74 ? $this->l10n->t("%s created a new post", $item['author-name'])
75 : $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']));
76 $item['when'] = DateTimeFormat::local($item['created'], 'r');
77 $item['ago'] = Temporal::getRelativeDate($item['created']);
85 * @return \Friendica\Object\Notification\Notification
87 * @throws InternalServerErrorException
89 private function createFromItem(array $item)
91 $item = $this->formatItem($item);
93 // Transform the different types of notification in an usable array
94 switch ($item['verb'] ?? '') {
96 return new \Friendica\Object\Notification\Notification(
98 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
99 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
100 $item['author-link'],
101 $this->l10n->t("%s liked %s's post", $item['author-name'], $item['parent-author-name']),
104 $item['seen'] ?? false);
106 case Activity::DISLIKE:
107 return new \Friendica\Object\Notification\Notification(
109 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
110 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
111 $item['author-link'],
112 $this->l10n->t("%s disliked %s's post", $item['author-name'], $item['parent-author-name']),
115 $item['seen'] ?? false);
117 case Activity::ATTEND:
118 return new \Friendica\Object\Notification\Notification(
120 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
121 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
122 $item['author-link'],
123 $this->l10n->t("%s is attending %s's event", $item['author-name'], $item['parent-author-name']),
126 $item['seen'] ?? false);
128 case Activity::ATTENDNO:
129 return new \Friendica\Object\Notification\Notification(
131 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
132 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
133 $item['author-link'],
134 $this->l10n->t("%s is not attending %s's event", $item['author-name'], $item['parent-author-name']),
137 $item['seen'] ?? false);
139 case Activity::ATTENDMAYBE:
140 return new \Friendica\Object\Notification\Notification(
142 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
143 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
144 $item['author-link'],
145 $this->l10n->t("%s may attending %s's event", $item['author-name'], $item['parent-author-name']),
148 $item['seen'] ?? false);
150 case Activity::FRIEND:
151 if (!isset($item['object'])) {
152 return new \Friendica\Object\Notification\Notification(
160 $item['seen'] ?? false);
163 $xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
164 $obj = XML::parseString($xmlHead . $item['object']);
165 $item['fname'] = $obj->title;
167 return new \Friendica\Object\Notification\Notification(
169 $this->baseUrl->get(true) . '/display/' . $item['parent-guid'],
170 Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO),
171 $item['author-link'],
172 $this->l10n->t("%s is now friends with %s", $item['author-name'], $item['fname']),
175 $item['seen'] ?? false);
178 return new \Friendica\Object\Notification\Notification(
186 $item['seen'] ?? false);
192 * Get system notifications
194 * @param bool $seen False => only include notifications into the query
195 * which aren't marked as "seen"
196 * @param int $start Start the query at this point
197 * @param int $limit Maximum number of query results
199 * @return \Friendica\Module\Notifications\Notification[]
201 public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
203 $conditions = ['uid' => local_user()];
206 $conditions['seen'] = false;
210 $params['order'] = ['date' => 'DESC'];
211 $params['limit'] = [$start, $limit];
213 $formattedNotifications = [];
215 $notifications = $this->notification->select($conditions, $params);
217 foreach ($notifications as $notification) {
218 $formattedNotifications[] = new \Friendica\Object\Notification\Notification(
220 $this->baseUrl->get(true) . '/notification/view/' . $notification->id,
221 Proxy::proxifyUrl($notification->photo, false, Proxy::SIZE_MICRO),
223 strip_tags(BBCode::convert($notification->msg)),
224 DateTimeFormat::local($notification->date, 'r'),
225 Temporal::getRelativeDate($notification->date),
226 $notification->seen);
228 } catch (Exception $e) {
229 $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
232 return $formattedNotifications;
236 * Get network notifications
238 * @param bool $seen False => only include notifications into the query
239 * which aren't marked as "seen"
240 * @param int $start Start the query at this point
241 * @param int $limit Maximum number of query results
243 * @return \Friendica\Object\Notification\Notification[]
245 public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
247 $conditions = ['wall' => false, 'uid' => local_user()];
250 $conditions['unseen'] = true;
253 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
254 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
255 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
257 $formattedNotifications = [];
260 $items = Item::selectForUser(local_user(), $fields, $conditions, $params);
262 while ($item = $this->dba->fetch($items)) {
263 $formattedNotifications[] = $this->createFromItem($item);
265 } catch (Exception $e) {
266 $this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
269 return $formattedNotifications;
273 * Get personal notifications
275 * @param bool $seen False => only include notifications into the query
276 * which aren't marked as "seen"
277 * @param int $start Start the query at this point
278 * @param int $limit Maximum number of query results
280 * @return \Friendica\Object\Notification\Notification[]
282 public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
284 $myUrl = str_replace('http://', '', $this->nurl);
285 $diaspUrl = str_replace('/profile/', '/u/', $myUrl);
287 $condition = ["NOT `wall` AND `uid` = ? AND (`item`.`author-id` = ? OR `item`.`tag` REGEXP ? OR `item`.`tag` REGEXP ?)",
288 local_user(), public_contact(), $myUrl . '\\]', $diaspUrl . '\\]'];
291 $condition[0] .= " AND `unseen`";
294 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
295 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
296 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
298 $formattedNotifications = [];
301 $items = Item::selectForUser(local_user(), $fields, $condition, $params);
303 while ($item = $this->dba->fetch($items)) {
304 $formattedNotifications[] = $this->createFromItem($item);
306 } catch (Exception $e) {
307 $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
310 return $formattedNotifications;
314 * Get home notifications
316 * @param bool $seen False => only include notifications into the query
317 * which aren't marked as "seen"
318 * @param int $start Start the query at this point
319 * @param int $limit Maximum number of query results
321 * @return \Friendica\Object\Notification\Notification[]
323 public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT)
325 $condition = ['wall' => true, 'uid' => local_user()];
328 $condition['unseen'] = true;
331 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
332 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid'];
333 $params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
335 $formattedNotifications = [];
338 $items = Item::selectForUser(local_user(), $fields, $condition, $params);
340 while ($item = $this->dba->fetch($items)) {
341 $item = $this->formatItem($item);
343 // Overwrite specific fields, not default item format
344 $item['label'] = 'comment';
345 $item['text'] = $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']);
347 $formattedNotifications[] = $this->createFromItem($item);
349 } catch (Exception $e) {
350 $this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
353 return $formattedNotifications;