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