]> git.mxchange.org Git - friendica.git/blob - mod/network.php
Make frio more consistent by replacing textual links with icons everywhere. (#5415)
[friendica.git] / mod / network.php
1 <?php
2
3 /**
4  * @file mod/network.php
5  */
6
7 use Friendica\App;
8 use Friendica\Content\Feature;
9 use Friendica\Content\ForumManager;
10 use Friendica\Content\Nav;
11 use Friendica\Content\Widget;
12 use Friendica\Core\ACL;
13 use Friendica\Core\Addon;
14 use Friendica\Core\Config;
15 use Friendica\Core\L10n;
16 use Friendica\Core\PConfig;
17 use Friendica\Core\System;
18 use Friendica\Database\DBA;
19 use Friendica\Database\DBM;
20 use Friendica\Model\Contact;
21 use Friendica\Model\Group;
22 use Friendica\Model\Item;
23 use Friendica\Model\Profile;
24 use Friendica\Module\Login;
25 use Friendica\Util\DateTimeFormat;
26
27 require_once 'include/conversation.php';
28 require_once 'include/items.php';
29
30 function network_init(App $a)
31 {
32         if (!local_user()) {
33                 notice(L10n::t('Permission denied.') . EOL);
34                 return;
35         }
36
37         $search = (x($_GET, 'search') ? escape_tags($_GET['search']) : '');
38
39         if (($search != '') && !empty($_GET['submit'])) {
40                 goaway('search?search=' . urlencode($search));
41         }
42
43         if (x($_GET, 'save')) {
44                 $exists = DBA::exists('search', ['uid' => local_user(), 'term' => $search]);
45                 if (!$exists) {
46                         DBA::insert('search', ['uid' => local_user(), 'term' => $search]);
47                 }
48         }
49         if (x($_GET, 'remove')) {
50                 DBA::delete('search', ['uid' => local_user(), 'term' => $search]);
51         }
52
53         $is_a_date_query = false;
54
55         $group_id = (($a->argc > 1 && is_numeric($a->argv[1])) ? intval($a->argv[1]) : 0);
56
57         $cid = 0;
58         if (x($_GET, 'cid') && intval($_GET['cid']) != 0) {
59                 $cid = $_GET['cid'];
60                 $_GET['nets'] = 'all';
61                 $group_id = 0;
62         }
63
64         if ($a->argc > 1) {
65                 for ($x = 1; $x < $a->argc; $x ++) {
66                         if (is_a_date_arg($a->argv[$x])) {
67                                 $is_a_date_query = true;
68                                 break;
69                         }
70                 }
71         }
72
73         // convert query string to array. remove friendica args
74         $query_array = [];
75         $query_string = str_replace($a->cmd . '?', '', $a->query_string);
76         parse_str($query_string, $query_array);
77         array_shift($query_array);
78
79         // fetch last used network view and redirect if needed
80         if (!$is_a_date_query) {
81                 $sel_nets = defaults($_GET, 'nets', false);
82                 $sel_tabs = network_query_get_sel_tab($a);
83                 $sel_groups = network_query_get_sel_group($a);
84                 $last_sel_tabs = PConfig::get(local_user(), 'network.view', 'tab.selected');
85
86                 $remember_tab = ($sel_tabs[0] === 'active' && is_array($last_sel_tabs) && $last_sel_tabs[0] !== 'active');
87
88                 $net_baseurl = '/network';
89                 $net_args = [];
90
91                 if ($sel_groups !== false) {
92                         $net_baseurl .= '/' . $sel_groups;
93                 }
94
95                 if ($remember_tab) {
96                         // redirect if current selected tab is '/network' and
97                         // last selected tab is _not_ '/network?f=&order=comment'.
98                         // and this isn't a date query
99
100                         $tab_baseurls = [
101                                 '',     //all
102                                 '',     //postord
103                                 '',     //conv
104                                 '/new', //new
105                                 '',     //starred
106                                 '',     //bookmarked
107                         ];
108                         $tab_args = [
109                                 'f=&order=comment', //all
110                                 'f=&order=post',    //postord
111                                 'f=&conv=1',        //conv
112                                 '',                 //new
113                                 'f=&star=1',        //starred
114                                 'f=&bmark=1',       //bookmarked
115                         ];
116
117                         $k = array_search('active', $last_sel_tabs);
118
119                         if ($k != 3) {
120                                 $net_baseurl .= $tab_baseurls[$k];
121
122                                 // parse out tab queries
123                                 $dest_qa = [];
124                                 $dest_qs = $tab_args[$k];
125                                 parse_str($dest_qs, $dest_qa);
126                                 $net_args = array_merge($net_args, $dest_qa);
127                         } else {
128                                 $remember_tab = false;
129                         }
130                 }
131
132                 if ($sel_nets !== false) {
133                         $net_args['nets'] = $sel_nets;
134                 }
135
136                 if ($remember_tab) {
137                         $net_args = array_merge($query_array, $net_args);
138                         $net_queries = build_querystring($net_args);
139
140                         $redir_url = ($net_queries ? $net_baseurl . '?' . $net_queries : $net_baseurl);
141
142                         goaway(System::baseUrl() . $redir_url);
143                 }
144         }
145
146         // If nets is set to all, unset it
147         if (x($_GET, 'nets') && $_GET['nets'] === 'all') {
148                 unset($_GET['nets']);
149         }
150
151         if (!x($a->page, 'aside')) {
152                 $a->page['aside'] = '';
153         }
154
155         $a->page['aside'] .= (Feature::isEnabled(local_user(), 'groups') ?
156                 Group::sidebarWidget('network/0', 'network', 'standard', $group_id) : '');
157         $a->page['aside'] .= (Feature::isEnabled(local_user(), 'forumlist_widget') ? ForumManager::widget(local_user(), $cid) : '');
158         $a->page['aside'] .= posted_date_widget('network', local_user(), false);
159         $a->page['aside'] .= Widget::networks('network', (x($_GET, 'nets') ? $_GET['nets'] : ''));
160         $a->page['aside'] .= saved_searches($search);
161         $a->page['aside'] .= Widget::fileAs('network', (x($_GET, 'file') ? $_GET['file'] : ''));
162 }
163
164 function saved_searches($search)
165 {
166         if (!Feature::isEnabled(local_user(), 'savedsearch')) {
167                 return '';
168         }
169
170         $a = get_app();
171
172         $srchurl = '/network?f='
173                 . ((x($_GET, 'cid'))   ? '&cid='   . $_GET['cid']   : '')
174                 . ((x($_GET, 'star'))  ? '&star='  . $_GET['star']  : '')
175                 . ((x($_GET, 'bmark')) ? '&bmark=' . $_GET['bmark'] : '')
176                 . ((x($_GET, 'conv'))  ? '&conv='  . $_GET['conv']  : '')
177                 . ((x($_GET, 'nets'))  ? '&nets='  . $_GET['nets']  : '')
178                 . ((x($_GET, 'cmin'))  ? '&cmin='  . $_GET['cmin']  : '')
179                 . ((x($_GET, 'cmax'))  ? '&cmax='  . $_GET['cmax']  : '')
180                 . ((x($_GET, 'file'))  ? '&file='  . $_GET['file']  : '');
181         ;
182
183         $o = '';
184
185         $terms = DBA::select('search', ['id', 'term'], ['uid' => local_user()]);
186         $saved = [];
187
188         while ($rr = DBA::fetch($terms)) {
189                 $saved[] = [
190                         'id'          => $rr['id'],
191                         'term'        => $rr['term'],
192                         'encodedterm' => urlencode($rr['term']),
193                         'delete'      => L10n::t('Remove term'),
194                         'selected'    => ($search == $rr['term']),
195                 ];
196         }
197
198         $tpl = get_markup_template('saved_searches_aside.tpl');
199         $o = replace_macros($tpl, [
200                 '$title'     => L10n::t('Saved Searches'),
201                 '$add'       => L10n::t('add'),
202                 '$searchbox' => search($search, 'netsearch-box', $srchurl, true),
203                 '$saved'     => $saved,
204         ]);
205
206         return $o;
207 }
208
209 /**
210  * Return selected tab from query
211  *
212  * urls -> returns
213  *              '/network'                                      => $no_active = 'active'
214  *              '/network?f=&order=comment'     => $comment_active = 'active'
215  *              '/network?f=&order=post'        => $postord_active = 'active'
216  *              '/network?f=&conv=1',           => $conv_active = 'active'
217  *              '/network/new',                         => $new_active = 'active'
218  *              '/network?f=&star=1',           => $starred_active = 'active'
219  *              '/network?f=&bmark=1',          => $bookmarked_active = 'active'
220  *
221  * @return Array ($no_active, $comment_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active);
222  */
223 function network_query_get_sel_tab(App $a)
224 {
225         $no_active = '';
226         $starred_active = '';
227         $new_active = '';
228         $bookmarked_active = '';
229         $all_active = '';
230         $conv_active = '';
231         $postord_active = '';
232
233         if (($a->argc > 1 && $a->argv[1] === 'new') || ($a->argc > 2 && $a->argv[2] === 'new')) {
234                 $new_active = 'active';
235         }
236
237         if (x($_GET, 'star')) {
238                 $starred_active = 'active';
239         }
240
241         if (x($_GET, 'bmark')) {
242                 $bookmarked_active = 'active';
243         }
244
245         if (x($_GET, 'conv')) {
246                 $conv_active = 'active';
247         }
248
249         if (($new_active == '') && ($starred_active == '') && ($bookmarked_active == '') && ($conv_active == '')) {
250                 $no_active = 'active';
251         }
252
253         if ($no_active == 'active' && x($_GET, 'order')) {
254                 switch($_GET['order']) {
255                         case 'post'    : $postord_active = 'active'; $no_active=''; break;
256                         case 'comment' : $all_active     = 'active'; $no_active=''; break;
257                 }
258         }
259
260         return [$no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active];
261 }
262
263 function network_query_get_sel_group(App $a)
264 {
265         $group = false;
266
267         if ($a->argc >= 2 && is_numeric($a->argv[1])) {
268                 $group = $a->argv[1];
269         }
270
271         return $group;
272 }
273
274 /**
275  * @brief Sets the pager data and returns SQL
276  *
277  * @param App $a The global App
278  * @param integer $update Used for the automatic reloading
279  * @return string SQL with the appropriate LIMIT clause
280  */
281 function networkPager($a, $update)
282 {
283         if ($update) {
284                 // only setup pagination on initial page view
285                 return ' LIMIT 100';
286         }
287
288         //  check if we serve a mobile device and get the user settings
289         //  accordingly
290         if ($a->is_mobile) {
291                 $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_mobile_network');
292                 $itemspage_network = ((intval($itemspage_network)) ? $itemspage_network : 20);
293         } else {
294                 $itemspage_network = PConfig::get(local_user(), 'system', 'itemspage_network');
295                 $itemspage_network = ((intval($itemspage_network)) ? $itemspage_network : 40);
296         }
297
298         //  now that we have the user settings, see if the theme forces
299         //  a maximum item number which is lower then the user choice
300         if (($a->force_max_items > 0) && ($a->force_max_items < $itemspage_network)) {
301                 $itemspage_network = $a->force_max_items;
302         }
303
304         $a->set_pager_itemspage($itemspage_network);
305
306         return sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
307 }
308
309 /**
310  * @brief Sets items as seen
311  *
312  * @param array $condition The array with the SQL condition
313  */
314 function networkSetSeen($condition)
315 {
316         if (empty($condition)) {
317                 return;
318         }
319
320         $unseen = DBA::exists('item', $condition);
321
322         if ($unseen) {
323                 $r = Item::update(['unseen' => false], $condition);
324         }
325 }
326
327 /**
328  * @brief Create the conversation HTML
329  *
330  * @param App $a The global App
331  * @param array $items Items of the conversation
332  * @param string $mode Display mode for the conversation
333  * @param integer $update Used for the automatic reloading
334  * @return string HTML of the conversation
335  */
336 function networkConversation($a, $items, $mode, $update, $ordering = '')
337 {
338         // Set this so that the conversation function can find out contact info for our wall-wall items
339         $a->page_contact = $a->contact;
340
341         $o = conversation($a, $items, $mode, $update, false, $ordering, local_user());
342
343         if (!$update) {
344                 if (PConfig::get(local_user(), 'system', 'infinite_scroll')) {
345                         $o .= scroll_loader();
346                 } else {
347                         $o .= alt_pager($a, count($items));
348                 }
349         }
350
351         return $o;
352 }
353
354 function network_content(App $a, $update = 0, $parent = 0)
355 {
356         if (!local_user()) {
357                 return Login::form();
358         }
359
360         /// @TODO Is this really necessary? $a is already available to hooks
361         $arr = ['query' => $a->query_string];
362         Addon::callHooks('network_content_init', $arr);
363
364         $flat_mode = false;
365
366         if ($a->argc > 1) {
367                 for ($x = 1; $x < $a->argc; $x ++) {
368                         if ($a->argv[$x] === 'new') {
369                                 $flat_mode = true;
370                         }
371                 }
372         }
373
374         if (x($_GET, 'file')) {
375                 $flat_mode = true;
376         }
377
378         if ($flat_mode) {
379                 $o = networkFlatView($a, $update);
380         } else {
381                 $o = networkThreadedView($a, $update, $parent);
382         }
383
384         return $o;
385 }
386
387 /**
388  * @brief Get the network content in flat view
389  *
390  * @param App $a The global App
391  * @param integer $update Used for the automatic reloading
392  * @return string HTML of the network content in flat view
393  */
394 function networkFlatView(App $a, $update = 0)
395 {
396         // Rawmode is used for fetching new content at the end of the page
397         $rawmode = (isset($_GET['mode']) && ($_GET['mode'] == 'raw'));
398
399         if (isset($_GET['last_id'])) {
400                 $last_id = intval($_GET['last_id']);
401         } else {
402                 $last_id = 0;
403         }
404
405         $o = '';
406
407         $file = ((x($_GET, 'file')) ? $_GET['file'] : '');
408
409         if (!$update && !$rawmode) {
410                 $tabs = network_tabs($a);
411                 $o .= $tabs;
412
413                 Nav::setSelected('network');
414
415                 $x = [
416                         'is_owner' => true,
417                         'allow_location' => $a->user['allow_location'],
418                         'default_location' => $a->user['default-location'],
419                         'nickname' => $a->user['nickname'],
420                         'lockstate' => (((is_array($a->user) &&
421                         ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) ||
422                         (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'),
423                         'default_perms' => ACL::getDefaultUserPermissions($a->user),
424                         'acl' => ACL::getFullSelectorHTML($a->user, true),
425                         'bang' => '',
426                         'visitor' => 'block',
427                         'profile_uid' => local_user(),
428                         'content' => '',
429                 ];
430
431                 $o .= status_editor($a, $x);
432
433                 if (!Config::get('theme', 'hide_eventlist')) {
434                         $o .= Profile::getBirthdays();
435                         $o .= Profile::getEventsReminderHTML();
436                 }
437         }
438
439         $pager_sql = networkPager($a, $update);
440
441         if (strlen($file)) {
442                 $condition = ["`term` = ? AND `otype` = ? AND `type` = ? AND `uid` = ?",
443                         $file, TERM_OBJ_POST, TERM_FILE, local_user()];
444                 $params = ['order' => ['tid' => true], 'limit' => [$a->pager['start'], $a->pager['itemspage']]];
445                 $result = DBA::select('term', ['oid'], $condition);
446
447                 $posts = [];
448                 while ($term = DBA::fetch($result)) {
449                         $posts[] = $term['oid'];
450                 }
451                 DBA::close($terms);
452
453                 $condition = ['uid' => local_user(), 'id' => $posts];
454         } else {
455                 $condition = ['uid' => local_user()];
456         }
457
458         $params = ['order' => ['id' => true], 'limit' => [$a->pager['start'], $a->pager['itemspage']]];
459         $result = Item::selectForUser(local_user(), [], $condition, $params);
460         $items = Item::inArray($result);
461
462         $condition = ['unseen' => true, 'uid' => local_user()];
463         networkSetSeen($condition);
464
465         $o .= networkConversation($a, $items, 'network-new', $update);
466
467         return $o;
468 }
469
470 /**
471  * @brief Get the network content in threaded view
472  *
473  * @param App $a The global App
474  * @param integer $update Used for the automatic reloading
475  * @return string HTML of the network content in flat view
476  */
477 function networkThreadedView(App $a, $update, $parent)
478 {
479         // Rawmode is used for fetching new content at the end of the page
480         $rawmode = (isset($_GET['mode']) AND ( $_GET['mode'] == 'raw'));
481
482         if (isset($_GET['last_received']) && isset($_GET['last_commented']) && isset($_GET['last_created']) && isset($_GET['last_id'])) {
483                 $last_received = DBM::date($_GET['last_received']);
484                 $last_commented = DBM::date($_GET['last_commented']);
485                 $last_created = DBM::date($_GET['last_created']);
486                 $last_id = intval($_GET['last_id']);
487         } else {
488                 $last_received = '';
489                 $last_commented = '';
490                 $last_created = '';
491                 $last_id = 0;
492         }
493
494         $datequery = $datequery2 = '';
495
496         $gid = 0;
497
498         if ($a->argc > 1) {
499                 for ($x = 1; $x < $a->argc; $x ++) {
500                         if (is_a_date_arg($a->argv[$x])) {
501                                 if ($datequery) {
502                                         $datequery2 = escape_tags($a->argv[$x]);
503                                 } else {
504                                         $datequery = escape_tags($a->argv[$x]);
505                                         $_GET['order'] = 'post';
506                                 }
507                         } elseif (intval($a->argv[$x])) {
508                                 $gid = intval($a->argv[$x]);
509                                 $def_acl = ['allow_gid' => '<' . $gid . '>'];
510                         }
511                 }
512         }
513
514         $o = '';
515
516         $cid   = intval(defaults($_GET, 'cid'  , 0));
517         $star  = intval(defaults($_GET, 'star' , 0));
518         $bmark = intval(defaults($_GET, 'bmark', 0));
519         $conv  = intval(defaults($_GET, 'conv' , 0));
520         $order = notags(defaults($_GET, 'order', 'comment'));
521         $nets  =        defaults($_GET, 'nets' , '');
522
523         if ($cid) {
524                 $def_acl = ['allow_cid' => '<' . intval($cid) . '>'];
525         }
526
527         if ($nets) {
528                 $r = DBA::select('contact', ['id'], ['uid' => local_user(), 'network' => $nets], ['self' => false]);
529
530                 $str = '';
531                 while ($rr = DBA::fetch($r)) {
532                         $str .= '<' . $rr['id'] . '>';
533                 }
534                 if (strlen($str)) {
535                         $def_acl = ['allow_cid' => $str];
536                 }
537         }
538
539         if (!$update && !$rawmode) {
540                 $tabs = network_tabs($a);
541                 $o .= $tabs;
542
543                 if ($gid) {
544                         if (($t = Contact::getOStatusCountByGroupId($gid)) && !PConfig::get(local_user(), 'system', 'nowarn_insecure')) {
545                                 notice(L10n::tt("Warning: This group contains %s member from a network that doesn't allow non public messages.",
546                                                 "Warning: This group contains %s members from a network that doesn't allow non public messages.",
547                                                 $t) . EOL);
548                                 notice(L10n::t("Messages in this group won't be send to these receivers.").EOL);
549                         }
550                 }
551
552                 Nav::setSelected('network');
553
554                 $content = '';
555
556                 if ($cid) {
557                         // If $cid belongs to a communitity forum or a privat goup,.add a mention to the status editor
558                         $condition = ["`id` = ? AND (`forum` OR `prv`)", $cid];
559                         $contact = DBA::selectFirst('contact', ['addr', 'nick'], $condition);
560                         if (DBM::is_result($contact)) {
561                                 if ($contact['addr'] != '') {
562                                         $content = '!' . $contact['addr'];
563                                 } else {
564                                         $content = '!' . $contact['nick'] . '+' . $cid;
565                                 }
566                         }
567                 }
568
569                 $x = [
570                         'is_owner' => true,
571                         'allow_location' => $a->user['allow_location'],
572                         'default_location' => $a->user['default-location'],
573                         'nickname' => $a->user['nickname'],
574                         'lockstate' => ((($gid) || ($cid) || ($nets) || (is_array($a->user) &&
575                         ((strlen($a->user['allow_cid'])) || (strlen($a->user['allow_gid'])) ||
576                         (strlen($a->user['deny_cid'])) || (strlen($a->user['deny_gid']))))) ? 'lock' : 'unlock'),
577                         'default_perms' => ACL::getDefaultUserPermissions($a->user),
578                         'acl' => ACL::getFullSelectorHTML((($gid || $cid || $nets) ? $def_acl : $a->user), true),
579                         'bang' => (($gid || $cid || $nets) ? '!' : ''),
580                         'visitor' => 'block',
581                         'profile_uid' => local_user(),
582                         'content' => $content,
583                 ];
584
585                 $o .= status_editor($a, $x);
586         }
587
588         // We don't have to deal with ACLs on this page. You're looking at everything
589         // that belongs to you, hence you can see all of it. We will filter by group if
590         // desired.
591
592         $sql_post_table = '';
593         $sql_options = ($star ? " AND `thread`.`starred` " : '');
594         $sql_options .= ($bmark ? sprintf(" AND `thread`.`post-type` = %d ", Item::PT_PAGE) : '');
595         $sql_extra = $sql_options;
596         $sql_extra2 = '';
597         $sql_extra3 = '';
598         $sql_table = '`thread`';
599         $sql_parent = '`iid`';
600         $sql_order = '';
601
602         if ($update) {
603                 $sql_table = '`item`';
604                 $sql_parent = '`parent`';
605                 $sql_post_table = " INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`";
606         }
607
608         $sql_nets = (($nets) ? sprintf(" AND $sql_table.`network` = '%s' ", dbesc($nets)) : '');
609         $sql_tag_nets = (($nets) ? sprintf(" AND `item`.`network` = '%s' ", dbesc($nets)) : '');
610
611         if ($gid) {
612                 $group = DBA::selectFirst('group', ['name'], ['id' => $gid, 'uid' => local_user()]);
613                 if (!DBM::is_result($group)) {
614                         if ($update) {
615                                 killme();
616                         }
617                         notice(L10n::t('No such group') . EOL);
618                         goaway('network/0');
619                         // NOTREACHED
620                 }
621
622                 $contacts = Group::expand([$gid]);
623
624                 if ((is_array($contacts)) && count($contacts)) {
625                         $contact_str_self = '';
626
627                         $contact_str = implode(',', $contacts);
628                         $self = DBA::selectFirst('contact', ['id'], ['uid' => local_user(), 'self' => true]);
629                         if (DBM::is_result($self)) {
630                                 $contact_str_self = $self['id'];
631                         }
632
633                         $sql_post_table .= " INNER JOIN `item` AS `temp1` ON `temp1`.`id` = " . $sql_table . "." . $sql_parent;
634                         $sql_extra3 .= " AND (`thread`.`contact-id` IN ($contact_str) ";
635                         $sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . protect_sprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))";
636                 } else {
637                         $sql_extra3 .= " AND false ";
638                         info(L10n::t('Group is empty'));
639                 }
640
641                 $o = replace_macros(get_markup_template('section_title.tpl'), [
642                         '$title' => L10n::t('Group: %s', $group['name'])
643                 ]) . $o;
644         } elseif ($cid) {
645                 $fields = ['id', 'name', 'network', 'writable', 'nurl',
646                         'forum', 'prv', 'contact-type', 'addr', 'thumb', 'location'];
647                 $condition = ["`id` = ? AND (NOT `blocked` OR `pending`)", $cid];
648                 $contact = DBA::selectFirst('contact', $fields, $condition);
649                 if (DBM::is_result($contact)) {
650                         $sql_extra = " AND " . $sql_table . ".`contact-id` = " . intval($cid);
651
652                         $entries[0] = [
653                                 'id' => 'network',
654                                 'name' => htmlentities($contact['name']),
655                                 'itemurl' => defaults($contact, 'addr', $contact['nurl']),
656                                 'thumb' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB),
657                                 'details' => $contact['location'],
658                         ];
659
660                         $entries[0]['account_type'] = Contact::getAccountType($contact);
661
662                         $o = replace_macros(get_markup_template('viewcontact_template.tpl'), [
663                                 'contacts' => $entries,
664                                 'id' => 'network',
665                         ]) . $o;
666
667                         if ($contact['network'] === NETWORK_OSTATUS && $contact['writable'] && !PConfig::get(local_user(),'system','nowarn_insecure')) {
668                                 notice(L10n::t('Private messages to this person are at risk of public disclosure.') . EOL);
669                         }
670                 } else {
671                         notice(L10n::t('Invalid contact.') . EOL);
672                         goaway('network');
673                         // NOTREACHED
674                 }
675         }
676
677         if (!$gid && !$cid && !$update && !Config::get('theme', 'hide_eventlist')) {
678                 $o .= Profile::getBirthdays();
679                 $o .= Profile::getEventsReminderHTML();
680         }
681
682         if ($datequery) {
683                 $sql_extra3 .= protect_sprintf(sprintf(" AND $sql_table.created <= '%s' ",
684                                 dbesc(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
685         }
686         if ($datequery2) {
687                 $sql_extra3 .= protect_sprintf(sprintf(" AND $sql_table.created >= '%s' ",
688                                 dbesc(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
689         }
690
691         if ($conv) {
692                 $sql_extra3 .= " AND $sql_table.`mention`";
693         }
694
695         // Normal conversation view
696         if ($order === 'post') {
697                 $ordering = '`created`';
698                 $order_mode = 'created';
699         } else {
700                 $ordering = '`commented`';
701                 $order_mode = 'commented';
702         }
703
704         $sql_order = "$sql_table.$ordering";
705
706         if (x($_GET, 'offset')) {
707                 $sql_range = sprintf(" AND $sql_order <= '%s'", dbesc($_GET['offset']));
708         } else {
709                 $sql_range = '';
710         }
711
712         $pager_sql = networkPager($a, $update);
713
714         $last_date = '';
715
716         switch ($order_mode) {
717                 case 'received':
718                         if ($last_received != '') {
719                                 $last_date = $last_received;
720                                 $sql_range .= sprintf(" AND $sql_table.`received` < '%s'", dbesc($last_received));
721                                 $a->set_pager_page(1);
722                                 $pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
723                         }
724                         break;
725                 case 'commented':
726                         if ($last_commented != '') {
727                                 $last_date = $last_commented;
728                                 $sql_range .= sprintf(" AND $sql_table.`commented` < '%s'", dbesc($last_commented));
729                                 $a->set_pager_page(1);
730                                 $pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
731                         }
732                         break;
733                 case 'created':
734                         if ($last_created != '') {
735                                 $last_date = $last_created;
736                                 $sql_range .= sprintf(" AND $sql_table.`created` < '%s'", dbesc($last_created));
737                                 $a->set_pager_page(1);
738                                 $pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
739                         }
740                         break;
741                 case 'id':
742                         if (($last_id > 0) && ($sql_table == '`thread`')) {
743                                 $sql_range .= sprintf(" AND $sql_table.`iid` < '%s'", dbesc($last_id));
744                                 $a->set_pager_page(1);
745                                 $pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
746                         }
747                         break;
748         }
749
750         // Fetch a page full of parent items for this page
751         if ($update) {
752                 if (!empty($parent)) {
753                         // Load only a single thread
754                         $sql_extra4 = "`item`.`id` = ".intval($parent);
755                 } else {
756                         // Load all unseen items
757                         $sql_extra4 = "`item`.`unseen`";
758                         if (Config::get("system", "like_no_comment")) {
759                                 $sql_extra4 .= " AND `item`.`gravity` IN (" . GRAVITY_PARENT . "," . GRAVITY_COMMENT . ")";
760                         }
761                         if ($order === 'post') {
762                                 // Only show toplevel posts when updating posts in this order mode
763                                 $sql_extra4 .= " AND `item`.`id` = `item`.`parent`";
764                         }
765                 }
766
767                 $r = q("SELECT `item`.`parent-uri` AS `uri`, `item`.`parent` AS `item_id`, $sql_order AS `order_date`
768                         FROM `item` $sql_post_table
769                         STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
770                                 AND (NOT `contact`.`blocked` OR `contact`.`pending`)
771                                 AND (`item`.`parent-uri` != `item`.`uri`
772                                         OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
773                                         OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
774                         LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
775                         WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted`
776                         AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
777                         AND NOT `item`.`moderated` AND $sql_extra4
778                         $sql_extra3 $sql_extra $sql_range $sql_nets
779                         ORDER BY `order_date` DESC LIMIT 100",
780                         intval(CONTACT_IS_SHARING),
781                         intval(CONTACT_IS_FRIEND),
782                         intval(local_user()),
783                         intval(local_user())
784                 );
785         } else {
786                 $r = q("SELECT `item`.`uri`, `thread`.`iid` AS `item_id`, $sql_order AS `order_date`
787                         FROM `thread` $sql_post_table
788                         STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
789                                 AND (NOT `contact`.`blocked` OR `contact`.`pending`)
790                         STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid`
791                                 AND (`item`.`parent-uri` != `item`.`uri`
792                                         OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
793                                         OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
794                         LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
795                         WHERE `thread`.`uid` = %d AND `thread`.`visible` AND NOT `thread`.`deleted`
796                         AND NOT `thread`.`moderated`
797                         AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
798                         $sql_extra2 $sql_extra3 $sql_range $sql_extra $sql_nets
799                         ORDER BY `order_date` DESC $pager_sql",
800                         intval(CONTACT_IS_SHARING),
801                         intval(CONTACT_IS_FRIEND),
802                         intval(local_user()),
803                         intval(local_user())
804                 );
805         }
806
807         // Only show it when unfiltered (no groups, no networks, ...)
808         if (in_array($nets, ['', NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS]) && (strlen($sql_extra . $sql_extra2 . $sql_extra3) == 0)) {
809                 if (DBM::is_result($r)) {
810                         $top_limit = current($r)['order_date'];
811                         $bottom_limit = end($r)['order_date'];
812                         if (empty($_SESSION['network_last_top_limit']) || ($_SESSION['network_last_top_limit'] < $top_limit)) {
813                                 $_SESSION['network_last_top_limit'] = $top_limit;
814                         }
815                 } else {
816                         $top_limit = $bottom_limit = DateTimeFormat::utcNow();
817                 }
818
819                 // When checking for updates we need to fetch from the newest date to the newest date before
820                 // Only do this, when the last stored date isn't too long ago (10 times the update interval)
821                 $browser_update = PConfig::get(local_user(), 'system', 'update_interval', 40000) / 1000;
822
823                 if (($browser_update > 0) && $update && !empty($_SESSION['network_last_date']) &&
824                         (($bottom_limit < $_SESSION['network_last_date']) || ($top_limit == $bottom_limit)) &&
825                         ((time() - $_SESSION['network_last_date_timestamp']) < ($browser_update * 10))) {
826                         $bottom_limit = $_SESSION['network_last_date'];
827                 }
828                 $_SESSION['network_last_date'] = defaults($_SESSION, 'network_last_top_limit', $top_limit);
829                 $_SESSION['network_last_date_timestamp'] = time();
830
831                 if ($last_date > $top_limit) {
832                         $top_limit = $last_date;
833                 } elseif ($a->pager['page'] == 1) {
834                         // Highest possible top limit when we are on the first page
835                         $top_limit = DateTimeFormat::utcNow();
836                 }
837
838                 $items = DBA::p("SELECT `item`.`parent-uri` AS `uri`, 0 AS `item_id`, `item`.$ordering AS `order_date`, `author`.`url` AS `author-link` FROM `item`
839                         STRAIGHT_JOIN (SELECT `oid` FROM `term` WHERE `term` IN
840                                 (SELECT SUBSTR(`term`, 2) FROM `search` WHERE `uid` = ? AND `term` LIKE '#%') AND `otype` = ? AND `type` = ? AND `uid` = 0) AS `term`
841                         ON `item`.`id` = `term`.`oid`
842                         STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `item`.`author-id`
843                         WHERE `item`.`uid` = 0 AND `item`.$ordering < ? AND `item`.$ordering > ?
844                                 AND NOT `author`.`hidden` AND NOT `author`.`blocked`" . $sql_tag_nets,
845                         local_user(), TERM_OBJ_POST, TERM_HASHTAG,
846                         $top_limit, $bottom_limit);
847
848                 $data = DBA::inArray($items);
849
850                 if (count($data) > 0) {
851                         $tag_top_limit = current($data)['order_date'];
852                         if ($_SESSION['network_last_date'] < $tag_top_limit) {
853                                 $_SESSION['network_last_date'] = $tag_top_limit;
854                         }
855
856                         logger('Tagged items: ' . count($data) . ' - ' . $bottom_limit . ' - ' . $top_limit . ' - ' . local_user().' - '.(int)$update);
857                         $s = [];
858                         foreach ($r as $item) {
859                                 $s[$item['uri']] = $item;
860                         }
861                         foreach ($data as $item) {
862                                 // Don't show hash tag posts from blocked or ignored contacts
863                                 $condition = ["`nurl` = ? AND `uid` = ? AND (`blocked` OR `readonly`)",
864                                         normalise_link($item['author-link']), local_user()];
865                                 if (!DBA::exists('contact', $condition)) {
866                                         $s[$item['uri']] = $item;
867                                 }
868                         }
869                         $r = $s;
870                 }
871         }
872
873         $parents_str = '';
874         $date_offset = '';
875
876         $items = $r;
877
878         if (DBM::is_result($items)) {
879                 $parents_arr = [];
880
881                 foreach ($items as $item) {
882                         if ($date_offset < $item['order_date']) {
883                                 $date_offset = $item['order_date'];
884                         }
885                         if (!in_array($item['item_id'], $parents_arr) && ($item['item_id'] > 0)) {
886                                 $parents_arr[] = $item['item_id'];
887                         }
888                 }
889                 $parents_str = implode(', ', $parents_arr);
890         }
891
892         if (x($_GET, 'offset')) {
893                 $date_offset = $_GET['offset'];
894         }
895
896         $a->page_offset = $date_offset;
897
898         // We aren't going to try and figure out at the item, group, and page
899         // level which items you've seen and which you haven't. If you're looking
900         // at the top level network page just mark everything seen.
901
902         if (!$gid && !$cid && !$star) {
903                 $condition = ['unseen' => true, 'uid' => local_user()];
904                 networkSetSeen($condition);
905         } elseif ($parents_str) {
906                 $condition = ["`uid` = ? AND `unseen` AND `parent` IN (" . dbesc($parents_str) . ")", local_user()];
907                 networkSetSeen($condition);
908         }
909
910
911         $mode = 'network';
912         $o .= networkConversation($a, $items, $mode, $update, $ordering);
913
914         return $o;
915 }
916
917 /**
918  * @brief Get the network tabs menu
919  *
920  * @param App $a The global App
921  * @return string Html of the networktab
922  */
923 function network_tabs(App $a)
924 {
925         // item filter tabs
926         /// @TODO fix this logic, reduce duplication
927         /// $a->page['content'] .= '<div class="tabs-wrapper">';
928         list($no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a);
929
930         // if no tabs are selected, defaults to comments
931         if ($no_active == 'active') {
932                 $all_active = 'active';
933         }
934
935         $cmd = $a->cmd;
936
937         // tabs
938         $tabs = [
939                 [
940                         'label' => L10n::t('Commented Order'),
941                         'url'   => str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
942                         'sel'   => $all_active,
943                         'title' => L10n::t('Sort by Comment Date'),
944                         'id'    => 'commented-order-tab',
945                         'accesskey' => 'e',
946                 ],
947                 [
948                         'label' => L10n::t('Posted Order'),
949                         'url'   => str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''),
950                         'sel'   => $postord_active,
951                         'title' => L10n::t('Sort by Post Date'),
952                         'id'    => 'posted-order-tab',
953                         'accesskey' => 't',
954                 ],
955         ];
956
957         if (Feature::isEnabled(local_user(), 'personal_tab')) {
958                 $tabs[] = [
959                         'label' => L10n::t('Personal'),
960                         'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1',
961                         'sel'   => $conv_active,
962                         'title' => L10n::t('Posts that mention or involve you'),
963                         'id'    => 'personal-tab',
964                         'accesskey' => 'r',
965                 ];
966         }
967
968         if (Feature::isEnabled(local_user(), 'new_tab')) {
969                 $tabs[] = [
970                         'label' => L10n::t('New'),
971                         'url'   => 'network/new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''),
972                         'sel'   => $new_active,
973                         'title' => L10n::t('Activity Stream - by date'),
974                         'id'    => 'activitiy-by-date-tab',
975                         'accesskey' => 'w',
976                 ];
977         }
978
979         if (Feature::isEnabled(local_user(), 'link_tab')) {
980                 $tabs[] = [
981                         'label' => L10n::t('Shared Links'),
982                         'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1',
983                         'sel'   => $bookmarked_active,
984                         'title' => L10n::t('Interesting Links'),
985                         'id'    => 'shared-links-tab',
986                         'accesskey' => 'b',
987                 ];
988         }
989
990         if (Feature::isEnabled(local_user(), 'star_posts')) {
991                 $tabs[] = [
992                         'label' => L10n::t('Starred'),
993                         'url'   => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1',
994                         'sel'   => $starred_active,
995                         'title' => L10n::t('Favourite Posts'),
996                         'id'    => 'starred-posts-tab',
997                         'accesskey' => 'm',
998                 ];
999         }
1000
1001         // save selected tab, but only if not in file mode
1002         if (!x($_GET, 'file')) {
1003                 PConfig::set(local_user(), 'network.view', 'tab.selected', [
1004                         $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active
1005                 ]);
1006         }
1007
1008         $arr = ['tabs' => $tabs];
1009         Addon::callHooks('network_tabs', $arr);
1010
1011         $tpl = get_markup_template('common_tabs.tpl');
1012
1013         return replace_macros($tpl, ['$tabs' => $arr['tabs']]);
1014
1015         // --- end item filter tabs
1016 }