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