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