Merge pull request #9146 from tobiasd/2020.09-CHANGELOG
[friendica.git/.git] / include / enotify.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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 use Friendica\Content\Text\BBCode;
23 use Friendica\Core\Hook;
24 use Friendica\Core\Logger;
25 use Friendica\Core\Renderer;
26 use Friendica\Core\System;
27 use Friendica\Database\DBA;
28 use Friendica\DI;
29 use Friendica\Model\Item;
30 use Friendica\Model\ItemContent;
31 use Friendica\Model\Notify;
32 use Friendica\Model\User;
33 use Friendica\Model\UserItem;
34 use Friendica\Protocol\Activity;
35
36 /**
37  * Creates a notification entry and possibly sends a mail
38  *
39  * @param array $params Array with the elements:
40  *                      uid, item, parent, type, otype, verb, event,
41  *                      link, subject, body, to_name, to_email, source_name,
42  *                      source_link, activity, preamble, notify_flags,
43  *                      language, show_in_notification_page
44  * @return bool
45  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
46  */
47 function notification($params)
48 {
49         /** @var string the common prefix of a notification subject */
50         $subjectPrefix = DI::l10n()->t('[Friendica:Notify]');
51
52         // Temporary logging for finding the origin
53         if (!isset($params['uid'])) {
54                 Logger::notice('Missing parameters "uid".', ['params' => $params, 'callstack' => System::callstack()]);
55         }
56
57         // Ensure that the important fields are set at any time
58         $fields = ['notify-flags', 'language', 'username', 'email'];
59         $user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]);
60
61         if (!DBA::isResult($user)) {
62                 Logger::error('Unknown user', ['uid' =>  $params['uid']]);
63                 return false;
64         }
65
66         $params['notify_flags'] = ($params['notify_flags'] ?? '') ?: $user['notify-flags'];
67         $params['language']     = ($params['language']     ?? '') ?: $user['language'];
68         $params['to_name']      = ($params['to_name']      ?? '') ?: $user['username'];
69         $params['to_email']     = ($params['to_email']     ?? '') ?: $user['email'];
70
71         // from here on everything is in the recipients language
72         $l10n = DI::l10n()->withLang($params['language']);
73
74         $siteurl = DI::baseUrl()->get(true);
75         $sitename = DI::config()->get('config', 'sitename');
76
77         $hostname = DI::baseUrl()->getHostname();
78         if (strpos($hostname, ':')) {
79                 $hostname = substr($hostname, 0, strpos($hostname, ':'));
80         }
81
82         $user = User::getById($params['uid'], ['nickname', 'page-flags']);
83
84         // There is no need to create notifications for forum accounts
85         if (!DBA::isResult($user) || in_array($user["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
86                 return false;
87         }
88         $nickname = $user["nickname"];
89
90         // Creates a new email builder for the notification email
91         $emailBuilder = DI::emailer()->newNotifyMail();
92
93         // with $params['show_in_notification_page'] == false, the notification isn't inserted into
94         // the database, and an email is sent if applicable.
95         // default, if not specified: true
96         $show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true;
97
98         $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>');
99
100         if (array_key_exists('item', $params)) {
101                 $title = $params['item']['title'];
102                 $body = $params['item']['body'];
103         } else {
104                 $title = $body = '';
105         }
106
107         if (isset($params['item']['id'])) {
108                 $item_id = $params['item']['id'];
109         } else {
110                 $item_id = 0;
111         }
112
113         if (isset($params['item']['uri-id'])) {
114                 $uri_id = $params['item']['uri-id'];
115         } else {
116                 $uri_id = 0;
117         }
118
119         if (isset($params['parent'])) {
120                 $parent_id = $params['parent'];
121         } else {
122                 $parent_id = 0;
123         }
124
125         if (isset($params['item']['parent-uri-id'])) {
126                 $parent_uri_id = $params['item']['parent-uri-id'];
127         } else {
128                 $parent_uri_id = 0;
129         }
130
131         $epreamble = '';
132         $preamble  = '';
133         $subject   = '';
134         $sitelink  = '';
135         $tsitelink = '';
136         $hsitelink = '';
137         $itemlink  = '';
138
139         if ($params['type'] == Notify\Type::MAIL) {
140                 $itemlink = $siteurl.'/message/'.$params['item']['id'];
141                 $params["link"] = $itemlink;
142
143                 $subject = $l10n->t('%s New mail received at %s', $subjectPrefix, $sitename);
144
145                 $preamble = $l10n->t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename);
146                 $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]');
147
148                 $sitelink = $l10n->t('Please visit %s to view and/or reply to your private messages.');
149                 $tsitelink = sprintf($sitelink, $siteurl.'/message/'.$params['item']['id']);
150                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'/message/'.$params['item']['id'].'">'.$sitename.'</a>');
151         }
152
153         if ($params['type'] == Notify\Type::COMMENT || $params['type'] == Notify\Type::TAG_SELF) {
154                 $thread = Item::selectFirstThreadForUser($params['uid'], ['ignored'], ['iid' => $parent_id, 'deleted' => false]);
155                 if (DBA::isResult($thread) && $thread['ignored']) {
156                         Logger::log('Thread ' . $parent_id . ' will be ignored', Logger::DEBUG);
157                         return false;
158                 }
159
160                 // Check to see if there was already a tag notify or comment notify for this post.
161                 // If so don't create a second notification
162                 /// @todo In the future we should store the notification with the highest "value" and replace notifications
163                 $condition = ['type' => [Notify\Type::TAG_SELF, Notify\Type::COMMENT, Notify\Type::SHARE],
164                         'link' => $params['link'], 'uid' => $params['uid']];
165                 if (DBA::exists('notify', $condition)) {
166                         return false;
167                 }
168
169                 // if it's a post figure out who's post it is.
170                 $item = null;
171                 if ($params['otype'] === Notify\ObjectType::ITEM && $parent_id) {
172                         $item = Item::selectFirstForUser($params['uid'], Item::ITEM_FIELDLIST, ['id' => $parent_id, 'deleted' => false]);
173                 }
174
175                 if (empty($item)) {
176                         return false;
177                 }
178
179                 $item_post_type = Item::postType($item);
180
181                 $content = ItemContent::getPlaintextPost($item, 70);
182                 if (!empty($content['text'])) {
183                         $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
184                 } else {
185                         $title = '';
186                 }
187
188                 // First go for the general message
189
190                 // "George Bull's post"
191                 if ($params['activity']['origin_comment']) {
192                         $message = $l10n->t('%1$s replied to you on %2$s\'s %3$s %4$s');
193                 } elseif ($params['activity']['explicit_tagged']) {
194                         $message = $l10n->t('%1$s tagged you on %2$s\'s %3$s %4$s');
195                 } else {
196                         $message = $l10n->t('%1$s commented on %2$s\'s %3$s %4$s');
197                 }
198
199                 $dest_str = sprintf($message, $params['source_name'], $item['author-name'], $item_post_type, $title);
200
201                 // Then look for the special cases
202
203                 // "your post"
204                 if ($params['activity']['origin_thread']) {
205                         if ($params['activity']['origin_comment']) {
206                                 $message = $l10n->t('%1$s replied to you on your %2$s %3$s');
207                         } elseif ($params['activity']['explicit_tagged']) {
208                                 $message = $l10n->t('%1$s tagged you on your %2$s %3$s');
209                         } else {
210                                 $message = $l10n->t('%1$s commented on your %2$s %3$s');
211                         }
212
213                         $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title);
214                 // "their post"
215                 } elseif ($item['author-link'] == $params['source_link']) {
216                         if ($params['activity']['origin_comment']) {
217                                 $message = $l10n->t('%1$s replied to you on their %2$s %3$s');
218                         } elseif ($params['activity']['explicit_tagged']) {
219                                 $message = $l10n->t('%1$s tagged you on their %2$s %3$s');
220                         } else {
221                                 $message = $l10n->t('%1$s commented on their %2$s %3$s');
222                         }
223
224                         $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title);
225                 }
226
227                 // Some mail software relies on subject field for threading.
228                 // So, we cannot have different subjects for notifications of the same thread.
229                 // Before this we have the name of the replier on the subject rendering
230                 // different subjects for messages on the same thread.
231                 if ($params['activity']['explicit_tagged']) {
232                         $subject = $l10n->t('%s %s tagged you', $subjectPrefix, $params['source_name']);
233
234                         $preamble = $l10n->t('%1$s tagged you at %2$s', $params['source_name'], $sitename);
235                 } else {
236                         $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $parent_id, $params['source_name']);
237
238                         $preamble = $l10n->t('%s commented on an item/conversation you have been following.', $params['source_name']);
239                 }
240
241                 $epreamble = $dest_str;
242
243                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
244                 $tsitelink = sprintf($sitelink, $siteurl);
245                 $hsitelink = sprintf($sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
246                 $itemlink =  $params['link'];
247         }
248
249         if ($params['type'] == Notify\Type::WALL) {
250                 $subject = $l10n->t('%s %s posted to your profile wall', $subjectPrefix, $params['source_name']);
251
252                 $preamble = $l10n->t('%1$s posted to your profile wall at %2$s', $params['source_name'], $sitename);
253                 $epreamble = $l10n->t('%1$s posted to [url=%2$s]your wall[/url]',
254                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
255                         $params['link']
256                 );
257
258                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
259                 $tsitelink = sprintf($sitelink, $siteurl);
260                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
261                 $itemlink =  $params['link'];
262         }
263
264         if ($params['type'] == Notify\Type::SHARE) {
265                 if ($params['origin_link'] == $params['source_link']) {
266                         $subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $params['source_name']);
267
268                         $preamble = $l10n->t('%1$s shared a new post at %2$s', $params['source_name'], $sitename);
269                         $epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url].',
270                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
271                                 $params['link']
272                         );
273                 } else {
274                         $subject = $l10n->t('%s %s shared a post from %s', $subjectPrefix, $params['source_name'], $params['origin_name']);
275
276                         $preamble = $l10n->t('%1$s shared a post from %2$s at %3$s', $params['source_name'], $params['origin_name'], $sitename);
277                         $epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url] from %3$s.',
278                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
279                                 $params['link'], '[url='.$params['origin_link'].']'.$params['origin_name'].'[/url]'
280                         );                      
281                 }
282
283                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
284                 $tsitelink = sprintf($sitelink, $siteurl);
285                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
286                 $itemlink =  $params['link'];
287         }
288
289         if ($params['type'] == Notify\Type::POKE) {
290                 $subject = $l10n->t('%1$s %2$s poked you', $subjectPrefix, $params['source_name']);
291
292                 $preamble = $l10n->t('%1$s poked you at %2$s', $params['source_name'], $sitename);
293                 $epreamble = $l10n->t('%1$s [url=%2$s]poked you[/url].',
294                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
295                         $params['link']
296                 );
297
298                 $subject = str_replace('poked', $l10n->t($params['activity']), $subject);
299                 $preamble = str_replace('poked', $l10n->t($params['activity']), $preamble);
300                 $epreamble = str_replace('poked', $l10n->t($params['activity']), $epreamble);
301
302                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
303                 $tsitelink = sprintf($sitelink, $siteurl);
304                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
305                 $itemlink =  $params['link'];
306         }
307
308         if ($params['type'] == Notify\Type::TAG_SHARE) {
309                 $itemlink =  $params['link'];
310                 $subject = $l10n->t('%s %s tagged your post', $subjectPrefix, $params['source_name']);
311
312                 $preamble = $l10n->t('%1$s tagged your post at %2$s', $params['source_name'], $sitename);
313                 $epreamble = $l10n->t('%1$s tagged [url=%2$s]your post[/url]',
314                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
315                         $itemlink
316                 );
317
318                 $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
319                 $tsitelink = sprintf($sitelink, $siteurl);
320                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
321         }
322
323         if ($params['type'] == Notify\Type::INTRO) {
324                 $itemlink = $params['link'];
325                 $subject = $l10n->t('%s Introduction received', $subjectPrefix);
326
327                 $preamble = $l10n->t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename);
328                 $epreamble = $l10n->t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.',
329                         $itemlink,
330                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
331                 );
332
333                 $body = $l10n->t('You may visit their profile at %s', $params['source_link']);
334
335                 $sitelink = $l10n->t('Please visit %s to approve or reject the introduction.');
336                 $tsitelink = sprintf($sitelink, $siteurl);
337                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
338
339                 switch ($params['verb']) {
340                         case Activity::FRIEND:
341                                 // someone started to share with user (mostly OStatus)
342                                 $subject = $l10n->t('%s A new person is sharing with you', $subjectPrefix);
343
344                                 $preamble = $l10n->t('%1$s is sharing with you at %2$s', $params['source_name'], $sitename);
345                                 $epreamble = $l10n->t('%1$s is sharing with you at %2$s',
346                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
347                                         $sitename
348                                 );
349                                 break;
350                         case Activity::FOLLOW:
351                                 // someone started to follow the user (mostly OStatus)
352                                 $subject = $l10n->t('%s You have a new follower', $subjectPrefix);
353
354                                 $preamble = $l10n->t('You have a new follower at %2$s : %1$s', $params['source_name'], $sitename);
355                                 $epreamble = $l10n->t('You have a new follower at %2$s : %1$s',
356                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
357                                         $sitename
358                                 );
359                                 break;
360                         default:
361                                 // ACTIVITY_REQ_FRIEND is default activity for notifications
362                                 break;
363                 }
364         }
365
366         if ($params['type'] == Notify\Type::SUGGEST) {
367                 $itemlink =  $params['link'];
368                 $subject = $l10n->t('%s Friend suggestion received', $subjectPrefix);
369
370                 $preamble = $l10n->t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename);
371                 $epreamble = $l10n->t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.',
372                         $itemlink,
373                         '[url='.$params['item']['url'].']'.$params['item']['name'].'[/url]',
374                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
375                 );
376
377                 $body = $l10n->t('Name:').' '.$params['item']['name']."\n";
378                 $body .= $l10n->t('Photo:').' '.$params['item']['photo']."\n";
379                 $body .= $l10n->t('You may visit their profile at %s', $params['item']['url']);
380
381                 $sitelink = $l10n->t('Please visit %s to approve or reject the suggestion.');
382                 $tsitelink = sprintf($sitelink, $siteurl);
383                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
384         }
385
386         if ($params['type'] == Notify\Type::CONFIRM) {
387                 if ($params['verb'] == Activity::FRIEND) { // mutual connection
388                         $itemlink =  $params['link'];
389                         $subject = $l10n->t('%s Connection accepted', $subjectPrefix);
390
391                         $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
392                         $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].',
393                                 $itemlink,
394                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
395                         );
396
397                         $body =  $l10n->t('You are now mutual friends and may exchange status updates, photos, and email without restriction.');
398
399                         $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.');
400                         $tsitelink = sprintf($sitelink, $siteurl);
401                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
402                 } else { // ACTIVITY_FOLLOW
403                         $itemlink =  $params['link'];
404                         $subject = $l10n->t('%s Connection accepted', $subjectPrefix);
405
406                         $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
407                         $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].',
408                                 $itemlink,
409                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
410                         );
411
412                         $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']);
413                         $body .= "\n\n";
414                         $body .= $l10n->t('\'%1$s\' may choose to extend this into a two-way or more permissive relationship in the future.', $params['source_name']);
415
416                         $sitelink = $l10n->t('Please visit %s  if you wish to make any changes to this relationship.');
417                         $tsitelink = sprintf($sitelink, $siteurl);
418                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
419                 }
420         }
421
422         if ($params['type'] == Notify\Type::SYSTEM) {
423                 switch($params['event']) {
424                         case "SYSTEM_REGISTER_REQUEST":
425                                 $itemlink =  $params['link'];
426                                 $subject = $l10n->t('[Friendica System Notify]') . ' ' . $l10n->t('registration request');
427
428                                 $preamble = $l10n->t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename);
429                                 $epreamble = $l10n->t('You\'ve received a [url=%1$s]registration request[/url] from %2$s.',
430                                         $itemlink,
431                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
432                                 );
433
434                                 $body = $l10n->t("Full Name:    %s\nSite Location:      %s\nLogin Name: %s (%s)",
435                                         $params['source_name'],
436                                         $siteurl, $params['source_mail'],
437                                         $params['source_nick']
438                                 );
439
440                                 $sitelink = $l10n->t('Please visit %s to approve or reject the request.');
441                                 $tsitelink = sprintf($sitelink, $params['link']);
442                                 $hsitelink = sprintf($sitelink, '<a href="'.$params['link'].'">'.$sitename.'</a><br><br>');
443                                 break;
444                         case "SYSTEM_DB_UPDATE_FAIL":
445                                 break;
446                 }
447         }
448
449         $subject .= " (".$nickname."@".$hostname.")";
450
451         $h = [
452                 'params'    => $params,
453                 'subject'   => $subject,
454                 'preamble'  => $preamble,
455                 'epreamble' => $epreamble,
456                 'body'      => $body,
457                 'sitelink'  => $sitelink,
458                 'tsitelink' => $tsitelink,
459                 'hsitelink' => $hsitelink,
460                 'itemlink'  => $itemlink
461         ];
462
463         Hook::callAll('enotify', $h);
464
465         $subject   = $h['subject'];
466
467         $preamble  = $h['preamble'];
468         $epreamble = $h['epreamble'];
469
470         $body      = $h['body'];
471
472         $tsitelink = $h['tsitelink'];
473         $hsitelink = $h['hsitelink'];
474         $itemlink  = $h['itemlink'];
475
476         $notify_id = 0;
477
478         if ($show_in_notification_page) {
479                 $notification = DI::notify()->insert([
480                         'name'          => $params['source_name'] ?? '',
481                         'name_cache'    => substr(strip_tags(BBCode::convert($params['source_name'])), 0, 255),
482                         'url'           => $params['source_link'] ?? '',
483                         'photo'         => $params['source_photo'] ?? '',
484                         'link'          => $itemlink ?? '',
485                         'uid'           => $params['uid'] ?? 0,
486                         'iid'           => $item_id,
487                         'uri-id'        => $uri_id,
488                         'parent'        => $parent_id,
489                         'parent-uri-id' => $parent_uri_id,
490                         'type'          => $params['type'] ?? '',
491                         'verb'          => $params['verb'] ?? '',
492                         'otype'         => $params['otype'] ?? '',
493                 ]);
494
495                 // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook
496                 if (!$notification) {
497                         return false;
498                 }
499
500                 $notification->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $notification->link]);
501
502                 DI::notify()->update($notification);
503
504                 $itemlink  = DI::baseUrl() . '/notification/' . $notification->id;
505                 $notify_id = $notification->id;
506         }
507
508         // send email notification if notification preferences permit
509         if ((intval($params['notify_flags']) & intval($params['type']))
510                 || $params['type'] == Notify\Type::SYSTEM) {
511
512                 Logger::log('sending notification email');
513
514                 if (isset($params['parent']) && (intval($params['parent']) != 0)) {
515                         $parent = Item::selectFirst(['guid'], ['id' => $params['parent']]);
516                         $message_id = "<" . $parent['guid'] . "@" . gethostname() . ">";
517
518                         // Is this the first email notification for this parent item and user?
519                         if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) {
520                                 Logger::log("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid']), Logger::DEBUG);
521
522                                 $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'],
523                                         'master-parent-uri-id' => $parent_uri_id,
524                                         'receiver-uid' => $params['uid'], 'parent-item' => 0];
525                                 DBA::insert('notify-threads', $fields);
526
527                                 $emailBuilder->setHeader('Message-ID', $message_id);
528                                 $log_msg                = "include/enotify: No previous notification found for this parent:\n" .
529                                                           "  parent: ${params['parent']}\n" . "  uid   : ${params['uid']}\n";
530                                 Logger::log($log_msg, Logger::DEBUG);
531                         } else {
532                                 // If not, just "follow" the thread.
533                                 $emailBuilder->setHeader('References', $message_id);
534                                 $emailBuilder->setHeader('In-Reply-To', $message_id);
535                                 Logger::log("There's already a notification for this parent.", Logger::DEBUG);
536                         }
537                 }
538
539                 $datarray = [
540                         'preamble'     => $preamble,
541                         'type'         => $params['type'],
542                         'parent'       => $parent_id,
543                         'source_name'  => $params['source_name'] ?? null,
544                         'source_link'  => $params['source_link'] ?? null,
545                         'source_photo' => $params['source_photo'] ?? null,
546                         'uid'          => $params['uid'],
547                         'hsitelink'    => $hsitelink,
548                         'tsitelink'    => $tsitelink,
549                         'itemlink'     => $itemlink,
550                         'title'        => $title,
551                         'body'         => $body,
552                         'subject'      => $subject,
553                 ];
554
555                 Hook::callAll('enotify_mail', $datarray);
556
557                 $builder = DI::emailer()
558                         ->newNotifyMail()
559                         ->addHeaders($datarray['headers'])
560                         ->withRecipient($params['to_email'])
561                         ->forUser([
562                                 'uid' => $datarray['uid'],
563                                 'language' => $params['language'],
564                         ])
565                         ->withNotification($datarray['subject'], $datarray['preamble'], $datarray['title'], $datarray['body'])
566                         ->withSiteLink($datarray['tsitelink'], $datarray['hsitelink'])
567                         ->withItemLink($datarray['itemlink']);
568
569                 // If a photo is present, add it to the email
570                 if (!empty($datarray['source_photo'])) {
571                         $emailBuilder->withPhoto(
572                                 $datarray['source_photo'],
573                                 $datarray['source_link'] ?? $sitelink,
574                                 $datarray['source_name'] ?? $sitename);
575                 }
576
577                 $email = $emailBuilder->build();
578
579                 // use the Emailer class to send the message
580                 return DI::emailer()->send($email);
581         }
582
583         return false;
584 }
585
586 /**
587  * Checks for users who should be notified
588  *
589  * @param int $itemid ID of the item for which the check should be done
590  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
591  */
592 function check_user_notification($itemid) {
593         // fetch all users with notifications
594         $useritems = DBA::select('user-item', ['uid', 'notification-type'], ['iid' => $itemid]);
595         while ($useritem = DBA::fetch($useritems)) {
596                 check_item_notification($itemid, $useritem['uid'], $useritem['notification-type']);
597         }
598         DBA::close($useritems);
599 }
600
601 /**
602  * Checks for item related notifications and sends them
603  *
604  * @param int    $itemid            ID of the item for which the check should be done
605  * @param int    $uid               User ID
606  * @param int    $notification_type Notification bits
607  * @return bool
608  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
609  */
610 function check_item_notification($itemid, $uid, $notification_type) {
611         $fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'thr-parent-id',
612                 'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id',
613                 'gravity', 'guid', 'parent-uri', 'uri', 'contact-id', 'network'];
614         $condition = ['id' => $itemid, 'deleted' => false];
615         $item = Item::selectFirstForUser($uid, $fields, $condition);
616         if (!DBA::isResult($item)) {
617                 return false;
618         }
619
620         // Generate the notification array
621         $params = [];
622         $params['uid'] = $uid;
623         $params['item'] = $item;
624         $params['parent'] = $item['parent'];
625         $params['link'] = DI::baseUrl() . '/display/' . urlencode($item['guid']);
626         $params['otype'] = 'item';
627         $params['origin_name'] = $params['source_name'] = $item['author-name'];
628         $params['origin_link'] = $params['source_link'] = $item['author-link'];
629         $params['origin_photo'] = $params['source_photo'] = $item['author-avatar'];
630
631         // Set the activity flags
632         $params['activity']['explicit_tagged'] = ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED);
633         $params['activity']['implicit_tagged'] = ($notification_type & UserItem::NOTIF_IMPLICIT_TAGGED);
634         $params['activity']['origin_comment'] = ($notification_type & UserItem::NOTIF_DIRECT_COMMENT);
635         $params['activity']['origin_thread'] = ($notification_type & UserItem::NOTIF_THREAD_COMMENT);
636         $params['activity']['thread_comment'] = ($notification_type & UserItem::NOTIF_COMMENT_PARTICIPATION);
637         $params['activity']['thread_activity'] = ($notification_type & UserItem::NOTIF_ACTIVITY_PARTICIPATION);
638
639         // Tagging a user in a direct post (first comment level) means a direct comment
640         if ($params['activity']['explicit_tagged'] && ($notification_type & UserItem::NOTIF_DIRECT_THREAD_COMMENT)) {
641                 $params['activity']['origin_comment'] = true;
642         }
643
644         if ($notification_type & UserItem::NOTIF_SHARED) {
645                 $params['type'] = Notify\Type::SHARE;
646                 $params['verb'] = Activity::POST;
647
648                 // Special treatment for posts that had been shared via "announce"
649                 if ($item['gravity'] == GRAVITY_ACTIVITY) {
650                         $parent_item = Item::selectFirst($fields, ['uri-id' => $item['thr-parent-id'], 'uid' => [$uid, 0]]);
651                         if (DBA::isResult($parent_item)) {
652                                 // Don't notify on own entries
653                                 if (User::getIdForURL($parent_item['author-link']) == $uid) {
654                                         return false;
655                                 }
656
657                                 $params['origin_name'] = $parent_item['author-name'];
658                                 $params['origin_link'] = $parent_item['author-link'];
659                                 $params['origin_photo'] = $parent_item['author-avatar'];
660                                 $params['item'] = $parent_item;
661                         }
662                 }
663         } elseif ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED) {
664                 $params['type'] = Notify\Type::TAG_SELF;
665                 $params['verb'] = Activity::TAG;
666         } elseif ($notification_type & UserItem::NOTIF_IMPLICIT_TAGGED) {
667                 $params['type'] = Notify\Type::COMMENT;
668                 $params['verb'] = Activity::POST;
669         } elseif ($notification_type & UserItem::NOTIF_THREAD_COMMENT) {
670                 $params['type'] = Notify\Type::COMMENT;
671                 $params['verb'] = Activity::POST;
672         } elseif ($notification_type & UserItem::NOTIF_DIRECT_COMMENT) {
673                 $params['type'] = Notify\Type::COMMENT;
674                 $params['verb'] = Activity::POST;
675         } elseif ($notification_type & UserItem::NOTIF_COMMENT_PARTICIPATION) {
676                 $params['type'] = Notify\Type::COMMENT;
677                 $params['verb'] = Activity::POST;
678         } elseif ($notification_type & UserItem::NOTIF_ACTIVITY_PARTICIPATION) {
679                 $params['type'] = Notify\Type::COMMENT;
680                 $params['verb'] = Activity::POST;
681         } else {
682                 return false;
683         }
684
685         notification($params);
686 }