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