]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
Merge pull request #9578 from annando/onepoll-failed
[friendica.git] / src / Module / Contact.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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;
23
24 use Friendica\BaseModule;
25 use Friendica\Content\ContactSelector;
26 use Friendica\Content\Nav;
27 use Friendica\Content\Pager;
28 use Friendica\Content\Text\BBCode;
29 use Friendica\Content\Widget;
30 use Friendica\Core\ACL;
31 use Friendica\Core\Hook;
32 use Friendica\Core\Protocol;
33 use Friendica\Core\Renderer;
34 use Friendica\Core\Theme;
35 use Friendica\Core\Worker;
36 use Friendica\Database\DBA;
37 use Friendica\DI;
38 use Friendica\Model;
39 use Friendica\Model\User;
40 use Friendica\Module\Security\Login;
41 use Friendica\Network\HTTPException\BadRequestException;
42 use Friendica\Network\HTTPException\NotFoundException;
43 use Friendica\Util\DateTimeFormat;
44 use Friendica\Util\Strings;
45
46 /**
47  *  Manages and show Contacts and their content
48  */
49 class Contact extends BaseModule
50 {
51         const TAB_CONVERSATIONS = 1;
52         const TAB_POSTS = 2;
53         const TAB_PROFILE = 3;
54         const TAB_CONTACTS = 4;
55         const TAB_ADVANCED = 5;
56
57         private static function batchActions()
58         {
59                 if (empty($_POST['contact_batch']) || !is_array($_POST['contact_batch'])) {
60                         return;
61                 }
62
63                 $contacts_id = $_POST['contact_batch'];
64
65                 $stmt = DBA::select('contact', ['id', 'archive'], ['id' => $contacts_id, 'uid' => local_user(), 'self' => false, 'deleted' => false]);
66                 $orig_records = DBA::toArray($stmt);
67
68                 $count_actions = 0;
69                 foreach ($orig_records as $orig_record) {
70                         $contact_id = $orig_record['id'];
71                         if (!empty($_POST['contacts_batch_update'])) {
72                                 self::updateContactFromPoll($contact_id);
73                                 $count_actions++;
74                         }
75                         if (!empty($_POST['contacts_batch_block'])) {
76                                 self::blockContact($contact_id);
77                                 $count_actions++;
78                         }
79                         if (!empty($_POST['contacts_batch_ignore'])) {
80                                 self::ignoreContact($contact_id);
81                                 $count_actions++;
82                         }
83                         if (!empty($_POST['contacts_batch_archive'])
84                                 && self::archiveContact($contact_id, $orig_record)
85                         ) {
86                                 $count_actions++;
87                         }
88                         if (!empty($_POST['contacts_batch_drop'])) {
89                                 self::dropContact($orig_record);
90                                 $count_actions++;
91                         }
92                 }
93                 if ($count_actions > 0) {
94                         info(DI::l10n()->tt('%d contact edited.', '%d contacts edited.', $count_actions));
95                 }
96
97                 DI::baseUrl()->redirect('contact');
98         }
99
100         public static function post(array $parameters = [])
101         {
102                 $a = DI::app();
103
104                 if (!local_user()) {
105                         return;
106                 }
107
108                 // @TODO: Replace with parameter from router
109                 if ($a->argv[1] === 'batch') {
110                         self::batchActions();
111                         return;
112                 }
113
114                 // @TODO: Replace with parameter from router
115                 $contact_id = intval($a->argv[1]);
116                 if (!$contact_id) {
117                         return;
118                 }
119
120                 if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false])) {
121                         notice(DI::l10n()->t('Could not access contact record.'));
122                         DI::baseUrl()->redirect('contact');
123                         return; // NOTREACHED
124                 }
125
126                 Hook::callAll('contact_edit_post', $_POST);
127
128                 $hidden = !empty($_POST['hidden']);
129
130                 $notify = !empty($_POST['notify']);
131
132                 $fetch_further_information = intval($_POST['fetch_further_information'] ?? 0);
133
134                 $remote_self = $_POST['remote_self'] ?? false;
135
136                 $ffi_keyword_denylist = Strings::escapeHtml(trim($_POST['ffi_keyword_denylist'] ?? ''));
137
138                 $priority = intval($_POST['poll'] ?? 0);
139                 if ($priority > 5 || $priority < 0) {
140                         $priority = 0;
141                 }
142
143                 $info = Strings::escapeHtml(trim($_POST['info'] ?? ''));
144
145                 $r = DBA::update('contact', [
146                         'priority'   => $priority,
147                         'info'       => $info,
148                         'hidden'     => $hidden,
149                         'notify_new_posts' => $notify,
150                         'fetch_further_information' => $fetch_further_information,
151                         'remote_self' => $remote_self,
152                         'ffi_keyword_denylist'     => $ffi_keyword_denylist],
153                         ['id' => $contact_id, 'uid' => local_user()]
154                 );
155
156                 if (!DBA::isResult($r)) {
157                         notice(DI::l10n()->t('Failed to update contact record.'));
158                 }
159
160                 $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]);
161                 if (DBA::isResult($contact)) {
162                         $a->data['contact'] = $contact;
163                 }
164
165                 return;
166         }
167
168         /* contact actions */
169
170         private static function updateContactFromPoll($contact_id)
171         {
172                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]);
173                 if (!DBA::isResult($contact)) {
174                         return;
175                 }
176
177                 if ($contact['network'] == Protocol::OSTATUS) {
178                         $user = Model\User::getById($contact['uid']);
179                         $result = Model\Contact::createFromProbe($user, $contact['url'], false, $contact['network']);
180
181                         if ($result['success']) {
182                                 DBA::update('contact', ['subhub' => 1], ['id' => $contact_id]);
183                         }
184                 } else {
185                         // pull feed and consume it, which should subscribe to the hub.
186                         Worker::add(PRIORITY_HIGH, 'OnePoll', $contact_id, 'force');
187                 }
188         }
189
190         private static function updateContactFromProbe($contact_id)
191         {
192                 $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => [0, local_user()], 'deleted' => false]);
193                 if (!DBA::isResult($contact)) {
194                         return;
195                 }
196
197                 // Update the entry in the contact table
198                 Model\Contact::updateFromProbe($contact_id);
199         }
200
201         /**
202          * Toggles the blocked status of a contact identified by id.
203          *
204          * @param $contact_id
205          * @throws \Exception
206          */
207         private static function blockContact($contact_id)
208         {
209                 $blocked = !Model\Contact\User::isBlocked($contact_id, local_user());
210                 Model\Contact\User::setBlocked($contact_id, local_user(), $blocked);
211         }
212
213         /**
214          * Toggles the ignored status of a contact identified by id.
215          *
216          * @param $contact_id
217          * @throws \Exception
218          */
219         private static function ignoreContact($contact_id)
220         {
221                 $ignored = !Model\Contact\User::isIgnored($contact_id, local_user());
222                 Model\Contact\User::setIgnored($contact_id, local_user(), $ignored);
223         }
224
225         /**
226          * Toggles the archived status of a contact identified by id.
227          * If the current status isn't provided, this will always archive the contact.
228          *
229          * @param $contact_id
230          * @param $orig_record
231          * @return bool
232          * @throws \Exception
233          */
234         private static function archiveContact($contact_id, $orig_record)
235         {
236                 $archived = empty($orig_record['archive']);
237                 $r = DBA::update('contact', ['archive' => $archived], ['id' => $contact_id, 'uid' => local_user()]);
238
239                 return DBA::isResult($r);
240         }
241
242         private static function dropContact($orig_record)
243         {
244                 $owner = Model\User::getOwnerDataById(local_user());
245                 if (!DBA::isResult($owner)) {
246                         return;
247                 }
248
249                 Model\Contact::terminateFriendship($owner, $orig_record, true);
250                 Model\Contact::remove($orig_record['id']);
251         }
252
253         public static function content(array $parameters = [], $update = 0)
254         {
255                 if (!local_user()) {
256                         return Login::form($_SERVER['REQUEST_URI']);
257                 }
258
259                 $a = DI::app();
260
261                 $search = Strings::escapeTags(trim($_GET['search'] ?? ''));
262                 $nets   = Strings::escapeTags(trim($_GET['nets']   ?? ''));
263                 $rel    = Strings::escapeTags(trim($_GET['rel']    ?? ''));
264                 $group  = Strings::escapeTags(trim($_GET['group']  ?? ''));
265
266                 $accounttype = $_GET['accounttype'] ?? '';
267                 $accounttypeid = User::getAccountTypeByString($accounttype);
268
269                 $page = DI::page();
270
271                 $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
272                 $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
273                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
274                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
275
276                 $contact = null;
277                 // @TODO: Replace with parameter from router
278                 if ($a->argc == 2 && intval($a->argv[1])
279                         || $a->argc == 3 && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])
280                 ) {
281                         $contact_id = intval($a->argv[1]);
282
283                         // Ensure to use the user contact when the public contact was provided
284                         $data = Model\Contact::getPublicAndUserContacID($contact_id, local_user());
285                         if (!empty($data['user']) && ($contact_id == $data['public'])) {
286                                 $contact_id = $data['user'];
287                         }
288
289                         $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'deleted' => false]);
290
291                         // Don't display contacts that are about to be deleted
292                         if ($contact['network'] == Protocol::PHANTOM) {
293                                 $contact = false;
294                         }
295                 }
296
297                 if (DBA::isResult($contact)) {
298                         if ($contact['self']) {
299                                 // @TODO: Replace with parameter from router
300                                 if (($a->argc == 3) && intval($a->argv[1]) && in_array($a->argv[2], ['posts', 'conversations'])) {
301                                         DI::baseUrl()->redirect('profile/' . $contact['nick']);
302                                 } else {
303                                         DI::baseUrl()->redirect('profile/' . $contact['nick'] . '/profile');
304                                 }
305                         }
306
307                         $a->data['contact'] = $contact;
308
309                         if (($contact['network'] != '') && ($contact['network'] != Protocol::DFRN)) {
310                                 $network_link = Strings::formatNetworkName($contact['network'], $contact['url']);
311                         } else {
312                                 $network_link = '';
313                         }
314
315                         $follow_link = '';
316                         $unfollow_link = '';
317                         if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
318                                 if ($contact['uid'] && in_array($contact['rel'], [Model\Contact::SHARING, Model\Contact::FRIEND])) {
319                                         $unfollow_link = 'unfollow?url=' . urlencode($contact['url']) . '&auto=1';
320                                 } elseif(!$contact['pending']) {
321                                         $follow_link = 'follow?url=' . urlencode($contact['url']) . '&auto=1';
322                                 }
323                         }
324
325                         $wallmessage_link = '';
326                         if ($contact['uid'] && Model\Contact::canReceivePrivateMessages($contact)) {
327                                 $wallmessage_link = 'message/new/' . $contact['id'];
328                         }
329
330                         $vcard_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
331                                 '$name'         => $contact['name'],
332                                 '$photo'        => Model\Contact::getPhoto($contact),
333                                 '$url'          => Model\Contact::magicLinkByContact($contact, $contact['url']),
334                                 '$addr'         => $contact['addr'] ?? '',
335                                 '$network_link' => $network_link,
336                                 '$network'      => DI::l10n()->t('Network:'),
337                                 '$account_type' => Model\Contact::getAccountType($contact),
338                                 '$follow'       => DI::l10n()->t('Follow'),
339                                 '$follow_link'   => $follow_link,
340                                 '$unfollow'     => DI::l10n()->t('Unfollow'),
341                                 '$unfollow_link' => $unfollow_link,
342                                 '$wallmessage'  => DI::l10n()->t('Message'),
343                                 '$wallmessage_link' => $wallmessage_link,
344                         ]);
345
346                         $findpeople_widget = '';
347                         $follow_widget = '';
348                         $account_widget = '';
349                         $networks_widget = '';
350                         $rel_widget = '';
351
352                         if ($contact['uid'] != 0) {
353                                 $groups_widget = Model\Group::sidebarWidget('contact', 'group', 'full', 'everyone', $contact_id);
354                         } else {
355                                 $groups_widget = '';
356                         }
357                 } else {
358                         $vcard_widget = '';
359                         $findpeople_widget = Widget::findPeople();
360                         if (isset($_GET['add'])) {
361                                 $follow_widget = Widget::follow($_GET['add']);
362                         } else {
363                                 $follow_widget = Widget::follow();
364                         }
365
366                         $account_widget = Widget::accounttypes($_SERVER['REQUEST_URI'], $accounttype);
367                         $networks_widget = Widget::networks($_SERVER['REQUEST_URI'], $nets);
368                         $rel_widget = Widget::contactRels($_SERVER['REQUEST_URI'], $rel);
369                         $groups_widget = Widget::groups($_SERVER['REQUEST_URI'], $group);
370                 }
371
372                 DI::page()['aside'] .= $vcard_widget . $findpeople_widget . $follow_widget . $account_widget . $groups_widget . $networks_widget . $rel_widget;
373
374                 $tpl = Renderer::getMarkupTemplate('contacts-head.tpl');
375                 DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
376                         '$baseurl' => DI::baseUrl()->get(true),
377                 ]);
378
379                 $o = '';
380                 Nav::setSelected('contact');
381
382                 if (!local_user()) {
383                         notice(DI::l10n()->t('Permission denied.'));
384                         return Login::form();
385                 }
386
387                 if ($a->argc == 3) {
388                         $contact_id = intval($a->argv[1]);
389                         if (!$contact_id) {
390                                 throw new BadRequestException();
391                         }
392
393                         // @TODO: Replace with parameter from router
394                         $cmd = $a->argv[2];
395
396                         $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'self' => false, 'deleted' => false]);
397                         if (!DBA::isResult($orig_record)) {
398                                 throw new NotFoundException(DI::l10n()->t('Contact not found'));
399                         }
400
401                         if ($cmd === 'update' && ($orig_record['uid'] != 0)) {
402                                 self::updateContactFromPoll($contact_id);
403                                 DI::baseUrl()->redirect('contact/' . $contact_id);
404                                 // NOTREACHED
405                         }
406
407                         if ($cmd === 'updateprofile') {
408                                 self::updateContactFromProbe($contact_id);
409                                 DI::baseUrl()->redirect('contact/' . $contact_id);
410                                 // NOTREACHED
411                         }
412
413                         if ($cmd === 'block') {
414                                 self::blockContact($contact_id);
415
416                                 $blocked = Model\Contact\User::isBlocked($contact_id, local_user());
417                                 info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked')));
418
419                                 DI::baseUrl()->redirect('contact/' . $contact_id);
420                                 // NOTREACHED
421                         }
422
423                         if ($cmd === 'ignore') {
424                                 self::ignoreContact($contact_id);
425
426                                 $ignored = Model\Contact\User::isIgnored($contact_id, local_user());
427                                 info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored')));
428
429                                 DI::baseUrl()->redirect('contact/' . $contact_id);
430                                 // NOTREACHED
431                         }
432
433                         if ($cmd === 'archive' && ($orig_record['uid'] != 0)) {
434                                 $r = self::archiveContact($contact_id, $orig_record);
435                                 if ($r) {
436                                         $archived = (($orig_record['archive']) ? 0 : 1);
437                                         info((($archived) ? DI::l10n()->t('Contact has been archived') : DI::l10n()->t('Contact has been unarchived')));
438                                 }
439
440                                 DI::baseUrl()->redirect('contact/' . $contact_id);
441                                 // NOTREACHED
442                         }
443
444                         if ($cmd === 'drop' && ($orig_record['uid'] != 0)) {
445                                 // Check if we should do HTML-based delete confirmation
446                                 if (!empty($_REQUEST['confirm'])) {
447                                         DI::page()['aside'] = '';
448
449                                         return Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_drop_confirm.tpl'), [
450                                                 '$header' => DI::l10n()->t('Drop contact'),
451                                                 '$contact' => self::getContactTemplateVars($orig_record),
452                                                 '$method' => 'get',
453                                                 '$message' => DI::l10n()->t('Do you really want to delete this contact?'),
454                                                 '$confirm' => DI::l10n()->t('Yes'),
455                                                 '$confirm_url' => DI::args()->getCommand(),
456                                                 '$confirm_name' => 'confirmed',
457                                                 '$cancel' => DI::l10n()->t('Cancel'),
458                                         ]);
459                                 }
460                                 // Now check how the user responded to the confirmation query
461                                 if (!empty($_REQUEST['canceled'])) {
462                                         DI::baseUrl()->redirect('contact');
463                                 }
464
465                                 self::dropContact($orig_record);
466                                 info(DI::l10n()->t('Contact has been removed.'));
467
468                                 DI::baseUrl()->redirect('contact');
469                                 // NOTREACHED
470                         }
471                         if ($cmd === 'posts') {
472                                 return self::getPostsHTML($a, $contact_id);
473                         }
474                         if ($cmd === 'conversations') {
475                                 return self::getConversationsHMTL($a, $contact_id, $update);
476                         }
477                 }
478
479                 $_SESSION['return_path'] = DI::args()->getQueryString();
480
481                 if (!empty($a->data['contact']) && is_array($a->data['contact'])) {
482                         $contact = $a->data['contact'];
483
484                         DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
485                                 '$baseurl' => DI::baseUrl()->get(true),
486                         ]);
487
488                         $contact['blocked']  = Model\Contact\User::isBlocked($contact['id'], local_user());
489                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
490
491                         $relation_text = '';
492                         switch ($contact['rel']) {
493                                 case Model\Contact::FRIEND:
494                                         $relation_text = DI::l10n()->t('You are mutual friends with %s');
495                                         break;
496
497                                 case Model\Contact::FOLLOWER;
498                                         $relation_text = DI::l10n()->t('You are sharing with %s');
499                                         break;
500
501                                 case Model\Contact::SHARING;
502                                         $relation_text = DI::l10n()->t('%s is sharing with you');
503                                         break;
504
505                                 default:
506                                         break;
507                         }
508
509                         if ($contact['uid'] == 0) {
510                                 $relation_text = '';
511                         }
512
513                         if (!in_array($contact['network'], array_merge(Protocol::FEDERATED, [Protocol::TWITTER]))) {
514                                 $relation_text = '';
515                         }
516
517                         $relation_text = sprintf($relation_text, $contact['name']);
518
519                         $url = Model\Contact::magicLink($contact['url']);
520                         if (strpos($url, 'redir/') === 0) {
521                                 $sparkle = ' class="sparkle" ';
522                         } else {
523                                 $sparkle = '';
524                         }
525
526                         $insecure = DI::l10n()->t('Private communications are not available for this contact.');
527
528                         $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? DI::l10n()->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
529
530                         if ($contact['last-update'] > DBA::NULL_DATETIME) {
531                                 $last_update .= ' ' . ($contact['failed'] ? DI::l10n()->t('(Update was not successful)') : DI::l10n()->t('(Update was successful)'));
532                         }
533                         $lblsuggest = (($contact['network'] === Protocol::DFRN) ? DI::l10n()->t('Suggest friends') : '');
534
535                         $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
536
537                         $nettype = DI::l10n()->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']));
538
539                         // tabs
540                         $tab_str = self::getTabsHTML($contact, self::TAB_PROFILE);
541
542                         $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? DI::l10n()->t('Communications lost with this contact!') : '');
543
544                         $fetch_further_information = null;
545                         if ($contact['network'] == Protocol::FEED) {
546                                 $fetch_further_information = [
547                                         'fetch_further_information',
548                                         DI::l10n()->t('Fetch further information for feeds'),
549                                         $contact['fetch_further_information'],
550                                         DI::l10n()->t('Fetch information like preview pictures, title and teaser from the feed item. You can activate this if the feed doesn\'t contain much text. Keywords are taken from the meta header in the feed item and are posted as hash tags.'),
551                                         [
552                                                 '0' => DI::l10n()->t('Disabled'),
553                                                 '1' => DI::l10n()->t('Fetch information'),
554                                                 '3' => DI::l10n()->t('Fetch keywords'),
555                                                 '2' => DI::l10n()->t('Fetch information and keywords')
556                                         ]
557                                 ];
558                         }
559
560                         // Disable remote self for everything except feeds.
561                         // There is an issue when you repeat an item from maybe twitter and you got comments from friendica and twitter
562                         // Problem is, you couldn't reply to both networks.
563                         $allow_remote_self = in_array($contact['network'], [Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
564                                 && DI::config()->get('system', 'allow_users_remote_self');
565
566                         if ($contact['network'] == Protocol::FEED) {
567                                 $remote_self_options = ['0' => DI::l10n()->t('No mirroring'), '1' => DI::l10n()->t('Mirror as forwarded posting'), '2' => DI::l10n()->t('Mirror as my own posting')];
568                         } else {
569                                 $remote_self_options = ['0' => DI::l10n()->t('No mirroring'), '2' => DI::l10n()->t('Mirror as my own posting')];
570                         }
571
572                         $poll_interval = null;
573                         if ((($contact['network'] == Protocol::FEED) && !DI::config()->get('system', 'adjust_poll_frequency')) || ($contact['network']== Protocol::MAIL)) {
574                                 $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled);
575                         }
576
577                         // Load contactact related actions like hide, suggest, delete and others
578                         $contact_actions = self::getContactActions($contact);
579
580                         if ($contact['uid'] != 0) {
581                                 $lbl_info1 = DI::l10n()->t('Contact Information / Notes');
582                                 $contact_settings_label = DI::l10n()->t('Contact Settings');
583                         } else {
584                                 $lbl_info1 = null;
585                                 $contact_settings_label = null;
586                         }
587
588                         $tpl = Renderer::getMarkupTemplate('contact_edit.tpl');
589                         $o .= Renderer::replaceMacros($tpl, [
590                                 '$header'         => DI::l10n()->t('Contact'),
591                                 '$tab_str'        => $tab_str,
592                                 '$submit'         => DI::l10n()->t('Submit'),
593                                 '$lbl_info1'      => $lbl_info1,
594                                 '$lbl_info2'      => DI::l10n()->t('Their personal note'),
595                                 '$reason'         => trim(Strings::escapeTags($contact['reason'])),
596                                 '$infedit'        => DI::l10n()->t('Edit contact notes'),
597                                 '$common_link'    => 'contact/' . $contact['id'] . '/contacts/common',
598                                 '$relation_text'  => $relation_text,
599                                 '$visit'          => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
600                                 '$blockunblock'   => DI::l10n()->t('Block/Unblock contact'),
601                                 '$ignorecont'     => DI::l10n()->t('Ignore contact'),
602                                 '$lblrecent'      => DI::l10n()->t('View conversations'),
603                                 '$lblsuggest'     => $lblsuggest,
604                                 '$nettype'        => $nettype,
605                                 '$poll_interval'  => $poll_interval,
606                                 '$poll_enabled'   => $poll_enabled,
607                                 '$lastupdtext'    => DI::l10n()->t('Last update:'),
608                                 '$lost_contact'   => $lost_contact,
609                                 '$updpub'         => DI::l10n()->t('Update public posts'),
610                                 '$last_update'    => $last_update,
611                                 '$udnow'          => DI::l10n()->t('Update now'),
612                                 '$contact_id'     => $contact['id'],
613                                 '$block_text'     => ($contact['blocked'] ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
614                                 '$ignore_text'    => ($contact['readonly'] ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
615                                 '$insecure'       => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
616                                 '$info'           => $contact['info'],
617                                 '$cinfo'          => ['info', '', $contact['info'], ''],
618                                 '$blocked'        => ($contact['blocked'] ? DI::l10n()->t('Currently blocked') : ''),
619                                 '$ignored'        => ($contact['readonly'] ? DI::l10n()->t('Currently ignored') : ''),
620                                 '$archived'       => ($contact['archive'] ? DI::l10n()->t('Currently archived') : ''),
621                                 '$pending'        => ($contact['pending'] ? DI::l10n()->t('Awaiting connection acknowledge') : ''),
622                                 '$hidden'         => ['hidden', DI::l10n()->t('Hide this contact from others'), ($contact['hidden'] == 1), DI::l10n()->t('Replies/likes to your public posts <strong>may</strong> still be visible')],
623                                 '$notify'         => ['notify', DI::l10n()->t('Notification for new posts'), ($contact['notify_new_posts'] == 1), DI::l10n()->t('Send a notification of every new post of this contact')],
624                                 '$fetch_further_information' => $fetch_further_information,
625                                 '$ffi_keyword_denylist' => ['ffi_keyword_denylist', DI::l10n()->t('Keyword Deny List'), $contact['ffi_keyword_denylist'], DI::l10n()->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')],
626                                 '$photo'          => Model\Contact::getPhoto($contact),
627                                 '$name'           => $contact['name'],
628                                 '$sparkle'        => $sparkle,
629                                 '$url'            => $url,
630                                 '$profileurllabel'=> DI::l10n()->t('Profile URL'),
631                                 '$profileurl'     => $contact['url'],
632                                 '$account_type'   => Model\Contact::getAccountType($contact),
633                                 '$location'       => BBCode::convert($contact['location']),
634                                 '$location_label' => DI::l10n()->t('Location:'),
635                                 '$xmpp'           => BBCode::convert($contact['xmpp']),
636                                 '$xmpp_label'     => DI::l10n()->t('XMPP:'),
637                                 '$about'          => BBCode::convert($contact['about'], false),
638                                 '$about_label'    => DI::l10n()->t('About:'),
639                                 '$keywords'       => $contact['keywords'],
640                                 '$keywords_label' => DI::l10n()->t('Tags:'),
641                                 '$contact_action_button' => DI::l10n()->t('Actions'),
642                                 '$contact_actions'=> $contact_actions,
643                                 '$contact_status' => DI::l10n()->t('Status'),
644                                 '$contact_settings_label' => $contact_settings_label,
645                                 '$contact_profile_label' => DI::l10n()->t('Profile'),
646                                 '$allow_remote_self' => $allow_remote_self,
647                                 '$remote_self'       => ['remote_self',
648                                         DI::l10n()->t('Mirror postings from this contact'),
649                                         $contact['remote_self'],
650                                         DI::l10n()->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
651                                         $remote_self_options
652                                 ],      
653                         ]);
654
655                         $arr = ['contact' => $contact, 'output' => $o];
656
657                         Hook::callAll('contact_edit', $arr);
658
659                         return $arr['output'];
660                 }
661
662                 $sql_values = [local_user()];
663
664                 // @TODO: Replace with parameter from router
665                 $type = $a->argv[1] ?? '';
666
667                 switch ($type) {
668                         case 'blocked':
669                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`blocked`)";
670                                 // This makes the query look for contact.uid = 0
671                                 array_unshift($sql_values, 0);
672                                 break;
673                         case 'hidden':
674                                 $sql_extra = " AND `hidden` AND NOT `blocked` AND NOT `pending`";
675                                 break;
676                         case 'ignored':
677                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`ignored`)";
678                                 // This makes the query look for contact.uid = 0
679                                 array_unshift($sql_values, 0);
680                                 break;
681                         case 'archived':
682                                 $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`";
683                                 break;
684                         case 'pending':
685                                 $sql_extra = " AND `pending` AND NOT `archive` AND NOT `failed` AND ((`rel` = ?)
686                                         OR EXISTS (SELECT `id` FROM `intro` WHERE `contact-id` = `contact`.`id` AND NOT `ignore`))";
687                                 $sql_values[] = Model\Contact::SHARING;
688                                 break;
689                         default:
690                                 $sql_extra = " AND NOT `archive` AND NOT `blocked` AND NOT `pending`";
691                                 break;
692                 }
693
694                 if (isset($accounttypeid)) {
695                         $sql_extra .= " AND `contact-type` = ?";
696                         $sql_values[] = $accounttypeid;
697                 }
698
699                 $searching = false;
700                 $search_hdr = null;
701                 if ($search) {
702                         $searching = true;
703                         $search_hdr = $search;
704                         $search_txt = preg_quote($search);
705                         $sql_extra .= " AND (name REGEXP ? OR url REGEXP ? OR nick REGEXP ?)";
706                         $sql_values[] = $search_txt;
707                         $sql_values[] = $search_txt;
708                         $sql_values[] = $search_txt;
709                 }
710
711                 if ($nets) {
712                         $sql_extra .= " AND network = ? ";
713                         $sql_values[] = $nets;
714                 }
715
716                 switch ($rel) {
717                         case 'followers':
718                                 $sql_extra .= " AND `rel` IN (?, ?)";
719                                 $sql_values[] = Model\Contact::FOLLOWER;
720                                 $sql_values[] = Model\Contact::FRIEND;
721                                 break;
722                         case 'following':
723                                 $sql_extra .= " AND `rel` IN (?, ?)";
724                                 $sql_values[] = Model\Contact::SHARING;
725                                 $sql_values[] = Model\Contact::FRIEND;
726                                 break;
727                         case 'mutuals':
728                                 $sql_extra .= " AND `rel` = ?";
729                                 $sql_values[] = Model\Contact::FRIEND;
730                                 break;
731                 }
732
733                 if ($group) {
734                         $sql_extra = " AND EXISTS(SELECT `id` FROM `group_member` WHERE `gid` = ? AND `contact`.`id` = `contact-id`)";
735                         $sql_values[] = $group;
736                 }
737
738                 $total = 0;
739                 $stmt = DBA::p("SELECT COUNT(*) AS `total`
740                         FROM `contact`
741                         WHERE `uid` = ?
742                         AND `self` = 0
743                         AND NOT `deleted`
744                         $sql_extra
745                         " . Widget::unavailableNetworks(),
746                         $sql_values
747                 );
748                 if (DBA::isResult($stmt)) {
749                         $total = DBA::fetch($stmt)['total'];
750                 }
751                 DBA::close($stmt);
752
753                 $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
754
755                 $sql_values[] = $pager->getStart();
756                 $sql_values[] = $pager->getItemsPerPage();
757
758                 $contacts = [];
759
760                 $stmt = DBA::p("SELECT *
761                         FROM `contact`
762                         WHERE `uid` = ?
763                         AND `self` = 0
764                         AND NOT `deleted`
765                         $sql_extra
766                         ORDER BY `name` ASC
767                         LIMIT ?, ?",
768                         $sql_values
769                 );
770                 while ($contact = DBA::fetch($stmt)) {
771                         $contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], local_user());
772                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
773                         $contacts[] = self::getContactTemplateVars($contact);
774                 }
775                 DBA::close($stmt);
776
777                 $tabs = [
778                         [
779                                 'label' => DI::l10n()->t('All Contacts'),
780                                 'url'   => 'contact',
781                                 'sel'   => !$type ? 'active' : '',
782                                 'title' => DI::l10n()->t('Show all contacts'),
783                                 'id'    => 'showall-tab',
784                                 'accesskey' => 'l',
785                         ],
786                         [
787                                 'label' => DI::l10n()->t('Pending'),
788                                 'url'   => 'contact/pending',
789                                 'sel'   => $type == 'pending' ? 'active' : '',
790                                 'title' => DI::l10n()->t('Only show pending contacts'),
791                                 'id'    => 'showpending-tab',
792                                 'accesskey' => 'p',
793                         ],
794                         [
795                                 'label' => DI::l10n()->t('Blocked'),
796                                 'url'   => 'contact/blocked',
797                                 'sel'   => $type == 'blocked' ? 'active' : '',
798                                 'title' => DI::l10n()->t('Only show blocked contacts'),
799                                 'id'    => 'showblocked-tab',
800                                 'accesskey' => 'b',
801                         ],
802                         [
803                                 'label' => DI::l10n()->t('Ignored'),
804                                 'url'   => 'contact/ignored',
805                                 'sel'   => $type == 'ignored' ? 'active' : '',
806                                 'title' => DI::l10n()->t('Only show ignored contacts'),
807                                 'id'    => 'showignored-tab',
808                                 'accesskey' => 'i',
809                         ],
810                         [
811                                 'label' => DI::l10n()->t('Archived'),
812                                 'url'   => 'contact/archived',
813                                 'sel'   => $type == 'archived' ? 'active' : '',
814                                 'title' => DI::l10n()->t('Only show archived contacts'),
815                                 'id'    => 'showarchived-tab',
816                                 'accesskey' => 'y',
817                         ],
818                         [
819                                 'label' => DI::l10n()->t('Hidden'),
820                                 'url'   => 'contact/hidden',
821                                 'sel'   => $type == 'hidden' ? 'active' : '',
822                                 'title' => DI::l10n()->t('Only show hidden contacts'),
823                                 'id'    => 'showhidden-tab',
824                                 'accesskey' => 'h',
825                         ],
826                         [
827                                 'label' => DI::l10n()->t('Groups'),
828                                 'url'   => 'group',
829                                 'sel'   => '',
830                                 'title' => DI::l10n()->t('Organize your contact groups'),
831                                 'id'    => 'contactgroups-tab',
832                                 'accesskey' => 'e',
833                         ],
834                 ];
835
836                 $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
837                 $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
838
839                 switch ($rel) {
840                         case 'followers': $header = DI::l10n()->t('Followers'); break;
841                         case 'following': $header = DI::l10n()->t('Following'); break;
842                         case 'mutuals':   $header = DI::l10n()->t('Mutual friends'); break;
843                         default:          $header = DI::l10n()->t('Contacts');
844                 }
845
846                 switch ($type) {
847                         case 'pending':  $header .= ' - ' . DI::l10n()->t('Pending'); break;
848                         case 'blocked':  $header .= ' - ' . DI::l10n()->t('Blocked'); break;
849                         case 'hidden':   $header .= ' - ' . DI::l10n()->t('Hidden'); break;
850                         case 'ignored':  $header .= ' - ' . DI::l10n()->t('Ignored'); break;
851                         case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break;
852                 }
853
854                 $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
855
856                 $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
857                 $o .= Renderer::replaceMacros($tpl, [
858                         '$header'     => $header,
859                         '$tabs'       => $tabs_html,
860                         '$total'      => $total,
861                         '$search'     => $search_hdr,
862                         '$desc'       => DI::l10n()->t('Search your contacts'),
863                         '$finding'    => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
864                         '$submit'     => DI::l10n()->t('Find'),
865                         '$cmd'        => DI::args()->getCommand(),
866                         '$contacts'   => $contacts,
867                         '$contact_drop_confirm' => DI::l10n()->t('Do you really want to delete this contact?'),
868                         'multiselect' => 1,
869                         '$batch_actions' => [
870                                 'contacts_batch_update'  => DI::l10n()->t('Update'),
871                                 'contacts_batch_block'   => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
872                                 'contacts_batch_ignore'  => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
873                                 'contacts_batch_archive' => DI::l10n()->t('Archive') . '/' . DI::l10n()->t('Unarchive'),
874                                 'contacts_batch_drop'    => DI::l10n()->t('Delete'),
875                         ],
876                         '$h_batch_actions' => DI::l10n()->t('Batch Actions'),
877                         '$paginate'   => $pager->renderFull($total),
878                 ]);
879
880                 return $o;
881         }
882
883         /**
884          * List of pages for the Contact TabBar
885          *
886          * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
887          *
888          * @param array $contact    The contact array
889          * @param int   $active_tab 1 if tab should be marked as active
890          *
891          * @return string HTML string of the contact page tabs buttons.
892          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
893          * @throws \ImagickException
894          */
895         public static function getTabsHTML(array $contact, int $active_tab)
896         {
897                 $cid = $pcid = $contact['id'];
898                 $data = Model\Contact::getPublicAndUserContacID($contact['id'], local_user());
899                 if (!empty($data['user']) && ($contact['id'] == $data['public'])) {
900                         $cid = $data['user'];
901                 } elseif (!empty($data['public'])) {
902                         $pcid = $data['public'];
903                 }
904
905                 // tabs
906                 $tabs = [
907                         [
908                                 'label' => DI::l10n()->t('Status'),
909                                 'url'   => 'contact/' . $pcid . '/conversations',
910                                 'sel'   => (($active_tab == self::TAB_CONVERSATIONS) ? 'active' : ''),
911                                 'title' => DI::l10n()->t('Conversations started by this contact'),
912                                 'id'    => 'status-tab',
913                                 'accesskey' => 'm',
914                         ],
915                         [
916                                 'label' => DI::l10n()->t('Posts and Comments'),
917                                 'url'   => 'contact/' . $pcid . '/posts',
918                                 'sel'   => (($active_tab == self::TAB_POSTS) ? 'active' : ''),
919                                 'title' => DI::l10n()->t('Status Messages and Posts'),
920                                 'id'    => 'posts-tab',
921                                 'accesskey' => 'p',
922                         ],
923                         [
924                                 'label' => DI::l10n()->t('Profile'),
925                                 'url'   => 'contact/' . $cid,
926                                 'sel'   => (($active_tab == self::TAB_PROFILE) ? 'active' : ''),
927                                 'title' => DI::l10n()->t('Profile Details'),
928                                 'id'    => 'profile-tab',
929                                 'accesskey' => 'o',
930                         ],
931                         ['label' => DI::l10n()->t('Contacts'),
932                                 'url'   => 'contact/' . $pcid . '/contacts',
933                                 'sel'   => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''),
934                                 'title' => DI::l10n()->t('View all known contacts'),
935                                 'id'    => 'contacts-tab',
936                                 'accesskey' => 't'
937                         ],
938                 ];
939
940                 if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) {
941                         $tabs[] = ['label' => DI::l10n()->t('Advanced'),
942                                 'url'   => 'contact/' . $cid . '/advanced/',
943                                 'sel'   => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''),
944                                 'title' => DI::l10n()->t('Advanced Contact Settings'),
945                                 'id'    => 'advanced-tab',
946                                 'accesskey' => 'r'
947                         ];
948                 }
949
950                 $tab_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
951                 $tab_str = Renderer::replaceMacros($tab_tpl, ['$tabs' => $tabs]);
952
953                 return $tab_str;
954         }
955
956         public static function getConversationsHMTL($a, $contact_id, $update, $parent = 0)
957         {
958                 $o = '';
959
960                 if (!$update) {
961                         // We need the editor here to be able to reshare an item.
962                         if (local_user()) {
963                                 $x = [
964                                         'is_owner' => true,
965                                         'allow_location' => $a->user['allow_location'],
966                                         'default_location' => $a->user['default-location'],
967                                         'nickname' => $a->user['nickname'],
968                                         'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
969                                         'acl' => ACL::getFullSelectorHTML(DI::page(), $a->user, true),
970                                         'bang' => '',
971                                         'visitor' => 'block',
972                                         'profile_uid' => local_user(),
973                                 ];
974                                 $o = status_editor($a, $x, 0, true);
975                         }
976                 }
977
978                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
979
980                 if (!$update) {
981                         $o .= self::getTabsHTML($contact, self::TAB_CONVERSATIONS);
982                 }
983
984                 if (DBA::isResult($contact)) {
985                         DI::page()['aside'] = '';
986
987                         if (!$update) {
988                                 $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
989                                 Model\Profile::load($a, '', $profiledata, true);
990                         }
991
992                         if ($contact['uid'] == 0) {
993                                 $o .= Model\Contact::getPostsFromId($contact['id'], true, $update, $parent);
994                         } else {
995                                 $o .= Model\Contact::getPostsFromUrl($contact['url'], true, $update, $parent);
996                         }
997                 }
998
999                 return $o;
1000         }
1001
1002         private static function getPostsHTML($a, $contact_id)
1003         {
1004                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
1005
1006                 $o = self::getTabsHTML($contact, self::TAB_POSTS);
1007
1008                 if (DBA::isResult($contact)) {
1009                         DI::page()['aside'] = '';
1010
1011                         $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
1012
1013                         if (local_user() && in_array($profiledata['network'], Protocol::FEDERATED)) {
1014                                 $profiledata['remoteconnect'] = DI::baseUrl() . '/follow?url=' . urlencode($profiledata['url']);
1015                         }
1016
1017                         Model\Profile::load($a, '', $profiledata, true);
1018
1019                         if ($contact['uid'] == 0) {
1020                                 $o .= Model\Contact::getPostsFromId($contact['id']);
1021                         } else {
1022                                 $o .= Model\Contact::getPostsFromUrl($contact['url']);
1023                         }
1024                 }
1025
1026                 return $o;
1027         }
1028
1029         /**
1030          * Return the fields for the contact template
1031          *
1032          * @param array $contact Contact array
1033          * @return array Template fields
1034          */
1035         public static function getContactTemplateVars(array $contact)
1036         {
1037                 $alt_text = '';
1038
1039                 if (!empty($contact['url']) && isset($contact['uid']) && ($contact['uid'] == 0) && local_user()) {
1040                         $personal = Model\Contact::getByURL($contact['url'], false, ['uid', 'rel', 'self'], local_user());
1041                         if (!empty($personal)) {
1042                                 $contact['uid'] = $personal['uid'];
1043                                 $contact['rel'] = $personal['rel'];
1044                                 $contact['self'] = $personal['self'];
1045                         }
1046                 }
1047
1048                 if (!empty($contact['uid']) && !empty($contact['rel']) && local_user() == $contact['uid']) {
1049                         switch ($contact['rel']) {
1050                                 case Model\Contact::FRIEND:
1051                                         $alt_text = DI::l10n()->t('Mutual Friendship');
1052                                         break;
1053
1054                                 case Model\Contact::FOLLOWER;
1055                                         $alt_text = DI::l10n()->t('is a fan of yours');
1056                                         break;
1057
1058                                 case Model\Contact::SHARING;
1059                                         $alt_text = DI::l10n()->t('you are a fan of');
1060                                         break;
1061
1062                                 default:
1063                                         break;
1064                         }
1065                 }
1066
1067                 $url = Model\Contact::magicLink($contact['url']);
1068
1069                 if (strpos($url, 'redir/') === 0) {
1070                         $sparkle = ' class="sparkle" ';
1071                 } else {
1072                         $sparkle = '';
1073                 }
1074
1075                 if ($contact['pending']) {
1076                         if (in_array($contact['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) {
1077                                 $alt_text = DI::l10n()->t('Pending outgoing contact request');
1078                         } else {
1079                                 $alt_text = DI::l10n()->t('Pending incoming contact request');
1080                         }
1081                 }
1082
1083                 if ($contact['self']) {
1084                         $alt_text = DI::l10n()->t('This is you');
1085                         $url = $contact['url'];
1086                         $sparkle = '';
1087                 }
1088
1089                 return [
1090                         'id'           => $contact['id'],
1091                         'url'          => $url,
1092                         'img_hover'    => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
1093                         'photo_menu'   => Model\Contact::photoMenu($contact),
1094                         'thumb'        => Model\Contact::getThumb($contact),
1095                         'alt_text'     => $alt_text,
1096                         'name'         => $contact['name'],
1097                         'nick'         => $contact['nick'],
1098                         'details'      => $contact['location'], 
1099                         'tags'         => $contact['keywords'],
1100                         'about'        => $contact['about'],
1101                         'account_type' => Model\Contact::getAccountType($contact),
1102                         'sparkle'      => $sparkle,
1103                         'itemurl'      => ($contact['addr'] ?? '') ?: $contact['url'],
1104                         'network'      => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']),
1105                 ];
1106         }
1107
1108         /**
1109          * Gives a array with actions which can performed to a given contact
1110          *
1111          * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
1112          *
1113          * @param array $contact Data about the Contact
1114          * @return array with contact related actions
1115          */
1116         private static function getContactActions($contact)
1117         {
1118                 $poll_enabled = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
1119                 $contact_actions = [];
1120
1121                 // Provide friend suggestion only for Friendica contacts
1122                 if ($contact['network'] === Protocol::DFRN) {
1123                         $contact_actions['suggest'] = [
1124                                 'label' => DI::l10n()->t('Suggest friends'),
1125                                 'url'   => 'fsuggest/' . $contact['id'],
1126                                 'title' => '',
1127                                 'sel'   => '',
1128                                 'id'    => 'suggest',
1129                         ];
1130                 }
1131
1132                 if ($poll_enabled) {
1133                         $contact_actions['update'] = [
1134                                 'label' => DI::l10n()->t('Update now'),
1135                                 'url'   => 'contact/' . $contact['id'] . '/update',
1136                                 'title' => '',
1137                                 'sel'   => '',
1138                                 'id'    => 'update',
1139                         ];
1140                 }
1141
1142                 if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
1143                         $contact_actions['updateprofile'] = [
1144                                 'label' => DI::l10n()->t('Refetch contact data'),
1145                                 'url'   => 'contact/' . $contact['id'] . '/updateprofile',
1146                                 'title' => '',
1147                                 'sel'   => '',
1148                                 'id'    => 'updateprofile',
1149                         ];
1150                 }
1151
1152                 $contact_actions['block'] = [
1153                         'label' => (intval($contact['blocked']) ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
1154                         'url'   => 'contact/' . $contact['id'] . '/block',
1155                         'title' => DI::l10n()->t('Toggle Blocked status'),
1156                         'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1157                         'id'    => 'toggle-block',
1158                 ];
1159
1160                 $contact_actions['ignore'] = [
1161                         'label' => (intval($contact['readonly']) ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
1162                         'url'   => 'contact/' . $contact['id'] . '/ignore',
1163                         'title' => DI::l10n()->t('Toggle Ignored status'),
1164                         'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1165                         'id'    => 'toggle-ignore',
1166                 ];
1167
1168                 if ($contact['uid'] != 0) {
1169                         $contact_actions['archive'] = [
1170                                 'label' => (intval($contact['archive']) ? DI::l10n()->t('Unarchive') : DI::l10n()->t('Archive')),
1171                                 'url'   => 'contact/' . $contact['id'] . '/archive',
1172                                 'title' => DI::l10n()->t('Toggle Archive status'),
1173                                 'sel'   => (intval($contact['archive']) ? 'active' : ''),
1174                                 'id'    => 'toggle-archive',
1175                         ];
1176
1177                         $contact_actions['delete'] = [
1178                                 'label' => DI::l10n()->t('Delete'),
1179                                 'url'   => 'contact/' . $contact['id'] . '/drop',
1180                                 'title' => DI::l10n()->t('Delete contact'),
1181                                 'sel'   => '',
1182                                 'id'    => 'delete',
1183                         ];
1184                 }
1185
1186                 return $contact_actions;
1187         }
1188 }