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