]> git.mxchange.org Git - friendica.git/blob - src/Module/Notifications/Ping.php
6faea810e23649a046740cdc16f146aba0ec26a3
[friendica.git] / src / Module / Notifications / Ping.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Module\Notifications;
23
24 use Friendica\BaseModule;
25 use Friendica\Content\ForumManager;
26 use Friendica\Content\Text\BBCode;
27 use Friendica\Core\Cache\Enum\Duration;
28 use Friendica\Core\Hook;
29 use Friendica\Core\Renderer;
30 use Friendica\Database\DBA;
31 use Friendica\DI;
32 use Friendica\Model\Contact;
33 use Friendica\Model\Group;
34 use Friendica\Model\Notification;
35 use Friendica\Model\Post;
36 use Friendica\Model\Verb;
37 use Friendica\Module\Register;
38 use Friendica\Navigation\Notifications\Entity;
39 use Friendica\Network\HTTPException;
40 use Friendica\Protocol\Activity;
41 use Friendica\Util\DateTimeFormat;
42 use Friendica\Util\Proxy;
43 use Friendica\Util\Temporal;
44
45 class Ping extends BaseModule
46 {
47         protected function rawContent(array $request = [])
48         {
49                 $regs          = [];
50                 $notifications = [];
51
52                 $intro_count    = 0;
53                 $mail_count     = 0;
54                 $home_count     = 0;
55                 $network_count  = 0;
56                 $register_count = 0;
57                 $sysnotify_count = 0;
58                 $groups_unseen  = [];
59                 $forums_unseen  = [];
60
61                 $all_events       = 0;
62                 $all_events_today = 0;
63                 $events           = 0;
64                 $events_today     = 0;
65                 $birthdays        = 0;
66                 $birthdays_today  = 0;
67
68
69                 if (local_user()) {
70                         $notifications = $this->getNotificationList(local_user());
71
72                         $condition = [
73                                 "`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)",
74                                 local_user(), Verb::getID(Activity::FOLLOW)
75                         ];
76                         $items = Post::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition, ['limit' => 1000]);
77                         if (DBA::isResult($items)) {
78                                 $items_unseen = Post::toArray($items, false);
79                                 $arr = ['items' => $items_unseen];
80                                 Hook::callAll('network_ping', $arr);
81
82                                 foreach ($items_unseen as $item) {
83                                         if ($item['wall']) {
84                                                 $home_count++;
85                                         } else {
86                                                 $network_count++;
87                                         }
88                                 }
89                         }
90                         DBA::close($items);
91
92                         if ($network_count) {
93                                 // Find out how unseen network posts are spread across groups
94                                 $group_counts = Group::countUnseen();
95                                 if (DBA::isResult($group_counts)) {
96                                         foreach ($group_counts as $group_count) {
97                                                 if ($group_count['count'] > 0) {
98                                                         $groups_unseen[] = $group_count;
99                                                 }
100                                         }
101                                 }
102
103                                 $forum_counts = ForumManager::countUnseenItems();
104                                 if (DBA::isResult($forum_counts)) {
105                                         foreach ($forum_counts as $forum_count) {
106                                                 if ($forum_count['count'] > 0) {
107                                                         $forums_unseen[] = $forum_count;
108                                                 }
109                                         }
110                                 }
111                         }
112
113                         $intros1 = DBA::toArray(DBA::p(
114                                 "SELECT  `intro`.`id`, `intro`.`datetime`,
115                         `contact`.`name`, `contact`.`url`, `contact`.`photo`
116                         FROM `intro` INNER JOIN `contact` ON `intro`.`suggest-cid` = `contact`.`id`
117                         WHERE `intro`.`uid` = ? AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`suggest-cid` != 0",
118                                 local_user()
119                         ));
120                         $intros2 = DBA::toArray(DBA::p(
121                                 "SELECT `intro`.`id`, `intro`.`datetime`,
122                         `contact`.`name`, `contact`.`url`, `contact`.`photo`
123                         FROM `intro` INNER JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
124                         WHERE `intro`.`uid` = ? AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`contact-id` != 0 AND (`intro`.`suggest-cid` = 0 OR `intro`.`suggest-cid` IS NULL)",
125                                 local_user()
126                         ));
127
128                         $intro_count = count($intros1) + count($intros2);
129                         $intros = $intros1 + $intros2;
130
131                         $myurl = DI::baseUrl() . '/profile/' . DI::app()->getLoggedInUserNickname();
132                         $mail_count = DBA::count('mail', ["`uid` = ? AND NOT `seen` AND `from-url` != ?", local_user(), $myurl]);
133
134                         if (intval(DI::config()->get('config', 'register_policy')) === Register::APPROVE && DI::app()->isSiteAdmin()) {
135                                 $regs = \Friendica\Model\Register::getPending();
136
137                                 if (DBA::isResult($regs)) {
138                                         $register_count = count($regs);
139                                 }
140                         }
141
142                         $cachekey = "ping_init:" . local_user();
143                         $ev = DI::cache()->get($cachekey);
144                         if (is_null($ev)) {
145                                 $ev = DBA::selectToArray('event', ['type', 'start'],
146                                         ["`uid` = ? AND `start` < ? AND `finish` > ? AND NOT `ignore`",
147                                                 local_user(), DateTimeFormat::utc('now + 7 days'), DateTimeFormat::utcNow()]);
148                                 if (DBA::isResult($ev)) {
149                                         DI::cache()->set($cachekey, $ev, Duration::HOUR);
150                                 }
151                         }
152
153                         if (DBA::isResult($ev)) {
154                                 $all_events = count($ev);
155
156                                 if ($all_events) {
157                                         $str_now = DateTimeFormat::localNow('Y-m-d');
158                                         foreach ($ev as $x) {
159                                                 $bd = false;
160                                                 if ($x['type'] === 'birthday') {
161                                                         $birthdays ++;
162                                                         $bd = true;
163                                                 } else {
164                                                         $events ++;
165                                                 }
166                                                 if (DateTimeFormat::local($x['start'], 'Y-m-d') === $str_now) {
167                                                         $all_events_today ++;
168                                                         if ($bd) {
169                                                                 $birthdays_today ++;
170                                                         } else {
171                                                                 $events_today ++;
172                                                         }
173                                                 }
174                                         }
175                                 }
176                         }
177
178
179                         foreach ($notifications as $notification) {
180                                 if ($notification['seen'] == 0) {
181                                         $sysnotify_count ++;
182                                 }
183                         }
184
185                         // merge all notification types in one array
186                         if (DBA::isResult($intros)) {
187                                 foreach ($intros as $intro) {
188                                         $notifications[] = [
189                                                 'href'    => DI::baseUrl() . '/notifications/intros/' . $intro['id'],
190                                                 'contact' => [
191                                                         'name'    => strip_tags(BBCode::convert($intro['name'])),
192                                                         'url'     => $intro['url'],
193                                                 ],
194                                                 'message' => DI::l10n()->t('{0}} wants to follow you'),
195                                                 'date'    => $intro['datetime'],
196                                                 'seen'    => false,
197                                         ];
198                                 }
199                         }
200
201                         if (DBA::isResult($regs)) {
202                                 if (count($regs) <= 1 || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
203                                         foreach ($regs as $reg) {
204                                                 $notifications[] = [
205                                                         'href'    => DI::baseUrl()->get(true) . '/admin/users/pending',
206                                                         'contact' => [
207                                                                 'name'    => $reg['name'],
208                                                                 'url'     => $reg['url'],
209                                                         ],
210                                                         'message' => DI::l10n()->t('{0} requested registration'),
211                                                         'date'    => $reg['created'],
212                                                         'seen'    => false,
213                                                 ];
214                                         }
215                                 } else {
216                                         $notifications[] = [
217                                                 'href'    => DI::baseUrl()->get(true) . '/admin/users/pending',
218                                                 'contact' => [
219                                                         'name'    => $regs[0]['name'],
220                                                         'url'     => $regs[0]['url'],
221                                                 ],
222                                                 'message' => DI::l10n()->t('{0} and %d others requested registration', count($regs) - 1),
223                                                 'date'    => $regs[0]['created'],
224                                                 'seen'    => false,
225                                         ];
226                                 }
227                         }
228
229                         // sort notifications by $[]['date']
230                         $sort_function = function ($a, $b) {
231                                 $adate = strtotime($a['date']);
232                                 $bdate = strtotime($b['date']);
233
234                                 // Unseen messages are kept at the top
235                                 // The value 31536000 means one year. This should be enough :-)
236                                 if (!$a['seen']) {
237                                         $adate += 31536000;
238                                 }
239                                 if (!$b['seen']) {
240                                         $bdate += 31536000;
241                                 }
242
243                                 if ($adate == $bdate) {
244                                         return 0;
245                                 }
246                                 return ($adate < $bdate) ? 1 : -1;
247                         };
248                         usort($notifications, $sort_function);
249                 }
250
251                 $sysmsgs = [];
252                 $sysmsgs_info = [];
253
254                 if (!empty($_SESSION['sysmsg'])) {
255                         $sysmsgs = $_SESSION['sysmsg'];
256                         unset($_SESSION['sysmsg']);
257                 }
258
259                 if (!empty($_SESSION['sysmsg_info'])) {
260                         $sysmsgs_info = $_SESSION['sysmsg_info'];
261                         unset($_SESSION['sysmsg_info']);
262                 }
263
264                 $notification_count = $sysnotify_count + $intro_count + $register_count;
265
266                 $tpl = Renderer::getMarkupTemplate('notifications/nav/notify.tpl');
267
268                 $data = [];
269                 $data['intro']    = $intro_count;
270                 $data['mail']     = $mail_count;
271                 $data['net']      = ($network_count < 1000) ? $network_count : '999+';
272                 $data['home']     = ($home_count < 1000) ? $home_count : '999+';
273                 $data['register'] = $register_count;
274
275                 $data['all-events']       = $all_events;
276                 $data['all-events-today'] = $all_events_today;
277                 $data['events']           = $events;
278                 $data['events-today']     = $events_today;
279                 $data['birthdays']        = $birthdays;
280                 $data['birthdays-today']  = $birthdays_today;
281                 $data['groups'] = $groups_unseen;
282                 $data['forums'] = $forums_unseen;
283                 $data['notification'] = ($notification_count < 50) ? $notification_count : '49+';
284                 $data['notifications'] = array_map(function ($navNotification) use ($tpl) {
285                         $navNotification['contact']['photo'] = Contact::getAvatarUrlForUrl($navNotification['contact']['url'], local_user(), Proxy::SIZE_MICRO);
286
287                         $navNotification['timestamp'] = strtotime($navNotification['date']);
288                         $navNotification['localdate'] = DateTimeFormat::local($navNotification['date']);
289                         $navNotification['ago']       = Temporal::getRelativeDate($navNotification['date']);
290                         $navNotification['richtext']  = Entity\Notify::formatMessage($navNotification['contact']['name'], $navNotification['message']);
291                         $navNotification['plaintext'] = strip_tags($navNotification['richtext']);
292                         $navNotification['html']      = Renderer::replaceMacros($tpl, [
293                                 'notify' => $navNotification,
294                         ]);
295
296                         return $navNotification;
297                 }, $notifications);
298                 $data['sysmsgs'] = [
299                         'notice' => $sysmsgs,
300                         'info' => $sysmsgs_info
301                 ];
302
303                 $json_payload = json_encode(["result" => $data]);
304
305                 if (isset($_GET['callback'])) {
306                         // JSONP support
307                         header("Content-type: application/javascript");
308                         echo $_GET['callback'] . '(' . $json_payload . ')';
309                 } else {
310                         header("Content-type: application/json");
311                         echo $json_payload;
312                 }
313
314                 exit();
315         }
316
317         /**
318          * Retrieves the notifications array for the given user ID
319          *
320          * @param int $uid User id
321          * @return array Associative array of notifications
322          * @throws HTTPException\InternalServerErrorException
323          */
324         private function getNotificationList(int $uid): array
325         {
326                 $result  = [];
327                 $offset  = 0;
328                 $seen    = false;
329                 $seensql = 'NOT';
330                 $order   = 'DESC';
331                 $quit    = false;
332
333                 do {
334                         $notifies = DBA::toArray(DBA::p(
335                                 "SELECT `notify`.*, `post`.`visible`, `post`.`deleted`
336                         FROM `notify`
337                         LEFT JOIN `post` ON `post`.`uri-id` = `notify`.`uri-id`
338                         WHERE `notify`.`uid` = ? AND `notify`.`msg` != ''
339                         AND NOT (`notify`.`type` IN (?, ?))
340                         AND $seensql `notify`.`seen` ORDER BY `notify`.`date` $order LIMIT ?, 50",
341                                 $uid,
342                                 Notification\Type::INTRO,
343                                 Notification\Type::MAIL,
344                                 $offset
345                         ));
346
347                         if (!$notifies && !$seen) {
348                                 $seen = true;
349                                 $seensql = '';
350                                 $order = 'DESC';
351                                 $offset = 0;
352                         } elseif (!$notifies) {
353                                 $quit = true;
354                         } else {
355                                 $offset += 50;
356                         }
357
358                         foreach ($notifies as $notify) {
359                                 $notify['visible'] = $notify['visible'] ?? true;
360                                 $notify['deleted'] = $notify['deleted'] ?? 0;
361
362                                 if ($notify['msg_cache']) {
363                                         $notify['name'] = $notify['name_cache'];
364                                         $notify['message'] = $notify['msg_cache'];
365                                 } else {
366                                         $notify['name'] = strip_tags(BBCode::convert($notify['name']));
367                                         $notify['message'] = BBCode::toPlaintext($notify['msg']);
368
369                                         // @todo Replace this with a call of the Notify model class
370                                         DBA::update('notify', ['name_cache' => $notify['name'], 'msg_cache' => $notify['message']], ['id' => $notify['id']]);
371                                 }
372
373                                 if ($notify['visible']
374                                         && !$notify['deleted']
375                                         && empty($result['p:' . $notify['parent']])
376                                 ) {
377                                         $notification = [
378                                                 'href' => DI::baseUrl() . '/notify/' . $notify['id'],
379                                                 'contact' => [
380                                                         'name'  => $notify['name'],
381                                                         'url'   => $notify['url'],
382                                                 ],
383                                                 'message' => $notify['message'],
384                                                 'date'    => $notify['date'],
385                                                 'seen'    => $notify['seen'],
386                                         ];
387
388                                         // Should we condense the notifications or show them all?
389                                         if (($notify['verb'] != Activity::POST) || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
390                                                 $result[] = $notification;
391                                         } else {
392                                                 $result['p:' . $notify['parent']] = $notification;
393                                         }
394                                 }
395                         }
396                 } while ((count($result) < 50) && !$quit);
397
398                 return($result);
399         }
400 }