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