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