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