]> git.mxchange.org Git - friendica.git/blob - include/enotify.php
Some easy to replace "q" calls have been replaced by "DBA" calls (#5632)
[friendica.git] / include / enotify.php
1 <?php
2 /**
3  * @file include/enotify.php
4  */
5
6 use Friendica\Content\Text\BBCode;
7 use Friendica\Core\Addon;
8 use Friendica\Core\Config;
9 use Friendica\Core\L10n;
10 use Friendica\Core\System;
11 use Friendica\Database\DBA;
12 use Friendica\Model\Contact;
13 use Friendica\Model\Item;
14 use Friendica\Util\DateTimeFormat;
15 use Friendica\Util\Emailer;
16
17 /**
18  * @brief Creates a notification entry and possibly sends a mail
19  *
20  * @param array $params Array with the elements:
21  *                      uid, item, parent, type, otype, verb, event,
22  *                      link, subject, body, to_name, to_email, source_name,
23  *                      source_link, activity, preamble, notify_flags,
24  *                      language, show_in_notification_page
25  */
26 function notification($params)
27 {
28         $a = get_app();
29
30         // Temporary logging for finding the origin
31         if (!isset($params['language']) || !isset($params['uid'])) {
32                 logger('Missing parameters.' . System::callstack());
33         }
34
35         // from here on everything is in the recipients language
36         L10n::pushLang($params['language']);
37
38         $banner = L10n::t('Friendica Notification');
39         $product = FRIENDICA_PLATFORM;
40         $siteurl = System::baseUrl(true);
41         $thanks = L10n::t('Thank You,');
42         $sitename = Config::get('config', 'sitename');
43         if (Config::get('config', 'admin_name')) {
44                 $site_admin = L10n::t('%1$s, %2$s Administrator', Config::get('config', 'admin_name'), $sitename);
45         } else {
46                 $site_admin = L10n::t('%s Administrator', $sitename);
47         }
48
49         $sender_name = $sitename;
50         $hostname = $a->get_hostname();
51         if (strpos($hostname, ':')) {
52                 $hostname = substr($hostname, 0, strpos($hostname, ':'));
53         }
54
55         $sender_email = $a->getSenderEmailAddress();
56
57         if ($params['type'] != SYSTEM_EMAIL) {
58                 $user = DBA::selectFirst('user', ['nickname', 'page-flags'],
59                         ['uid' => $params['uid']]);
60
61                 // There is no need to create notifications for forum accounts
62                 if (!DBA::isResult($user) || in_array($user["page-flags"], [Contact::PAGE_COMMUNITY, Contact::PAGE_PRVGROUP])) {
63                         return;
64                 }
65                 $nickname = $user["nickname"];
66         } else {
67                 $nickname = '';
68         }
69
70         // with $params['show_in_notification_page'] == false, the notification isn't inserted into
71         // the database, and an email is sent if applicable.
72         // default, if not specified: true
73         $show_in_notification_page = ((x($params, 'show_in_notification_page')) ? $params['show_in_notification_page']:true);
74
75         $additional_mail_header = "";
76         $additional_mail_header .= "Precedence: list\n";
77         $additional_mail_header .= "X-Friendica-Host: ".$hostname."\n";
78         $additional_mail_header .= "X-Friendica-Account: <".$nickname."@".$hostname.">\n";
79         $additional_mail_header .= "X-Friendica-Platform: ".FRIENDICA_PLATFORM."\n";
80         $additional_mail_header .= "X-Friendica-Version: ".FRIENDICA_VERSION."\n";
81         $additional_mail_header .= "List-ID: <notification.".$hostname.">\n";
82         $additional_mail_header .= "List-Archive: <".System::baseUrl()."/notifications/system>\n";
83
84         if (array_key_exists('item', $params)) {
85                 $title = $params['item']['title'];
86                 $body = $params['item']['body'];
87         } else {
88                 $title = $body = '';
89         }
90
91         if (isset($params['item']['id'])) {
92                 $item_id = $params['item']['id'];
93         } else {
94                 $item_id = 0;
95         }
96
97         if (isset($params['parent'])) {
98                 $parent_id = $params['parent'];
99         } else {
100                 $parent_id = 0;
101         }
102
103         $epreamble = '';
104
105         if ($params['type'] == NOTIFY_MAIL) {
106                 $itemlink = $siteurl.'/message/'.$params['item']['id'];
107                 $params["link"] = $itemlink;
108
109                 $subject = L10n::t('[Friendica:Notify] New mail received at %s', $sitename);
110
111                 $preamble = L10n::t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename);
112                 $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]');
113
114                 $sitelink = L10n::t('Please visit %s to view and/or reply to your private messages.');
115                 $tsitelink = sprintf($sitelink, $siteurl.'/message/'.$params['item']['id']);
116                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'/message/'.$params['item']['id'].'">'.$sitename.'</a>');
117         }
118
119         if ($params['type'] == NOTIFY_COMMENT) {
120                 $thread = Item::selectFirstThreadForUser($params['uid'] ,['ignored'], ['iid' => $parent_id]);
121                 if (DBA::isResult($thread) && $thread["ignored"]) {
122                         logger("Thread ".$parent_id." will be ignored", LOGGER_DEBUG);
123                         return;
124                 }
125
126                 // Check to see if there was already a tag notify or comment notify for this post.
127                 // If so don't create a second notification
128                 $condition = ['type' => [NOTIFY_TAGSELF, NOTIFY_COMMENT, NOTIFY_SHARE],
129                         'link' => $params['link'], 'uid' => $params['uid']];
130                 if (DBA::exists('notify', $condition)) {
131                         L10n::popLang();
132                         return;
133                 }
134
135                 // if it's a post figure out who's post it is.
136
137                 $item = null;
138
139                 if ($params['otype'] === 'item' && $parent_id) {
140                         $item = Item::selectFirstForUser($params['uid'], Item::ITEM_FIELDLIST, ['id' => $parent_id]);
141                 }
142
143                 $item_post_type = item_post_type($item);
144                 $itemlink = $item['plink'];
145
146                 // "a post"
147                 $dest_str = L10n::t('%1$s commented on [url=%2$s]a %3$s[/url]',
148                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
149                         $itemlink,
150                         $item_post_type
151                 );
152
153                 // "George Bull's post"
154                 if ($item) {
155                         $dest_str = L10n::t('%1$s commented on [url=%2$s]%3$s\'s %4$s[/url]',
156                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
157                                 $itemlink,
158                                 $item['author-name'],
159                                 $item_post_type
160                         );
161                 }
162
163                 // "your post"
164                 if (DBA::isResult($item) && $item['owner-id'] == $item['author-id'] && $item['wall']) {
165                         $dest_str = L10n::t('%1$s commented on [url=%2$s]your %3$s[/url]',
166                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
167                                 $itemlink,
168                                 $item_post_type
169                         );
170                 }
171
172                 // Some mail softwares relies on subject field for threading.
173                 // So, we cannot have different subjects for notifications of the same thread.
174                 // Before this we have the name of the replier on the subject rendering
175                 // differents subjects for messages on the same thread.
176
177                 $subject = L10n::t('[Friendica:Notify] Comment to conversation #%1$d by %2$s', $parent_id, $params['source_name']);
178
179                 $preamble = L10n::t('%s commented on an item/conversation you have been following.', $params['source_name']);
180                 $epreamble = $dest_str;
181
182                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
183                 $tsitelink = sprintf($sitelink, $siteurl);
184                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
185                 $itemlink =  $params['link'];
186         }
187
188         if ($params['type'] == NOTIFY_WALL) {
189                 $subject = L10n::t('[Friendica:Notify] %s posted to your profile wall', $params['source_name']);
190
191                 $preamble = L10n::t('%1$s posted to your profile wall at %2$s', $params['source_name'], $sitename);
192                 $epreamble = L10n::t('%1$s posted to [url=%2$s]your wall[/url]',
193                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
194                         $params['link']
195                 );
196
197                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
198                 $tsitelink = sprintf($sitelink, $siteurl);
199                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
200                 $itemlink =  $params['link'];
201         }
202
203         if ($params['type'] == NOTIFY_TAGSELF) {
204                 $subject = L10n::t('[Friendica:Notify] %s tagged you', $params['source_name']);
205
206                 $preamble = L10n::t('%1$s tagged you at %2$s', $params['source_name'], $sitename);
207                 $epreamble = L10n::t('%1$s [url=%2$s]tagged you[/url].',
208                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
209                         $params['link']
210                 );
211
212                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
213                 $tsitelink = sprintf($sitelink, $siteurl);
214                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
215                 $itemlink =  $params['link'];
216         }
217
218         if ($params['type'] == NOTIFY_SHARE) {
219                 $subject = L10n::t('[Friendica:Notify] %s shared a new post', $params['source_name']);
220
221                 $preamble = L10n::t('%1$s shared a new post at %2$s', $params['source_name'], $sitename);
222                 $epreamble = L10n::t('%1$s [url=%2$s]shared a post[/url].',
223                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
224                         $params['link']
225                 );
226
227                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
228                 $tsitelink = sprintf($sitelink, $siteurl);
229                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
230                 $itemlink =  $params['link'];
231         }
232
233         if ($params['type'] == NOTIFY_POKE) {
234                 $subject = L10n::t('[Friendica:Notify] %1$s poked you', $params['source_name']);
235
236                 $preamble = L10n::t('%1$s poked you at %2$s', $params['source_name'], $sitename);
237                 $epreamble = L10n::t('%1$s [url=%2$s]poked you[/url].',
238                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
239                         $params['link']
240                 );
241
242                 $subject = str_replace('poked', L10n::t($params['activity']), $subject);
243                 $preamble = str_replace('poked', L10n::t($params['activity']), $preamble);
244                 $epreamble = str_replace('poked', L10n::t($params['activity']), $epreamble);
245
246                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
247                 $tsitelink = sprintf($sitelink, $siteurl);
248                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
249                 $itemlink =  $params['link'];
250         }
251
252         if ($params['type'] == NOTIFY_TAGSHARE) {
253                 $itemlink =  $params['link'];
254                 $subject = L10n::t('[Friendica:Notify] %s tagged your post', $params['source_name']);
255
256                 $preamble = L10n::t('%1$s tagged your post at %2$s', $params['source_name'], $sitename);
257                 $epreamble = L10n::t('%1$s tagged [url=%2$s]your post[/url]',
258                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
259                         $itemlink
260                 );
261
262                 $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.');
263                 $tsitelink = sprintf($sitelink, $siteurl);
264                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
265         }
266
267         if ($params['type'] == NOTIFY_INTRO) {
268                 $itemlink = $params['link'];
269                 $subject = L10n::t('[Friendica:Notify] Introduction received');
270
271                 $preamble = L10n::t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename);
272                 $epreamble = L10n::t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.',
273                         $itemlink,
274                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
275                 );
276
277                 $body = L10n::t('You may visit their profile at %s', $params['source_link']);
278
279                 $sitelink = L10n::t('Please visit %s to approve or reject the introduction.');
280                 $tsitelink = sprintf($sitelink, $siteurl);
281                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
282
283                 switch ($params['verb']) {
284                         case ACTIVITY_FRIEND:
285                                 // someone started to share with user (mostly OStatus)
286                                 $subject = L10n::t('[Friendica:Notify] A new person is sharing with you');
287
288                                 $preamble = L10n::t('%1$s is sharing with you at %2$s', $params['source_name'], $sitename);
289                                 $epreamble = L10n::t('%1$s is sharing with you at %2$s',
290                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
291                                         $sitename
292                                 );
293                                 break;
294                         case ACTIVITY_FOLLOW:
295                                 // someone started to follow the user (mostly OStatus)
296                                 $subject = L10n::t('[Friendica:Notify] You have a new follower');
297
298                                 $preamble = L10n::t('You have a new follower at %2$s : %1$s', $params['source_name'], $sitename);
299                                 $epreamble = L10n::t('You have a new follower at %2$s : %1$s',
300                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
301                                         $sitename
302                                 );
303                                 break;
304                         default:
305                                 // ACTIVITY_REQ_FRIEND is default activity for notifications
306                                 break;
307                 }
308         }
309
310         if ($params['type'] == NOTIFY_SUGGEST) {
311                 $itemlink =  $params['link'];
312                 $subject = L10n::t('[Friendica:Notify] Friend suggestion received');
313
314                 $preamble = L10n::t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename);
315                 $epreamble = L10n::t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.',
316                         $itemlink,
317                         '[url='.$params['item']['url'].']'.$params['item']['name'].'[/url]',
318                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
319                 );
320
321                 $body = L10n::t('Name:').' '.$params['item']['name']."\n";
322                 $body .= L10n::t('Photo:').' '.$params['item']['photo']."\n";
323                 $body .= L10n::t('You may visit their profile at %s', $params['item']['url']);
324
325                 $sitelink = L10n::t('Please visit %s to approve or reject the suggestion.');
326                 $tsitelink = sprintf($sitelink, $siteurl);
327                 $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
328         }
329
330         if ($params['type'] == NOTIFY_CONFIRM) {
331                 if ($params['verb'] == ACTIVITY_FRIEND) { // mutual connection
332                         $itemlink =  $params['link'];
333                         $subject = L10n::t('[Friendica:Notify] Connection accepted');
334
335                         $preamble = L10n::t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
336                         $epreamble = L10n::t('%2$s has accepted your [url=%1$s]connection request[/url].',
337                                 $itemlink,
338                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
339                         );
340
341                         $body =  L10n::t('You are now mutual friends and may exchange status updates, photos, and email without restriction.');
342
343                         $sitelink = L10n::t('Please visit %s if you wish to make any changes to this relationship.');
344                         $tsitelink = sprintf($sitelink, $siteurl);
345                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
346                 } else { // ACTIVITY_FOLLOW
347                         $itemlink =  $params['link'];
348                         $subject = L10n::t('[Friendica:Notify] Connection accepted');
349
350                         $preamble = L10n::t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename);
351                         $epreamble = L10n::t('%2$s has accepted your [url=%1$s]connection request[/url].',
352                                 $itemlink,
353                                 '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
354                         );
355
356                         $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']);
357                         $body .= "\n\n";
358                         $body .= L10n::t('\'%1$s\' may choose to extend this into a two-way or more permissive relationship in the future.', $params['source_name']);
359
360                         $sitelink = L10n::t('Please visit %s  if you wish to make any changes to this relationship.');
361                         $tsitelink = sprintf($sitelink, $siteurl);
362                         $hsitelink = sprintf($sitelink, '<a href="'.$siteurl.'">'.$sitename.'</a>');
363                 }
364         }
365
366         if ($params['type'] == NOTIFY_SYSTEM) {
367                 switch($params['event']) {
368                         case "SYSTEM_REGISTER_REQUEST":
369                                 $itemlink =  $params['link'];
370                                 $subject = L10n::t('[Friendica System Notify]') . ' ' . L10n::t('registration request');
371
372                                 $preamble = L10n::t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename);
373                                 $epreamble = L10n::t('You\'ve received a [url=%1$s]registration request[/url] from %2$s.',
374                                         $itemlink,
375                                         '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
376                                 );
377
378                                 $body = L10n::t("Full Name:     %s\nSite Location:      %s\nLogin Name: %s (%s)",
379                                         $params['source_name'],
380                                         $siteurl, $params['source_mail'],
381                                         $params['source_nick']
382                                 );
383
384                                 $sitelink = L10n::t('Please visit %s to approve or reject the request.');
385                                 $tsitelink = sprintf($sitelink, $params['link']);
386                                 $hsitelink = sprintf($sitelink, '<a href="'.$params['link'].'">'.$sitename.'</a><br><br>');
387                                 break;
388                         case "SYSTEM_DB_UPDATE_FAIL":
389                                 break;
390                 }
391         }
392
393         if ($params['type'] == SYSTEM_EMAIL) {
394                 // not part of the notifications.
395                 // it just send a mail to the user.
396                 // It will be used by the system to send emails to users (like
397                 // password reset, invitations and so) using one look (but without
398                 // add a notification to the user, with could be inexistent)
399                 $subject = $params['subject'];
400
401                 $preamble = $params['preamble'];
402
403                 $body =  $params['body'];
404
405                 $sitelink = "";
406                 $tsitelink = "";
407                 $hsitelink = "";
408                 $itemlink =  "";
409                 $show_in_notification_page = false;
410         }
411
412         $subject .= " (".$nickname."@".$hostname.")";
413
414         $h = [
415                 'params'    => $params,
416                 'subject'   => $subject,
417                 'preamble'  => $preamble,
418                 'epreamble' => $epreamble,
419                 'body'      => $body,
420                 'sitelink'  => $sitelink,
421                 'tsitelink' => $tsitelink,
422                 'hsitelink' => $hsitelink,
423                 'itemlink'  => $itemlink
424         ];
425
426         Addon::callHooks('enotify', $h);
427
428         $subject   = $h['subject'];
429
430         $preamble  = $h['preamble'];
431         $epreamble = $h['epreamble'];
432
433         $body      = $h['body'];
434
435         $tsitelink = $h['tsitelink'];
436         $hsitelink = $h['hsitelink'];
437         $itemlink  = $h['itemlink'];
438
439         if ($show_in_notification_page) {
440                 logger("adding notification entry", LOGGER_DEBUG);
441                 do {
442                         $dups = false;
443                         $hash = random_string();
444                         if (DBA::exists('notify', ['hash' => $hash])) {
445                                 $dups = true;
446                         }
447                 } while ($dups == true);
448
449                 /// @TODO One statement is enough
450                 $datarray = [];
451                 $datarray['hash']  = $hash;
452                 $datarray['name']  = $params['source_name'];
453                 $datarray['name_cache'] = strip_tags(BBCode::convert($params['source_name']));
454                 $datarray['url']   = $params['source_link'];
455                 $datarray['photo'] = $params['source_photo'];
456                 $datarray['date']  = DateTimeFormat::utcNow();
457                 $datarray['uid']   = $params['uid'];
458                 $datarray['link']  = $itemlink;
459                 $datarray['iid']   = $item_id;
460                 $datarray['parent'] = $parent_id;
461                 $datarray['type']  = $params['type'];
462                 $datarray['verb']  = $params['verb'];
463                 $datarray['otype'] = $params['otype'];
464                 $datarray['abort'] = false;
465
466                 Addon::callHooks('enotify_store', $datarray);
467
468                 if ($datarray['abort']) {
469                         L10n::popLang();
470                         return False;
471                 }
472
473                 // create notification entry in DB
474                 $fields = ['hash' => $datarray['hash'], 'name' => $datarray['name'], 'url' => $datarray['url'],
475                         'photo' => $datarray['photo'], 'date' => $datarray['date'], 'uid' => $datarray['uid'],
476                         'link' => $datarray['link'], 'iid' => $datarray['iid'], 'parent' => $datarray['parent'],
477                         'type' => $datarray['type'], 'verb' => $datarray['verb'], 'otype' => $datarray['otype'],
478                         'name_cache' => $datarray["name_cache"]];
479                 DBA::insert('notify', $fields);
480
481                 $notify_id = DBA::lastInsertId();
482
483                 // we seem to have a lot of duplicate comment notifications due to race conditions, mostly from forums
484                 // After we've stored everything, look again to see if there are any duplicates and if so remove them
485                 $p = q("SELECT `id` FROM `notify` WHERE `type` IN (%d, %d) AND `link` = '%s' AND `uid` = %d ORDER BY `id`",
486                         intval(NOTIFY_TAGSELF),
487                         intval(NOTIFY_COMMENT),
488                         DBA::escape($params['link']),
489                         intval($params['uid'])
490                 );
491                 if ($p && (count($p) > 1)) {
492                         for ($d = 1; $d < count($p); $d ++) {
493                                 DBA::delete('notify', ['id' => $p[$d]['id']]);
494                         }
495
496                         // only continue on if we stored the first one
497                         if ($notify_id != $p[0]['id']) {
498                                 L10n::popLang();
499                                 return false;
500                         }
501                 }
502
503                 $itemlink = System::baseUrl().'/notify/view/'.$notify_id;
504                 $msg = replace_macros($epreamble, ['$itemlink' => $itemlink]);
505                 $msg_cache = format_notification_message($datarray['name_cache'], strip_tags(BBCode::convert($msg)));
506
507                 $fields = ['msg' => $msg, 'msg_cache' => $msg_cache];
508                 $condition = ['id' => $notify_id, 'uid' => $params['uid']];
509                 DBA::update('notify', $fields, $condition);
510         }
511
512         // send email notification if notification preferences permit
513         if ((!empty($params['notify_flags']) & intval($params['type']))
514                 || $params['type'] == NOTIFY_SYSTEM
515                 || $params['type'] == SYSTEM_EMAIL) {
516
517                 logger('sending notification email');
518
519                 if (isset($params['parent']) && (intval($params['parent']) != 0)) {
520                         $id_for_parent = $params['parent']."@".$hostname;
521
522                         // Is this the first email notification for this parent item and user?
523                         if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) {
524                                 logger("notify_id:".intval($notify_id).", parent: ".intval($params['parent'])."uid: ".intval($params['uid']), LOGGER_DEBUG);
525
526                                 $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'],
527                                         'receiver-uid' => $params['uid'], 'parent-item' => 0];
528                                 DBA::insert('notify-threads', $fields);
529
530                                 $additional_mail_header .= "Message-ID: <${id_for_parent}>\n";
531                                 $log_msg = "include/enotify: No previous notification found for this parent:\n".
532                                                 "  parent: ${params['parent']}\n"."  uid   : ${params['uid']}\n";
533                                 logger($log_msg, LOGGER_DEBUG);
534                         } else {
535                                 // If not, just "follow" the thread.
536                                 $additional_mail_header .= "References: <${id_for_parent}>\nIn-Reply-To: <${id_for_parent}>\n";
537                                 logger("There's already a notification for this parent.", LOGGER_DEBUG);
538                         }
539                 }
540
541                 $textversion = BBCode::toPlaintext($body);
542                 $htmlversion = BBCode::convert($body);
543
544                 $datarray = [];
545                 $datarray['banner'] = $banner;
546                 $datarray['product'] = $product;
547                 $datarray['preamble'] = $preamble;
548                 $datarray['sitename'] = $sitename;
549                 $datarray['siteurl'] = $siteurl;
550                 $datarray['type'] = $params['type'];
551                 $datarray['parent'] = $parent_id;
552                 $datarray['source_name'] = defaults($params, 'source_name', '');
553                 $datarray['source_link'] = defaults($params, 'source_link', '');
554                 $datarray['source_photo'] = defaults($params, 'source_photo', '');
555                 $datarray['uid'] = $params['uid'];
556                 $datarray['username'] = defaults($params, 'to_name', '');
557                 $datarray['hsitelink'] = $hsitelink;
558                 $datarray['tsitelink'] = $tsitelink;
559                 $datarray['hitemlink'] = '<a href="'.$itemlink.'">'.$itemlink.'</a>';
560                 $datarray['titemlink'] = $itemlink;
561                 $datarray['thanks'] = $thanks;
562                 $datarray['site_admin'] = $site_admin;
563                 $datarray['title'] = stripslashes($title);
564                 $datarray['htmlversion'] = $htmlversion;
565                 $datarray['textversion'] = $textversion;
566                 $datarray['subject'] = $subject;
567                 $datarray['headers'] = $additional_mail_header;
568
569                 Addon::callHooks('enotify_mail', $datarray);
570
571                 // check whether sending post content in email notifications is allowed
572                 // always true for SYSTEM_EMAIL
573                 $content_allowed = ((!Config::get('system', 'enotify_no_content')) || ($params['type'] == SYSTEM_EMAIL));
574
575                 // load the template for private message notifications
576                 $tpl = get_markup_template('email_notify_html.tpl');
577                 $email_html_body = replace_macros($tpl, [
578                         '$banner'       => $datarray['banner'],
579                         '$product'      => $datarray['product'],
580                         '$preamble'     => str_replace("\n", "<br>\n", $datarray['preamble']),
581                         '$sitename'     => $datarray['sitename'],
582                         '$siteurl'      => $datarray['siteurl'],
583                         '$source_name'  => $datarray['source_name'],
584                         '$source_link'  => $datarray['source_link'],
585                         '$source_photo' => $datarray['source_photo'],
586                         '$username'     => $datarray['username'],
587                         '$hsitelink'    => $datarray['hsitelink'],
588                         '$hitemlink'    => $datarray['hitemlink'],
589                         '$thanks'       => $datarray['thanks'],
590                         '$site_admin'   => $datarray['site_admin'],
591                         '$title'        => $datarray['title'],
592                         '$htmlversion'  => $datarray['htmlversion'],
593                         '$content_allowed'      => $content_allowed,
594                 ]);
595
596                 // load the template for private message notifications
597                 $tpl = get_markup_template('email_notify_text.tpl');
598                 $email_text_body = replace_macros($tpl, [
599                         '$banner'       => $datarray['banner'],
600                         '$product'      => $datarray['product'],
601                         '$preamble'     => $datarray['preamble'],
602                         '$sitename'     => $datarray['sitename'],
603                         '$siteurl'      => $datarray['siteurl'],
604                         '$source_name'  => $datarray['source_name'],
605                         '$source_link'  => $datarray['source_link'],
606                         '$source_photo' => $datarray['source_photo'],
607                         '$username'     => $datarray['username'],
608                         '$tsitelink'    => $datarray['tsitelink'],
609                         '$titemlink'    => $datarray['titemlink'],
610                         '$thanks'       => $datarray['thanks'],
611                         '$site_admin'   => $datarray['site_admin'],
612                         '$title'        => $datarray['title'],
613                         '$textversion'  => $datarray['textversion'],
614                         '$content_allowed'      => $content_allowed,
615                 ]);
616
617                 // use the Emailer class to send the message
618                 return Emailer::send(
619                         [
620                         'uid' => $params['uid'],
621                         'fromName' => $sender_name,
622                         'fromEmail' => $sender_email,
623                         'replyTo' => $sender_email,
624                         'toEmail' => $params['to_email'],
625                         'messageSubject' => $datarray['subject'],
626                         'htmlVersion' => $email_html_body,
627                         'textVersion' => $email_text_body,
628                         'additionalMailHeader' => $datarray['headers']]
629                 );
630         }
631
632         return false;
633 }
634
635 /**
636  * @brief Checks for users who should be notified
637  *
638  * @param int $itemid ID of the item for which the check should be done
639  */
640 function check_user_notification($itemid) {
641         // fetch all users in the thread
642         $users = DBA::p("SELECT DISTINCT(`contact`.`uid`) FROM `item`
643                         INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` != 0
644                         WHERE `parent` IN (SELECT `parent` FROM `item` WHERE `id`=?)", $itemid);
645         while ($user = DBA::fetch($users)) {
646                 check_item_notification($itemid, $user['uid']);
647         }
648         DBA::close($users);
649 }
650
651 /**
652  * @brief Checks for item related notifications and sends them
653  *
654  * @param int $itemid ID of the item for which the check should be done
655  * @param int $uid User ID
656  * @param string $defaulttype (Optional) Forces a notification with this type.
657  */
658 function check_item_notification($itemid, $uid, $defaulttype = "") {
659         $notification_data = ["uid" => $uid, "profiles" => []];
660         Addon::callHooks('check_item_notification', $notification_data);
661
662         $profiles = $notification_data["profiles"];
663
664         $fields = ['notify-flags', 'language', 'username', 'email', 'nickname'];
665         $user = DBA::selectFirst('user', $fields, ['uid' => $uid]);
666         if (!DBA::isResult($user)) {
667                 return false;
668         }
669
670         $owner = DBA::selectFirst('contact', ['url'], ['self' => true, 'uid' => $uid]);
671         if (!DBA::isResult($owner)) {
672                 return false;
673         }
674
675         // This is our regular URL format
676         $profiles[] = $owner["url"];
677
678         // Notifications from Diaspora are often with an URL in the Diaspora format
679         $profiles[] = System::baseUrl()."/u/".$user["nickname"];
680
681         $profiles2 = [];
682
683         foreach ($profiles AS $profile) {
684                 // Check for invalid profile urls. 13 should be the shortest possible profile length:
685                 // http://a.bc/d
686                 // Additionally check for invalid urls that would return the normalised value "http:"
687                 if ((strlen($profile) >= 13) && (normalise_link($profile) != "http:")) {
688                         if (!in_array($profile, $profiles2))
689                                 $profiles2[] = $profile;
690
691                         $profile = normalise_link($profile);
692                         if (!in_array($profile, $profiles2))
693                                 $profiles2[] = $profile;
694
695                         $profile = str_replace("http://", "https://", $profile);
696                         if (!in_array($profile, $profiles2))
697                                 $profiles2[] = $profile;
698                 }
699         }
700
701         $profiles = $profiles2;
702
703         $ret = DBA::select('contact', ['id'], ['uid' => 0, 'nurl' => $profiles]);
704
705         $contacts = [];
706
707         while ($contact = DBA::fetch($ret)) {
708                 $contacts[] = $contact['id'];
709         }
710
711         DBA::close($ret);
712
713         // Only act if it is a "real" post
714         // We need the additional check for the "local_profile" because of mixed situations on connector networks
715         $fields = ['id', 'mention', 'tag', 'parent', 'title', 'body',
716                 'author-link', 'author-name', 'author-avatar', 'author-id',
717                 'guid', 'parent-uri', 'uri', 'contact-id', 'network'];
718         $condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
719         $item = Item::selectFirst($fields, $condition);
720         if (!DBA::isResult($item) || in_array($item['author-id'], $contacts)) {
721                 return;
722         }
723
724         // Generate the notification array
725         $params = [];
726         $params["uid"] = $uid;
727         $params["notify_flags"] = $user["notify-flags"];
728         $params["language"] = $user["language"];
729         $params["to_name"] = $user["username"];
730         $params["to_email"] = $user["email"];
731         $params["item"] = $item;
732         $params["parent"] = $item["parent"];
733         $params["link"] = System::baseUrl().'/display/'.urlencode($item["guid"]);
734         $params["otype"] = 'item';
735         $params["source_name"] = $item["author-name"];
736         $params["source_link"] = $item["author-link"];
737         $params["source_photo"] = $item["author-avatar"];
738
739         if ($item["parent-uri"] === $item["uri"]) {
740                 // Send a notification for every new post?
741                 $send_notification = DBA::exists('contact', ['id' => $item['contact-id'], 'notify_new_posts' => true]);
742
743                 if (!$send_notification) {
744                         $tags = q("SELECT `url` FROM `term` WHERE `otype` = %d AND `oid` = %d AND `type` = %d AND `uid` = %d",
745                                 intval(TERM_OBJ_POST), intval($itemid), intval(TERM_MENTION), intval($uid));
746
747                         if (DBA::isResult($tags)) {
748                                 foreach ($tags AS $tag) {
749                                         $condition = ['nurl' => normalise_link($tag["url"]), 'uid' => $uid, 'notify_new_posts' => true];
750                                         $r = DBA::exists('contact', $condition);
751                                         if ($r) {
752                                                 $send_notification = true;
753                                         }
754                                 }
755                         }
756                 }
757
758                 if ($send_notification) {
759                         $params["type"] = NOTIFY_SHARE;
760                         $params["verb"] = ACTIVITY_TAG;
761                 }
762         }
763
764         // Is the user mentioned in this post?
765         $tagged = false;
766
767         foreach ($profiles AS $profile) {
768                 if (strpos($item["tag"], "=".$profile."]") || strpos($item["body"], "=".$profile."]"))
769                         $tagged = true;
770         }
771
772         if ($item["mention"] || $tagged || ($defaulttype == NOTIFY_TAGSELF)) {
773                 $params["type"] = NOTIFY_TAGSELF;
774                 $params["verb"] = ACTIVITY_TAG;
775         }
776
777         // Is it a post that the user had started?
778         $fields = ['ignored', 'mention'];
779         $thread = Item::selectFirstThreadForUser($params['uid'], $fields, ['iid' => $item["parent"]]);
780
781         if ($thread['mention'] && !$thread['ignored'] && !isset($params["type"])) {
782                 $params["type"] = NOTIFY_COMMENT;
783                 $params["verb"] = ACTIVITY_POST;
784         }
785
786         // And now we check for participation of one of our contacts in the thread
787         $condition = ['parent' => $item["parent"], 'author-id' => $contacts];
788
789         if (!$thread['ignored'] && !isset($params["type"]) && Item::exists($condition)) {
790                 $params["type"] = NOTIFY_COMMENT;
791                 $params["verb"] = ACTIVITY_POST;
792         }
793
794         if (isset($params["type"])) {
795                 notification($params);
796         }
797 }
798
799 /**
800  * @brief Formats a notification message with the notification author
801  *
802  * Replace the name with {0} but ensure to make that only once. The {0} is used
803  * later and prints the name in bold.
804  *
805  * @param string $name
806  * @param string $message
807  * @return string Formatted message
808  */
809 function format_notification_message($name, $message) {
810         if ($name != '') {
811                 $pos = strpos($message, $name);
812         } else {
813                 $pos = false;
814         }
815
816         if ($pos !== false) {
817                 $message = substr_replace($message, '{0}', $pos, strlen($name));
818         }
819
820         return $message;
821 }