]> git.mxchange.org Git - friendica.git/blob - src/Navigation/Notifications/Depository/Notify.php
Merge pull request #10893 from nupplaphil/feat/depository_introduction
[friendica.git] / src / Navigation / Notifications / Depository / Notify.php
1 <?php
2
3 namespace Friendica\Navigation\Notifications\Depository;
4
5 use Friendica\App\BaseURL;
6 use Friendica\BaseDepository;
7 use Friendica\Content\Text\Plaintext;
8 use Friendica\Core\Config\IConfig;
9 use Friendica\Core\Hook;
10 use Friendica\Core\L10n;
11 use Friendica\Core\System;
12 use Friendica\Database\Database;
13 use Friendica\Database\DBA;
14 use Friendica\Model;
15 use Friendica\Navigation\Notifications\Collection;
16 use Friendica\Navigation\Notifications\Entity;
17 use Friendica\Navigation\Notifications\Exception;
18 use Friendica\Navigation\Notifications\Factory;
19 use Friendica\Network\HTTPException;
20 use Friendica\Protocol\Activity;
21 use Friendica\Util\DateTimeFormat;
22 use Friendica\Util\Emailer;
23 use Psr\Log\LoggerInterface;
24
25 class Notify extends BaseDepository
26 {
27         /** @var Factory\Notify  */
28         protected $factory;
29
30         /** @var L10n  */
31         protected $l10n;
32
33         /** @var BaseURL  */
34         protected $baseUrl;
35
36         /** @var IConfig */
37         protected $config;
38
39         /** @var Emailer */
40         protected $emailer;
41
42         /** @var Factory\Notification */
43         protected $notification;
44
45         protected static $table_name = 'notify';
46
47         public function __construct(Database $database, LoggerInterface $logger, L10n $l10n, BaseURL $baseUrl, IConfig $config, Emailer $emailer, Factory\Notification $notification, Factory\Notify $factory = null)
48         {
49                 $this->l10n         = $l10n;
50                 $this->baseUrl      = $baseUrl;
51                 $this->config       = $config;
52                 $this->emailer      = $emailer;
53                 $this->notification = $notification;
54
55                 parent::__construct($database, $logger, $factory ?? new Factory\Notify($logger));
56         }
57
58         /**
59          * @param array $condition
60          * @param array $params
61          * @return Entity\Notify
62          * @throws HTTPException\NotFoundException
63          */
64         private function selectOne(array $condition, array $params = []): Entity\Notify
65         {
66                 return parent::_selectOne($condition, $params);
67         }
68
69         private function select(array $condition, array $params = []): Collection\Notifies
70         {
71                 return new Collection\Notifies(parent::_select($condition, $params)->getArrayCopy());
72         }
73
74         public function countForUser($uid, array $condition, array $params = []): int
75         {
76                 $condition = DBA::mergeConditions($condition, ['uid' => $uid]);
77
78                 return $this->count($condition, $params);
79         }
80
81         public function existsForUser($uid, array $condition): bool
82         {
83                 $condition = DBA::mergeConditions($condition, ['uid' => $uid]);
84
85                 return $this->exists($condition);
86         }
87
88         /**
89          * @param int $id
90          * @return Entity\Notify
91          * @throws HTTPException\NotFoundException
92          */
93         public function selectOneById(int $id): Entity\Notify
94         {
95                 return $this->selectOne(['id' => $id]);
96         }
97
98         public function selectForUser(int $uid, array $condition, array $params): Collection\Notifies
99         {
100                 $condition = DBA::mergeConditions($condition, ['uid' => $uid]);
101
102                 return $this->select($condition, $params);
103         }
104
105         /**
106          * Returns notifications for the user, unread first, ordered in descending chronological order.
107          *
108          * @param int $uid
109          * @param int $limit
110          * @return Collection\Notifies
111          */
112         public function selectAllForUser(int $uid, int $limit): Collection\Notifies
113         {
114                 return $this->selectForUser($uid, [], ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => $limit]);
115         }
116
117         public function setAllSeenForUser(int $uid, array $condition = []): bool
118         {
119                 $condition = DBA::mergeConditions($condition, ['uid' => $uid]);
120
121                 return $this->db->update(self::$table_name, ['seen' => true], $condition);
122         }
123
124         /**
125          * @param Entity\Notify $Notify
126          * @return Entity\Notify
127          * @throws HTTPException\NotFoundException
128          * @throws HTTPException\InternalServerErrorException
129          * @throws Exception\NotificationCreationInterceptedException
130          */
131         public function save(Entity\Notify $Notify): Entity\Notify
132         {
133                 $fields = [
134                         'type'          => $Notify->type,
135                         'name'          => $Notify->name,
136                         'url'           => $Notify->url,
137                         'photo'         => $Notify->photo,
138                         'msg'           => $Notify->msg,
139                         'uid'           => $Notify->uid,
140                         'link'          => $Notify->link,
141                         'iid'           => $Notify->itemId,
142                         'parent'        => $Notify->parent,
143                         'seen'          => $Notify->seen,
144                         'verb'          => $Notify->verb,
145                         'otype'         => $Notify->otype,
146                         'name_cache'    => $Notify->name_cache,
147                         'msg_cache'     => $Notify->msg_cache,
148                         'uri-id'        => $Notify->uriId,
149                         'parent-uri-id' => $Notify->parentUriId,
150                 ];
151
152                 if ($Notify->id) {
153                         $this->db->update(self::$table_name, $fields, ['id' => $Notify->id]);
154                 } else {
155                         $fields['date'] = DateTimeFormat::utcNow();
156                         Hook::callAll('enotify_store', $fields);
157
158                         $this->db->insert(self::$table_name, $fields);
159
160                         $Notify = $this->selectOneById($this->db->lastInsertId());
161                 }
162
163                 return $Notify;
164         }
165
166         public function setAllSeenForRelatedNotify(Entity\Notify $Notify): bool
167         {
168                 $condition = [
169                         '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?',
170                         $Notify->link,
171                         $Notify->parent,
172                         $Notify->otype,
173                         $Notify->uid
174                 ];
175                 return $this->db->update(self::$table_name, ['seen' => true], $condition);
176         }
177
178         /**
179          * Creates a notification entry and possibly sends a mail
180          *
181          * @param array $params Array with the elements:
182          *                      type, event, otype, activity, verb, uid, cid, item, link,
183          *                      source_name, source_mail, source_nick, source_link, source_photo,
184          *                      show_in_notification_page
185          *
186          * @return bool
187          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
188          */
189         function createFromArray($params)
190         {
191                 /** @var string the common prefix of a notification subject */
192                 $subjectPrefix = $this->l10n->t('[Friendica:Notify]');
193
194                 // Temporary logging for finding the origin
195                 if (!isset($params['uid'])) {
196                         $this->logger->notice('Missing parameters "uid".', ['params' => $params, 'callstack' => System::callstack()]);
197                 }
198
199                 // Ensure that the important fields are set at any time
200                 $fields = ['nickname', 'page-flags', 'notify-flags', 'language', 'username', 'email'];
201                 $user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]);
202
203                 if (!DBA::isResult($user)) {
204                         $this->logger->error('Unknown user', ['uid' =>  $params['uid']]);
205                         return false;
206                 }
207
208                 // There is no need to create notifications for forum accounts
209                 if (in_array($user['page-flags'], [Model\User::PAGE_FLAGS_COMMUNITY, Model\User::PAGE_FLAGS_PRVGROUP])) {
210                         return false;
211                 }
212
213                 $params['notify_flags'] = $user['notify-flags'];
214                 $params['language']     = $user['language'];
215                 $params['to_name']      = $user['username'];
216                 $params['to_email']     = $user['email'];
217
218                 // from here on everything is in the recipients language
219                 $l10n = $this->l10n->withLang($params['language']);
220
221                 if (!empty($params['cid'])) {
222                         $contact = Model\Contact::getById($params['cid'], ['url', 'name', 'photo']);
223                         if (DBA::isResult($contact)) {
224                                 $params['source_link'] = $contact['url'];
225                                 $params['source_name'] = $contact['name'];
226                                 $params['source_photo'] = $contact['photo'];
227                         }
228                 }
229
230                 $siteurl = $this->baseUrl->get(true);
231                 $sitename = $this->config->get('config', 'sitename');
232
233                 // with $params['show_in_notification_page'] == false, the notification isn't inserted into
234                 // the database, and an email is sent if applicable.
235                 // default, if not specified: true
236                 $show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true;
237
238                 $title = $params['item']['title'] ?? '';
239                 $body = $params['item']['body'] ?? '';
240
241                 $parent_id = $params['item']['parent'] ?? 0;
242                 $parent_uri_id = $params['item']['parent-uri-id'] ?? 0;
243
244                 $epreamble = '';
245                 $preamble  = '';
246                 $subject   = '';
247                 $sitelink  = '';
248                 $tsitelink = '';
249                 $hsitelink = '';
250                 $itemlink  = '';
251
252                 switch ($params['type']) {
253                         case Model\Notification\Type::MAIL:
254                                 $itemlink = $params['link'];
255
256                                 $subject = $l10n->t('%s New mail received at %s', $subjectPrefix, $sitename);
257
258                                 $preamble = $l10n->t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename);
259                                 $epreamble = $l10n->t('%1$s sent you %2$s.', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', '[url=' . $itemlink . ']' . $l10n->t('a private message').'[/url]');
260
261                                 $sitelink = $l10n->t('Please visit %s to view and/or reply to your private messages.');
262                                 $tsitelink = sprintf($sitelink, $itemlink);
263                                 $hsitelink = sprintf($sitelink, '<a href="' . $itemlink . '">' . $sitename . '</a>');
264
265                                 // Mail notifications aren't using the "notify" table entry
266                                 $show_in_notification_page = false;
267                                 break;
268
269                         case Model\Notification\Type::COMMENT:
270                                 if (Model\Post\ThreadUser::getIgnored($parent_uri_id, $params['uid'])) {
271                                         $this->logger->info('Thread is ignored', ['parent' => $parent_id, 'parent-uri-id' => $parent_uri_id]);
272                                         return false;
273                                 }
274
275                                 $item = Model\Post::selectFirstForUser($params['uid'], Model\Item::ITEM_FIELDLIST, ['id' => $parent_id, 'deleted' => false]);
276                                 if (empty($item)) {
277                                         return false;
278                                 }
279
280                                 $item_post_type = Model\Item::postType($item, $l10n);
281
282                                 $content = Plaintext::getPost($item, 70);
283                                 if (!empty($content['text'])) {
284                                         $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
285                                 } else {
286                                         $title = '';
287                                 }
288
289                                 // First go for the general message
290
291                                 // "George Bull's post"
292                                 $message = $l10n->t('%1$s commented on %2$s\'s %3$s %4$s');
293                                 $dest_str = sprintf($message, $params['source_name'], $item['author-name'], $item_post_type, $title);
294
295                                 // "your post"
296                                 if ($item['wall']) {
297                                         $message = $l10n->t('%1$s commented on your %2$s %3$s');
298                                         $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title);
299                                 // "their post"
300                                 } elseif ($item['author-link'] == $params['source_link']) {
301                                         $message = $l10n->t('%1$s commented on their %2$s %3$s');
302                                         $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title);
303                                 }
304
305                                 $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $parent_id, $params['source_name']);
306
307                                 $preamble = $l10n->t('%s commented on an item/conversation you have been following.', $params['source_name']);
308
309                                 $epreamble = $dest_str;
310
311                                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
312                                 $tsitelink = sprintf($sitelink, $siteurl);
313                                 $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
314                                 $itemlink =  $params['link'];
315                                 break;
316
317                         case Model\Notification\Type::WALL:
318                                 $subject = $l10n->t('%s %s posted to your profile wall', $subjectPrefix, $params['source_name']);
319
320                                 $preamble = $l10n->t('%1$s posted to your profile wall at %2$s', $params['source_name'], $sitename);
321                                 $epreamble = $l10n->t('%1$s posted to [url=%2$s]your wall[/url]',
322                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
323                                         $params['link']
324                                 );
325
326                                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
327                                 $tsitelink = sprintf($sitelink, $siteurl);
328                                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
329                                 $itemlink =  $params['link'];
330                                 break;
331
332                         case Model\Notification\Type::POKE:
333                                 $subject = $l10n->t('%1$s %2$s poked you', $subjectPrefix, $params['source_name']);
334
335                                 $preamble = $l10n->t('%1$s poked you at %2$s', $params['source_name'], $sitename);
336                                 $epreamble = $l10n->t('%1$s [url=%2$s]poked you[/url].',
337                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
338                                         $params['link']
339                                 );
340
341                                 $subject = str_replace('poked', $l10n->t($params['activity']), $subject);
342                                 $preamble = str_replace('poked', $l10n->t($params['activity']), $preamble);
343                                 $epreamble = str_replace('poked', $l10n->t($params['activity']), $epreamble);
344
345                                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
346                                 $tsitelink = sprintf($sitelink, $siteurl);
347                                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
348                                 $itemlink =  $params['link'];
349                                 break;
350
351                         case Model\Notification\Type::INTRO:
352                                 $itemlink = $params['link'];
353                                 $subject = $l10n->t('%s Introduction received', $subjectPrefix);
354
355                                 $preamble = $l10n->t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename);
356                                 $epreamble = $l10n->t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.',
357                                         $itemlink,
358                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
359                                 );
360
361                                 $body = $l10n->t('You may visit their profile at %s', $params['source_link']);
362
363                                 $sitelink = $l10n->t('Please visit %s to approve or reject the introduction.');
364                                 $tsitelink = sprintf($sitelink, $siteurl);
365                                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
366
367                                 switch ($params['verb']) {
368                                         case Activity::FRIEND:
369                                                 // someone started to share with user (mostly OStatus)
370                                                 $subject = $l10n->t('%s A new person is sharing with you', $subjectPrefix);
371
372                                                 $preamble = $l10n->t('%1$s is sharing with you at %2$s', $params['source_name'], $sitename);
373                                                 $epreamble = $l10n->t('%1$s is sharing with you at %2$s',
374                                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
375                                                         $sitename
376                                                 );
377                                                 break;
378                                         case Activity::FOLLOW:
379                                                 // someone started to follow the user (mostly OStatus)
380                                                 $subject = $l10n->t('%s You have a new follower', $subjectPrefix);
381
382                                                 $preamble = $l10n->t('You have a new follower at %2$s : %1$s', $params['source_name'], $sitename);
383                                                 $epreamble = $l10n->t('You have a new follower at %2$s : %1$s',
384                                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
385                                                         $sitename
386                                                 );
387                                                 break;
388                                         default:
389                                                 // ACTIVITY_REQ_FRIEND is default activity for notifications
390                                                 break;
391                                 }
392                                 break;
393
394                         case Model\Notification\Type::SUGGEST:
395                                 $itemlink =  $params['link'];
396                                 $subject = $l10n->t('%s Friend suggestion received', $subjectPrefix);
397
398                                 $preamble = $l10n->t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename);
399                                 $epreamble = $l10n->t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.',
400                                         $itemlink,
401                                         '[url='.$params['item']['url'].']'.$params['item']['name'].'[/url]',
402                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
403                                 );
404
405                                 $body = $l10n->t('Name:').' '.$params['item']['name']."\n";
406                                 $body .= $l10n->t('Photo:').' '.$params['item']['photo']."\n";
407                                 $body .= $l10n->t('You may visit their profile at %s', $params['item']['url']);
408
409                                 $sitelink = $l10n->t('Please visit %s to approve or reject the suggestion.');
410                                 $tsitelink = sprintf($sitelink, $siteurl);
411                                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
412                                 break;
413
414                         case Model\Notification\Type::CONFIRM:
415                                 if ($params['verb'] == Activity::FRIEND) { // mutual connection
416                                         $itemlink =  $params['link'];
417                                         $subject = $l10n->t('%s Connection accepted', $subjectPrefix);
418
419                                         $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
420                                         $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].',
421                                                 $itemlink,
422                                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
423                                         );
424
425                                         $body =  $l10n->t('You are now mutual friends and may exchange status updates, photos, and email without restriction.');
426
427                                         $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.');
428                                         $tsitelink = sprintf($sitelink, $siteurl);
429                                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
430                                 } else { // ACTIVITY_FOLLOW
431                                         $itemlink =  $params['link'];
432                                         $subject = $l10n->t('%s Connection accepted', $subjectPrefix);
433
434                                         $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
435                                         $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].',
436                                                 $itemlink,
437                                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
438                                         );
439
440                                         $body =  $l10n->t('\'%1$s\' has chosen to accept you a fan, which restricts some forms of communication - such as private messaging and some profile interactions. If this is a celebrity or community page, these settings were applied automatically.', $params['source_name']);
441                                         $body .= "\n\n";
442                                         $body .= $l10n->t('\'%1$s\' may choose to extend this into a two-way or more permissive relationship in the future.', $params['source_name']);
443
444                                         $sitelink = $l10n->t('Please visit %s  if you wish to make any changes to this relationship.');
445                                         $tsitelink = sprintf($sitelink, $siteurl);
446                                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
447                                 }
448                                 break;
449
450                         case Model\Notification\Type::SYSTEM:
451                                 switch($params['event']) {
452                                         case "SYSTEM_REGISTER_REQUEST":
453                                                 $itemlink =  $params['link'];
454                                                 $subject = $l10n->t('[Friendica System Notify]') . ' ' . $l10n->t('registration request');
455
456                                                 $preamble = $l10n->t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename);
457                                                 $epreamble = $l10n->t('You\'ve received a [url=%1$s]registration request[/url] from %2$s.',
458                                                         $itemlink,
459                                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
460                                                 );
461
462                                                 $body = $l10n->t("Full Name:    %s\nSite Location:      %s\nLogin Name: %s (%s)",
463                                                         $params['source_name'],
464                                                         $siteurl, $params['source_mail'],
465                                                         $params['source_nick']
466                                                 );
467
468                                                 $sitelink = $l10n->t('Please visit %s to approve or reject the request.');
469                                                 $tsitelink = sprintf($sitelink, $params['link']);
470                                                 $hsitelink = sprintf($sitelink, '<a href="'.$params['link'].'">'.$sitename.'</a><br><br>');
471                                                 break;
472                                         case "SYSTEM_DB_UPDATE_FAIL":
473                                                 break;
474                                 }
475                                 break;
476
477                         default:
478                                 $this->logger->notice('Unhandled type', ['type' => $params['type']]);
479                                 return false;
480                 }
481
482                 return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page);
483         }
484
485         private function storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page)
486         {
487                 $item_id = $params['item']['id'] ?? 0;
488                 $uri_id = $params['item']['uri-id'] ?? null;
489                 $parent_id = $params['item']['parent'] ?? 0;
490                 $parent_uri_id = $params['item']['parent-uri-id'] ?? null;
491
492                 // Ensure that the important fields are set at any time
493                 $fields = ['nickname'];
494                 $user = Model\User::getById($params['uid'], $fields);
495
496                 $sitename = $this->config->get('config', 'sitename');
497
498                 $nickname = $user['nickname'];
499
500                 $hostname = $this->baseUrl->getHostname();
501                 if (strpos($hostname, ':')) {
502                         $hostname = substr($hostname, 0, strpos($hostname, ':'));
503                 }
504
505                 // Creates a new email builder for the notification email
506                 $emailBuilder = $this->emailer->newNotifyMail();
507
508                 $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>');
509
510                 $subject .= " (".$nickname."@".$hostname.")";
511
512                 $h = [
513                         'params'    => $params,
514                         'subject'   => $subject,
515                         'preamble'  => $preamble,
516                         'epreamble' => $epreamble,
517                         'body'      => $body,
518                         'sitelink'  => $sitelink,
519                         'tsitelink' => $tsitelink,
520                         'hsitelink' => $hsitelink,
521                         'itemlink'  => $itemlink
522                 ];
523
524                 Hook::callAll('enotify', $h);
525
526                 $subject   = $h['subject'];
527
528                 $preamble  = $h['preamble'];
529                 $epreamble = $h['epreamble'];
530
531                 $body      = $h['body'];
532
533                 $tsitelink = $h['tsitelink'];
534                 $hsitelink = $h['hsitelink'];
535                 $itemlink  = $h['itemlink'];
536
537                 $notify_id = 0;
538
539                 if ($show_in_notification_page) {
540                         $Notify = $this->factory->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id);
541                         try {
542                                 $Notify = $this->save($Notify);
543                         } catch (Exception\NotificationCreationInterceptedException $e) {
544                                 // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook
545                                 return false;
546                         }
547
548                         $Notify->updateMsgFromPreamble($epreamble);
549                         $Notify = $this->save($Notify);
550
551                         $itemlink  = $this->baseUrl->get() . '/notification/' . $Notify->id;
552                         $notify_id = $Notify->id;
553                 }
554
555                 // send email notification if notification preferences permit
556                 if ((intval($params['notify_flags']) & intval($params['type']))
557                         || $params['type'] == Model\Notification\Type::SYSTEM) {
558
559                         $this->logger->notice('sending notification email');
560
561                         if (isset($params['parent']) && (intval($params['parent']) != 0)) {
562                                 $parent = Model\Post::selectFirst(['guid'], ['id' => $params['parent']]);
563                                 $message_id = "<" . $parent['guid'] . "@" . gethostname() . ">";
564
565                                 // Is this the first email notification for this parent item and user?
566                                 if (!DBA::exists('notify-threads', ['master-parent-uri-id' => $parent_uri_id, 'receiver-uid' => $params['uid']])) {
567                                         $this->logger->info("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid']));
568
569                                         $fields = ['notify-id' => $notify_id, 'master-parent-uri-id' => $parent_uri_id,
570                                                 'receiver-uid' => $params['uid'], 'parent-item' => 0];
571                                         DBA::insert('notify-threads', $fields);
572
573                                         $emailBuilder->setHeader('Message-ID', $message_id);
574                                         $log_msg = "include/enotify: No previous notification found for this parent:\n" .
575                                                 "  parent: ${params['parent']}\n" . "  uid   : ${params['uid']}\n";
576                                         $this->logger->info($log_msg);
577                                 } else {
578                                         // If not, just "follow" the thread.
579                                         $emailBuilder->setHeader('References', $message_id);
580                                         $emailBuilder->setHeader('In-Reply-To', $message_id);
581                                         $this->logger->info("There's already a notification for this parent.");
582                                 }
583                         }
584
585                         $datarray = [
586                                 'preamble'     => $preamble,
587                                 'type'         => $params['type'],
588                                 'parent'       => $parent_id,
589                                 'source_name'  => $params['source_name'] ?? null,
590                                 'source_link'  => $params['source_link'] ?? null,
591                                 'source_photo' => $params['source_photo'] ?? null,
592                                 'uid'          => $params['uid'],
593                                 'hsitelink'    => $hsitelink,
594                                 'tsitelink'    => $tsitelink,
595                                 'itemlink'     => $itemlink,
596                                 'title'        => $title,
597                                 'body'         => $body,
598                                 'subject'      => $subject,
599                                 'headers'      => $emailBuilder->getHeaders(),
600                         ];
601
602                         Hook::callAll('enotify_mail', $datarray);
603
604                         $emailBuilder
605                                 ->withHeaders($datarray['headers'])
606                                 ->withRecipient($params['to_email'])
607                                 ->forUser([
608                                         'uid' => $datarray['uid'],
609                                         'language' => $params['language'],
610                                 ])
611                                 ->withNotification($datarray['subject'], $datarray['preamble'], $datarray['title'], $datarray['body'])
612                                 ->withSiteLink($datarray['tsitelink'], $datarray['hsitelink'])
613                                 ->withItemLink($datarray['itemlink']);
614
615                         // If a photo is present, add it to the email
616                         if (!empty($datarray['source_photo'])) {
617                                 $emailBuilder->withPhoto(
618                                         $datarray['source_photo'],
619                                         $datarray['source_link'] ?? $sitelink,
620                                         $datarray['source_name'] ?? $sitename);
621                         }
622
623                         $email = $emailBuilder->build();
624
625                         // use the Emailer class to send the message
626                         return $this->emailer->send($email);
627                 }
628
629                 return false;
630         }
631
632         public function createFromNotification(Entity\Notification $Notification)
633         {
634                 $this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
635
636                 if ($Notification->type === Model\Post\UserNotification::TYPE_NONE) {
637                         $this->logger->info('Not an item based notification, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
638                         return false;
639                 }
640
641                 $params = [];
642                 $params['verb']  = $Notification->verb;
643                 $params['uid']   = $Notification->uid;
644                 $params['otype'] = Model\Notification\ObjectType::ITEM;
645
646                 $user = Model\User::getById($Notification->uid);
647
648                 $params['notify_flags'] = $user['notify-flags'];
649                 $params['language']     = $user['language'];
650                 $params['to_name']      = $user['username'];
651                 $params['to_email']     = $user['email'];
652
653                 // from here on everything is in the recipients language
654                 $l10n = $this->l10n->withLang($user['language']);
655
656                 $contact = Model\Contact::getById($Notification->actorId, ['url', 'name', 'photo']);
657                 if (DBA::isResult($contact)) {
658                         $params['source_link']  = $contact['url'];
659                         $params['source_name']  = $contact['name'];
660                         $params['source_photo'] = $contact['photo'];
661                 }
662
663                 $item = Model\Post::selectFirstForUser($Notification->uid, Model\Item::ITEM_FIELDLIST,
664                         ['uid' => [0, $Notification->uid], 'uri-id' => $Notification->targetUriId, 'deleted' => false],
665                         ['order' => ['uid' => true]]);
666                 if (empty($item)) {
667                         $this->logger->info('Item not found', ['uri-id' => $Notification->targetUriId, 'type' => $Notification->type]);
668                         return false;
669                 }
670
671                 $params['item']   = $item;
672                 $params['parent'] = $item['parent'];
673                 $params['link']   = $this->baseUrl->get() . '/display/' . urlencode($item['guid']);
674
675                 $subjectPrefix = $l10n->t('[Friendica:Notify]');
676
677                 if (Model\Post\ThreadUser::getIgnored($Notification->parentUriId, $Notification->uid)) {
678                         $this->logger->info('Thread is ignored', ['parent-uri-id' => $Notification->parentUriId, 'type' => $Notification->type]);
679                         return false;
680                 }
681
682                 // Check to see if there was already a tag notify or comment notify for this post.
683                 // If so don't create a second notification
684                 $condition = ['type' => [Model\Notification\Type::TAG_SELF, Model\Notification\Type::COMMENT, Model\Notification\Type::SHARE],
685                         'link' => $params['link'], 'verb' => Activity::POST];
686                 if ($this->existsForUser($Notification->uid, $condition)) {
687                         $this->logger->info('Duplicate found, quitting', $condition + ['uid' => $Notification->uid]);
688                         return false;
689                 }
690
691                 $content = Plaintext::getPost($item, 70);
692                 if (!empty($content['text'])) {
693                         $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
694                 } else {
695                         $title = $item['title'];
696                 }
697
698                 // Some mail software relies on subject field for threading.
699                 // So, we cannot have different subjects for notifications of the same thread.
700                 // Before this we have the name of the replier on the subject rendering
701                 // different subjects for messages on the same thread.
702                 if ($Notification->type === Model\Post\UserNotification::TYPE_EXPLICIT_TAGGED) {
703                         $params['type'] = Model\Notification\Type::TAG_SELF;
704                         $subject        = $l10n->t('%s %s tagged you', $subjectPrefix, $contact['name']);
705                 } elseif ($Notification->type === Model\Post\UserNotification::TYPE_SHARED) {
706                         $params['type'] = Model\Notification\Type::SHARE;
707                         $subject        = $l10n->t('%s %s shared a new post', $subjectPrefix, $contact['name']);
708                 } else {
709                         $params['type'] = Model\Notification\Type::COMMENT;
710                         $subject        = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']);
711                 }
712
713                 $msg = $this->notification->getMessageFromNotification($Notification, $this->baseUrl, $l10n);
714                 if (empty($msg)) {
715                         $this->logger->info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
716                         return false;
717                 }
718
719                 $preamble  = $msg['plain'];
720                 $epreamble = $msg['rich'];
721
722                 $sitename = $this->config->get('config', 'sitename');
723                 $siteurl  = $this->baseUrl->get(true);
724
725                 $sitelink  = $l10n->t('Please visit %s to view and/or reply to the conversation.');
726                 $tsitelink = sprintf($sitelink, $siteurl);
727                 $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
728                 $itemlink  = $params['link'];
729
730                 $this->logger->info('Perform notification', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
731
732                 return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $item['body'], $itemlink, true);
733         }
734 }