]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
Merge remote-tracking branch 'upstream/develop' into app-user2
[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::createFromProbeForUser($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                                 '$matrix'         => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
611                                 '$matrix_label'   => DI::l10n()->t('Matrix:'),
612                                 '$about'          => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
613                                 '$about_label'    => DI::l10n()->t('About:'),
614                                 '$keywords'       => $contact['keywords'],
615                                 '$keywords_label' => DI::l10n()->t('Tags:'),
616                                 '$contact_action_button' => DI::l10n()->t('Actions'),
617                                 '$contact_actions'=> $contact_actions,
618                                 '$contact_status' => DI::l10n()->t('Status'),
619                                 '$contact_settings_label' => $contact_settings_label,
620                                 '$contact_profile_label' => DI::l10n()->t('Profile'),
621                                 '$allow_remote_self' => $allow_remote_self,
622                                 '$remote_self'       => ['remote_self',
623                                         DI::l10n()->t('Mirror postings from this contact'),
624                                         $contact['remote_self'],
625                                         DI::l10n()->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
626                                         $remote_self_options
627                                 ],
628                         ]);
629
630                         $arr = ['contact' => $contact, 'output' => $o];
631
632                         Hook::callAll('contact_edit', $arr);
633
634                         return $arr['output'];
635                 }
636
637                 $sql_values = [local_user()];
638
639                 // @TODO: Replace with parameter from router
640                 $type = DI::args()->getArgv()[1] ?? '';
641
642                 switch ($type) {
643                         case 'blocked':
644                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`blocked`)";
645                                 // This makes the query look for contact.uid = 0
646                                 array_unshift($sql_values, 0);
647                                 break;
648                         case 'hidden':
649                                 $sql_extra = " AND `hidden` AND NOT `blocked` AND NOT `pending`";
650                                 break;
651                         case 'ignored':
652                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`ignored`)";
653                                 // This makes the query look for contact.uid = 0
654                                 array_unshift($sql_values, 0);
655                                 break;
656                         case 'archived':
657                                 $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`";
658                                 break;
659                         case 'pending':
660                                 $sql_extra = " AND `pending` AND NOT `archive` AND NOT `failed` AND ((`rel` = ?)
661                                         OR EXISTS (SELECT `id` FROM `intro` WHERE `contact-id` = `contact`.`id` AND NOT `ignore`))";
662                                 $sql_values[] = Model\Contact::SHARING;
663                                 break;
664                         default:
665                                 $sql_extra = " AND NOT `archive` AND NOT `blocked` AND NOT `pending`";
666                                 break;
667                 }
668
669                 if (isset($accounttypeid)) {
670                         $sql_extra .= " AND `contact-type` = ?";
671                         $sql_values[] = $accounttypeid;
672                 }
673
674                 $searching = false;
675                 $search_hdr = null;
676                 if ($search) {
677                         $searching = true;
678                         $search_hdr = $search;
679                         $search_txt = preg_quote($search);
680                         $sql_extra .= " AND (name REGEXP ? OR url REGEXP ? OR nick REGEXP ?)";
681                         $sql_values[] = $search_txt;
682                         $sql_values[] = $search_txt;
683                         $sql_values[] = $search_txt;
684                 }
685
686                 if ($nets) {
687                         $sql_extra .= " AND network = ? ";
688                         $sql_values[] = $nets;
689                 }
690
691                 switch ($rel) {
692                         case 'followers':
693                                 $sql_extra .= " AND `rel` IN (?, ?)";
694                                 $sql_values[] = Model\Contact::FOLLOWER;
695                                 $sql_values[] = Model\Contact::FRIEND;
696                                 break;
697                         case 'following':
698                                 $sql_extra .= " AND `rel` IN (?, ?)";
699                                 $sql_values[] = Model\Contact::SHARING;
700                                 $sql_values[] = Model\Contact::FRIEND;
701                                 break;
702                         case 'mutuals':
703                                 $sql_extra .= " AND `rel` = ?";
704                                 $sql_values[] = Model\Contact::FRIEND;
705                                 break;
706                 }
707
708                 if ($group) {
709                         $sql_extra = " AND EXISTS(SELECT `id` FROM `group_member` WHERE `gid` = ? AND `contact`.`id` = `contact-id`)";
710                         $sql_values[] = $group;
711                 }
712
713                 $total = 0;
714                 $stmt = DBA::p("SELECT COUNT(*) AS `total`
715                         FROM `contact`
716                         WHERE `uid` = ?
717                         AND `self` = 0
718                         AND NOT `deleted`
719                         $sql_extra
720                         " . Widget::unavailableNetworks(),
721                         $sql_values
722                 );
723                 if (DBA::isResult($stmt)) {
724                         $total = DBA::fetch($stmt)['total'];
725                 }
726                 DBA::close($stmt);
727
728                 $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
729
730                 $sql_values[] = $pager->getStart();
731                 $sql_values[] = $pager->getItemsPerPage();
732
733                 $contacts = [];
734
735                 $stmt = DBA::p("SELECT *
736                         FROM `contact`
737                         WHERE `uid` = ?
738                         AND `self` = 0
739                         AND NOT `deleted`
740                         $sql_extra
741                         ORDER BY `name` ASC
742                         LIMIT ?, ?",
743                         $sql_values
744                 );
745                 while ($contact = DBA::fetch($stmt)) {
746                         $contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], local_user());
747                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
748                         $contacts[] = self::getContactTemplateVars($contact);
749                 }
750                 DBA::close($stmt);
751
752                 $tabs = [
753                         [
754                                 'label' => DI::l10n()->t('All Contacts'),
755                                 'url'   => 'contact',
756                                 'sel'   => !$type ? 'active' : '',
757                                 'title' => DI::l10n()->t('Show all contacts'),
758                                 'id'    => 'showall-tab',
759                                 'accesskey' => 'l',
760                         ],
761                         [
762                                 'label' => DI::l10n()->t('Pending'),
763                                 'url'   => 'contact/pending',
764                                 'sel'   => $type == 'pending' ? 'active' : '',
765                                 'title' => DI::l10n()->t('Only show pending contacts'),
766                                 'id'    => 'showpending-tab',
767                                 'accesskey' => 'p',
768                         ],
769                         [
770                                 'label' => DI::l10n()->t('Blocked'),
771                                 'url'   => 'contact/blocked',
772                                 'sel'   => $type == 'blocked' ? 'active' : '',
773                                 'title' => DI::l10n()->t('Only show blocked contacts'),
774                                 'id'    => 'showblocked-tab',
775                                 'accesskey' => 'b',
776                         ],
777                         [
778                                 'label' => DI::l10n()->t('Ignored'),
779                                 'url'   => 'contact/ignored',
780                                 'sel'   => $type == 'ignored' ? 'active' : '',
781                                 'title' => DI::l10n()->t('Only show ignored contacts'),
782                                 'id'    => 'showignored-tab',
783                                 'accesskey' => 'i',
784                         ],
785                         [
786                                 'label' => DI::l10n()->t('Archived'),
787                                 'url'   => 'contact/archived',
788                                 'sel'   => $type == 'archived' ? 'active' : '',
789                                 'title' => DI::l10n()->t('Only show archived contacts'),
790                                 'id'    => 'showarchived-tab',
791                                 'accesskey' => 'y',
792                         ],
793                         [
794                                 'label' => DI::l10n()->t('Hidden'),
795                                 'url'   => 'contact/hidden',
796                                 'sel'   => $type == 'hidden' ? 'active' : '',
797                                 'title' => DI::l10n()->t('Only show hidden contacts'),
798                                 'id'    => 'showhidden-tab',
799                                 'accesskey' => 'h',
800                         ],
801                         [
802                                 'label' => DI::l10n()->t('Groups'),
803                                 'url'   => 'group',
804                                 'sel'   => '',
805                                 'title' => DI::l10n()->t('Organize your contact groups'),
806                                 'id'    => 'contactgroups-tab',
807                                 'accesskey' => 'e',
808                         ],
809                 ];
810
811                 $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
812                 $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
813
814                 switch ($rel) {
815                         case 'followers': $header = DI::l10n()->t('Followers'); break;
816                         case 'following': $header = DI::l10n()->t('Following'); break;
817                         case 'mutuals':   $header = DI::l10n()->t('Mutual friends'); break;
818                         default:          $header = DI::l10n()->t('Contacts');
819                 }
820
821                 switch ($type) {
822                         case 'pending':  $header .= ' - ' . DI::l10n()->t('Pending'); break;
823                         case 'blocked':  $header .= ' - ' . DI::l10n()->t('Blocked'); break;
824                         case 'hidden':   $header .= ' - ' . DI::l10n()->t('Hidden'); break;
825                         case 'ignored':  $header .= ' - ' . DI::l10n()->t('Ignored'); break;
826                         case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break;
827                 }
828
829                 $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
830
831                 $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
832                 $o .= Renderer::replaceMacros($tpl, [
833                         '$header'     => $header,
834                         '$tabs'       => $tabs_html,
835                         '$total'      => $total,
836                         '$search'     => $search_hdr,
837                         '$desc'       => DI::l10n()->t('Search your contacts'),
838                         '$finding'    => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
839                         '$submit'     => DI::l10n()->t('Find'),
840                         '$cmd'        => DI::args()->getCommand(),
841                         '$contacts'   => $contacts,
842                         '$contact_drop_confirm' => DI::l10n()->t('Do you really want to delete this contact?'),
843                         'multiselect' => 1,
844                         '$batch_actions' => [
845                                 'contacts_batch_update'  => DI::l10n()->t('Update'),
846                                 'contacts_batch_block'   => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
847                                 'contacts_batch_ignore'  => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
848                                 'contacts_batch_archive' => DI::l10n()->t('Archive') . '/' . DI::l10n()->t('Unarchive'),
849                                 'contacts_batch_drop'    => DI::l10n()->t('Delete'),
850                         ],
851                         '$h_batch_actions' => DI::l10n()->t('Batch Actions'),
852                         '$paginate'   => $pager->renderFull($total),
853                 ]);
854
855                 return $o;
856         }
857
858         /**
859          * List of pages for the Contact TabBar
860          *
861          * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
862          *
863          * @param array $contact    The contact array
864          * @param int   $active_tab 1 if tab should be marked as active
865          *
866          * @return string HTML string of the contact page tabs buttons.
867          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
868          * @throws \ImagickException
869          */
870         public static function getTabsHTML(array $contact, int $active_tab)
871         {
872                 $cid = $pcid = $contact['id'];
873                 $data = Model\Contact::getPublicAndUserContactID($contact['id'], local_user());
874                 if (!empty($data['user']) && ($contact['id'] == $data['public'])) {
875                         $cid = $data['user'];
876                 } elseif (!empty($data['public'])) {
877                         $pcid = $data['public'];
878                 }
879
880                 // tabs
881                 $tabs = [
882                         [
883                                 'label' => DI::l10n()->t('Status'),
884                                 'url'   => 'contact/' . $pcid . '/conversations',
885                                 'sel'   => (($active_tab == self::TAB_CONVERSATIONS) ? 'active' : ''),
886                                 'title' => DI::l10n()->t('Conversations started by this contact'),
887                                 'id'    => 'status-tab',
888                                 'accesskey' => 'm',
889                         ],
890                         [
891                                 'label' => DI::l10n()->t('Posts and Comments'),
892                                 'url'   => 'contact/' . $pcid . '/posts',
893                                 'sel'   => (($active_tab == self::TAB_POSTS) ? 'active' : ''),
894                                 'title' => DI::l10n()->t('Status Messages and Posts'),
895                                 'id'    => 'posts-tab',
896                                 'accesskey' => 'p',
897                         ],
898                         [
899                                 'label' => DI::l10n()->t('Profile'),
900                                 'url'   => 'contact/' . $cid,
901                                 'sel'   => (($active_tab == self::TAB_PROFILE) ? 'active' : ''),
902                                 'title' => DI::l10n()->t('Profile Details'),
903                                 'id'    => 'profile-tab',
904                                 'accesskey' => 'o',
905                         ],
906                         ['label' => DI::l10n()->t('Contacts'),
907                                 'url'   => 'contact/' . $pcid . '/contacts',
908                                 'sel'   => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''),
909                                 'title' => DI::l10n()->t('View all known contacts'),
910                                 'id'    => 'contacts-tab',
911                                 'accesskey' => 't'
912                         ],
913                 ];
914
915                 if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) {
916                         $tabs[] = ['label' => DI::l10n()->t('Advanced'),
917                                 'url'   => 'contact/' . $cid . '/advanced/',
918                                 'sel'   => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''),
919                                 'title' => DI::l10n()->t('Advanced Contact Settings'),
920                                 'id'    => 'advanced-tab',
921                                 'accesskey' => 'r'
922                         ];
923                 }
924
925                 $tab_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
926                 $tab_str = Renderer::replaceMacros($tab_tpl, ['$tabs' => $tabs]);
927
928                 return $tab_str;
929         }
930
931         public static function getConversationsHMTL($a, $contact_id, $update, $parent = 0)
932         {
933                 $o = '';
934
935                 if (!$update) {
936                         // We need the editor here to be able to reshare an item.
937                         if (local_user()) {
938                                 $o = status_editor($a, [], 0, true);
939                         }
940                 }
941
942                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
943
944                 if (!$update) {
945                         $o .= self::getTabsHTML($contact, self::TAB_CONVERSATIONS);
946                 }
947
948                 if (DBA::isResult($contact)) {
949                         if (!$update) {
950                                 $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
951                                 DI::page()['aside'] = Widget\VCard::getHTML($profiledata);
952                         } else {
953                                 DI::page()['aside'] = '';
954                         }
955
956                         if ($contact['uid'] == 0) {
957                                 $o .= Model\Contact::getPostsFromId($contact['id'], true, $update, $parent);
958                         } else {
959                                 $o .= Model\Contact::getPostsFromUrl($contact['url'], true, $update, $parent);
960                         }
961                 }
962
963                 return $o;
964         }
965
966         private static function getPostsHTML($a, $contact_id)
967         {
968                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
969
970                 $o = self::getTabsHTML($contact, self::TAB_POSTS);
971
972                 if (DBA::isResult($contact)) {
973                         $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
974
975                         if (local_user() && in_array($profiledata['network'], Protocol::FEDERATED)) {
976                                 $profiledata['remoteconnect'] = DI::baseUrl() . '/follow?url=' . urlencode($profiledata['url']);
977                         }
978
979                         DI::page()['aside'] = Widget\VCard::getHTML($profiledata);
980
981                         if ($contact['uid'] == 0) {
982                                 $o .= Model\Contact::getPostsFromId($contact['id']);
983                         } else {
984                                 $o .= Model\Contact::getPostsFromUrl($contact['url']);
985                         }
986                 }
987
988                 return $o;
989         }
990
991         /**
992          * Return the fields for the contact template
993          *
994          * @param array $contact Contact array
995          * @return array Template fields
996          */
997         public static function getContactTemplateVars(array $contact)
998         {
999                 $alt_text = '';
1000
1001                 if (!empty($contact['url']) && isset($contact['uid']) && ($contact['uid'] == 0) && local_user()) {
1002                         $personal = Model\Contact::getByURL($contact['url'], false, ['uid', 'rel', 'self'], local_user());
1003                         if (!empty($personal)) {
1004                                 $contact['uid'] = $personal['uid'];
1005                                 $contact['rel'] = $personal['rel'];
1006                                 $contact['self'] = $personal['self'];
1007                         }
1008                 }
1009
1010                 if (!empty($contact['uid']) && !empty($contact['rel']) && local_user() == $contact['uid']) {
1011                         switch ($contact['rel']) {
1012                                 case Model\Contact::FRIEND:
1013                                         $alt_text = DI::l10n()->t('Mutual Friendship');
1014                                         break;
1015
1016                                 case Model\Contact::FOLLOWER;
1017                                         $alt_text = DI::l10n()->t('is a fan of yours');
1018                                         break;
1019
1020                                 case Model\Contact::SHARING;
1021                                         $alt_text = DI::l10n()->t('you are a fan of');
1022                                         break;
1023
1024                                 default:
1025                                         break;
1026                         }
1027                 }
1028
1029                 $url = Model\Contact::magicLinkByContact($contact);
1030
1031                 if (strpos($url, 'redir/') === 0) {
1032                         $sparkle = ' class="sparkle" ';
1033                 } else {
1034                         $sparkle = '';
1035                 }
1036
1037                 if ($contact['pending']) {
1038                         if (in_array($contact['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) {
1039                                 $alt_text = DI::l10n()->t('Pending outgoing contact request');
1040                         } else {
1041                                 $alt_text = DI::l10n()->t('Pending incoming contact request');
1042                         }
1043                 }
1044
1045                 if ($contact['self']) {
1046                         $alt_text = DI::l10n()->t('This is you');
1047                         $url = $contact['url'];
1048                         $sparkle = '';
1049                 }
1050
1051                 return [
1052                         'id'           => $contact['id'],
1053                         'url'          => $url,
1054                         'img_hover'    => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
1055                         'photo_menu'   => Model\Contact::photoMenu($contact),
1056                         'thumb'        => Model\Contact::getThumb($contact, true),
1057                         'alt_text'     => $alt_text,
1058                         'name'         => $contact['name'],
1059                         'nick'         => $contact['nick'],
1060                         'details'      => $contact['location'],
1061                         'tags'         => $contact['keywords'],
1062                         'about'        => $contact['about'],
1063                         'account_type' => Model\Contact::getAccountType($contact),
1064                         'sparkle'      => $sparkle,
1065                         'itemurl'      => ($contact['addr'] ?? '') ?: $contact['url'],
1066                         'network'      => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']),
1067                 ];
1068         }
1069
1070         /**
1071          * Gives a array with actions which can performed to a given contact
1072          *
1073          * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others
1074          *
1075          * @param array $contact Data about the Contact
1076          * @return array with contact related actions
1077          */
1078         private static function getContactActions($contact)
1079         {
1080                 $poll_enabled = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
1081                 $contact_actions = [];
1082
1083                 // Provide friend suggestion only for Friendica contacts
1084                 if ($contact['network'] === Protocol::DFRN) {
1085                         $contact_actions['suggest'] = [
1086                                 'label' => DI::l10n()->t('Suggest friends'),
1087                                 'url'   => 'fsuggest/' . $contact['id'],
1088                                 'title' => '',
1089                                 'sel'   => '',
1090                                 'id'    => 'suggest',
1091                         ];
1092                 }
1093
1094                 if ($poll_enabled) {
1095                         $contact_actions['update'] = [
1096                                 'label' => DI::l10n()->t('Update now'),
1097                                 'url'   => 'contact/' . $contact['id'] . '/update',
1098                                 'title' => '',
1099                                 'sel'   => '',
1100                                 'id'    => 'update',
1101                         ];
1102                 }
1103
1104                 if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
1105                         $contact_actions['updateprofile'] = [
1106                                 'label' => DI::l10n()->t('Refetch contact data'),
1107                                 'url'   => 'contact/' . $contact['id'] . '/updateprofile',
1108                                 'title' => '',
1109                                 'sel'   => '',
1110                                 'id'    => 'updateprofile',
1111                         ];
1112                 }
1113
1114                 $contact_actions['block'] = [
1115                         'label' => (intval($contact['blocked']) ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
1116                         'url'   => 'contact/' . $contact['id'] . '/block',
1117                         'title' => DI::l10n()->t('Toggle Blocked status'),
1118                         'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1119                         'id'    => 'toggle-block',
1120                 ];
1121
1122                 $contact_actions['ignore'] = [
1123                         'label' => (intval($contact['readonly']) ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
1124                         'url'   => 'contact/' . $contact['id'] . '/ignore',
1125                         'title' => DI::l10n()->t('Toggle Ignored status'),
1126                         'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1127                         'id'    => 'toggle-ignore',
1128                 ];
1129
1130                 if ($contact['uid'] != 0) {
1131                         $contact_actions['archive'] = [
1132                                 'label' => (intval($contact['archive']) ? DI::l10n()->t('Unarchive') : DI::l10n()->t('Archive')),
1133                                 'url'   => 'contact/' . $contact['id'] . '/archive',
1134                                 'title' => DI::l10n()->t('Toggle Archive status'),
1135                                 'sel'   => (intval($contact['archive']) ? 'active' : ''),
1136                                 'id'    => 'toggle-archive',
1137                         ];
1138
1139                         $contact_actions['delete'] = [
1140                                 'label' => DI::l10n()->t('Delete'),
1141                                 'url'   => 'contact/' . $contact['id'] . '/drop',
1142                                 'title' => DI::l10n()->t('Delete contact'),
1143                                 'sel'   => '',
1144                                 'id'    => 'delete',
1145                         ];
1146                 }
1147
1148                 return $contact_actions;
1149         }
1150 }