]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
Add group membership filter widget in contact list
[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
330                         if ($contact['uid'] != 0) {
331                                 $groups_widget = Model\Group::sidebarWidget('contact', 'group', 'full', 'everyone', $contact_id);
332                         } else {
333                                 $groups_widget = '';
334                         }
335                 } else {
336                         $vcard_widget = '';
337                         $findpeople_widget = Widget::findPeople();
338                         if (isset($_GET['add'])) {
339                                 $follow_widget = Widget::follow($_GET['add']);
340                         } else {
341                                 $follow_widget = Widget::follow();
342                         }
343
344                         $networks_widget = Widget::networks($_SERVER['REQUEST_URI'], $nets);
345                         $rel_widget = Widget::contactRels($_SERVER['REQUEST_URI'], $rel);
346                         $groups_widget = Widget::groups($_SERVER['REQUEST_URI'], $group);
347                 }
348
349                 DI::page()['aside'] .= $vcard_widget . $findpeople_widget . $follow_widget . $groups_widget . $networks_widget . $rel_widget;
350
351                 $tpl = Renderer::getMarkupTemplate('contacts-head.tpl');
352                 DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
353                         '$baseurl' => DI::baseUrl()->get(true),
354                 ]);
355
356                 $o = '';
357                 Nav::setSelected('contact');
358
359                 if (!local_user()) {
360                         notice(DI::l10n()->t('Permission denied.') . EOL);
361                         return Login::form();
362                 }
363
364                 if ($a->argc == 3) {
365                         $contact_id = intval($a->argv[1]);
366                         if (!$contact_id) {
367                                 throw new BadRequestException();
368                         }
369
370                         // @TODO: Replace with parameter from router
371                         $cmd = $a->argv[2];
372
373                         $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'self' => false, 'deleted' => false]);
374                         if (!DBA::isResult($orig_record)) {
375                                 throw new NotFoundException(DI::l10n()->t('Contact not found'));
376                         }
377
378                         if ($cmd === 'update' && ($orig_record['uid'] != 0)) {
379                                 self::updateContactFromPoll($contact_id);
380                                 DI::baseUrl()->redirect('contact/' . $contact_id);
381                                 // NOTREACHED
382                         }
383
384                         if ($cmd === 'updateprofile' && ($orig_record['uid'] != 0)) {
385                                 self::updateContactFromProbe($contact_id);
386                                 DI::baseUrl()->redirect('crepair/' . $contact_id);
387                                 // NOTREACHED
388                         }
389
390                         if ($cmd === 'block') {
391                                 self::blockContact($contact_id);
392
393                                 $blocked = Model\Contact::isBlockedByUser($contact_id, local_user());
394                                 info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked')) . EOL);
395
396                                 DI::baseUrl()->redirect('contact/' . $contact_id);
397                                 // NOTREACHED
398                         }
399
400                         if ($cmd === 'ignore') {
401                                 self::ignoreContact($contact_id);
402
403                                 $ignored = Model\Contact::isIgnoredByUser($contact_id, local_user());
404                                 info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored')) . EOL);
405
406                                 DI::baseUrl()->redirect('contact/' . $contact_id);
407                                 // NOTREACHED
408                         }
409
410                         if ($cmd === 'archive' && ($orig_record['uid'] != 0)) {
411                                 $r = self::archiveContact($contact_id, $orig_record);
412                                 if ($r) {
413                                         $archived = (($orig_record['archive']) ? 0 : 1);
414                                         info((($archived) ? DI::l10n()->t('Contact has been archived') : DI::l10n()->t('Contact has been unarchived')) . EOL);
415                                 }
416
417                                 DI::baseUrl()->redirect('contact/' . $contact_id);
418                                 // NOTREACHED
419                         }
420
421                         if ($cmd === 'drop' && ($orig_record['uid'] != 0)) {
422                                 // Check if we should do HTML-based delete confirmation
423                                 if (!empty($_REQUEST['confirm'])) {
424                                         // <form> can't take arguments in its 'action' parameter
425                                         // so add any arguments as hidden inputs
426                                         $query = explode_querystring(DI::args()->getQueryString());
427                                         $inputs = [];
428                                         foreach ($query['args'] as $arg) {
429                                                 if (strpos($arg, 'confirm=') === false) {
430                                                         $arg_parts = explode('=', $arg);
431                                                         $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
432                                                 }
433                                         }
434
435                                         DI::page()['aside'] = '';
436
437                                         return Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_drop_confirm.tpl'), [
438                                                 '$header' => DI::l10n()->t('Drop contact'),
439                                                 '$contact' => self::getContactTemplateVars($orig_record),
440                                                 '$method' => 'get',
441                                                 '$message' => DI::l10n()->t('Do you really want to delete this contact?'),
442                                                 '$extra_inputs' => $inputs,
443                                                 '$confirm' => DI::l10n()->t('Yes'),
444                                                 '$confirm_url' => $query['base'],
445                                                 '$confirm_name' => 'confirmed',
446                                                 '$cancel' => DI::l10n()->t('Cancel'),
447                                         ]);
448                                 }
449                                 // Now check how the user responded to the confirmation query
450                                 if (!empty($_REQUEST['canceled'])) {
451                                         DI::baseUrl()->redirect('contact');
452                                 }
453
454                                 self::dropContact($orig_record);
455                                 info(DI::l10n()->t('Contact has been removed.') . EOL);
456
457                                 DI::baseUrl()->redirect('contact');
458                                 // NOTREACHED
459                         }
460                         if ($cmd === 'posts') {
461                                 return self::getPostsHTML($a, $contact_id);
462                         }
463                         if ($cmd === 'conversations') {
464                                 return self::getConversationsHMTL($a, $contact_id, $update);
465                         }
466                 }
467
468                 $_SESSION['return_path'] = DI::args()->getQueryString();
469
470                 if (!empty($a->data['contact']) && is_array($a->data['contact'])) {
471                         $contact = $a->data['contact'];
472
473                         DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
474                                 '$baseurl' => DI::baseUrl()->get(true),
475                         ]);
476
477                         $contact['blocked']  = Model\Contact::isBlockedByUser($contact['id'], local_user());
478                         $contact['readonly'] = Model\Contact::isIgnoredByUser($contact['id'], local_user());
479
480                         $dir_icon = '';
481                         $relation_text = '';
482                         switch ($contact['rel']) {
483                                 case Model\Contact::FRIEND:
484                                         $dir_icon = 'images/lrarrow.gif';
485                                         $relation_text = DI::l10n()->t('You are mutual friends with %s');
486                                         break;
487
488                                 case Model\Contact::FOLLOWER;
489                                         $dir_icon = 'images/larrow.gif';
490                                         $relation_text = DI::l10n()->t('You are sharing with %s');
491                                         break;
492
493                                 case Model\Contact::SHARING;
494                                         $dir_icon = 'images/rarrow.gif';
495                                         $relation_text = DI::l10n()->t('%s is sharing with you');
496                                         break;
497
498                                 default:
499                                         break;
500                         }
501
502                         if ($contact['uid'] == 0) {
503                                 $relation_text = '';
504                         }
505
506                         if (!in_array($contact['network'], Protocol::FEDERATED)) {
507                                 $relation_text = '';
508                         }
509
510                         $relation_text = sprintf($relation_text, $contact['name']);
511
512                         $url = Model\Contact::magicLink($contact['url']);
513                         if (strpos($url, 'redir/') === 0) {
514                                 $sparkle = ' class="sparkle" ';
515                         } else {
516                                 $sparkle = '';
517                         }
518
519                         $insecure = DI::l10n()->t('Private communications are not available for this contact.');
520
521                         $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? DI::l10n()->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
522
523                         if ($contact['last-update'] > DBA::NULL_DATETIME) {
524                                 $last_update .= ' ' . (($contact['last-update'] <= $contact['success_update']) ? DI::l10n()->t('(Update was successful)') : DI::l10n()->t('(Update was not successful)'));
525                         }
526                         $lblsuggest = (($contact['network'] === Protocol::DFRN) ? DI::l10n()->t('Suggest friends') : '');
527
528                         $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
529
530                         $nettype = DI::l10n()->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']));
531
532                         // tabs
533                         $tab_str = self::getTabsHTML($a, $contact, 3);
534
535                         $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? DI::l10n()->t('Communications lost with this contact!') : '');
536
537                         $fetch_further_information = null;
538                         if ($contact['network'] == Protocol::FEED) {
539                                 $fetch_further_information = [
540                                         'fetch_further_information',
541                                         DI::l10n()->t('Fetch further information for feeds'),
542                                         $contact['fetch_further_information'],
543                                         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.'),
544                                         [
545                                                 '0' => DI::l10n()->t('Disabled'),
546                                                 '1' => DI::l10n()->t('Fetch information'),
547                                                 '3' => DI::l10n()->t('Fetch keywords'),
548                                                 '2' => DI::l10n()->t('Fetch information and keywords')
549                                         ]
550                                 ];
551                         }
552
553                         $poll_interval = null;
554                         if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
555                                 $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled);
556                         }
557
558                         $profile_select = null;
559                         if ($contact['network'] == Protocol::DFRN) {
560                                 $profile_select = ContactSelector::profileAssign($contact['profile-id'], $contact['network'] !== Protocol::DFRN);
561                         }
562
563                         // Load contactact related actions like hide, suggest, delete and others
564                         $contact_actions = self::getContactActions($contact);
565
566                         if ($contact['uid'] != 0) {
567                                 $lbl_vis1 = DI::l10n()->t('Profile Visibility');
568                                 $lbl_info1 = DI::l10n()->t('Contact Information / Notes');
569                                 $contact_settings_label = DI::l10n()->t('Contact Settings');
570                         } else {
571                                 $lbl_vis1 = null;
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_vis1'       => $lbl_vis1,
582                                 '$lbl_vis2'       => DI::l10n()->t('Please choose the profile you would like to display to %s when viewing your profile securely.', $contact['name']),
583                                 '$lbl_info1'      => $lbl_info1,
584                                 '$lbl_info2'      => DI::l10n()->t('Their personal note'),
585                                 '$reason'         => trim(Strings::escapeTags($contact['reason'])),
586                                 '$infedit'        => DI::l10n()->t('Edit contact notes'),
587                                 '$common_link'    => 'common/loc/' . local_user() . '/' . $contact['id'],
588                                 '$relation_text'  => $relation_text,
589                                 '$visit'          => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
590                                 '$blockunblock'   => DI::l10n()->t('Block/Unblock contact'),
591                                 '$ignorecont'     => DI::l10n()->t('Ignore contact'),
592                                 '$lblcrepair'     => DI::l10n()->t('Repair URL settings'),
593                                 '$lblrecent'      => DI::l10n()->t('View conversations'),
594                                 '$lblsuggest'     => $lblsuggest,
595                                 '$nettype'        => $nettype,
596                                 '$poll_interval'  => $poll_interval,
597                                 '$poll_enabled'   => $poll_enabled,
598                                 '$lastupdtext'    => DI::l10n()->t('Last update:'),
599                                 '$lost_contact'   => $lost_contact,
600                                 '$updpub'         => DI::l10n()->t('Update public posts'),
601                                 '$last_update'    => $last_update,
602                                 '$udnow'          => DI::l10n()->t('Update now'),
603                                 '$profile_select' => $profile_select,
604                                 '$contact_id'     => $contact['id'],
605                                 '$block_text'     => ($contact['blocked'] ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
606                                 '$ignore_text'    => ($contact['readonly'] ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
607                                 '$insecure'       => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
608                                 '$info'           => $contact['info'],
609                                 '$cinfo'          => ['info', '', $contact['info'], ''],
610                                 '$blocked'        => ($contact['blocked'] ? DI::l10n()->t('Currently blocked') : ''),
611                                 '$ignored'        => ($contact['readonly'] ? DI::l10n()->t('Currently ignored') : ''),
612                                 '$archived'       => ($contact['archive'] ? DI::l10n()->t('Currently archived') : ''),
613                                 '$pending'        => ($contact['pending'] ? DI::l10n()->t('Awaiting connection acknowledge') : ''),
614                                 '$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')],
615                                 '$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')],
616                                 '$fetch_further_information' => $fetch_further_information,
617                                 '$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')],
618                                 '$photo'          => $contact['photo'],
619                                 '$name'           => $contact['name'],
620                                 '$dir_icon'       => $dir_icon,
621                                 '$sparkle'        => $sparkle,
622                                 '$url'            => $url,
623                                 '$profileurllabel'=> DI::l10n()->t('Profile URL'),
624                                 '$profileurl'     => $contact['url'],
625                                 '$account_type'   => Model\Contact::getAccountType($contact),
626                                 '$location'       => BBCode::convert($contact['location']),
627                                 '$location_label' => DI::l10n()->t('Location:'),
628                                 '$xmpp'           => BBCode::convert($contact['xmpp']),
629                                 '$xmpp_label'     => DI::l10n()->t('XMPP:'),
630                                 '$about'          => BBCode::convert($contact['about'], false),
631                                 '$about_label'    => DI::l10n()->t('About:'),
632                                 '$keywords'       => $contact['keywords'],
633                                 '$keywords_label' => DI::l10n()->t('Tags:'),
634                                 '$contact_action_button' => DI::l10n()->t('Actions'),
635                                 '$contact_actions'=> $contact_actions,
636                                 '$contact_status' => DI::l10n()->t('Status'),
637                                 '$contact_settings_label' => $contact_settings_label,
638                                 '$contact_profile_label' => DI::l10n()->t('Profile'),
639                         ]);
640
641                         $arr = ['contact' => $contact, 'output' => $o];
642
643                         Hook::callAll('contact_edit', $arr);
644
645                         return $arr['output'];
646                 }
647
648                 $sql_values = [local_user()];
649
650                 // @TODO: Replace with parameter from router
651                 $type = $a->argv[1] ?? '';
652
653                 switch ($type) {
654                         case 'blocked':
655                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`blocked`)";
656                                 // This makes the query look for contact.uid = 0
657                                 array_unshift($sql_values, 0);
658                                 break;
659                         case 'hidden':
660                                 $sql_extra = " AND `hidden` AND NOT `blocked` AND NOT `pending`";
661                                 break;
662                         case 'ignored':
663                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`ignored`)";
664                                 // This makes the query look for contact.uid = 0
665                                 array_unshift($sql_values, 0);
666                                 break;
667                         case 'archived':
668                                 $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`";
669                                 break;
670                         case 'pending':
671                                 $sql_extra = " AND `pending` AND NOT `archive` AND ((`rel` = ?)
672                                         OR EXISTS (SELECT `id` FROM `intro` WHERE `contact-id` = `contact`.`id` AND NOT `ignore`))";
673                                 $sql_values[] = Model\Contact::SHARING;
674                                 break;
675                         default:
676                                 $sql_extra = " AND NOT `archive` AND NOT `blocked` AND NOT `pending`";
677                                 break;
678                 }
679
680                 $searching = false;
681                 $search_hdr = null;
682                 if ($search) {
683                         $searching = true;
684                         $search_hdr = $search;
685                         $search_txt = preg_quote($search);
686                         $sql_extra .= " AND (name REGEXP ? OR url REGEXP ? OR nick REGEXP ?)";
687                         $sql_values[] = $search_txt;
688                         $sql_values[] = $search_txt;
689                         $sql_values[] = $search_txt;
690                 }
691
692                 if ($nets) {
693                         $sql_extra .= " AND network = ? ";
694                         $sql_values[] = $nets;
695                 }
696
697                 switch ($rel) {
698                         case 'followers':
699                                 $sql_extra .= " AND `rel` IN (?, ?)";
700                                 $sql_values[] = Model\Contact::FOLLOWER;
701                                 $sql_values[] = Model\Contact::FRIEND;
702                                 break;
703                         case 'following':
704                                 $sql_extra .= " AND `rel` IN (?, ?)";
705                                 $sql_values[] = Model\Contact::SHARING;
706                                 $sql_values[] = Model\Contact::FRIEND;
707                                 break;
708                         case 'mutuals':
709                                 $sql_extra .= " AND `rel` = ?";
710                                 $sql_values[] = Model\Contact::FRIEND;
711                                 break;
712                 }
713
714                 if ($group) {
715                         $sql_extra = " AND EXISTS(SELECT `id` FROM `group_member` WHERE `gid` = ? AND `contact`.`id` = `contact-id`)";
716                         $sql_values[] = $group;
717                 }
718
719                 $sql_extra .= Widget::unavailableNetworks();
720
721                 $total = 0;
722                 $stmt = DBA::p("SELECT COUNT(*) AS `total`
723                         FROM `contact`
724                         WHERE `uid` = ?
725                         AND `self` = 0
726                         AND NOT `deleted`
727                         $sql_extra",
728                         $sql_values
729                 );
730                 if (DBA::isResult($stmt)) {
731                         $total = DBA::fetch($stmt)['total'];
732                 }
733                 DBA::close($stmt);
734
735                 $pager = new Pager(DI::args()->getQueryString());
736
737                 $sql_values[] = $pager->getStart();
738                 $sql_values[] = $pager->getItemsPerPage();
739
740                 $contacts = [];
741
742                 $stmt = DBA::p("SELECT *
743                         FROM `contact`
744                         WHERE `uid` = ?
745                         AND `self` = 0
746                         AND NOT `deleted`
747                         $sql_extra
748                         ORDER BY `name` ASC
749                         LIMIT ?, ?",
750                         $sql_values
751                 );
752                 while ($contact = DBA::fetch($stmt)) {
753                         $contact['blocked'] = Model\Contact::isBlockedByUser($contact['id'], local_user());
754                         $contact['readonly'] = Model\Contact::isIgnoredByUser($contact['id'], local_user());
755                         $contacts[] = self::getContactTemplateVars($contact);
756                 }
757                 DBA::close($stmt);
758
759                 $tabs = [
760                         [
761                                 'label' => DI::l10n()->t('All Contacts'),
762                                 'url'   => 'contact',
763                                 'sel'   => !$type ? 'active' : '',
764                                 'title' => DI::l10n()->t('Show all contacts'),
765                                 'id'    => 'showall-tab',
766                                 'accesskey' => 'l',
767                         ],
768                         [
769                                 'label' => DI::l10n()->t('Pending'),
770                                 'url'   => 'contact/pending',
771                                 'sel'   => $type == 'pending' ? 'active' : '',
772                                 'title' => DI::l10n()->t('Only show pending contacts'),
773                                 'id'    => 'showpending-tab',
774                                 'accesskey' => 'p',
775                         ],
776                         [
777                                 'label' => DI::l10n()->t('Blocked'),
778                                 'url'   => 'contact/blocked',
779                                 'sel'   => $type == 'blocked' ? 'active' : '',
780                                 'title' => DI::l10n()->t('Only show blocked contacts'),
781                                 'id'    => 'showblocked-tab',
782                                 'accesskey' => 'b',
783                         ],
784                         [
785                                 'label' => DI::l10n()->t('Ignored'),
786                                 'url'   => 'contact/ignored',
787                                 'sel'   => $type == 'ignored' ? 'active' : '',
788                                 'title' => DI::l10n()->t('Only show ignored contacts'),
789                                 'id'    => 'showignored-tab',
790                                 'accesskey' => 'i',
791                         ],
792                         [
793                                 'label' => DI::l10n()->t('Archived'),
794                                 'url'   => 'contact/archived',
795                                 'sel'   => $type == 'archived' ? 'active' : '',
796                                 'title' => DI::l10n()->t('Only show archived contacts'),
797                                 'id'    => 'showarchived-tab',
798                                 'accesskey' => 'y',
799                         ],
800                         [
801                                 'label' => DI::l10n()->t('Hidden'),
802                                 'url'   => 'contact/hidden',
803                                 'sel'   => $type == 'hidden' ? 'active' : '',
804                                 'title' => DI::l10n()->t('Only show hidden contacts'),
805                                 'id'    => 'showhidden-tab',
806                                 'accesskey' => 'h',
807                         ],
808                         [
809                                 'label' => DI::l10n()->t('Groups'),
810                                 'url'   => 'group',
811                                 'sel'   => '',
812                                 'title' => DI::l10n()->t('Organize your contact groups'),
813                                 'id'    => 'contactgroups-tab',
814                                 'accesskey' => 'e',
815                         ],
816                 ];
817
818                 $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
819                 $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
820
821                 switch ($rel) {
822                         case 'followers': $header = DI::l10n()->t('Followers'); break;
823                         case 'following': $header = DI::l10n()->t('Following'); break;
824                         case 'mutuals':   $header = DI::l10n()->t('Mutual friends'); break;
825                         default:          $header = DI::l10n()->t('Contacts');
826                 }
827
828                 switch ($type) {
829                         case 'pending':  $header .= ' - ' . DI::l10n()->t('Pending'); break;
830                         case 'blocked':  $header .= ' - ' . DI::l10n()->t('Blocked'); break;
831                         case 'hidden':   $header .= ' - ' . DI::l10n()->t('Hidden'); break;
832                         case 'ignored':  $header .= ' - ' . DI::l10n()->t('Ignored'); break;
833                         case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break;
834                 }
835
836                 $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
837
838                 $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
839                 $o .= Renderer::replaceMacros($tpl, [
840                         '$header'     => $header,
841                         '$tabs'       => $tabs_html,
842                         '$total'      => $total,
843                         '$search'     => $search_hdr,
844                         '$desc'       => DI::l10n()->t('Search your contacts'),
845                         '$finding'    => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
846                         '$submit'     => DI::l10n()->t('Find'),
847                         '$cmd'        => DI::args()->getCommand(),
848                         '$contacts'   => $contacts,
849                         '$contact_drop_confirm' => DI::l10n()->t('Do you really want to delete this contact?'),
850                         'multiselect' => 1,
851                         '$batch_actions' => [
852                                 'contacts_batch_update'  => DI::l10n()->t('Update'),
853                                 'contacts_batch_block'   => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
854                                 'contacts_batch_ignore'  => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
855                                 'contacts_batch_archive' => DI::l10n()->t('Archive') . '/' . DI::l10n()->t('Unarchive'),
856                                 'contacts_batch_drop'    => DI::l10n()->t('Delete'),
857                         ],
858                         '$h_batch_actions' => DI::l10n()->t('Batch Actions'),
859                         '$paginate'   => $pager->renderFull($total),
860                 ]);
861
862                 return $o;
863         }
864
865         /**
866          * List of pages for the Contact TabBar
867          *
868          * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
869          *
870          * @param App   $a
871          * @param array $contact    The contact array
872          * @param int   $active_tab 1 if tab should be marked as active
873          *
874          * @return string HTML string of the contact page tabs buttons.
875          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
876          */
877         public static function getTabsHTML($a, $contact, $active_tab)
878         {
879                 // tabs
880                 $tabs = [
881                         [
882                                 'label' => DI::l10n()->t('Status'),
883                                 'url'   => "contact/" . $contact['id'] . "/conversations",
884                                 'sel'   => (($active_tab == 1) ? 'active' : ''),
885                                 'title' => DI::l10n()->t('Conversations started by this contact'),
886                                 'id'    => 'status-tab',
887                                 'accesskey' => 'm',
888                         ],
889                         [
890                                 'label' => DI::l10n()->t('Posts and Comments'),
891                                 'url'   => "contact/" . $contact['id'] . "/posts",
892                                 'sel'   => (($active_tab == 2) ? 'active' : ''),
893                                 'title' => DI::l10n()->t('Status Messages and Posts'),
894                                 'id'    => 'posts-tab',
895                                 'accesskey' => 'p',
896                         ],
897                         [
898                                 'label' => DI::l10n()->t('Profile'),
899                                 'url'   => "contact/" . $contact['id'],
900                                 'sel'   => (($active_tab == 3) ? 'active' : ''),
901                                 'title' => DI::l10n()->t('Profile Details'),
902                                 'id'    => 'profile-tab',
903                                 'accesskey' => 'o',
904                         ]
905                 ];
906
907                 // Show this tab only if there is visible friend list
908                 $x = Model\GContact::countAllFriends(local_user(), $contact['id']);
909                 if ($x) {
910                         $tabs[] = ['label' => DI::l10n()->t('Contacts'),
911                                 'url'   => "allfriends/" . $contact['id'],
912                                 'sel'   => (($active_tab == 4) ? 'active' : ''),
913                                 'title' => DI::l10n()->t('View all contacts'),
914                                 'id'    => 'allfriends-tab',
915                                 'accesskey' => 't'];
916                 }
917
918                 // Show this tab only if there is visible common friend list
919                 $common = Model\GContact::countCommonFriends(local_user(), $contact['id']);
920                 if ($common) {
921                         $tabs[] = ['label' => DI::l10n()->t('Common Friends'),
922                                 'url'   => "common/loc/" . local_user() . "/" . $contact['id'],
923                                 'sel'   => (($active_tab == 5) ? 'active' : ''),
924                                 'title' => DI::l10n()->t('View all common friends'),
925                                 'id'    => 'common-loc-tab',
926                                 'accesskey' => 'd'
927                         ];
928                 }
929
930                 if (!empty($contact['uid'])) {
931                         $tabs[] = ['label' => DI::l10n()->t('Advanced'),
932                                 'url'   => 'crepair/' . $contact['id'],
933                                 'sel'   => (($active_tab == 6) ? 'active' : ''),
934                                 'title' => DI::l10n()->t('Advanced Contact Settings'),
935                                 'id'    => 'advanced-tab',
936                                 'accesskey' => 'r'
937                         ];
938                 }
939
940                 $tab_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
941                 $tab_str = Renderer::replaceMacros($tab_tpl, ['$tabs' => $tabs]);
942
943                 return $tab_str;
944         }
945
946         private static function getConversationsHMTL($a, $contact_id, $update)
947         {
948                 $o = '';
949
950                 if (!$update) {
951                         // We need the editor here to be able to reshare an item.
952                         if (local_user()) {
953                                 $x = [
954                                         'is_owner' => true,
955                                         'allow_location' => $a->user['allow_location'],
956                                         'default_location' => $a->user['default-location'],
957                                         'nickname' => $a->user['nickname'],
958                                         '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'),
959                                         'acl' => ACL::getFullSelectorHTML(DI::page(), $a->user, true),
960                                         'bang' => '',
961                                         'visitor' => 'block',
962                                         'profile_uid' => local_user(),
963                                 ];
964                                 $o = status_editor($a, $x, 0, true);
965                         }
966                 }
967
968                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
969
970                 if (!$update) {
971                         $o .= self::getTabsHTML($a, $contact, 1);
972                 }
973
974                 if (DBA::isResult($contact)) {
975                         DI::page()['aside'] = '';
976
977                         $profiledata = Model\Contact::getDetailsByURL($contact['url']);
978
979                         Model\Profile::load($a, '', 0, $profiledata, true);
980                         $o .= Model\Contact::getPostsFromUrl($contact['url'], true, $update);
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::getDetailsByURL($contact['url']);
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, '', 0, $profiledata, true);
1002                         $o .= Model\Contact::getPostsFromUrl($contact['url']);
1003                 }
1004
1005                 return $o;
1006         }
1007
1008         public static function getContactTemplateVars(array $rr)
1009         {
1010                 $dir_icon = '';
1011                 $alt_text = '';
1012
1013                 if (!empty($rr['uid']) && !empty($rr['rel'])) {
1014                         switch ($rr['rel']) {
1015                                 case Model\Contact::FRIEND:
1016                                         $dir_icon = 'images/lrarrow.gif';
1017                                         $alt_text = DI::l10n()->t('Mutual Friendship');
1018                                         break;
1019
1020                                 case Model\Contact::FOLLOWER;
1021                                         $dir_icon = 'images/larrow.gif';
1022                                         $alt_text = DI::l10n()->t('is a fan of yours');
1023                                         break;
1024
1025                                 case Model\Contact::SHARING;
1026                                         $dir_icon = 'images/rarrow.gif';
1027                                         $alt_text = DI::l10n()->t('you are a fan of');
1028                                         break;
1029
1030                                 default:
1031                                         break;
1032                         }
1033                 }
1034
1035                 $url = Model\Contact::magicLink($rr['url']);
1036
1037                 if (strpos($url, 'redir/') === 0) {
1038                         $sparkle = ' class="sparkle" ';
1039                 } else {
1040                         $sparkle = '';
1041                 }
1042
1043                 if ($rr['pending']) {
1044                         if (in_array($rr['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) {
1045                                 $alt_text = DI::l10n()->t('Pending outgoing contact request');
1046                         } else {
1047                                 $alt_text = DI::l10n()->t('Pending incoming contact request');
1048                         }
1049                 }
1050
1051                 if ($rr['self']) {
1052                         $dir_icon = 'images/larrow.gif';
1053                         $alt_text = DI::l10n()->t('This is you');
1054                         $url = $rr['url'];
1055                         $sparkle = '';
1056                 }
1057
1058                 return [
1059                         'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']),
1060                         'edit_hover'=> DI::l10n()->t('Edit contact'),
1061                         'photo_menu'=> Model\Contact::photoMenu($rr),
1062                         'id'        => $rr['id'],
1063                         'alt_text'  => $alt_text,
1064                         'dir_icon'  => $dir_icon,
1065                         'thumb'     => ProxyUtils::proxifyUrl($rr['thumb'], false, ProxyUtils::SIZE_THUMB),
1066                         'name'      => $rr['name'],
1067                         'username'  => $rr['name'],
1068                         'account_type' => Model\Contact::getAccountType($rr),
1069                         'sparkle'   => $sparkle,
1070                         'itemurl'   => ($rr['addr'] ?? '') ?: $rr['url'],
1071                         'url'       => $url,
1072                         'network'   => ContactSelector::networkToName($rr['network'], $rr['url'], $rr['protocol']),
1073                         'nick'      => $rr['nick'],
1074                 ];
1075         }
1076
1077         /**
1078          * Gives a array with actions which can performed to a given contact
1079          *
1080          * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
1081          *
1082          * @param array $contact Data about the Contact
1083          * @return array with contact related actions
1084          */
1085         private static function getContactActions($contact)
1086         {
1087                 $poll_enabled = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
1088                 $contact_actions = [];
1089
1090                 // Provide friend suggestion only for Friendica contacts
1091                 if ($contact['network'] === Protocol::DFRN) {
1092                         $contact_actions['suggest'] = [
1093                                 'label' => DI::l10n()->t('Suggest friends'),
1094                                 'url'   => 'fsuggest/' . $contact['id'],
1095                                 'title' => '',
1096                                 'sel'   => '',
1097                                 'id'    => 'suggest',
1098                         ];
1099                 }
1100
1101                 if ($poll_enabled) {
1102                         $contact_actions['update'] = [
1103                                 'label' => DI::l10n()->t('Update now'),
1104                                 'url'   => 'contact/' . $contact['id'] . '/update',
1105                                 'title' => '',
1106                                 'sel'   => '',
1107                                 'id'    => 'update',
1108                         ];
1109                 }
1110
1111                 $contact_actions['block'] = [
1112                         'label' => (intval($contact['blocked']) ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
1113                         'url'   => 'contact/' . $contact['id'] . '/block',
1114                         'title' => DI::l10n()->t('Toggle Blocked status'),
1115                         'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1116                         'id'    => 'toggle-block',
1117                 ];
1118
1119                 $contact_actions['ignore'] = [
1120                         'label' => (intval($contact['readonly']) ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
1121                         'url'   => 'contact/' . $contact['id'] . '/ignore',
1122                         'title' => DI::l10n()->t('Toggle Ignored status'),
1123                         'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1124                         'id'    => 'toggle-ignore',
1125                 ];
1126
1127                 if ($contact['uid'] != 0) {
1128                         $contact_actions['archive'] = [
1129                                 'label' => (intval($contact['archive']) ? DI::l10n()->t('Unarchive') : DI::l10n()->t('Archive')),
1130                                 'url'   => 'contact/' . $contact['id'] . '/archive',
1131                                 'title' => DI::l10n()->t('Toggle Archive status'),
1132                                 'sel'   => (intval($contact['archive']) ? 'active' : ''),
1133                                 'id'    => 'toggle-archive',
1134                         ];
1135
1136                         $contact_actions['delete'] = [
1137                                 'label' => DI::l10n()->t('Delete'),
1138                                 'url'   => 'contact/' . $contact['id'] . '/drop',
1139                                 'title' => DI::l10n()->t('Delete contact'),
1140                                 'sel'   => '',
1141                                 'id'    => 'delete',
1142                         ];
1143                 }
1144
1145                 return $contact_actions;
1146         }
1147 }