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