]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
dac706c2f6a2dcd7c5aaba7e0fe90ab28fb36103
[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                 $orig_records = Model\Contact::selectToArray(['id', 'uid'], ['id' => $_POST['contact_batch'], 'uid' => [0, local_user()], 'self' => false, 'deleted' => false]);
63
64                 $count_actions = 0;
65                 foreach ($orig_records as $orig_record) {
66                         $cdata = Model\Contact::getPublicAndUserContactID($orig_record['id'], local_user());
67                         if (empty($cdata)) {
68                                 continue;
69                         }
70
71                         if (!empty($_POST['contacts_batch_update']) && $cdata['user']) {
72                                 self::updateContactFromPoll($cdata['user']);
73                                 $count_actions++;
74                         }
75
76                         if (!empty($_POST['contacts_batch_block'])) {
77                                 self::toggleBlockContact($cdata['public']);
78                                 $count_actions++;
79                         }
80
81                         if (!empty($_POST['contacts_batch_ignore'])) {
82                                 self::toggleIgnoreContact($cdata['public']);
83                                 $count_actions++;
84                         }
85
86                         if (!empty($_POST['contacts_batch_drop']) && $cdata['user']
87                                 && self::dropContact($cdata['user'], local_user())
88                         ) {
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($_POST['redirect_url'] ?? 'contact');
97         }
98
99         public static function post(array $parameters = [])
100         {
101                 if (!local_user()) {
102                         return;
103                 }
104
105                 // @TODO: Replace with parameter from router
106                 if (DI::args()->getArgv()[1] === 'batch') {
107                         self::batchActions();
108                         return;
109                 }
110
111                 // @TODO: Replace with parameter from router
112                 $contact_id = intval(DI::args()->getArgv()[1]);
113                 if (!$contact_id) {
114                         return;
115                 }
116
117                 if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false])) {
118                         notice(DI::l10n()->t('Could not access contact record.'));
119                         DI::baseUrl()->redirect('contact');
120                         return; // NOTREACHED
121                 }
122
123                 Hook::callAll('contact_edit_post', $_POST);
124
125                 $hidden = !empty($_POST['hidden']);
126
127                 $notify = !empty($_POST['notify']);
128
129                 $fetch_further_information = intval($_POST['fetch_further_information'] ?? 0);
130
131                 $remote_self = $_POST['remote_self'] ?? false;
132
133                 $ffi_keyword_denylist = Strings::escapeHtml(trim($_POST['ffi_keyword_denylist'] ?? ''));
134
135                 $priority = intval($_POST['poll'] ?? 0);
136                 if ($priority > 5 || $priority < 0) {
137                         $priority = 0;
138                 }
139
140                 $info = Strings::escapeHtml(trim($_POST['info'] ?? ''));
141
142                 $r = Model\Contact::update([
143                         'priority'   => $priority,
144                         'info'       => $info,
145                         'hidden'     => $hidden,
146                         'notify_new_posts' => $notify,
147                         'fetch_further_information' => $fetch_further_information,
148                         'remote_self' => $remote_self,
149                         'ffi_keyword_denylist'     => $ffi_keyword_denylist],
150                         ['id' => $contact_id, 'uid' => local_user()]
151                 );
152
153                 if (!DBA::isResult($r)) {
154                         notice(DI::l10n()->t('Failed to update contact record.'));
155                 }
156                 return;
157         }
158
159         /* contact actions */
160
161         /**
162          * @param int $contact_id Id of contact with uid != 0
163          * @throws NotFoundException
164          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
165          * @throws \ImagickException
166          */
167         private static function updateContactFromPoll(int $contact_id)
168         {
169                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]);
170                 if (!DBA::isResult($contact)) {
171                         return;
172                 }
173
174                 if ($contact['network'] == Protocol::OSTATUS) {
175                         $result = Model\Contact::createFromProbeForUser($contact['uid'], $contact['url'], $contact['network']);
176
177                         if ($result['success']) {
178                                 Model\Contact::update(['subhub' => 1], ['id' => $contact_id]);
179                         }
180
181                         // pull feed and consume it, which should subscribe to the hub.
182                         Worker::add(PRIORITY_HIGH, 'OnePoll', $contact_id, 'force');
183                 } else {
184                         Worker::add(PRIORITY_HIGH, 'UpdateContact', $contact_id);
185                 }
186         }
187
188         /**
189          * @param int $contact_id Id of the contact with uid != 0
190          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
191          * @throws \ImagickException
192          */
193         private static function updateContactFromProbe(int $contact_id)
194         {
195                 $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]);
196                 if (!DBA::isResult($contact)) {
197                         return;
198                 }
199
200                 // Update the entry in the contact table
201                 Model\Contact::updateFromProbe($contact_id);
202         }
203
204         /**
205          * Toggles the blocked status of a contact identified by id.
206          *
207          * @param int $contact_id Id of the contact with uid = 0
208          * @throws \Exception
209          */
210         private static function toggleBlockContact(int $contact_id)
211         {
212                 $blocked = !Model\Contact\User::isBlocked($contact_id, local_user());
213                 Model\Contact\User::setBlocked($contact_id, local_user(), $blocked);
214         }
215
216         /**
217          * Toggles the ignored status of a contact identified by id.
218          *
219          * @param int $contact_id Id of the contact with uid = 0
220          * @throws \Exception
221          */
222         private static function toggleIgnoreContact(int $contact_id)
223         {
224                 $ignored = !Model\Contact\User::isIgnored($contact_id, local_user());
225                 Model\Contact\User::setIgnored($contact_id, local_user(), $ignored);
226         }
227
228         /**
229          * @param int $contact_id Id for contact with uid != 0
230          * @param int $uid        Id for user we want to drop the contact for
231          * @return bool
232          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
233          * @throws \ImagickException
234          */
235         private static function dropContact(int $contact_id, int $uid): bool
236         {
237                 $contact = Model\Contact::getContactForUser($contact_id, $uid);
238                 if (!DBA::isResult($contact)) {
239                         return false;
240                 }
241
242                 $owner = Model\User::getOwnerDataById($uid);
243                 if (!DBA::isResult($owner)) {
244                         return false;
245                 }
246
247                 Model\Contact::terminateFriendship($owner, $contact, true);
248                 Model\Contact::remove($contact['id']);
249
250                 return true;
251         }
252
253         public static function content(array $parameters = [], $update = 0)
254         {
255                 if (!local_user()) {
256                         return Login::form($_SERVER['REQUEST_URI']);
257                 }
258
259                 $a = DI::app();
260
261                 $search = Strings::escapeTags(trim($_GET['search'] ?? ''));
262                 $nets   = Strings::escapeTags(trim($_GET['nets']   ?? ''));
263                 $rel    = Strings::escapeTags(trim($_GET['rel']    ?? ''));
264                 $group  = Strings::escapeTags(trim($_GET['group']  ?? ''));
265
266                 $accounttype = $_GET['accounttype'] ?? '';
267                 $accounttypeid = User::getAccountTypeByString($accounttype);
268
269                 $page = DI::page();
270
271                 $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
272                 $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
273                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
274                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
275
276                 $contact = null;
277                 // @TODO: Replace with parameter from router
278                 if (DI::args()->getArgc() == 2 && intval(DI::args()->getArgv()[1])
279                         || DI::args()->getArgc() == 3 && intval(DI::args()->getArgv()[1]) && in_array(DI::args()->getArgv()[2], ['posts', 'conversations'])
280                 ) {
281                         $contact_id = intval(DI::args()->getArgv()[1]);
282
283                         // Ensure to use the user contact when the public contact was provided
284                         $data = Model\Contact::getPublicAndUserContactID($contact_id, local_user());
285                         if (!empty($data['user']) && ($contact_id == $data['public'])) {
286                                 $contact_id = $data['user'];
287                         }
288
289                         $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'deleted' => false]);
290
291                         // Don't display contacts that are about to be deleted
292                         if ($contact['network'] == Protocol::PHANTOM) {
293                                 $contact = false;
294                         }
295                 }
296
297                 if (DBA::isResult($contact)) {
298                         if ($contact['self']) {
299                                 // @TODO: Replace with parameter from router
300                                 if ((DI::args()->getArgc() == 3) && intval(DI::args()->getArgv()[1]) && in_array(DI::args()->getArgv()[2], ['posts', 'conversations'])) {
301                                         DI::baseUrl()->redirect('profile/' . $contact['nick']);
302                                 } else {
303                                         DI::baseUrl()->redirect('profile/' . $contact['nick'] . '/profile');
304                                 }
305                         }
306
307                         $vcard_widget = Widget\VCard::getHTML($contact);
308
309                         $findpeople_widget = '';
310                         $follow_widget = '';
311                         $account_widget = '';
312                         $networks_widget = '';
313                         $rel_widget = '';
314
315                         if ($contact['uid'] != 0) {
316                                 $groups_widget = Model\Group::sidebarWidget('contact', 'group', 'full', 'everyone', $contact_id);
317                         } else {
318                                 $groups_widget = '';
319                         }
320                 } else {
321                         $vcard_widget = '';
322                         $findpeople_widget = Widget::findPeople();
323                         if (isset($_GET['add'])) {
324                                 $follow_widget = Widget::follow($_GET['add']);
325                         } else {
326                                 $follow_widget = Widget::follow();
327                         }
328
329                         $account_widget = Widget::accounttypes($_SERVER['REQUEST_URI'], $accounttype);
330                         $networks_widget = Widget::networks($_SERVER['REQUEST_URI'], $nets);
331                         $rel_widget = Widget::contactRels($_SERVER['REQUEST_URI'], $rel);
332                         $groups_widget = Widget::groups($_SERVER['REQUEST_URI'], $group);
333                 }
334
335                 DI::page()['aside'] .= $vcard_widget . $findpeople_widget . $follow_widget . $account_widget . $groups_widget . $networks_widget . $rel_widget;
336
337                 $tpl = Renderer::getMarkupTemplate('contacts-head.tpl');
338                 DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
339                         '$baseurl' => DI::baseUrl()->get(true),
340                 ]);
341
342                 $o = '';
343                 Nav::setSelected('contact');
344
345                 if (!local_user()) {
346                         notice(DI::l10n()->t('Permission denied.'));
347                         return Login::form();
348                 }
349
350                 if (DI::args()->getArgc() == 3) {
351                         $contact_id = intval(DI::args()->getArgv()[1]);
352                         if (!$contact_id) {
353                                 throw new BadRequestException();
354                         }
355
356                         // @TODO: Replace with parameter from router
357                         $cmd = DI::args()->getArgv()[2];
358
359                         $orig_record = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => [0, local_user()], 'self' => false, 'deleted' => false]);
360                         if (!DBA::isResult($orig_record)) {
361                                 throw new NotFoundException(DI::l10n()->t('Contact not found'));
362                         }
363
364                         $cdata = Model\Contact::getPublicAndUserContactID($orig_record['id'], local_user());
365                         if (empty($cdata)) {
366                                 throw new NotFoundException(DI::l10n()->t('Contact not found'));
367                         }
368
369                         if ($cmd === 'update' && $cdata['user']) {
370                                 self::updateContactFromPoll($cdata['user']);
371                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
372                                 // NOTREACHED
373                         }
374
375                         if ($cmd === 'updateprofile' && $cdata['user']) {
376                                 self::updateContactFromProbe($cdata['user']);
377                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
378                                 // NOTREACHED
379                         }
380
381                         if ($cmd === 'block') {
382                                 if (public_contact() === $cdata['public']) {
383                                         throw new BadRequestException(DI::l10n()->t('You can\'t block yourself'));
384                                 }
385
386                                 self::toggleBlockContact($cdata['public']);
387
388                                 $blocked = Model\Contact\User::isBlocked($contact_id, local_user());
389                                 info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked')));
390
391                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
392                                 // NOTREACHED
393                         }
394
395                         if ($cmd === 'ignore') {
396                                 if (public_contact() === $cdata['public']) {
397                                         throw new BadRequestException(DI::l10n()->t('You can\'t ignore yourself'));
398                                 }
399
400                                 self::toggleIgnoreContact($cdata['public']);
401
402                                 $ignored = Model\Contact\User::isIgnored($cdata['public'], local_user());
403                                 info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored')));
404
405                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
406                                 // NOTREACHED
407                         }
408
409                         if ($cmd === 'drop' && $cdata['user']) {
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                                 if (self::dropContact($cdata['user'], local_user())) {
431                                         info(DI::l10n()->t('Contact has been removed.'));
432                                 }
433
434                                 DI::baseUrl()->redirect('contact');
435                                 // NOTREACHED
436                         }
437                         if ($cmd === 'posts') {
438                                 return self::getPostsHTML($a, $contact_id);
439                         }
440                         if ($cmd === 'conversations') {
441                                 return self::getConversationsHMTL($a, $contact_id, $update);
442                         }
443                 }
444
445                 $_SESSION['return_path'] = DI::args()->getQueryString();
446
447                 if (!empty($contact)) {
448                         DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
449                                 '$baseurl' => DI::baseUrl()->get(true),
450                         ]);
451
452                         $contact['blocked']  = Model\Contact\User::isBlocked($contact['id'], local_user());
453                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
454
455                         $relation_text = '';
456                         switch ($contact['rel']) {
457                                 case Model\Contact::FRIEND:
458                                         $relation_text = DI::l10n()->t('You are mutual friends with %s');
459                                         break;
460
461                                 case Model\Contact::FOLLOWER;
462                                         $relation_text = DI::l10n()->t('You are sharing with %s');
463                                         break;
464
465                                 case Model\Contact::SHARING;
466                                         $relation_text = DI::l10n()->t('%s is sharing with you');
467                                         break;
468
469                                 default:
470                                         break;
471                         }
472
473                         if ($contact['uid'] == 0) {
474                                 $relation_text = '';
475                         }
476
477                         if (!in_array($contact['network'], array_merge(Protocol::FEDERATED, [Protocol::TWITTER]))) {
478                                 $relation_text = '';
479                         }
480
481                         $relation_text = sprintf($relation_text, $contact['name']);
482
483                         $url = Model\Contact::magicLinkByContact($contact);
484                         if (strpos($url, 'redir/') === 0) {
485                                 $sparkle = ' class="sparkle" ';
486                         } else {
487                                 $sparkle = '';
488                         }
489
490                         $insecure = DI::l10n()->t('Private communications are not available for this contact.');
491
492                         $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? DI::l10n()->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
493
494                         if ($contact['last-update'] > DBA::NULL_DATETIME) {
495                                 $last_update .= ' ' . ($contact['failed'] ? DI::l10n()->t('(Update was not successful)') : DI::l10n()->t('(Update was successful)'));
496                         }
497                         $lblsuggest = (($contact['network'] === Protocol::DFRN) ? DI::l10n()->t('Suggest friends') : '');
498
499                         $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
500
501                         $nettype = DI::l10n()->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']));
502
503                         // tabs
504                         $tab_str = self::getTabsHTML($contact, self::TAB_PROFILE);
505
506                         $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? DI::l10n()->t('Communications lost with this contact!') : '');
507
508                         $fetch_further_information = null;
509                         if ($contact['network'] == Protocol::FEED) {
510                                 $fetch_further_information = [
511                                         'fetch_further_information',
512                                         DI::l10n()->t('Fetch further information for feeds'),
513                                         $contact['fetch_further_information'],
514                                         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.'),
515                                         [
516                                                 '0' => DI::l10n()->t('Disabled'),
517                                                 '1' => DI::l10n()->t('Fetch information'),
518                                                 '3' => DI::l10n()->t('Fetch keywords'),
519                                                 '2' => DI::l10n()->t('Fetch information and keywords')
520                                         ]
521                                 ];
522                         }
523
524                         // Disable remote self for everything except feeds.
525                         // There is an issue when you repeat an item from maybe twitter and you got comments from friendica and twitter
526                         // Problem is, you couldn't reply to both networks.
527                         $allow_remote_self = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
528                                 && DI::config()->get('system', 'allow_users_remote_self');
529
530                         if ($contact['network'] == Protocol::FEED) {
531                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
532                                         Model\Contact::MIRROR_FORWARDED => DI::l10n()->t('Mirror as forwarded posting'),
533                                         Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting')];
534                         } elseif (in_array($contact['network'], [Protocol::ACTIVITYPUB])) {
535                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
536                                 Model\Contact::MIRROR_NATIVE_RESHARE => DI::l10n()->t('Native reshare')];
537                         } elseif (in_array($contact['network'], [Protocol::DFRN])) {
538                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
539                                 Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting'),
540                                 Model\Contact::MIRROR_NATIVE_RESHARE => DI::l10n()->t('Native reshare')];
541                         } else {
542                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
543                                         Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting')];
544                         }
545
546                         $poll_interval = null;
547                         if ((($contact['network'] == Protocol::FEED) && !DI::config()->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) {
548                                 $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled);
549                         }
550
551                         // Load contactact related actions like hide, suggest, delete and others
552                         $contact_actions = self::getContactActions($contact);
553
554                         if ($contact['uid'] != 0) {
555                                 $lbl_info1 = DI::l10n()->t('Contact Information / Notes');
556                                 $contact_settings_label = DI::l10n()->t('Contact Settings');
557                         } else {
558                                 $lbl_info1 = null;
559                                 $contact_settings_label = null;
560                         }
561
562                         $tpl = Renderer::getMarkupTemplate('contact_edit.tpl');
563                         $o .= Renderer::replaceMacros($tpl, [
564                                 '$header'         => DI::l10n()->t('Contact'),
565                                 '$tab_str'        => $tab_str,
566                                 '$submit'         => DI::l10n()->t('Submit'),
567                                 '$lbl_info1'      => $lbl_info1,
568                                 '$lbl_info2'      => DI::l10n()->t('Their personal note'),
569                                 '$reason'         => trim(Strings::escapeTags($contact['reason'])),
570                                 '$infedit'        => DI::l10n()->t('Edit contact notes'),
571                                 '$common_link'    => 'contact/' . $contact['id'] . '/contacts/common',
572                                 '$relation_text'  => $relation_text,
573                                 '$visit'          => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
574                                 '$blockunblock'   => DI::l10n()->t('Block/Unblock contact'),
575                                 '$ignorecont'     => DI::l10n()->t('Ignore contact'),
576                                 '$lblrecent'      => DI::l10n()->t('View conversations'),
577                                 '$lblsuggest'     => $lblsuggest,
578                                 '$nettype'        => $nettype,
579                                 '$poll_interval'  => $poll_interval,
580                                 '$poll_enabled'   => $poll_enabled,
581                                 '$lastupdtext'    => DI::l10n()->t('Last update:'),
582                                 '$lost_contact'   => $lost_contact,
583                                 '$updpub'         => DI::l10n()->t('Update public posts'),
584                                 '$last_update'    => $last_update,
585                                 '$udnow'          => DI::l10n()->t('Update now'),
586                                 '$contact_id'     => $contact['id'],
587                                 '$block_text'     => ($contact['blocked'] ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
588                                 '$ignore_text'    => ($contact['readonly'] ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
589                                 '$insecure'       => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
590                                 '$info'           => $contact['info'],
591                                 '$cinfo'          => ['info', '', $contact['info'], ''],
592                                 '$blocked'        => ($contact['blocked'] ? DI::l10n()->t('Currently blocked') : ''),
593                                 '$ignored'        => ($contact['readonly'] ? DI::l10n()->t('Currently ignored') : ''),
594                                 '$archived'       => ($contact['archive'] ? DI::l10n()->t('Currently archived') : ''),
595                                 '$pending'        => ($contact['pending'] ? DI::l10n()->t('Awaiting connection acknowledge') : ''),
596                                 '$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')],
597                                 '$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')],
598                                 '$fetch_further_information' => $fetch_further_information,
599                                 '$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')],
600                                 '$photo'          => Model\Contact::getPhoto($contact),
601                                 '$name'           => $contact['name'],
602                                 '$sparkle'        => $sparkle,
603                                 '$url'            => $url,
604                                 '$profileurllabel'=> DI::l10n()->t('Profile URL'),
605                                 '$profileurl'     => $contact['url'],
606                                 '$account_type'   => Model\Contact::getAccountType($contact),
607                                 '$location'       => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
608                                 '$location_label' => DI::l10n()->t('Location:'),
609                                 '$xmpp'           => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
610                                 '$xmpp_label'     => DI::l10n()->t('XMPP:'),
611                                 '$matrix'         => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
612                                 '$matrix_label'   => DI::l10n()->t('Matrix:'),
613                                 '$about'          => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
614                                 '$about_label'    => DI::l10n()->t('About:'),
615                                 '$keywords'       => $contact['keywords'],
616                                 '$keywords_label' => DI::l10n()->t('Tags:'),
617                                 '$contact_action_button' => DI::l10n()->t('Actions'),
618                                 '$contact_actions'=> $contact_actions,
619                                 '$contact_status' => DI::l10n()->t('Status'),
620                                 '$contact_settings_label' => $contact_settings_label,
621                                 '$contact_profile_label' => DI::l10n()->t('Profile'),
622                                 '$allow_remote_self' => $allow_remote_self,
623                                 '$remote_self'       => ['remote_self',
624                                         DI::l10n()->t('Mirror postings from this contact'),
625                                         $contact['remote_self'],
626                                         DI::l10n()->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
627                                         $remote_self_options
628                                 ],
629                         ]);
630
631                         $arr = ['contact' => $contact, 'output' => $o];
632
633                         Hook::callAll('contact_edit', $arr);
634
635                         return $arr['output'];
636                 }
637
638                 $sql_values = [local_user()];
639
640                 // @TODO: Replace with parameter from router
641                 $type = DI::args()->getArgv()[1] ?? '';
642
643                 switch ($type) {
644                         case 'blocked':
645                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`blocked`)";
646                                 // This makes the query look for contact.uid = 0
647                                 array_unshift($sql_values, 0);
648                                 break;
649                         case 'hidden':
650                                 $sql_extra = " AND `hidden` AND NOT `blocked` AND NOT `pending`";
651                                 break;
652                         case 'ignored':
653                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`ignored`)";
654                                 // This makes the query look for contact.uid = 0
655                                 array_unshift($sql_values, 0);
656                                 break;
657                         case 'archived':
658                                 $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`";
659                                 break;
660                         case 'pending':
661                                 $sql_extra = " AND `pending` AND NOT `archive` AND NOT `failed` AND ((`rel` = ?)
662                                         OR EXISTS (SELECT `id` FROM `intro` WHERE `contact-id` = `contact`.`id` AND NOT `ignore`))";
663                                 $sql_values[] = Model\Contact::SHARING;
664                                 break;
665                         default:
666                                 $sql_extra = " AND NOT `archive` AND NOT `blocked` AND NOT `pending`";
667                                 break;
668                 }
669
670                 if (isset($accounttypeid)) {
671                         $sql_extra .= " AND `contact-type` = ?";
672                         $sql_values[] = $accounttypeid;
673                 }
674
675                 $searching = false;
676                 $search_hdr = null;
677                 if ($search) {
678                         $searching = true;
679                         $search_hdr = $search;
680                         $search_txt = preg_quote($search);
681                         $sql_extra .= " AND (name REGEXP ? OR url REGEXP ? OR nick REGEXP ?)";
682                         $sql_values[] = $search_txt;
683                         $sql_values[] = $search_txt;
684                         $sql_values[] = $search_txt;
685                 }
686
687                 if ($nets) {
688                         $sql_extra .= " AND network = ? ";
689                         $sql_values[] = $nets;
690                 }
691
692                 switch ($rel) {
693                         case 'followers':
694                                 $sql_extra .= " AND `rel` IN (?, ?)";
695                                 $sql_values[] = Model\Contact::FOLLOWER;
696                                 $sql_values[] = Model\Contact::FRIEND;
697                                 break;
698                         case 'following':
699                                 $sql_extra .= " AND `rel` IN (?, ?)";
700                                 $sql_values[] = Model\Contact::SHARING;
701                                 $sql_values[] = Model\Contact::FRIEND;
702                                 break;
703                         case 'mutuals':
704                                 $sql_extra .= " AND `rel` = ?";
705                                 $sql_values[] = Model\Contact::FRIEND;
706                                 break;
707                 }
708
709                 if ($group) {
710                         $sql_extra = " AND EXISTS(SELECT `id` FROM `group_member` WHERE `gid` = ? AND `contact`.`id` = `contact-id`)";
711                         $sql_values[] = $group;
712                 }
713
714                 $total = 0;
715                 $stmt = DBA::p("SELECT COUNT(*) AS `total`
716                         FROM `contact`
717                         WHERE `uid` = ?
718                         AND `self` = 0
719                         AND NOT `deleted`
720                         $sql_extra
721                         " . Widget::unavailableNetworks(),
722                         $sql_values
723                 );
724                 if (DBA::isResult($stmt)) {
725                         $total = DBA::fetch($stmt)['total'];
726                 }
727                 DBA::close($stmt);
728
729                 $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
730
731                 $sql_values[] = $pager->getStart();
732                 $sql_values[] = $pager->getItemsPerPage();
733
734                 $contacts = [];
735
736                 $stmt = DBA::p("SELECT *
737                         FROM `contact`
738                         WHERE `uid` = ?
739                         AND `self` = 0
740                         AND NOT `deleted`
741                         $sql_extra
742                         ORDER BY `name` ASC
743                         LIMIT ?, ?",
744                         $sql_values
745                 );
746                 while ($contact = DBA::fetch($stmt)) {
747                         $contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], local_user());
748                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
749                         $contacts[] = self::getContactTemplateVars($contact);
750                 }
751                 DBA::close($stmt);
752
753                 $tabs = [
754                         [
755                                 'label' => DI::l10n()->t('All Contacts'),
756                                 'url'   => 'contact',
757                                 'sel'   => !$type ? 'active' : '',
758                                 'title' => DI::l10n()->t('Show all contacts'),
759                                 'id'    => 'showall-tab',
760                                 'accesskey' => 'l',
761                         ],
762                         [
763                                 'label' => DI::l10n()->t('Pending'),
764                                 'url'   => 'contact/pending',
765                                 'sel'   => $type == 'pending' ? 'active' : '',
766                                 'title' => DI::l10n()->t('Only show pending contacts'),
767                                 'id'    => 'showpending-tab',
768                                 'accesskey' => 'p',
769                         ],
770                         [
771                                 'label' => DI::l10n()->t('Blocked'),
772                                 'url'   => 'contact/blocked',
773                                 'sel'   => $type == 'blocked' ? 'active' : '',
774                                 'title' => DI::l10n()->t('Only show blocked contacts'),
775                                 'id'    => 'showblocked-tab',
776                                 'accesskey' => 'b',
777                         ],
778                         [
779                                 'label' => DI::l10n()->t('Ignored'),
780                                 'url'   => 'contact/ignored',
781                                 'sel'   => $type == 'ignored' ? 'active' : '',
782                                 'title' => DI::l10n()->t('Only show ignored contacts'),
783                                 'id'    => 'showignored-tab',
784                                 'accesskey' => 'i',
785                         ],
786                         [
787                                 'label' => DI::l10n()->t('Archived'),
788                                 'url'   => 'contact/archived',
789                                 'sel'   => $type == 'archived' ? 'active' : '',
790                                 'title' => DI::l10n()->t('Only show archived contacts'),
791                                 'id'    => 'showarchived-tab',
792                                 'accesskey' => 'y',
793                         ],
794                         [
795                                 'label' => DI::l10n()->t('Hidden'),
796                                 'url'   => 'contact/hidden',
797                                 'sel'   => $type == 'hidden' ? 'active' : '',
798                                 'title' => DI::l10n()->t('Only show hidden contacts'),
799                                 'id'    => 'showhidden-tab',
800                                 'accesskey' => 'h',
801                         ],
802                         [
803                                 'label' => DI::l10n()->t('Groups'),
804                                 'url'   => 'group',
805                                 'sel'   => '',
806                                 'title' => DI::l10n()->t('Organize your contact groups'),
807                                 'id'    => 'contactgroups-tab',
808                                 'accesskey' => 'e',
809                         ],
810                 ];
811
812                 $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
813                 $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
814
815                 switch ($rel) {
816                         case 'followers': $header = DI::l10n()->t('Followers'); break;
817                         case 'following': $header = DI::l10n()->t('Following'); break;
818                         case 'mutuals':   $header = DI::l10n()->t('Mutual friends'); break;
819                         default:          $header = DI::l10n()->t('Contacts');
820                 }
821
822                 switch ($type) {
823                         case 'pending':  $header .= ' - ' . DI::l10n()->t('Pending'); break;
824                         case 'blocked':  $header .= ' - ' . DI::l10n()->t('Blocked'); break;
825                         case 'hidden':   $header .= ' - ' . DI::l10n()->t('Hidden'); break;
826                         case 'ignored':  $header .= ' - ' . DI::l10n()->t('Ignored'); break;
827                         case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break;
828                 }
829
830                 $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
831
832                 $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
833                 $o .= Renderer::replaceMacros($tpl, [
834                         '$header'     => $header,
835                         '$tabs'       => $tabs_html,
836                         '$total'      => $total,
837                         '$search'     => $search_hdr,
838                         '$desc'       => DI::l10n()->t('Search your contacts'),
839                         '$finding'    => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
840                         '$submit'     => DI::l10n()->t('Find'),
841                         '$cmd'        => DI::args()->getCommand(),
842                         '$contacts'   => $contacts,
843                         '$contact_drop_confirm' => DI::l10n()->t('Do you really want to delete this contact?'),
844                         'multiselect' => 1,
845                         '$batch_actions' => [
846                                 'contacts_batch_update'  => DI::l10n()->t('Update'),
847                                 'contacts_batch_block'   => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
848                                 'contacts_batch_ignore'  => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
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', '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['delete'] = [
1132                                 'label' => DI::l10n()->t('Delete'),
1133                                 'url'   => 'contact/' . $contact['id'] . '/drop',
1134                                 'title' => DI::l10n()->t('Delete contact'),
1135                                 'sel'   => '',
1136                                 'id'    => 'delete',
1137                         ];
1138                 }
1139
1140                 return $contact_actions;
1141         }
1142 }