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