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