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