]> git.mxchange.org Git - friendica.git/blob - src/Core/NotificationsManager.php
Replace remaining functions in include/datetime by Temporal methods
[friendica.git] / src / Core / NotificationsManager.php
1 <?php
2 /**
3  * @file src/Core/NotificationsManager.php
4  * @brief Methods for read and write notifications from/to database
5  *  or for formatting notifications
6  */
7 namespace Friendica\Core;
8
9 use Friendica\BaseObject;
10 use Friendica\Core\L10n;
11 use Friendica\Core\PConfig;
12 use Friendica\Core\System;
13 use Friendica\Database\DBM;
14 use Friendica\Model\Contact;
15 use Friendica\Model\Profile;
16 use Friendica\Util\DateTimeFormat;
17 use Friendica\Util\Temporal;
18 use Friendica\Util\XML;
19
20 require_once 'include/dba.php';
21 require_once 'include/html2plain.php';
22 require_once 'include/datetime.php';
23 require_once 'include/bbcode.php';
24
25 /**
26  * @brief Methods for read and write notifications from/to database
27  *  or for formatting notifications
28  */
29 class NotificationsManager extends BaseObject
30 {
31         /**
32          * @brief set some extra note properties
33          *
34          * @param array $notes array of note arrays from db
35          * @return array Copy of input array with added properties
36          *
37          * Set some extra properties to note array from db:
38          *  - timestamp as int in default TZ
39          *  - date_rel : relative date string
40          *  - msg_html: message as html string
41          *  - msg_plain: message as plain text string
42          */
43         private function _set_extra($notes)
44         {
45                 $rets = [];
46                 foreach ($notes as $n) {
47                         $local_time = DateTimeFormat::local($n['date']);
48                         $n['timestamp'] = strtotime($local_time);
49                         $n['date_rel'] = Temporal::getRelativeDate($n['date']);
50                         $n['msg_html'] = bbcode($n['msg'], false, false, false, false);
51                         $n['msg_plain'] = explode("\n", trim(html2plain($n['msg_html'], 0)))[0];
52
53                         $rets[] = $n;
54                 }
55                 return $rets;
56         }
57
58         /**
59          * @brief Get all notifications for local_user()
60          *
61          * @param array  $filter optional Array "column name"=>value: filter query by columns values
62          * @param string $order  optional Space separated list of column to sort by.
63          *                       Prepend name with "+" to sort ASC, "-" to sort DESC. Default to "-date"
64          * @param string $limit  optional Query limits
65          *
66          * @return array of results or false on errors
67          */
68         public function getAll($filter = [], $order = "-date", $limit = "")
69         {
70                 $filter_str = [];
71                 $filter_sql = "";
72                 foreach ($filter as $column => $value) {
73                         $filter_str[] = sprintf("`%s` = '%s'", $column, dbesc($value));
74                 }
75                 if (count($filter_str) > 0) {
76                         $filter_sql = "AND " . implode(" AND ", $filter_str);
77                 }
78
79                 $aOrder = explode(" ", $order);
80                 $asOrder = [];
81                 foreach ($aOrder as $o) {
82                         $dir = "asc";
83                         if ($o[0] === "-") {
84                                 $dir = "desc";
85                                 $o = substr($o, 1);
86                         }
87                         if ($o[0] === "+") {
88                                 $dir = "asc";
89                                 $o = substr($o, 1);
90                         }
91                         $asOrder[] = "$o $dir";
92                 }
93                 $order_sql = implode(", ", $asOrder);
94
95                 if ($limit != "") {
96                         $limit = " LIMIT " . $limit;
97                 }
98                 $r = q(
99                         "SELECT * FROM `notify` WHERE `uid` = %d $filter_sql ORDER BY $order_sql $limit",
100                         intval(local_user())
101                 );
102
103                 if (DBM::is_result($r)) {
104                         return $this->_set_extra($r);
105                 }
106
107                 return false;
108         }
109
110         /**
111          * @brief Get one note for local_user() by $id value
112          *
113          * @param int $id identity
114          * @return array note values or null if not found
115          */
116         public function getByID($id)
117         {
118                 $r = q(
119                         "SELECT * FROM `notify` WHERE `id` = %d AND `uid` = %d LIMIT 1",
120                         intval($id),
121                         intval(local_user())
122                 );
123                 if (DBM::is_result($r)) {
124                         return $this->_set_extra($r)[0];
125                 }
126                 return null;
127         }
128
129         /**
130          * @brief set seen state of $note of local_user()
131          *
132          * @param array $note note array
133          * @param bool  $seen optional true or false, default true
134          * @return bool true on success, false on errors
135          */
136         public function setSeen($note, $seen = true)
137         {
138                 return q(
139                         "UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
140                         intval($seen),
141                         dbesc($note['link']),
142                         intval($note['parent']),
143                         dbesc($note['otype']),
144                         intval(local_user())
145                 );
146         }
147
148         /**
149          * @brief set seen state of all notifications of local_user()
150          *
151          * @param bool $seen optional true or false. default true
152          * @return bool true on success, false on error
153          */
154         public function setAllSeen($seen = true)
155         {
156                 return q(
157                         "UPDATE `notify` SET `seen` = %d WHERE `uid` = %d",
158                         intval($seen),
159                         intval(local_user())
160                 );
161         }
162
163         /**
164          * @brief List of pages for the Notifications TabBar
165          *
166          * @return array with with notifications TabBar data
167          */
168         public function getTabs()
169         {
170                 $tabs = [
171                         [
172                                 'label' => L10n::t('System'),
173                                 'url'   => 'notifications/system',
174                                 'sel'   => ((self::getApp()->argv[1] == 'system') ? 'active' : ''),
175                                 'id'    => 'system-tab',
176                                 'accesskey' => 'y',
177                         ],
178                         [
179                                 'label' => L10n::t('Network'),
180                                 'url'   => 'notifications/network',
181                                 'sel'   => ((self::getApp()->argv[1] == 'network') ? 'active' : ''),
182                                 'id'    => 'network-tab',
183                                 'accesskey' => 'w',
184                         ],
185                         [
186                                 'label' => L10n::t('Personal'),
187                                 'url'   => 'notifications/personal',
188                                 'sel'   => ((self::getApp()->argv[1] == 'personal') ? 'active' : ''),
189                                 'id'    => 'personal-tab',
190                                 'accesskey' => 'r',
191                         ],
192                         [
193                                 'label' => L10n::t('Home'),
194                                 'url'   => 'notifications/home',
195                                 'sel'   => ((self::getApp()->argv[1] == 'home') ? 'active' : ''),
196                                 'id'    => 'home-tab',
197                                 'accesskey' => 'h',
198                         ],
199                         [
200                                 'label' => L10n::t('Introductions'),
201                                 'url'   => 'notifications/intros',
202                                 'sel'   => ((self::getApp()->argv[1] == 'intros') ? 'active' : ''),
203                                 'id'    => 'intro-tab',
204                                 'accesskey' => 'i',
205                         ],
206                 ];
207
208                 return $tabs;
209         }
210
211         /**
212          * @brief Format the notification query in an usable array
213          *
214          * @param array  $notifs The array from the db query
215          * @param string $ident  The notifications identifier (e.g. network)
216          * @return array
217          *      string 'label' => The type of the notification
218          *      string 'link' => URL to the source
219          *      string 'image' => The avatar image
220          *      string 'url' => The profile url of the contact
221          *      string 'text' => The notification text
222          *      string 'when' => The date of the notification
223          *      string 'ago' => T relative date of the notification
224          *      bool 'seen' => Is the notification marked as "seen"
225          */
226         private function formatNotifs($notifs, $ident = "")
227         {
228                 $notif = [];
229                 $arr = [];
230
231                 if (DBM::is_result($notifs)) {
232                         foreach ($notifs as $it) {
233                                 // Because we use different db tables for the notification query
234                                 // we have sometimes $it['unseen'] and sometimes $it['seen].
235                                 // So we will have to transform $it['unseen']
236                                 if (array_key_exists('unseen', $it)) {
237                                         $it['seen'] = ($it['unseen'] > 0 ? false : true);
238                                 }
239
240                                 // Depending on the identifier of the notification we need to use different defaults
241                                 switch ($ident) {
242                                         case 'system':
243                                                 $default_item_label = 'notify';
244                                                 $default_item_link = System::baseUrl(true) . '/notify/view/' . $it['id'];
245                                                 $default_item_image = proxy_url($it['photo'], false, PROXY_SIZE_MICRO);
246                                                 $default_item_url = $it['url'];
247                                                 $default_item_text = strip_tags(bbcode($it['msg']));
248                                                 $default_item_when = DateTimeFormat::local($it['date'], 'r');
249                                                 $default_item_ago = Temporal::getRelativeDate($it['date']);
250                                                 break;
251
252                                         case 'home':
253                                                 $default_item_label = 'comment';
254                                                 $default_item_link = System::baseUrl(true) . '/display/' . $it['pguid'];
255                                                 $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
256                                                 $default_item_url = $it['author-link'];
257                                                 $default_item_text = L10n::t("%s commented on %s's post", $it['author-name'], $it['pname']);
258                                                 $default_item_when = DateTimeFormat::local($it['created'], 'r');
259                                                 $default_item_ago = Temporal::getRelativeDate($it['created']);
260                                                 break;
261
262                                         default:
263                                                 $default_item_label = (($it['id'] == $it['parent']) ? 'post' : 'comment');
264                                                 $default_item_link = System::baseUrl(true) . '/display/' . $it['pguid'];
265                                                 $default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
266                                                 $default_item_url = $it['author-link'];
267                                                 $default_item_text = (($it['id'] == $it['parent'])
268                                                                         ? L10n::t("%s created a new post", $it['author-name'])
269                                                                         : L10n::t("%s commented on %s's post", $it['author-name'], $it['pname']));
270                                                 $default_item_when = DateTimeFormat::local($it['created'], 'r');
271                                                 $default_item_ago = Temporal::getRelativeDate($it['created']);
272                                 }
273
274                                 // Transform the different types of notification in an usable array
275                                 switch ($it['verb']) {
276                                         case ACTIVITY_LIKE:
277                                                 $notif = [
278                                                         'label' => 'like',
279                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
280                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
281                                                         'url' => $it['author-link'],
282                                                         'text' => L10n::t("%s liked %s's post", $it['author-name'], $it['pname']),
283                                                         'when' => $default_item_when,
284                                                         'ago' => $default_item_ago,
285                                                         'seen' => $it['seen']
286                                                 ];
287                                                 break;
288
289                                         case ACTIVITY_DISLIKE:
290                                                 $notif = [
291                                                         'label' => 'dislike',
292                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
293                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
294                                                         'url' => $it['author-link'],
295                                                         'text' => L10n::t("%s disliked %s's post", $it['author-name'], $it['pname']),
296                                                         'when' => $default_item_when,
297                                                         'ago' => $default_item_ago,
298                                                         'seen' => $it['seen']
299                                                 ];
300                                                 break;
301
302                                         case ACTIVITY_ATTEND:
303                                                 $notif = [
304                                                         'label' => 'attend',
305                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
306                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
307                                                         'url' => $it['author-link'],
308                                                         'text' => L10n::t("%s is attending %s's event", $it['author-name'], $it['pname']),
309                                                         'when' => $default_item_when,
310                                                         'ago' => $default_item_ago,
311                                                         'seen' => $it['seen']
312                                                 ];
313                                                 break;
314
315                                         case ACTIVITY_ATTENDNO:
316                                                 $notif = [
317                                                         'label' => 'attendno',
318                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
319                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
320                                                         'url' => $it['author-link'],
321                                                         'text' => L10n::t("%s is not attending %s's event", $it['author-name'], $it['pname']),
322                                                         'when' => $default_item_when,
323                                                         'ago' => $default_item_ago,
324                                                         'seen' => $it['seen']
325                                                 ];
326                                                 break;
327
328                                         case ACTIVITY_ATTENDMAYBE:
329                                                 $notif = [
330                                                         'label' => 'attendmaybe',
331                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
332                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
333                                                         'url' => $it['author-link'],
334                                                         'text' => L10n::t("%s may attend %s's event", $it['author-name'], $it['pname']),
335                                                         'when' => $default_item_when,
336                                                         'ago' => $default_item_ago,
337                                                         'seen' => $it['seen']
338                                                 ];
339                                                 break;
340
341                                         case ACTIVITY_FRIEND:
342                                                 $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
343                                                 $obj = XML::parseString($xmlhead . $it['object']);
344                                                 $it['fname'] = $obj->title;
345
346                                                 $notif = [
347                                                         'label' => 'friend',
348                                                         'link' => System::baseUrl(true) . '/display/' . $it['pguid'],
349                                                         'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
350                                                         'url' => $it['author-link'],
351                                                         'text' => L10n::t("%s is now friends with %s", $it['author-name'], $it['fname']),
352                                                         'when' => $default_item_when,
353                                                         'ago' => $default_item_ago,
354                                                         'seen' => $it['seen']
355                                                 ];
356                                                 break;
357
358                                         default:
359                                                 $notif = [
360                                                         'label' => $default_item_label,
361                                                         'link' => $default_item_link,
362                                                         'image' => $default_item_image,
363                                                         'url' => $default_item_url,
364                                                         'text' => $default_item_text,
365                                                         'when' => $default_item_when,
366                                                         'ago' => $default_item_ago,
367                                                         'seen' => $it['seen']
368                                                 ];
369                                 }
370
371                                 $arr[] = $notif;
372                         }
373                 }
374
375                 return $arr;
376         }
377
378         /**
379          * @brief Total number of network notifications
380          * @param int|string $seen If 0 only include notifications into the query
381          *                             which aren't marked as "seen"
382          *
383          * @return int Number of network notifications
384          */
385         private function networkTotal($seen = 0)
386         {
387                 $sql_seen = "";
388
389                 if ($seen === 0) {
390                         $sql_seen = " AND `item`.`unseen` = 1 ";
391                 }
392
393                 $r = q(
394                         "SELECT COUNT(*) AS `total`
395                                 FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
396                                 WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
397                                  `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
398                                 $sql_seen",
399                         intval(local_user())
400                 );
401                 if (DBM::is_result($r)) {
402                         return $r[0]['total'];
403                 }
404
405                 return 0;
406         }
407
408         /**
409          * @brief Get network notifications
410          *
411          * @param int|string $seen  If 0 only include notifications into the query
412          *                              which aren't marked as "seen"
413          * @param int        $start Start the query at this point
414          * @param int        $limit Maximum number of query results
415          *
416          * @return array with
417          *      string 'ident' => Notification identifier
418          *      int 'total' => Total number of available network notifications
419          *      array 'notifications' => Network notifications
420          */
421         public function networkNotifs($seen = 0, $start = 0, $limit = 80)
422         {
423                 $ident = 'network';
424                 $total = $this->networkTotal($seen);
425                 $notifs = [];
426                 $sql_seen = "";
427
428                 if ($seen === 0) {
429                         $sql_seen = " AND `item`.`unseen` = 1 ";
430                 }
431
432                 $r = q(
433                         "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
434                                 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
435                                 `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
436                         FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
437                         WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
438                                  `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
439                                 $sql_seen
440                         ORDER BY `item`.`created` DESC LIMIT %d, %d ",
441                         intval(local_user()),
442                         intval($start),
443                         intval($limit)
444                 );
445                 if (DBM::is_result($r)) {
446                         $notifs = $this->formatNotifs($r, $ident);
447                 }
448
449                 $arr = [
450                         'notifications' => $notifs,
451                         'ident' => $ident,
452                         'total' => $total,
453                 ];
454
455                 return $arr;
456         }
457
458         /**
459          * @brief Total number of system notifications
460          * @param int|string $seen If 0 only include notifications into the query
461          *                             which aren't marked as "seen"
462          *
463          * @return int Number of system notifications
464          */
465         private function systemTotal($seen = 0)
466         {
467                 $sql_seen = "";
468
469                 if ($seen === 0) {
470                         $sql_seen = " AND `seen` = 0 ";
471                 }
472
473                 $r = q(
474                         "SELECT COUNT(*) AS `total` FROM `notify` WHERE `uid` = %d $sql_seen",
475                         intval(local_user())
476                 );
477                 if (DBM::is_result($r)) {
478                         return $r[0]['total'];
479                 }
480
481                 return 0;
482         }
483
484         /**
485          * @brief Get system notifications
486          *
487          * @param int|string $seen  If 0 only include notifications into the query
488          *                              which aren't marked as "seen"
489          * @param int        $start Start the query at this point
490          * @param int        $limit Maximum number of query results
491          *
492          * @return array with
493          *      string 'ident' => Notification identifier
494          *      int 'total' => Total number of available system notifications
495          *      array 'notifications' => System notifications
496          */
497         public function systemNotifs($seen = 0, $start = 0, $limit = 80)
498         {
499                 $ident = 'system';
500                 $total = $this->systemTotal($seen);
501                 $notifs = [];
502                 $sql_seen = "";
503
504                 if ($seen === 0) {
505                         $sql_seen = " AND `seen` = 0 ";
506                 }
507
508                 $r = q(
509                         "SELECT `id`, `url`, `photo`, `msg`, `date`, `seen` FROM `notify`
510                                 WHERE `uid` = %d $sql_seen ORDER BY `date` DESC LIMIT %d, %d ",
511                         intval(local_user()),
512                         intval($start),
513                         intval($limit)
514                 );
515                 if (DBM::is_result($r)) {
516                         $notifs = $this->formatNotifs($r, $ident);
517                 }
518
519                 $arr = [
520                         'notifications' => $notifs,
521                         'ident' => $ident,
522                         'total' => $total,
523                 ];
524
525                 return $arr;
526         }
527
528         /**
529          * @brief Additional SQL query string for the personal notifications
530          *
531          * @return string The additional SQL query
532          */
533         private function personalSqlExtra()
534         {
535                 $myurl = System::baseUrl(true) . '/profile/' . self::getApp()->user['nickname'];
536                 $myurl = substr($myurl, strpos($myurl, '://') + 3);
537                 $myurl = str_replace(['www.', '.'], ['', '\\.'], $myurl);
538                 $diasp_url = str_replace('/profile/', '/u/', $myurl);
539                 $sql_extra = sprintf(
540                         " AND ( `item`.`author-link` regexp '%s' OR `item`.`tag` regexp '%s' OR `item`.`tag` regexp '%s' ) ",
541                         dbesc($myurl . '$'),
542                         dbesc($myurl . '\\]'),
543                         dbesc($diasp_url . '\\]')
544                 );
545
546                 return $sql_extra;
547         }
548
549         /**
550          * @brief Total number of personal notifications
551          * @param int|string $seen If 0 only include notifications into the query
552          *                             which aren't marked as "seen"
553          *
554          * @return int Number of personal notifications
555          */
556         private function personalTotal($seen = 0)
557         {
558                 $sql_seen = "";
559                 $sql_extra = $this->personalSqlExtra();
560
561                 if ($seen === 0) {
562                         $sql_seen = " AND `item`.`unseen` = 1 ";
563                 }
564
565                 $r = q(
566                         "SELECT COUNT(*) AS `total`
567                                 FROM `item` INNER JOIN `item` AS `pitem` ON  `pitem`.`id`=`item`.`parent`
568                                 WHERE `item`.`visible` = 1
569                                 $sql_extra
570                                 $sql_seen
571                                 AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 ",
572                         intval(local_user())
573                 );
574                 if (DBM::is_result($r)) {
575                         return $r[0]['total'];
576                 }
577
578                 return 0;
579         }
580
581         /**
582          * @brief Get personal notifications
583          *
584          * @param int|string $seen  If 0 only include notifications into the query
585          *                              which aren't marked as "seen"
586          * @param int        $start Start the query at this point
587          * @param int        $limit Maximum number of query results
588          *
589          * @return array with
590          *      string 'ident' => Notification identifier
591          *      int 'total' => Total number of available personal notifications
592          *      array 'notifications' => Personal notifications
593          */
594         public function personalNotifs($seen = 0, $start = 0, $limit = 80)
595         {
596                 $ident = 'personal';
597                 $total = $this->personalTotal($seen);
598                 $sql_extra = $this->personalSqlExtra();
599                 $notifs = [];
600                 $sql_seen = "";
601
602                 if ($seen === 0) {
603                         $sql_seen = " AND `item`.`unseen` = 1 ";
604                 }
605
606                 $r = q(
607                         "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
608                                 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
609                                 `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
610                         FROM `item` INNER JOIN `item` AS `pitem` ON  `pitem`.`id`=`item`.`parent`
611                         WHERE `item`.`visible` = 1
612                                 $sql_extra
613                                 $sql_seen
614                                 AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
615                         ORDER BY `item`.`created` DESC LIMIT %d, %d ",
616                         intval(local_user()),
617                         intval($start),
618                         intval($limit)
619                 );
620                 if (DBM::is_result($r)) {
621                         $notifs = $this->formatNotifs($r, $ident);
622                 }
623
624                 $arr = [
625                         'notifications' => $notifs,
626                         'ident' => $ident,
627                         'total' => $total,
628                 ];
629
630                 return $arr;
631         }
632
633         /**
634          * @brief Total number of home notifications
635          * @param int|string $seen If 0 only include notifications into the query
636          *                             which aren't marked as "seen"
637          *
638          * @return int Number of home notifications
639          */
640         private function homeTotal($seen = 0)
641         {
642                 $sql_seen = "";
643
644                 if ($seen === 0) {
645                         $sql_seen = " AND `item`.`unseen` = 1 ";
646                 }
647
648                 $r = q(
649                         "SELECT COUNT(*) AS `total` FROM `item`
650                                 WHERE `item`.`visible` = 1 AND
651                                  `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
652                                 $sql_seen",
653                         intval(local_user())
654                 );
655                 if (DBM::is_result($r)) {
656                         return $r[0]['total'];
657                 }
658
659                 return 0;
660         }
661
662         /**
663          * @brief Get home notifications
664          *
665          * @param int|string $seen  If 0 only include notifications into the query
666          *                              which aren't marked as "seen"
667          * @param int        $start Start the query at this point
668          * @param int        $limit Maximum number of query results
669          *
670          * @return array with
671          *      string 'ident' => Notification identifier
672          *      int 'total' => Total number of available home notifications
673          *      array 'notifications' => Home notifications
674          */
675         public function homeNotifs($seen = 0, $start = 0, $limit = 80)
676         {
677                 $ident = 'home';
678                 $total = $this->homeTotal($seen);
679                 $notifs = [];
680                 $sql_seen = "";
681
682                 if ($seen === 0) {
683                         $sql_seen = " AND `item`.`unseen` = 1 ";
684                 }
685
686                 $r = q(
687                         "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
688                                 `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
689                                 `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
690                         FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
691                         WHERE `item`.`visible` = 1 AND
692                                  `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
693                                 $sql_seen
694                         ORDER BY `item`.`created` DESC LIMIT %d, %d ",
695                         intval(local_user()),
696                         intval($start),
697                         intval($limit)
698                 );
699                 if (DBM::is_result($r)) {
700                         $notifs = $this->formatNotifs($r, $ident);
701                 }
702
703                 $arr = [
704                         'notifications' => $notifs,
705                         'ident' => $ident,
706                         'total' => $total,
707                 ];
708
709                 return $arr;
710         }
711
712         /**
713          * @brief Total number of introductions
714          * @param bool $all If false only include introductions into the query
715          *                      which aren't marked as ignored
716          *
717          * @return int Number of introductions
718          */
719         private function introTotal($all = false)
720         {
721                 $sql_extra = "";
722
723                 if (!$all) {
724                         $sql_extra = " AND `ignore` = 0 ";
725                 }
726
727                 $r = q(
728                         "SELECT COUNT(*) AS `total` FROM `intro`
729                         WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0 ",
730                         intval($_SESSION['uid'])
731                 );
732
733                 if (DBM::is_result($r)) {
734                         return $r[0]['total'];
735                 }
736
737                 return 0;
738         }
739
740         /**
741          * @brief Get introductions
742          *
743          * @param bool $all   If false only include introductions into the query
744          *                        which aren't marked as ignored
745          * @param int  $start Start the query at this point
746          * @param int  $limit Maximum number of query results
747          *
748          * @return array with
749          *      string 'ident' => Notification identifier
750          *      int 'total' => Total number of available introductions
751          *      array 'notifications' => Introductions
752          */
753         public function introNotifs($all = false, $start = 0, $limit = 80)
754         {
755                 $ident = 'introductions';
756                 $total = $this->introTotal($all);
757                 $notifs = [];
758                 $sql_extra = "";
759
760                 if (!$all) {
761                         $sql_extra = " AND `ignore` = 0 ";
762                 }
763
764                 /// @todo Fetch contact details by "Contact::getDetailsByUrl" instead of queries to contact, fcontact and gcontact
765                 $r = q(
766                         "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*,
767                                 `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`,
768                                 `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`,
769                                 `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`,
770                                 `gcontact`.`keywords` AS `gkeywords`, `gcontact`.`gender` AS `ggender`,
771                                 `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr`
772                         FROM `intro`
773                                 LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id`
774                                 LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl`
775                                 LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
776                         WHERE `intro`.`uid` = %d $sql_extra AND `intro`.`blocked` = 0
777                         LIMIT %d, %d",
778                         intval($_SESSION['uid']),
779                         intval($start),
780                         intval($limit)
781                 );
782                 if (DBM::is_result($r)) {
783                         $notifs = $this->formatIntros($r);
784                 }
785
786                 $arr = [
787                         'ident' => $ident,
788                         'total' => $total,
789                         'notifications' => $notifs,
790                 ];
791
792                 return $arr;
793         }
794
795         /**
796          * @brief Format the notification query in an usable array
797          *
798          * @param array $intros The array from the db query
799          * @return array with the introductions
800          */
801         private function formatIntros($intros)
802         {
803                 $knowyou = '';
804
805                 foreach ($intros as $it) {
806                         // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests.
807                         // We have to distinguish between these two because they use different data.
808                         // Contact suggestions
809                         if ($it['fid']) {
810                                 $return_addr = bin2hex(self::getApp()->user['nickname'] . '@' . self::getApp()->get_hostname() . ((self::getApp()->path) ? '/' . self::getApp()->path : ''));
811
812                                 $intro = [
813                                         'label' => 'friend_suggestion',
814                                         'notify_type' => L10n::t('Friend Suggestion'),
815                                         'intro_id' => $it['intro_id'],
816                                         'madeby' => $it['name'],
817                                         'contact_id' => $it['contact-id'],
818                                         'photo' => ((x($it, 'fphoto')) ? proxy_url($it['fphoto'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
819                                         'name' => $it['fname'],
820                                         'url' => Profile::zrl($it['furl']),
821                                         'hidden' => $it['hidden'] == 1,
822                                         'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0),
823                                         'knowyou' => $knowyou,
824                                         'note' => $it['note'],
825                                         'request' => $it['frequest'] . '?addr=' . $return_addr,
826                                 ];
827
828                                 // Normal connection requests
829                         } else {
830                                 $it = $this->getMissingIntroData($it);
831
832                                 // Don't show these data until you are connected. Diaspora is doing the same.
833                                 if ($it['gnetwork'] === NETWORK_DIASPORA) {
834                                         $it['glocation'] = "";
835                                         $it['gabout'] = "";
836                                         $it['ggender'] = "";
837                                 }
838                                 $intro = [
839                                         'label' => (($it['network'] !== NETWORK_OSTATUS) ? 'friend_request' : 'follower'),
840                                         'notify_type' => (($it['network'] !== NETWORK_OSTATUS) ? L10n::t('Friend/Connect Request') : L10n::t('New Follower')),
841                                         'dfrn_id' => $it['issued-id'],
842                                         'uid' => $_SESSION['uid'],
843                                         'intro_id' => $it['intro_id'],
844                                         'contact_id' => $it['contact-id'],
845                                         'photo' => ((x($it, 'photo')) ? proxy_url($it['photo'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
846                                         'name' => $it['name'],
847                                         'location' => bbcode($it['glocation'], false, false),
848                                         'about' => bbcode($it['gabout'], false, false),
849                                         'keywords' => $it['gkeywords'],
850                                         'gender' => $it['ggender'],
851                                         'hidden' => $it['hidden'] == 1,
852                                         'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0),
853                                         'url' => $it['url'],
854                                         'zrl' => Profile::zrl($it['url']),
855                                         'addr' => $it['gaddr'],
856                                         'network' => $it['gnetwork'],
857                                         'knowyou' => $it['knowyou'],
858                                         'note' => $it['note'],
859                                 ];
860                         }
861
862                         $arr[] = $intro;
863                 }
864
865                 return $arr;
866         }
867
868         /**
869          * @brief Check for missing contact data and try to fetch the data from
870          *     from other sources
871          *
872          * @param array $arr The input array with the intro data
873          *
874          * @return array The array with the intro data
875          */
876         private function getMissingIntroData($arr)
877         {
878                 // If the network and the addr isn't available from the gcontact
879                 // table entry, take the one of the contact table entry
880                 if ($arr['gnetwork'] == "") {
881                         $arr['gnetwork'] = $arr['network'];
882                 }
883                 if ($arr['gaddr'] == "") {
884                         $arr['gaddr'] = $arr['addr'];
885                 }
886
887                 // If the network and addr is still not available
888                 // get the missing data data from other sources
889                 if ($arr['gnetwork'] == "" || $arr['gaddr'] == "") {
890                         $ret = Contact::getDetailsByURL($arr['url']);
891
892                         if ($arr['gnetwork'] == "" && $ret['network'] != "") {
893                                 $arr['gnetwork'] = $ret['network'];
894                         }
895                         if ($arr['gaddr'] == "" && $ret['addr'] != "") {
896                                 $arr['gaddr'] = $ret['addr'];
897                         }
898                 }
899
900                 return $arr;
901         }
902 }