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