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