]> git.mxchange.org Git - friendica.git/blob - mod/ping.php
Force avatar update for Contact Advanced page
[friendica.git] / mod / ping.php
1 <?php
2 /**
3  * @file include/ping.php
4  */
5
6 use Friendica\App;
7 use Friendica\Content\ForumManager;
8 use Friendica\Content\Text\BBCode;
9 use Friendica\Core\Cache\Duration;
10 use Friendica\Core\Hook;
11 use Friendica\Database\DBA;
12 use Friendica\DI;
13 use Friendica\Model\Contact;
14 use Friendica\Model\Group;
15 use Friendica\Model\Item;
16 use Friendica\Util\DateTimeFormat;
17 use Friendica\Util\Temporal;
18 use Friendica\Util\Proxy as ProxyUtils;
19 use Friendica\Util\XML;
20
21 /**
22  * Outputs the counts and the lists of various notifications
23  *
24  * The output format can be controlled via the GET parameter 'format'. It can be
25  * - xml (deprecated legacy default)
26  * - json (outputs JSONP with the 'callback' GET parameter)
27  *
28  * Expected JSON structure:
29  * {
30  *        "result": {
31  *            "intro": 0,
32  *            "mail": 0,
33  *            "net": 0,
34  *            "home": 0,
35  *            "register": 0,
36  *            "all-events": 0,
37  *            "all-events-today": 0,
38  *            "events": 0,
39  *            "events-today": 0,
40  *            "birthdays": 0,
41  *            "birthdays-today": 0,
42  *            "groups": [ ],
43  *            "forums": [ ],
44  *            "notification": 0,
45  *            "notifications": [ ],
46  *            "sysmsgs": {
47  *                "notice": [ ],
48  *                "info": [ ]
49  *            }
50  *        }
51  *    }
52  *
53  * @param App $a The Friendica App instance
54  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
55  */
56 function ping_init(App $a)
57 {
58         $format = 'xml';
59
60         if (isset($_GET['format']) && $_GET['format'] == 'json') {
61                 $format = 'json';
62         }
63
64         $regs          = [];
65         $notifications = [];
66
67         $intro_count    = 0;
68         $mail_count     = 0;
69         $home_count     = 0;
70         $network_count  = 0;
71         $register_count = 0;
72         $sysnotify_count = 0;
73         $groups_unseen  = [];
74         $forums_unseen  = [];
75
76         $all_events       = 0;
77         $all_events_today = 0;
78         $events           = 0;
79         $events_today     = 0;
80         $birthdays        = 0;
81         $birthdays_today  = 0;
82
83         $data = [];
84         $data['intro']    = $intro_count;
85         $data['mail']     = $mail_count;
86         $data['net']      = $network_count;
87         $data['home']     = $home_count;
88         $data['register'] = $register_count;
89
90         $data['all-events']       = $all_events;
91         $data['all-events-today'] = $all_events_today;
92         $data['events']           = $events;
93         $data['events-today']     = $events_today;
94         $data['birthdays']        = $birthdays;
95         $data['birthdays-today']  = $birthdays_today;
96
97         if (local_user()) {
98                 // Different login session than the page that is calling us.
99                 if (!empty($_GET['uid']) && intval($_GET['uid']) != local_user()) {
100                         $data = ['result' => ['invalid' => 1]];
101
102                         if ($format == 'json') {
103                                 if (isset($_GET['callback'])) {
104                                         // JSONP support
105                                         header("Content-type: application/javascript");
106                                         echo $_GET['callback'] . '(' . json_encode($data) . ')';
107                                 } else {
108                                         header("Content-type: application/json");
109                                         echo json_encode($data);
110                                 }
111                         } else {
112                                 header("Content-type: text/xml");
113                                 echo XML::fromArray($data, $xml);
114                         }
115                         exit();
116                 }
117
118                 $notifs = ping_get_notifications(local_user());
119
120                 $condition = ["`unseen` AND `uid` = ? AND `contact-id` != ?", local_user(), local_user()];
121                 $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
122                         'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'wall'];
123                 $params = ['order' => ['received' => true]];
124                 $items = Item::selectForUser(local_user(), $fields, $condition, $params);
125
126                 if (DBA::isResult($items)) {
127                         $items_unseen = Item::inArray($items);
128                         $arr = ['items' => $items_unseen];
129                         Hook::callAll('network_ping', $arr);
130
131                         foreach ($items_unseen as $item) {
132                                 if ($item['wall']) {
133                                         $home_count++;
134                                 } else {
135                                         $network_count++;
136                                 }
137                         }
138                 }
139
140                 if ($network_count) {
141                         // Find out how unseen network posts are spread across groups
142                         $group_counts = Group::countUnseen();
143                         if (DBA::isResult($group_counts)) {
144                                 foreach ($group_counts as $group_count) {
145                                         if ($group_count['count'] > 0) {
146                                                 $groups_unseen[] = $group_count;
147                                         }
148                                 }
149                         }
150
151                         $forum_counts = ForumManager::countUnseenItems();
152                         if (DBA::isResult($forum_counts)) {
153                                 foreach ($forum_counts as $forum_count) {
154                                         if ($forum_count['count'] > 0) {
155                                                 $forums_unseen[] = $forum_count;
156                                         }
157                                 }
158                         }
159                 }
160
161                 $intros1 = q(
162                         "SELECT  `intro`.`id`, `intro`.`datetime`,
163                         `fcontact`.`name`, `fcontact`.`url`, `fcontact`.`photo`
164                         FROM `intro` LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
165                         WHERE `intro`.`uid` = %d  AND `intro`.`blocked` = 0 AND `intro`.`ignore` = 0 AND `intro`.`fid` != 0",
166                         intval(local_user())
167                 );
168                 $intros2 = q(
169                         "SELECT `intro`.`id`, `intro`.`datetime`,
170                         `contact`.`name`, `contact`.`url`, `contact`.`photo`
171                         FROM `intro` LEFT JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
172                         WHERE `intro`.`uid` = %d  AND `intro`.`blocked` = 0 AND `intro`.`ignore` = 0 AND `intro`.`contact-id` != 0",
173                         intval(local_user())
174                 );
175
176                 $intro_count = count($intros1) + count($intros2);
177                 $intros = $intros1 + $intros2;
178
179                 $myurl = DI::baseUrl() . '/profile/' . $a->user['nickname'];
180                 $mails = q(
181                         "SELECT `id`, `from-name`, `from-url`, `from-photo`, `created` FROM `mail`
182                         WHERE `uid` = %d AND `seen` = 0 AND `from-url` != '%s' ",
183                         intval(local_user()),
184                         DBA::escape($myurl)
185                 );
186                 $mail_count = count($mails);
187
188                 if (intval(DI::config()->get('config', 'register_policy')) === \Friendica\Module\Register::APPROVE && is_site_admin()) {
189                         $regs = Friendica\Model\Register::getPending();
190
191                         if (DBA::isResult($regs)) {
192                                 $register_count = count($regs);
193                         }
194                 }
195
196                 $cachekey = "ping_init:".local_user();
197                 $ev = DI::cache()->get($cachekey);
198                 if (is_null($ev)) {
199                         $ev = q(
200                                 "SELECT type, start, adjust FROM `event`
201                                 WHERE `event`.`uid` = %d AND `start` < '%s' AND `finish` > '%s' and `ignore` = 0
202                                 ORDER BY `start` ASC ",
203                                 intval(local_user()),
204                                 DBA::escape(DateTimeFormat::utc('now + 7 days')),
205                                 DBA::escape(DateTimeFormat::utcNow())
206                         );
207                         if (DBA::isResult($ev)) {
208                                 DI::cache()->set($cachekey, $ev, Duration::HOUR);
209                         }
210                 }
211
212                 if (DBA::isResult($ev)) {
213                         $all_events = count($ev);
214
215                         if ($all_events) {
216                                 $str_now = DateTimeFormat::timezoneNow($a->timezone, 'Y-m-d');
217                                 foreach ($ev as $x) {
218                                         $bd = false;
219                                         if ($x['type'] === 'birthday') {
220                                                 $birthdays ++;
221                                                 $bd = true;
222                                         } else {
223                                                 $events ++;
224                                         }
225                                         if (DateTimeFormat::convert($x['start'], ((intval($x['adjust'])) ? $a->timezone : 'UTC'), 'UTC', 'Y-m-d') === $str_now) {
226                                                 $all_events_today ++;
227                                                 if ($bd) {
228                                                         $birthdays_today ++;
229                                                 } else {
230                                                         $events_today ++;
231                                                 }
232                                         }
233                                 }
234                         }
235                 }
236
237                 $data['intro']    = $intro_count;
238                 $data['mail']     = $mail_count;
239                 $data['net']      = $network_count;
240                 $data['home']     = $home_count;
241                 $data['register'] = $register_count;
242
243                 $data['all-events']       = $all_events;
244                 $data['all-events-today'] = $all_events_today;
245                 $data['events']           = $events;
246                 $data['events-today']     = $events_today;
247                 $data['birthdays']        = $birthdays;
248                 $data['birthdays-today']  = $birthdays_today;
249
250                 if (DBA::isResult($notifs)) {
251                         foreach ($notifs as $notif) {
252                                 if ($notif['seen'] == 0) {
253                                         $sysnotify_count ++;
254                                 }
255                         }
256                 }
257
258                 // merge all notification types in one array
259                 if (DBA::isResult($intros)) {
260                         foreach ($intros as $intro) {
261                                 $notif = [
262                                         'id'      => 0,
263                                         'href'    => DI::baseUrl() . '/notifications/intros/' . $intro['id'],
264                                         'name'    => $intro['name'],
265                                         'url'     => $intro['url'],
266                                         'photo'   => $intro['photo'],
267                                         'date'    => $intro['datetime'],
268                                         'seen'    => false,
269                                         'message' => DI::l10n()->t('{0} wants to be your friend'),
270                                 ];
271                                 $notifs[] = $notif;
272                         }
273                 }
274
275                 if (DBA::isResult($regs)) {
276                         foreach ($regs as $reg) {
277                                 $notif = [
278                                         'id'      => 0,
279                                         'href'    => DI::baseUrl() . '/admin/users/',
280                                         'name'    => $reg['name'],
281                                         'url'     => $reg['url'],
282                                         'photo'   => $reg['micro'],
283                                         'date'    => $reg['created'],
284                                         'seen'    => false,
285                                         'message' => DI::l10n()->t('{0} requested registration'),
286                                 ];
287                                 $notifs[] = $notif;
288                         }
289                 }
290
291                 // sort notifications by $[]['date']
292                 $sort_function = function ($a, $b) {
293                         $adate = strtotime($a['date']);
294                         $bdate = strtotime($b['date']);
295
296                         // Unseen messages are kept at the top
297                         // The value 31536000 means one year. This should be enough :-)
298                         if (!$a['seen']) {
299                                 $adate += 31536000;
300                         }
301                         if (!$b['seen']) {
302                                 $bdate += 31536000;
303                         }
304
305                         if ($adate == $bdate) {
306                                 return 0;
307                         }
308                         return ($adate < $bdate) ? 1 : -1;
309                 };
310                 usort($notifs, $sort_function);
311
312                 if (DBA::isResult($notifs)) {
313                         foreach ($notifs as $notif) {
314                                 $contact = Contact::getDetailsByURL($notif['url']);
315                                 if (isset($contact['micro'])) {
316                                         $notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO);
317                                 } else {
318                                         $notif['photo'] = ProxyUtils::proxifyUrl($notif['photo'], false, ProxyUtils::SIZE_MICRO);
319                                 }
320
321                                 $local_time = DateTimeFormat::local($notif['date']);
322
323                                 $notifications[] = [
324                                         'id'        => $notif['id'],
325                                         'href'      => $notif['href'],
326                                         'name'      => $notif['name'],
327                                         'url'       => $notif['url'],
328                                         'photo'     => $notif['photo'],
329                                         'date'      => Temporal::getRelativeDate($notif['date']),
330                                         'message'   => $notif['message'],
331                                         'seen'      => $notif['seen'],
332                                         'timestamp' => strtotime($local_time)
333                                 ];
334                         }
335                 }
336         }
337
338         $sysmsgs = [];
339         $sysmsgs_info = [];
340
341         if (!empty($_SESSION['sysmsg'])) {
342                 $sysmsgs = $_SESSION['sysmsg'];
343                 unset($_SESSION['sysmsg']);
344         }
345
346         if (!empty($_SESSION['sysmsg_info'])) {
347                 $sysmsgs_info = $_SESSION['sysmsg_info'];
348                 unset($_SESSION['sysmsg_info']);
349         }
350
351         if ($format == 'json') {
352                 $data['groups'] = $groups_unseen;
353                 $data['forums'] = $forums_unseen;
354                 $data['notification'] = $sysnotify_count + $intro_count + $register_count;
355                 $data['notifications'] = $notifications;
356                 $data['sysmsgs'] = [
357                         'notice' => $sysmsgs,
358                         'info' => $sysmsgs_info
359                 ];
360
361                 $json_payload = json_encode(["result" => $data]);
362
363                 if (isset($_GET['callback'])) {
364                         // JSONP support
365                         header("Content-type: application/javascript");
366                         echo $_GET['callback'] . '(' . $json_payload . ')';
367                 } else {
368                         header("Content-type: application/json");
369                         echo $json_payload;
370                 }
371         } else {
372                 // Legacy slower XML format output
373                 $data = ping_format_xml_data($data, $sysnotify_count, $notifications, $sysmsgs, $sysmsgs_info, $groups_unseen, $forums_unseen);
374
375                 header("Content-type: text/xml");
376                 echo XML::fromArray(["result" => $data], $xml);
377         }
378
379         exit();
380 }
381
382 /**
383  * Retrieves the notifications array for the given user ID
384  *
385  * @param int $uid User id
386  * @return array Associative array of notifications
387  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
388  */
389 function ping_get_notifications($uid)
390 {
391         $result  = [];
392         $offset  = 0;
393         $seen    = false;
394         $seensql = "NOT";
395         $order   = "DESC";
396         $quit    = false;
397
398         do {
399                 $r = q(
400                         "SELECT `notify`.*, `item`.`visible`, `item`.`deleted`
401                         FROM `notify` LEFT JOIN `item` ON `item`.`id` = `notify`.`iid`
402                         WHERE `notify`.`uid` = %d AND `notify`.`msg` != ''
403                         AND NOT (`notify`.`type` IN (%d, %d))
404                         AND $seensql `notify`.`seen` ORDER BY `notify`.`date` $order LIMIT %d, 50",
405                         intval($uid),
406                         intval(NOTIFY_INTRO),
407                         intval(NOTIFY_MAIL),
408                         intval($offset)
409                 );
410
411                 if (!$r && !$seen) {
412                         $seen = true;
413                         $seensql = "";
414                         $order = "DESC";
415                         $offset = 0;
416                 } elseif (!$r) {
417                         $quit = true;
418                 } else {
419                         $offset += 50;
420                 }
421
422                 foreach ($r as $notification) {
423                         if (is_null($notification["visible"])) {
424                                 $notification["visible"] = true;
425                         }
426
427                         if (is_null($notification["deleted"])) {
428                                 $notification["deleted"] = 0;
429                         }
430
431                         if ($notification["msg_cache"]) {
432                                 $notification["name"] = $notification["name_cache"];
433                                 $notification["message"] = $notification["msg_cache"];
434                         } else {
435                                 $notification["name"] = strip_tags(BBCode::convert($notification["name"]));
436                                 $notification["message"] = Friendica\Model\Notify::formatMessage($notification["name"], strip_tags(BBCode::convert($notification["msg"])));
437
438                                 q(
439                                         "UPDATE `notify` SET `name_cache` = '%s', `msg_cache` = '%s' WHERE `id` = %d",
440                                         DBA::escape($notification["name"]),
441                                         DBA::escape($notification["message"]),
442                                         intval($notification["id"])
443                                 );
444                         }
445
446                         $notification["href"] = DI::baseUrl() . "/notification/" . $notification["id"];
447
448                         if ($notification["visible"]
449                                 && !$notification["deleted"]
450                                 && empty($result[$notification["parent"]])
451                         ) {
452                                 // Should we condense the notifications or show them all?
453                                 if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
454                                         $result[$notification["id"]] = $notification;
455                                 } else {
456                                         $result[$notification["parent"]] = $notification;
457                                 }
458                         }
459                 }
460         } while ((count($result) < 50) && !$quit);
461
462         return($result);
463 }
464
465 /**
466  * Backward-compatible XML formatting for ping.php output
467  * @deprecated
468  *
469  * @param array $data            The initial ping data array
470  * @param int   $sysnotify_count Number of unseen system notifications
471  * @param array $notifs          Complete list of notification
472  * @param array $sysmsgs         List of system notice messages
473  * @param array $sysmsgs_info    List of system info messages
474  * @param array $groups_unseen   List of unseen group items
475  * @param array $forums_unseen   List of unseen forum items
476  *
477  * @return array XML-transform ready data array
478  */
479 function ping_format_xml_data($data, $sysnotify_count, $notifs, $sysmsgs, $sysmsgs_info, $groups_unseen, $forums_unseen)
480 {
481         $notifications = [];
482         foreach ($notifs as $key => $notif) {
483                 $notifications[$key . ':note'] = $notif['message'];
484
485                 $notifications[$key . ':@attributes'] = [
486                         'id'        => $notif['id'],
487                         'href'      => $notif['href'],
488                         'name'      => $notif['name'],
489                         'url'       => $notif['url'],
490                         'photo'     => $notif['photo'],
491                         'date'      => $notif['date'],
492                         'seen'      => $notif['seen'],
493                         'timestamp' => $notif['timestamp']
494                 ];
495         }
496
497         $sysmsg = [];
498         foreach ($sysmsgs as $key => $m) {
499                 $sysmsg[$key . ':notice'] = $m;
500         }
501         foreach ($sysmsgs_info as $key => $m) {
502                 $sysmsg[$key . ':info'] = $m;
503         }
504
505         $data['notif'] = $notifications;
506         $data['@attributes'] = ['count' => $sysnotify_count + $data['intro'] + $data['mail'] + $data['register']];
507         $data['sysmsgs'] = $sysmsg;
508
509         if ($data['register'] == 0) {
510                 unset($data['register']);
511         }
512
513         $groups = [];
514         if (count($groups_unseen)) {
515                 foreach ($groups_unseen as $key => $item) {
516                         $groups[$key . ':group'] = $item['count'];
517                         $groups[$key . ':@attributes'] = ['id' => $item['id']];
518                 }
519                 $data['groups'] = $groups;
520         }
521
522         $forums = [];
523         if (count($forums_unseen)) {
524                 foreach ($forums_unseen as $key => $item) {
525                         $forums[$key . ':forum'] = $item['count'];
526                         $forums[$key . ':@attributes'] = ['id' => $item['id']];
527                 }
528                 $data['forums'] = $forums;
529         }
530
531         return $data;
532 }