]> git.mxchange.org Git - friendica.git/blob - src/Module/Conversation/Network.php
Some more API functions moved
[friendica.git] / src / Module / Conversation / Network.php
1 <?php
2
3 namespace Friendica\Module\Conversation;
4
5 use Friendica\BaseModule;
6 use Friendica\Content\BoundariesPager;
7 use Friendica\Content\ForumManager;
8 use Friendica\Content\Nav;
9 use Friendica\Content\Widget;
10 use Friendica\Content\Text\HTML;
11 use Friendica\Core\ACL;
12 use Friendica\Core\Hook;
13 use Friendica\Core\Renderer;
14 use Friendica\Core\Session;
15 use Friendica\Database\DBA;
16 use Friendica\DI;
17 use Friendica\Model\Contact;
18 use Friendica\Model\Group;
19 use Friendica\Model\Item;
20 use Friendica\Model\Post;
21 use Friendica\Model\Profile;
22 use Friendica\Model\User;
23 use Friendica\Model\Verb;
24 use Friendica\Module\Contact as ModuleContact;
25 use Friendica\Module\Security\Login;
26 use Friendica\Protocol\Activity;
27 use Friendica\Util\DateTimeFormat;
28
29 class Network extends BaseModule
30 {
31         /** @var int */
32         private static $groupId;
33         /** @var int */
34         private static $forumContactId;
35         /** @var string */
36         private static $selectedTab;
37         /** @var mixed */
38         private static $min_id;
39         /** @var mixed */
40         private static $max_id;
41         /** @var string */
42         private static $accountTypeString;
43         /** @var int */
44         private static $accountType;
45         /** @var string */
46         private static $network;
47         /** @var int */
48         private static $itemsPerPage;
49         /** @var string */
50         private static $dateFrom;
51         /** @var string */
52         private static $dateTo;
53         /** @var int */
54         private static $star;
55         /** @var int */
56         private static $mention;
57         /** @var string */
58         protected static $order;
59
60         public static function content(array $parameters = [])
61         {
62                 if (!local_user()) {
63                         return Login::form();
64                 }
65
66                 self::parseRequest($parameters, $_GET);
67
68                 $module = 'network';
69
70                 DI::page()['aside'] .= Widget::accounttypes($module, self::$accountTypeString);
71                 DI::page()['aside'] .= Group::sidebarWidget($module, $module . '/group', 'standard', self::$groupId);
72                 DI::page()['aside'] .= ForumManager::widget($module . '/forum', local_user(), self::$forumContactId);
73                 DI::page()['aside'] .= Widget::postedByYear($module . '/archive', local_user(), false);
74                 DI::page()['aside'] .= Widget::networks($module, !self::$forumContactId ? self::$network : '');
75                 DI::page()['aside'] .= Widget\SavedSearches::getHTML(DI::args()->getQueryString());
76                 DI::page()['aside'] .= Widget::fileAs('filed', null);
77
78                 $arr = ['query' => DI::args()->getQueryString()];
79                 Hook::callAll('network_content_init', $arr);
80
81                 $o = '';
82
83                 // Fetch a page full of parent items for this page
84                 $params = ['limit' => self::$itemsPerPage];
85                 $table = 'network-thread-view';
86
87                 $items = self::getItems($table, $params);
88
89                 if (DI::pConfig()->get(local_user(), 'system', 'infinite_scroll') && ($_GET['mode'] ?? '') != 'minimal') {
90                         $tpl = Renderer::getMarkupTemplate('infinite_scroll_head.tpl');
91                         $o .= Renderer::replaceMacros($tpl, ['$reload_uri' => DI::args()->getQueryString()]);
92                 }
93
94                 if (!(isset($_GET['mode']) AND ($_GET['mode'] == 'raw'))) {
95                         $o .= self::getTabsHTML(self::$selectedTab);
96
97                         Nav::setSelected(DI::args()->get(0));
98
99                         $content = '';
100
101                         if (self::$forumContactId) {
102                                 // If self::$forumContactId belongs to a communitity forum or a privat goup,.add a mention to the status editor
103                                 $condition = ["`id` = ? AND (`forum` OR `prv`)", self::$forumContactId];
104                                 $contact = DBA::selectFirst('contact', ['addr'], $condition);
105                                 if (!empty($contact['addr'])) {
106                                         $content = '!' . $contact['addr'];
107                                 }
108                         }
109
110                         $a = DI::app();
111
112                         $default_permissions = [];
113                         if (self::$groupId) {
114                                 $default_permissions['allow_gid'] = [self::$groupId];
115                         }
116
117                         $allowedCids = [];
118                         if (self::$forumContactId) {
119                                 $allowedCids[] = (int) self::$forumContactId;
120                         } elseif (self::$network) {
121                                 $condition = [
122                                         'uid'     => local_user(),
123                                         'network' => self::$network,
124                                         'self'    => false,
125                                         'blocked' => false,
126                                         'pending' => false,
127                                         'archive' => false,
128                                         'rel'     => [Contact::SHARING, Contact::FRIEND],
129                                 ];
130                                 $contactStmt = DBA::select('contact', ['id'], $condition);
131                                 while ($contact = DBA::fetch($contactStmt)) {
132                                         $allowedCids[] = (int) $contact['id'];
133                                 }
134                                 DBA::close($contactStmt);
135                         }
136
137                         if (count($allowedCids)) {
138                                 $default_permissions['allow_cid'] = $allowedCids;
139                         }
140
141                         $x = [
142                                 'lockstate' => self::$groupId || self::$forumContactId || self::$network || ACL::getLockstateForUserId($a->getLoggedInUserId()) ? 'lock' : 'unlock',
143                                 'acl' => ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId(), true, $default_permissions),
144                                 'bang' => ((self::$groupId || self::$forumContactId || self::$network) ? '!' : ''),
145                                 'content' => $content,
146                         ];
147
148                         $o .= DI::conversation()->statusEditor($x);
149                 }
150
151                 if (self::$groupId) {
152                         $group = DBA::selectFirst('group', ['name'], ['id' => self::$groupId, 'uid' => local_user()]);
153                         if (!DBA::isResult($group)) {
154                                 notice(DI::l10n()->t('No such group'));
155                         }
156
157                         $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [
158                                 '$title' => DI::l10n()->t('Group: %s', $group['name'])
159                         ]) . $o;
160                 } elseif (self::$forumContactId) {
161                         $contact = Contact::getById(self::$forumContactId);
162                         if (DBA::isResult($contact)) {
163                                 $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('viewcontact_template.tpl'), [
164                                         'contacts' => [ModuleContact::getContactTemplateVars($contact)],
165                                         'id' => DI::args()->get(0),
166                                 ]) . $o;
167                         } else {
168                                 notice(DI::l10n()->t('Invalid contact.'));
169                         }
170                 } elseif (!DI::config()->get('theme', 'hide_eventlist')) {
171                         $o .= Profile::getBirthdays();
172                         $o .= Profile::getEventsReminderHTML();
173                 }
174
175                 if (self::$order === 'received') {
176                         $ordering = '`received`';
177                 } else {
178                         $ordering = '`commented`';
179                 }
180
181                 $o .= DI::conversation()->create($items, 'network', false, false, $ordering, local_user());
182
183                 if (DI::pConfig()->get(local_user(), 'system', 'infinite_scroll')) {
184                         $o .= HTML::scrollLoader();
185                 } else {
186                         $pager = new BoundariesPager(
187                                 DI::l10n(),
188                                 DI::args()->getQueryString(),
189                                 $items[0][self::$order] ?? null,
190                                 $items[count($items) - 1][self::$order] ?? null,
191                                 self::$itemsPerPage
192                         );
193
194                         $o .= $pager->renderMinimal(count($items));
195                 }
196
197                 return $o;
198         }
199
200         /**
201          * Sets items as seen
202          *
203          * @param array $condition The array with the SQL condition
204          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
205          */
206         private static function setItemsSeenByCondition(array $condition)
207         {
208                 if (empty($condition)) {
209                         return;
210                 }
211
212                 $unseen = Post::exists($condition);
213
214                 if ($unseen) {
215                         /// @todo handle huge "unseen" updates in the background to avoid timeout errors
216                         Item::update(['unseen' => false], $condition);
217                 }
218         }
219
220         /**
221          * Get the network tabs menu
222          *
223          * @param string $selectedTab
224          * @return string Html of the network tabs
225          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
226          */
227         private static function getTabsHTML(string $selectedTab)
228         {
229                 $cmd = DI::args()->getCommand();
230
231                 // tabs
232                 $tabs = [
233                         [
234                                 'label' => DI::l10n()->t('Latest Activity'),
235                                 'url'   => $cmd . '?' . http_build_query(['order' => 'commented']),
236                                 'sel'   => !$selectedTab || $selectedTab == 'commented' ? 'active' : '',
237                                 'title' => DI::l10n()->t('Sort by latest activity'),
238                                 'id'    => 'activity-order-tab',
239                                 'accesskey' => 'e',
240                         ],
241                         [
242                                 'label' => DI::l10n()->t('Latest Posts'),
243                                 'url'   => $cmd . '?' . http_build_query(['order' => 'received']),
244                                 'sel'   => $selectedTab == 'received' ? 'active' : '',
245                                 'title' => DI::l10n()->t('Sort by post received date'),
246                                 'id'    => 'post-order-tab',
247                                 'accesskey' => 't',
248                         ],
249                         [
250                                 'label' => DI::l10n()->t('Personal'),
251                                 'url'   => $cmd . '?' . http_build_query(['mention' => true]),
252                                 'sel'   => $selectedTab == 'mention' ? 'active' : '',
253                                 'title' => DI::l10n()->t('Posts that mention or involve you'),
254                                 'id'    => 'personal-tab',
255                                 'accesskey' => 'r',
256                         ],
257                         [
258                                 'label' => DI::l10n()->t('Starred'),
259                                 'url'   => $cmd . '?' . http_build_query(['star' => true]),
260                                 'sel'   => $selectedTab == 'star' ? 'active' : '',
261                                 'title' => DI::l10n()->t('Favourite Posts'),
262                                 'id'    => 'starred-posts-tab',
263                                 'accesskey' => 'm',
264                         ],
265                 ];
266
267                 $arr = ['tabs' => $tabs];
268                 Hook::callAll('network_tabs', $arr);
269
270                 $tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
271
272                 return Renderer::replaceMacros($tpl, ['$tabs' => $arr['tabs']]);
273         }
274
275         protected static function parseRequest(array $parameters, array $get)
276         {
277                 self::$groupId = $parameters['group_id'] ?? 0;
278
279                 self::$forumContactId = $parameters['contact_id'] ?? 0;
280
281                 self::$selectedTab = Session::get('network-tab', DI::pConfig()->get(local_user(), 'network.view', 'selected_tab', ''));
282
283                 self::$order = 'commented';
284
285                 if (!empty($get['star'])) {
286                         self::$selectedTab = 'star';
287                         self::$star = true;
288                 } else {
289                         self::$star = self::$selectedTab == 'star';
290                 }
291
292                 if (!empty($get['mention'])) {
293                         self::$selectedTab = 'mention';
294                         self::$mention = true;
295                 } else {
296                         self::$mention = self::$selectedTab == 'mention';
297                 }
298
299                 if (!empty($get['order'])) {
300                         self::$selectedTab = $get['order'];
301                         self::$order = $get['order'];
302                         self::$star = false;
303                         self::$mention = false;
304                 } elseif (in_array(self::$selectedTab, ['received', 'star', 'mention'])) {
305                         self::$order = 'received';
306                 }
307
308                 self::$selectedTab = self::$selectedTab ?? self::$order;
309
310                 // Prohibit combined usage of "star" and "mention"
311                 if (self::$selectedTab == 'star') {
312                         self::$mention = false;
313                 } elseif (self::$selectedTab == 'mention') {
314                         self::$star = false;
315                 }
316
317                 Session::set('network-tab', self::$selectedTab);
318                 DI::pConfig()->set(local_user(), 'network.view', 'selected_tab', self::$selectedTab);
319
320                 self::$accountTypeString = $get['accounttype'] ?? $parameters['accounttype'] ?? '';
321                 self::$accountType = User::getAccountTypeByString(self::$accountTypeString);
322
323                 self::$network = $get['nets'] ?? '';
324
325                 self::$dateFrom = $parameters['from'] ?? '';
326                 self::$dateTo = $parameters['to'] ?? '';
327
328                 if (DI::mode()->isMobile()) {
329                         self::$itemsPerPage = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
330                                 DI::config()->get('system', 'itemspage_network_mobile'));
331                 } else {
332                         self::$itemsPerPage = DI::pConfig()->get(local_user(), 'system', 'itemspage_network',
333                                 DI::config()->get('system', 'itemspage_network'));
334                 }
335
336                 self::$min_id = $get['min_id'] ?? null;
337                 self::$max_id = $get['max_id'] ?? null;
338
339                 switch (self::$order) {
340                         case 'received':
341                                 self::$max_id = $get['last_received'] ?? self::$max_id;
342                                 break;
343                         case 'created':
344                                 self::$max_id = $get['last_created'] ?? self::$max_id;
345                                 break;
346                         case 'uriid':
347                                 self::$max_id = $get['last_uriid'] ?? self::$max_id;
348                                 break;
349                         default:
350                                 self::$order = 'commented';
351                                 self::$max_id = $get['last_commented'] ?? self::$max_id;
352                 }
353         }
354
355         protected static function getItems(string $table, array $params, array $conditionFields = [])
356         {
357                 $conditionFields['uid'] = local_user();
358                 $conditionStrings = [];
359
360                 if (!is_null(self::$accountType)) {
361                         $conditionFields['contact-type'] = self::$accountType;
362                 }
363
364                 if (self::$star) {
365                         $conditionFields['starred'] = true;
366                 }
367                 if (self::$mention) {
368                         $conditionFields['mention'] = true;
369                 }
370                 if (self::$network) {
371                         $conditionFields['network'] = self::$network;
372                 }
373
374                 if (self::$dateFrom) {
375                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` <= ? ", DateTimeFormat::convert(self::$dateFrom, 'UTC', DI::app()->getTimeZone())]);
376                 }
377                 if (self::$dateTo) {
378                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` >= ? ", DateTimeFormat::convert(self::$dateTo, 'UTC', DI::app()->getTimeZone())]);
379                 }
380
381                 if (self::$groupId) {
382                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)", self::$groupId]);
383                 } elseif (self::$forumContactId) {
384                         $conditionStrings = DBA::mergeConditions($conditionStrings, 
385                                 ["((`contact-id` = ?) OR EXISTS(SELECT `uri-id` FROM `post-user-view` WHERE `post-user-view`.`parent-uri-id` = " . DBA::quoteIdentifier($table) . ".`uri-id` AND (`contact-id` = ? AND `gravity` = ? AND `vid` = ? AND `uid` = ?)))",
386                                 self::$forumContactId, self::$forumContactId, GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), local_user()]);
387                 }
388
389                 // Currently only the order modes "received" and "commented" are in use
390                 if (isset(self::$max_id)) {
391                         switch (self::$order) {
392                                 case 'received':
393                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` < ?", self::$max_id]);
394                                         break;
395                                 case 'commented':
396                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`commented` < ?", self::$max_id]);
397                                         break;
398                                 case 'created':
399                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`created` < ?", self::$max_id]);
400                                         break;
401                                 case 'uriid':
402                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`uri-id` < ?", self::$max_id]);
403                                         break;
404                         }
405                 }
406
407                 if (isset(self::$min_id)) {
408                         switch (self::$order) {
409                                 case 'received':
410                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` > ?", self::$min_id]);
411                                         break;
412                                 case 'commented':
413                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`commented` > ?", self::$min_id]);
414                                         break;
415                                 case 'created':
416                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`created` > ?", self::$min_id]);
417                                         break;
418                                 case 'uriid':
419                                         $conditionStrings = DBA::mergeConditions($conditionStrings, ["`uri-id` > ?", self::$min_id]);
420                                         break;
421                         }
422                 }
423
424                 if (isset(self::$min_id) && !isset(self::$max_id)) {
425                         // min_id quirk: querying in reverse order with min_id gets the most recent rows, regardless of how close
426                         // they are to min_id. We change the query ordering to get the expected data, and we need to reverse the
427                         // order of the results.
428                         $params['order'] = [self::$order => false];
429                 } else {
430                         $params['order'] = [self::$order => true];
431                 }
432
433                 $items = DBA::selectToArray($table, [], DBA::mergeConditions($conditionFields, $conditionStrings), $params);
434
435                 // min_id quirk, continued
436                 if (isset(self::$min_id) && !isset(self::$max_id)) {
437                         $items = array_reverse($items);
438                 }
439
440                 if (DBA::isResult($items)) {
441                         $parents = array_column($items, 'parent-uri-id');
442                 } else {
443                         $parents = [];
444                 }
445
446                 // We aren't going to try and figure out at the item, group, and page
447                 // level which items you've seen and which you haven't. If you're looking
448                 // at the top level network page just mark everything seen.
449                 if (!self::$groupId && !self::$forumContactId && !self::$star && !self::$mention) {
450                         $condition = ['unseen' => true, 'uid' => local_user()];
451                         self::setItemsSeenByCondition($condition);
452                 } elseif (!empty($parents)) {
453                         $condition = ['unseen' => true, 'uid' => local_user(), 'parent-uri-id' => $parents];
454                         self::setItemsSeenByCondition($condition);
455                 }
456
457                 return $items;
458         }
459 }