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