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