]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact.php
e34c0043c7a0b28d8c5dcc98d027a6314a8d4c76
[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 = DBA::update('contact', [
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                                 DBA::update('contact', ['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', 'media'])
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', 'media'])) {
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 === 'media') {
380                                 return self::getPostsHTML($contact_id, true);
381                         }
382
383                         if ($cmd === 'conversations') {
384                                 return self::getConversationsHMTL($a, $contact_id, $update);
385                         }
386
387                         self::checkFormSecurityTokenRedirectOnError('contact/' . $contact_id, 'contact_action', 't');
388
389                         $cdata = Model\Contact::getPublicAndUserContactID($orig_record['id'], local_user());
390                         if (empty($cdata)) {
391                                 throw new NotFoundException(DI::l10n()->t('Contact not found'));
392                         }
393
394                         if ($cmd === 'update' && $cdata['user']) {
395                                 self::updateContactFromPoll($cdata['user']);
396                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
397                                 // NOTREACHED
398                         }
399
400                         if ($cmd === 'updateprofile' && $cdata['user']) {
401                                 self::updateContactFromProbe($cdata['user']);
402                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
403                                 // NOTREACHED
404                         }
405
406                         if ($cmd === 'block') {
407                                 if (public_contact() === $cdata['public']) {
408                                         throw new BadRequestException(DI::l10n()->t('You can\'t block yourself'));
409                                 }
410
411                                 self::toggleBlockContact($cdata['public']);
412
413                                 $blocked = Model\Contact\User::isBlocked($contact_id, local_user());
414                                 info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked')));
415
416                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
417                                 // NOTREACHED
418                         }
419
420                         if ($cmd === 'ignore') {
421                                 if (public_contact() === $cdata['public']) {
422                                         throw new BadRequestException(DI::l10n()->t('You can\'t ignore yourself'));
423                                 }
424
425                                 self::toggleIgnoreContact($cdata['public']);
426
427                                 $ignored = Model\Contact\User::isIgnored($cdata['public'], local_user());
428                                 info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored')));
429
430                                 DI::baseUrl()->redirect('contact/' . $cdata['public']);
431                                 // NOTREACHED
432                         }
433
434                         if ($cmd === 'drop' && $cdata['user']) {
435                                 // Check if we should do HTML-based delete confirmation
436                                 if (!empty($_REQUEST['confirm'])) {
437                                         DI::page()['aside'] = '';
438
439                                         return Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_drop_confirm.tpl'), [
440                                                 '$header' => DI::l10n()->t('Drop contact'),
441                                                 '$contact' => self::getContactTemplateVars($orig_record),
442                                                 '$method' => 'get',
443                                                 '$message' => DI::l10n()->t('Do you really want to delete this contact?'),
444                                                 '$confirm' => DI::l10n()->t('Yes'),
445                                                 '$confirm_url' => DI::args()->getCommand(),
446                                                 '$confirm_name' => 't',
447                                                 '$confirm_value' => BaseModule::getFormSecurityToken('contact_action'),
448                                                 '$cancel' => DI::l10n()->t('Cancel'),
449                                         ]);
450                                 }
451                                 // Now check how the user responded to the confirmation query
452                                 if (!empty($_REQUEST['canceled'])) {
453                                         DI::baseUrl()->redirect('contact');
454                                 }
455
456                                 if (self::dropContact($cdata['user'], local_user())) {
457                                         info(DI::l10n()->t('Contact has been removed.'));
458                                 }
459
460                                 DI::baseUrl()->redirect('contact');
461                                 // NOTREACHED
462                         }
463                 }
464
465                 $_SESSION['return_path'] = DI::args()->getQueryString();
466
467                 if (!empty($contact)) {
468                         DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
469                                 '$baseurl' => DI::baseUrl()->get(true),
470                         ]);
471
472                         $contact['blocked']  = Model\Contact\User::isBlocked($contact['id'], local_user());
473                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
474
475                         $relation_text = '';
476                         switch ($contact['rel']) {
477                                 case Model\Contact::FRIEND:
478                                         $relation_text = DI::l10n()->t('You are mutual friends with %s');
479                                         break;
480
481                                 case Model\Contact::FOLLOWER;
482                                         $relation_text = DI::l10n()->t('You are sharing with %s');
483                                         break;
484
485                                 case Model\Contact::SHARING;
486                                         $relation_text = DI::l10n()->t('%s is sharing with you');
487                                         break;
488
489                                 default:
490                                         break;
491                         }
492
493                         if ($contact['uid'] == 0) {
494                                 $relation_text = '';
495                         }
496
497                         if (!in_array($contact['network'], array_merge(Protocol::FEDERATED, [Protocol::TWITTER]))) {
498                                 $relation_text = '';
499                         }
500
501                         $relation_text = sprintf($relation_text, $contact['name']);
502
503                         $url = Model\Contact::magicLinkByContact($contact);
504                         if (strpos($url, 'redir/') === 0) {
505                                 $sparkle = ' class="sparkle" ';
506                         } else {
507                                 $sparkle = '';
508                         }
509
510                         $insecure = DI::l10n()->t('Private communications are not available for this contact.');
511
512                         $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? DI::l10n()->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
513
514                         if ($contact['last-update'] > DBA::NULL_DATETIME) {
515                                 $last_update .= ' ' . ($contact['failed'] ? DI::l10n()->t('(Update was not successful)') : DI::l10n()->t('(Update was successful)'));
516                         }
517                         $lblsuggest = (($contact['network'] === Protocol::DFRN) ? DI::l10n()->t('Suggest friends') : '');
518
519                         $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
520
521                         $nettype = DI::l10n()->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']));
522
523                         // tabs
524                         $tab_str = self::getTabsHTML($contact, self::TAB_PROFILE);
525
526                         $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? DI::l10n()->t('Communications lost with this contact!') : '');
527
528                         $fetch_further_information = null;
529                         if ($contact['network'] == Protocol::FEED) {
530                                 $fetch_further_information = [
531                                         'fetch_further_information',
532                                         DI::l10n()->t('Fetch further information for feeds'),
533                                         $contact['fetch_further_information'],
534                                         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.'),
535                                         [
536                                                 '0' => DI::l10n()->t('Disabled'),
537                                                 '1' => DI::l10n()->t('Fetch information'),
538                                                 '3' => DI::l10n()->t('Fetch keywords'),
539                                                 '2' => DI::l10n()->t('Fetch information and keywords')
540                                         ]
541                                 ];
542                         }
543
544                         // Disable remote self for everything except feeds.
545                         // There is an issue when you repeat an item from maybe twitter and you got comments from friendica and twitter
546                         // Problem is, you couldn't reply to both networks.
547                         $allow_remote_self = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
548                                 && DI::config()->get('system', 'allow_users_remote_self');
549
550                         if ($contact['network'] == Protocol::FEED) {
551                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
552                                         Model\Contact::MIRROR_FORWARDED => DI::l10n()->t('Mirror as forwarded posting'),
553                                         Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting')];
554                         } elseif (in_array($contact['network'], [Protocol::ACTIVITYPUB])) {
555                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
556                                 Model\Contact::MIRROR_NATIVE_RESHARE => DI::l10n()->t('Native reshare')];
557                         } elseif (in_array($contact['network'], [Protocol::DFRN])) {
558                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
559                                 Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting'),
560                                 Model\Contact::MIRROR_NATIVE_RESHARE => DI::l10n()->t('Native reshare')];
561                         } else {
562                                 $remote_self_options = [Model\Contact::MIRROR_DEACTIVATED => DI::l10n()->t('No mirroring'),
563                                         Model\Contact::MIRROR_OWN_POST => DI::l10n()->t('Mirror as my own posting')];
564                         }
565
566                         $poll_interval = null;
567                         if ((($contact['network'] == Protocol::FEED) && !DI::config()->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) {
568                                 $poll_interval = ContactSelector::pollInterval($contact['priority'], !$poll_enabled);
569                         }
570
571                         // Load contactact related actions like hide, suggest, delete and others
572                         $contact_actions = self::getContactActions($contact);
573
574                         if ($contact['uid'] != 0) {
575                                 $lbl_info1 = DI::l10n()->t('Contact Information / Notes');
576                                 $contact_settings_label = DI::l10n()->t('Contact Settings');
577                         } else {
578                                 $lbl_info1 = null;
579                                 $contact_settings_label = null;
580                         }
581
582                         $tpl = Renderer::getMarkupTemplate('contact_edit.tpl');
583                         $o .= Renderer::replaceMacros($tpl, [
584                                 '$header'         => DI::l10n()->t('Contact'),
585                                 '$tab_str'        => $tab_str,
586                                 '$submit'         => DI::l10n()->t('Submit'),
587                                 '$lbl_info1'      => $lbl_info1,
588                                 '$lbl_info2'      => DI::l10n()->t('Their personal note'),
589                                 '$reason'         => trim(Strings::escapeTags($contact['reason'])),
590                                 '$infedit'        => DI::l10n()->t('Edit contact notes'),
591                                 '$common_link'    => 'contact/' . $contact['id'] . '/contacts/common',
592                                 '$relation_text'  => $relation_text,
593                                 '$visit'          => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
594                                 '$blockunblock'   => DI::l10n()->t('Block/Unblock contact'),
595                                 '$ignorecont'     => DI::l10n()->t('Ignore contact'),
596                                 '$lblrecent'      => DI::l10n()->t('View conversations'),
597                                 '$lblsuggest'     => $lblsuggest,
598                                 '$nettype'        => $nettype,
599                                 '$poll_interval'  => $poll_interval,
600                                 '$poll_enabled'   => $poll_enabled,
601                                 '$lastupdtext'    => DI::l10n()->t('Last update:'),
602                                 '$lost_contact'   => $lost_contact,
603                                 '$updpub'         => DI::l10n()->t('Update public posts'),
604                                 '$last_update'    => $last_update,
605                                 '$udnow'          => DI::l10n()->t('Update now'),
606                                 '$contact_id'     => $contact['id'],
607                                 '$block_text'     => ($contact['blocked'] ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
608                                 '$ignore_text'    => ($contact['readonly'] ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
609                                 '$insecure'       => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
610                                 '$info'           => $contact['info'],
611                                 '$cinfo'          => ['info', '', $contact['info'], ''],
612                                 '$blocked'        => ($contact['blocked'] ? DI::l10n()->t('Currently blocked') : ''),
613                                 '$ignored'        => ($contact['readonly'] ? DI::l10n()->t('Currently ignored') : ''),
614                                 '$archived'       => ($contact['archive'] ? DI::l10n()->t('Currently archived') : ''),
615                                 '$pending'        => ($contact['pending'] ? DI::l10n()->t('Awaiting connection acknowledge') : ''),
616                                 '$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')],
617                                 '$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')],
618                                 '$fetch_further_information' => $fetch_further_information,
619                                 '$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')],
620                                 '$photo'          => Model\Contact::getPhoto($contact),
621                                 '$name'           => $contact['name'],
622                                 '$sparkle'        => $sparkle,
623                                 '$url'            => $url,
624                                 '$profileurllabel'=> DI::l10n()->t('Profile URL'),
625                                 '$profileurl'     => $contact['url'],
626                                 '$account_type'   => Model\Contact::getAccountType($contact),
627                                 '$location'       => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
628                                 '$location_label' => DI::l10n()->t('Location:'),
629                                 '$xmpp'           => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
630                                 '$xmpp_label'     => DI::l10n()->t('XMPP:'),
631                                 '$matrix'         => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
632                                 '$matrix_label'   => DI::l10n()->t('Matrix:'),
633                                 '$about'          => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
634                                 '$about_label'    => DI::l10n()->t('About:'),
635                                 '$keywords'       => $contact['keywords'],
636                                 '$keywords_label' => DI::l10n()->t('Tags:'),
637                                 '$contact_action_button' => DI::l10n()->t('Actions'),
638                                 '$contact_actions'=> $contact_actions,
639                                 '$contact_status' => DI::l10n()->t('Status'),
640                                 '$contact_settings_label' => $contact_settings_label,
641                                 '$contact_profile_label' => DI::l10n()->t('Profile'),
642                                 '$allow_remote_self' => $allow_remote_self,
643                                 '$remote_self'       => ['remote_self',
644                                         DI::l10n()->t('Mirror postings from this contact'),
645                                         $contact['remote_self'],
646                                         DI::l10n()->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
647                                         $remote_self_options
648                                 ],
649                         ]);
650
651                         $arr = ['contact' => $contact, 'output' => $o];
652
653                         Hook::callAll('contact_edit', $arr);
654
655                         return $arr['output'];
656                 }
657
658                 $sql_values = [local_user()];
659
660                 // @TODO: Replace with parameter from router
661                 $type = DI::args()->getArgv()[1] ?? '';
662
663                 switch ($type) {
664                         case 'blocked':
665                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`blocked`)";
666                                 // This makes the query look for contact.uid = 0
667                                 array_unshift($sql_values, 0);
668                                 break;
669                         case 'hidden':
670                                 $sql_extra = " AND `hidden` AND NOT `blocked` AND NOT `pending`";
671                                 break;
672                         case 'ignored':
673                                 $sql_extra = " AND EXISTS(SELECT `id` from `user-contact` WHERE `contact`.`id` = `user-contact`.`cid` and `user-contact`.`uid` = ? and `user-contact`.`ignored`)";
674                                 // This makes the query look for contact.uid = 0
675                                 array_unshift($sql_values, 0);
676                                 break;
677                         case 'archived':
678                                 $sql_extra = " AND `archive` AND NOT `blocked` AND NOT `pending`";
679                                 break;
680                         case 'pending':
681                                 $sql_extra = " AND `pending` AND NOT `archive` AND NOT `failed` AND ((`rel` = ?)
682                                         OR EXISTS (SELECT `id` FROM `intro` WHERE `contact-id` = `contact`.`id` AND NOT `ignore`))";
683                                 $sql_values[] = Model\Contact::SHARING;
684                                 break;
685                         default:
686                                 $sql_extra = " AND NOT `archive` AND NOT `blocked` AND NOT `pending`";
687                                 break;
688                 }
689
690                 if (isset($accounttypeid)) {
691                         $sql_extra .= " AND `contact-type` = ?";
692                         $sql_values[] = $accounttypeid;
693                 }
694
695                 $searching = false;
696                 $search_hdr = null;
697                 if ($search) {
698                         $searching = true;
699                         $search_hdr = $search;
700                         $search_txt = preg_quote($search);
701                         $sql_extra .= " AND (name REGEXP ? OR url REGEXP ? OR nick REGEXP ?)";
702                         $sql_values[] = $search_txt;
703                         $sql_values[] = $search_txt;
704                         $sql_values[] = $search_txt;
705                 }
706
707                 if ($nets) {
708                         $sql_extra .= " AND network = ? ";
709                         $sql_values[] = $nets;
710                 }
711
712                 switch ($rel) {
713                         case 'followers':
714                                 $sql_extra .= " AND `rel` IN (?, ?)";
715                                 $sql_values[] = Model\Contact::FOLLOWER;
716                                 $sql_values[] = Model\Contact::FRIEND;
717                                 break;
718                         case 'following':
719                                 $sql_extra .= " AND `rel` IN (?, ?)";
720                                 $sql_values[] = Model\Contact::SHARING;
721                                 $sql_values[] = Model\Contact::FRIEND;
722                                 break;
723                         case 'mutuals':
724                                 $sql_extra .= " AND `rel` = ?";
725                                 $sql_values[] = Model\Contact::FRIEND;
726                                 break;
727                 }
728
729                 if ($group) {
730                         $sql_extra = " AND EXISTS(SELECT `id` FROM `group_member` WHERE `gid` = ? AND `contact`.`id` = `contact-id`)";
731                         $sql_values[] = $group;
732                 }
733
734                 $total = 0;
735                 $stmt = DBA::p("SELECT COUNT(*) AS `total`
736                         FROM `contact`
737                         WHERE `uid` = ?
738                         AND `self` = 0
739                         AND NOT `deleted`
740                         $sql_extra
741                         " . Widget::unavailableNetworks(),
742                         $sql_values
743                 );
744                 if (DBA::isResult($stmt)) {
745                         $total = DBA::fetch($stmt)['total'];
746                 }
747                 DBA::close($stmt);
748
749                 $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
750
751                 $sql_values[] = $pager->getStart();
752                 $sql_values[] = $pager->getItemsPerPage();
753
754                 $contacts = [];
755
756                 $stmt = DBA::p("SELECT *
757                         FROM `contact`
758                         WHERE `uid` = ?
759                         AND `self` = 0
760                         AND NOT `deleted`
761                         $sql_extra
762                         ORDER BY `name` ASC
763                         LIMIT ?, ?",
764                         $sql_values
765                 );
766                 while ($contact = DBA::fetch($stmt)) {
767                         $contact['blocked'] = Model\Contact\User::isBlocked($contact['id'], local_user());
768                         $contact['readonly'] = Model\Contact\User::isIgnored($contact['id'], local_user());
769                         $contacts[] = self::getContactTemplateVars($contact);
770                 }
771                 DBA::close($stmt);
772
773                 $tabs = [
774                         [
775                                 'label' => DI::l10n()->t('All Contacts'),
776                                 'url'   => 'contact',
777                                 'sel'   => !$type ? 'active' : '',
778                                 'title' => DI::l10n()->t('Show all contacts'),
779                                 'id'    => 'showall-tab',
780                                 'accesskey' => 'l',
781                         ],
782                         [
783                                 'label' => DI::l10n()->t('Pending'),
784                                 'url'   => 'contact/pending',
785                                 'sel'   => $type == 'pending' ? 'active' : '',
786                                 'title' => DI::l10n()->t('Only show pending contacts'),
787                                 'id'    => 'showpending-tab',
788                                 'accesskey' => 'p',
789                         ],
790                         [
791                                 'label' => DI::l10n()->t('Blocked'),
792                                 'url'   => 'contact/blocked',
793                                 'sel'   => $type == 'blocked' ? 'active' : '',
794                                 'title' => DI::l10n()->t('Only show blocked contacts'),
795                                 'id'    => 'showblocked-tab',
796                                 'accesskey' => 'b',
797                         ],
798                         [
799                                 'label' => DI::l10n()->t('Ignored'),
800                                 'url'   => 'contact/ignored',
801                                 'sel'   => $type == 'ignored' ? 'active' : '',
802                                 'title' => DI::l10n()->t('Only show ignored contacts'),
803                                 'id'    => 'showignored-tab',
804                                 'accesskey' => 'i',
805                         ],
806                         [
807                                 'label' => DI::l10n()->t('Archived'),
808                                 'url'   => 'contact/archived',
809                                 'sel'   => $type == 'archived' ? 'active' : '',
810                                 'title' => DI::l10n()->t('Only show archived contacts'),
811                                 'id'    => 'showarchived-tab',
812                                 'accesskey' => 'y',
813                         ],
814                         [
815                                 'label' => DI::l10n()->t('Hidden'),
816                                 'url'   => 'contact/hidden',
817                                 'sel'   => $type == 'hidden' ? 'active' : '',
818                                 'title' => DI::l10n()->t('Only show hidden contacts'),
819                                 'id'    => 'showhidden-tab',
820                                 'accesskey' => 'h',
821                         ],
822                         [
823                                 'label' => DI::l10n()->t('Groups'),
824                                 'url'   => 'group',
825                                 'sel'   => '',
826                                 'title' => DI::l10n()->t('Organize your contact groups'),
827                                 'id'    => 'contactgroups-tab',
828                                 'accesskey' => 'e',
829                         ],
830                 ];
831
832                 $tabs_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
833                 $tabs_html = Renderer::replaceMacros($tabs_tpl, ['$tabs' => $tabs]);
834
835                 switch ($rel) {
836                         case 'followers': $header = DI::l10n()->t('Followers'); break;
837                         case 'following': $header = DI::l10n()->t('Following'); break;
838                         case 'mutuals':   $header = DI::l10n()->t('Mutual friends'); break;
839                         default:          $header = DI::l10n()->t('Contacts');
840                 }
841
842                 switch ($type) {
843                         case 'pending':  $header .= ' - ' . DI::l10n()->t('Pending'); break;
844                         case 'blocked':  $header .= ' - ' . DI::l10n()->t('Blocked'); break;
845                         case 'hidden':   $header .= ' - ' . DI::l10n()->t('Hidden'); break;
846                         case 'ignored':  $header .= ' - ' . DI::l10n()->t('Ignored'); break;
847                         case 'archived': $header .= ' - ' . DI::l10n()->t('Archived'); break;
848                 }
849
850                 $header .= $nets ? ' - ' . ContactSelector::networkToName($nets) : '';
851
852                 $tpl = Renderer::getMarkupTemplate('contacts-template.tpl');
853                 $o .= Renderer::replaceMacros($tpl, [
854                         '$header'     => $header,
855                         '$tabs'       => $tabs_html,
856                         '$total'      => $total,
857                         '$search'     => $search_hdr,
858                         '$desc'       => DI::l10n()->t('Search your contacts'),
859                         '$finding'    => $searching ? DI::l10n()->t('Results for: %s', $search) : '',
860                         '$submit'     => DI::l10n()->t('Find'),
861                         '$cmd'        => DI::args()->getCommand(),
862                         '$contacts'   => $contacts,
863                         '$form_security_token'  => BaseModule::getFormSecurityToken('contact_batch_actions'),
864                         '$contact_drop_confirm' => DI::l10n()->t('Do you really want to delete this contact?'),
865                         'multiselect' => 1,
866                         '$batch_actions' => [
867                                 'contacts_batch_update'  => DI::l10n()->t('Update'),
868                                 'contacts_batch_block'   => DI::l10n()->t('Block') . '/' . DI::l10n()->t('Unblock'),
869                                 'contacts_batch_ignore'  => DI::l10n()->t('Ignore') . '/' . DI::l10n()->t('Unignore'),
870                                 'contacts_batch_drop'    => DI::l10n()->t('Delete'),
871                         ],
872                         '$h_batch_actions' => DI::l10n()->t('Batch Actions'),
873                         '$paginate'   => $pager->renderFull($total),
874                 ]);
875
876                 return $o;
877         }
878
879         /**
880          * List of pages for the Contact TabBar
881          *
882          * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends'
883          *
884          * @param array $contact    The contact array
885          * @param int   $active_tab 1 if tab should be marked as active
886          *
887          * @return string HTML string of the contact page tabs buttons.
888          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
889          * @throws \ImagickException
890          */
891         public static function getTabsHTML(array $contact, int $active_tab)
892         {
893                 $cid = $pcid = $contact['id'];
894                 $data = Model\Contact::getPublicAndUserContactID($contact['id'], local_user());
895                 if (!empty($data['user']) && ($contact['id'] == $data['public'])) {
896                         $cid = $data['user'];
897                 } elseif (!empty($data['public'])) {
898                         $pcid = $data['public'];
899                 }
900
901                 // tabs
902                 $tabs = [
903                         [
904                                 'label' => DI::l10n()->t('Status'),
905                                 'url'   => 'contact/' . $pcid . '/conversations',
906                                 'sel'   => (($active_tab == self::TAB_CONVERSATIONS) ? 'active' : ''),
907                                 'title' => DI::l10n()->t('Conversations started by this contact'),
908                                 'id'    => 'status-tab',
909                                 'accesskey' => 'm',
910                         ],
911                         [
912                                 'label' => DI::l10n()->t('Posts and Comments'),
913                                 'url'   => 'contact/' . $pcid . '/posts',
914                                 'sel'   => (($active_tab == self::TAB_POSTS) ? 'active' : ''),
915                                 'title' => DI::l10n()->t('Status Messages and Posts'),
916                                 'id'    => 'posts-tab',
917                                 'accesskey' => 'p',
918                         ],
919                         [
920                                 'label' => DI::l10n()->t('Media'),
921                                 'url'   => 'contact/' . $pcid . '/media',
922                                 'sel'   => (($active_tab == self::TAB_MEDIA) ? 'active' : ''),
923                                 'title' => DI::l10n()->t('Posts containing media objects'),
924                                 'id'    => 'media-tab',
925                                 'accesskey' => 'd',
926                         ],
927                         [
928                                 'label' => DI::l10n()->t('Profile'),
929                                 'url'   => 'contact/' . $cid,
930                                 'sel'   => (($active_tab == self::TAB_PROFILE) ? 'active' : ''),
931                                 'title' => DI::l10n()->t('Profile Details'),
932                                 'id'    => 'profile-tab',
933                                 'accesskey' => 'o',
934                         ],
935                         ['label' => DI::l10n()->t('Contacts'),
936                                 'url'   => 'contact/' . $pcid . '/contacts',
937                                 'sel'   => (($active_tab == self::TAB_CONTACTS) ? 'active' : ''),
938                                 'title' => DI::l10n()->t('View all known contacts'),
939                                 'id'    => 'contacts-tab',
940                                 'accesskey' => 't'
941                         ],
942                 ];
943
944                 if (!empty($contact['network']) && in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) && ($cid != $pcid)) {
945                         $tabs[] = ['label' => DI::l10n()->t('Advanced'),
946                                 'url'   => 'contact/' . $cid . '/advanced/',
947                                 'sel'   => (($active_tab == self::TAB_ADVANCED) ? 'active' : ''),
948                                 'title' => DI::l10n()->t('Advanced Contact Settings'),
949                                 'id'    => 'advanced-tab',
950                                 'accesskey' => 'r'
951                         ];
952                 }
953
954                 $tab_tpl = Renderer::getMarkupTemplate('common_tabs.tpl');
955                 $tab_str = Renderer::replaceMacros($tab_tpl, ['$tabs' => $tabs]);
956
957                 return $tab_str;
958         }
959
960         public static function getConversationsHMTL($a, $contact_id, $update, $parent = 0)
961         {
962                 $o = '';
963
964                 if (!$update) {
965                         // We need the editor here to be able to reshare an item.
966                         if (local_user()) {
967                                 $o = DI::conversation()->statusEditor([], 0, true);
968                         }
969                 }
970
971                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
972
973                 if (!$update) {
974                         $o .= self::getTabsHTML($contact, self::TAB_CONVERSATIONS);
975                 }
976
977                 if (DBA::isResult($contact)) {
978                         if (!$update) {
979                                 $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
980                                 DI::page()['aside'] = Widget\VCard::getHTML($profiledata);
981                         } else {
982                                 DI::page()['aside'] = '';
983                         }
984
985                         if ($contact['uid'] == 0) {
986                                 $o .= Model\Contact::getPostsFromId($contact['id'], true, $update, $parent);
987                         } else {
988                                 $o .= Model\Contact::getPostsFromUrl($contact['url'], true, $update, $parent);
989                         }
990                 }
991
992                 return $o;
993         }
994
995         private static function getPostsHTML(int $contact_id, bool $only_media)
996         {
997                 $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]);
998
999                 $o = self::getTabsHTML($contact, self::TAB_POSTS);
1000
1001                 if (DBA::isResult($contact)) {
1002                         $profiledata = Model\Contact::getByURLForUser($contact['url'], local_user());
1003
1004                         if (local_user() && in_array($profiledata['network'], Protocol::FEDERATED)) {
1005                                 $profiledata['remoteconnect'] = DI::baseUrl() . '/follow?url=' . urlencode($profiledata['url']);
1006                         }
1007
1008                         DI::page()['aside'] = Widget\VCard::getHTML($profiledata);
1009
1010                         if ($contact['uid'] == 0) {
1011                                 $o .= Model\Contact::getPostsFromId($contact['id'], false, 0, 0, $only_media);
1012                         } else {
1013                                 $o .= Model\Contact::getPostsFromUrl($contact['url'], false, 0, 0, $only_media);
1014                         }
1015                 }
1016
1017                 return $o;
1018         }
1019
1020         /**
1021          * Return the fields for the contact template
1022          *
1023          * @param array $contact Contact array
1024          * @return array Template fields
1025          */
1026         public static function getContactTemplateVars(array $contact)
1027         {
1028                 $alt_text = '';
1029
1030                 if (!empty($contact['url']) && isset($contact['uid']) && ($contact['uid'] == 0) && local_user()) {
1031                         $personal = Model\Contact::getByURL($contact['url'], false, ['uid', 'rel', 'self'], local_user());
1032                         if (!empty($personal)) {
1033                                 $contact['uid'] = $personal['uid'];
1034                                 $contact['rel'] = $personal['rel'];
1035                                 $contact['self'] = $personal['self'];
1036                         }
1037                 }
1038
1039                 if (!empty($contact['uid']) && !empty($contact['rel']) && local_user() == $contact['uid']) {
1040                         switch ($contact['rel']) {
1041                                 case Model\Contact::FRIEND:
1042                                         $alt_text = DI::l10n()->t('Mutual Friendship');
1043                                         break;
1044
1045                                 case Model\Contact::FOLLOWER;
1046                                         $alt_text = DI::l10n()->t('is a fan of yours');
1047                                         break;
1048
1049                                 case Model\Contact::SHARING;
1050                                         $alt_text = DI::l10n()->t('you are a fan of');
1051                                         break;
1052
1053                                 default:
1054                                         break;
1055                         }
1056                 }
1057
1058                 $url = Model\Contact::magicLinkByContact($contact);
1059
1060                 if (strpos($url, 'redir/') === 0) {
1061                         $sparkle = ' class="sparkle" ';
1062                 } else {
1063                         $sparkle = '';
1064                 }
1065
1066                 if ($contact['pending']) {
1067                         if (in_array($contact['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) {
1068                                 $alt_text = DI::l10n()->t('Pending outgoing contact request');
1069                         } else {
1070                                 $alt_text = DI::l10n()->t('Pending incoming contact request');
1071                         }
1072                 }
1073
1074                 if ($contact['self']) {
1075                         $alt_text = DI::l10n()->t('This is you');
1076                         $url = $contact['url'];
1077                         $sparkle = '';
1078                 }
1079
1080                 return [
1081                         'id'           => $contact['id'],
1082                         'url'          => $url,
1083                         'img_hover'    => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
1084                         'photo_menu'   => Model\Contact::photoMenu($contact),
1085                         'thumb'        => Model\Contact::getThumb($contact, true),
1086                         'alt_text'     => $alt_text,
1087                         'name'         => $contact['name'],
1088                         'nick'         => $contact['nick'],
1089                         'details'      => $contact['location'],
1090                         'tags'         => $contact['keywords'],
1091                         'about'        => $contact['about'],
1092                         'account_type' => Model\Contact::getAccountType($contact),
1093                         'sparkle'      => $sparkle,
1094                         'itemurl'      => ($contact['addr'] ?? '') ?: $contact['url'],
1095                         'network'      => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']),
1096                 ];
1097         }
1098
1099         /**
1100          * Gives a array with actions which can performed to a given contact
1101          *
1102          * This includes actions like e.g. 'block', 'hide', 'delete' and others
1103          *
1104          * @param array $contact Data about the Contact
1105          * @return array with contact related actions
1106          */
1107         private static function getContactActions($contact)
1108         {
1109                 $poll_enabled = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
1110                 $contact_actions = [];
1111
1112                 $formSecurityToken = self::getFormSecurityToken('contact_action');
1113
1114                 // Provide friend suggestion only for Friendica contacts
1115                 if ($contact['network'] === Protocol::DFRN) {
1116                         $contact_actions['suggest'] = [
1117                                 'label' => DI::l10n()->t('Suggest friends'),
1118                                 'url'   => 'fsuggest/' . $contact['id'],
1119                                 'title' => '',
1120                                 'sel'   => '',
1121                                 'id'    => 'suggest',
1122                         ];
1123                 }
1124
1125                 if ($poll_enabled) {
1126                         $contact_actions['update'] = [
1127                                 'label' => DI::l10n()->t('Update now'),
1128                                 'url'   => 'contact/' . $contact['id'] . '/update?t=' . $formSecurityToken,
1129                                 'title' => '',
1130                                 'sel'   => '',
1131                                 'id'    => 'update',
1132                         ];
1133                 }
1134
1135                 if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
1136                         $contact_actions['updateprofile'] = [
1137                                 'label' => DI::l10n()->t('Refetch contact data'),
1138                                 'url'   => 'contact/' . $contact['id'] . '/updateprofile?t=' . $formSecurityToken,
1139                                 'title' => '',
1140                                 'sel'   => '',
1141                                 'id'    => 'updateprofile',
1142                         ];
1143                 }
1144
1145                 $contact_actions['block'] = [
1146                         'label' => (intval($contact['blocked']) ? DI::l10n()->t('Unblock') : DI::l10n()->t('Block')),
1147                         'url'   => 'contact/' . $contact['id'] . '/block?t=' . $formSecurityToken,
1148                         'title' => DI::l10n()->t('Toggle Blocked status'),
1149                         'sel'   => (intval($contact['blocked']) ? 'active' : ''),
1150                         'id'    => 'toggle-block',
1151                 ];
1152
1153                 $contact_actions['ignore'] = [
1154                         'label' => (intval($contact['readonly']) ? DI::l10n()->t('Unignore') : DI::l10n()->t('Ignore')),
1155                         'url'   => 'contact/' . $contact['id'] . '/ignore?t=' . $formSecurityToken,
1156                         'title' => DI::l10n()->t('Toggle Ignored status'),
1157                         'sel'   => (intval($contact['readonly']) ? 'active' : ''),
1158                         'id'    => 'toggle-ignore',
1159                 ];
1160
1161                 if ($contact['uid'] != 0) {
1162                         $contact_actions['delete'] = [
1163                                 'label' => DI::l10n()->t('Delete'),
1164                                 'url'   => 'contact/' . $contact['id'] . '/drop?t=' . $formSecurityToken,
1165                                 'title' => DI::l10n()->t('Delete contact'),
1166                                 'sel'   => '',
1167                                 'id'    => 'delete',
1168                         ];
1169                 }
1170
1171                 return $contact_actions;
1172         }
1173 }