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