]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact/Profile.php
Merge pull request #13385 from MrPetovan/bug/fatal-errors
[friendica.git] / src / Module / Contact / Profile.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, 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\Contact;
23
24 use Friendica\App;
25 use Friendica\BaseModule;
26 use Friendica\Contact\LocalRelationship;
27 use Friendica\Content\ContactSelector;
28 use Friendica\Content\Nav;
29 use Friendica\Content\Text\BBCode;
30 use Friendica\Content\Widget;
31 use Friendica\Core\Config\Capability\IManageConfigValues;
32 use Friendica\Core\Hook;
33 use Friendica\Core\L10n;
34 use Friendica\Core\Protocol;
35 use Friendica\Core\Renderer;
36 use Friendica\Core\Session\Capability\IHandleUserSessions;
37 use Friendica\Database\Database;
38 use Friendica\Database\DBA;
39 use Friendica\Model\Circle;
40 use Friendica\Model\Contact;
41 use Friendica\Module;
42 use Friendica\Module\Response;
43 use Friendica\Navigation\SystemMessages;
44 use Friendica\Network\HTTPException;
45 use Friendica\User\Settings;
46 use Friendica\Util\DateTimeFormat;
47 use Friendica\Util\Profiler;
48 use Psr\Log\LoggerInterface;
49
50 /**
51  *  Show a contact profile
52  */
53 class Profile extends BaseModule
54 {
55         /** @var LocalRelationship\Repository\LocalRelationship */
56         private $localRelationship;
57         /** @var App\Page */
58         private $page;
59         /** @var IManageConfigValues */
60         private $config;
61         /** @var IHandleUserSessions */
62         private $session;
63         /** @var SystemMessages */
64         private $systemMessages;
65         /** @var Database */
66         private $db;
67         /** @var Settings\Repository\UserGServer */
68         private $userGServer;
69
70         public function __construct(Settings\Repository\UserGServer $userGServer, Database $db, SystemMessages $systemMessages, IHandleUserSessions $session, L10n $l10n, LocalRelationship\Repository\LocalRelationship $localRelationship, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, App\Page $page, IManageConfigValues $config, array $server, array $parameters = [])
71         {
72                 parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
73
74                 $this->localRelationship = $localRelationship;
75                 $this->page              = $page;
76                 $this->config            = $config;
77                 $this->session           = $session;
78                 $this->systemMessages    = $systemMessages;
79                 $this->db                = $db;
80                 $this->userGServer       = $userGServer;
81         }
82
83         protected function post(array $request = [])
84         {
85                 if (!$this->session->getLocalUserId()) {
86                         return;
87                 }
88
89                 $contact_id = $this->parameters['id'];
90
91                 // Backward compatibility: The update still needs a user-specific contact ID
92                 // Change to user-contact table check by version 2022.03
93                 $cdata = Contact::getPublicAndUserContactID($contact_id, $this->session->getLocalUserId());
94                 if (empty($cdata['user']) || !$this->db->exists('contact', ['id' => $cdata['user'], 'deleted' => false])) {
95                         return;
96                 }
97
98                 Hook::callAll('contact_edit_post', $_POST);
99
100                 $fields = [];
101
102                 if (isset($_POST['hidden'])) {
103                         $fields['hidden'] = !empty($_POST['hidden']);
104                 }
105
106                 if (isset($_POST['notify_new_posts'])) {
107                         $fields['notify_new_posts'] = !empty($_POST['notify_new_posts']);
108                 }
109
110                 if (isset($_POST['fetch_further_information'])) {
111                         $fields['fetch_further_information'] = intval($_POST['fetch_further_information']);
112                 }
113
114                 if (isset($_POST['remote_self'])) {
115                         $fields['remote_self'] = intval($_POST['remote_self']);
116                 }
117
118                 if (isset($_POST['ffi_keyword_denylist'])) {
119                         $fields['ffi_keyword_denylist'] = $_POST['ffi_keyword_denylist'];
120                 }
121
122                 if (isset($_POST['poll'])) {
123                         $priority = intval($_POST['poll']);
124                         if ($priority > 5 || $priority < 0) {
125                                 $priority = 0;
126                         }
127
128                         $fields['priority'] = $priority;
129                 }
130
131                 if (isset($_POST['info'])) {
132                         $fields['info'] = $_POST['info'];
133                 }
134
135                 if (!Contact::update($fields, ['id' => $cdata['user'], 'uid' => $this->session->getLocalUserId()])) {
136                         $this->systemMessages->addNotice($this->t('Failed to update contact record.'));
137                 }
138         }
139
140         protected function content(array $request = []): string
141         {
142                 if (!$this->session->getLocalUserId()) {
143                         return Module\Security\Login::form($_SERVER['REQUEST_URI']);
144                 }
145
146                 // Backward compatibility: Ensure to use the public contact when the user contact is provided
147                 // Remove by version 2022.03
148                 $data = Contact::getPublicAndUserContactID(intval($this->parameters['id']), $this->session->getLocalUserId());
149                 if (empty($data)) {
150                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
151                 }
152
153                 $contact = Contact::getById($data['public']);
154                 if (!$this->db->isResult($contact)) {
155                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
156                 }
157
158                 // Don't display contacts that are about to be deleted
159                 if ($this->db->isResult($contact) && (!empty($contact['deleted']) || !empty($contact['network']) && $contact['network'] == Protocol::PHANTOM)) {
160                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
161                 }
162
163                 $localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $contact['id']);
164
165                 if ($localRelationship->rel === Contact::SELF) {
166                         $this->baseUrl->redirect('profile/' . $contact['nick'] . '/profile');
167                 }
168
169                 if (isset($this->parameters['action'])) {
170                         self::checkFormSecurityTokenRedirectOnError('contact/' . $contact['id'], 'contact_action', 't');
171
172                         $cmd = $this->parameters['action'];
173                         if ($cmd === 'update' && $localRelationship->rel !== Contact::NOTHING) {
174                                 Module\Contact::updateContactFromPoll($contact['id']);
175                         }
176
177                         if ($cmd === 'updateprofile') {
178                                 $this->updateContactFromProbe($contact['id']);
179                         }
180
181                         if ($cmd === 'block') {
182                                 if ($localRelationship->blocked) {
183                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
184                                         Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), false);
185
186                                         $message = $this->t('Contact has been unblocked');
187                                 } else {
188                                         // @TODO Backward compatibility, replace with $localRelationship->block()
189                                         Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), true);
190                                         $message = $this->t('Contact has been blocked');
191                                 }
192
193                                 // @TODO: add $this->localRelationship->save($localRelationship);
194                                 $this->systemMessages->addInfo($message);
195                         }
196
197                         if ($cmd === 'ignore') {
198                                 if ($localRelationship->ignored) {
199                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
200                                         Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), false);
201
202                                         $message = $this->t('Contact has been unignored');
203                                 } else {
204                                         // @TODO Backward compatibility, replace with $localRelationship->block()
205                                         Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), true);
206                                         $message = $this->t('Contact has been ignored');
207                                 }
208
209                                 // @TODO: add $this->localRelationship->save($localRelationship);
210                                 $this->systemMessages->addInfo($message);
211                         }
212
213                         if ($cmd === 'collapse') {
214                                 if ($localRelationship->collapsed) {
215                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
216                                         Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), false);
217
218                                         $message = $this->t('Contact has been uncollapsed');
219                                 } else {
220                                         // @TODO Backward compatibility, replace with $localRelationship->block()
221                                         Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), true);
222                                         $message = $this->t('Contact has been collapsed');
223                                 }
224
225                                 // @TODO: add $this->localRelationship->save($localRelationship);
226                                 $this->systemMessages->addInfo($message);
227                         }
228
229                         $this->baseUrl->redirect('contact/' . $contact['id']);
230                 }
231
232                 $vcard_widget  = Widget\VCard::getHTML($contact);
233                 $circles_widget = '';
234
235                 if (!in_array($localRelationship->rel, [Contact::NOTHING, Contact::SELF])) {
236                         $circles_widget = Circle::sidebarWidget('contact', 'circle', 'full', 'everyone', $data['user']);
237                 }
238
239                 $this->page['aside'] .= $vcard_widget . $circles_widget;
240
241                 $o = '';
242                 Nav::setSelected('contact');
243
244                 $_SESSION['return_path'] = $this->args->getQueryString();
245
246                 $this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
247                 ]);
248
249                 switch ($localRelationship->rel) {
250                         case Contact::FRIEND:   $relation_text = $this->t('You are mutual friends with %s', $contact['name']); break;
251                         case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $contact['name']); break;
252                         case Contact::SHARING:  $relation_text = $this->t('%s is sharing with you', $contact['name']); break;
253                         default:
254                                 $relation_text = '';
255                 }
256
257                 if (!Protocol::supportsFollow($contact['network'])) {
258                         $relation_text = '';
259                 }
260
261                 $url = Contact::magicLinkByContact($contact);
262                 if (strpos($url, 'contact/redir/') === 0) {
263                         $sparkle = ' class="sparkle" ';
264                 } else {
265                         $sparkle = '';
266                 }
267
268                 $insecure = $this->t('Private communications are not available for this contact.');
269
270                 // @TODO: Figure out why gsid can be empty
271                 if (empty($contact['gsid'])) {
272                         $this->logger->notice('Empty gsid for contact', ['contact' => $contact]);
273                 }
274
275                 $serverIgnored =
276                         $contact['gsid'] &&
277                         $this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $contact['gsid']) ?
278                                 $this->t('This contact is on a server you ignored.')
279                                 : '';
280
281                 $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? $this->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
282
283                 if ($contact['last-update'] > DBA::NULL_DATETIME) {
284                         $last_update .= ' ' . ($contact['failed'] ? $this->t('(Update was not successful)') : $this->t('(Update was successful)'));
285                 }
286                 $lblsuggest = (($contact['network'] === Protocol::DFRN) ? $this->t('Suggest friends') : '');
287
288                 $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
289
290                 $nettype = $this->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']));
291
292                 // tabs
293                 $tab_str = Module\Contact::getTabsHTML($contact, Module\Contact::TAB_PROFILE);
294
295                 $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? $this->t('Communications lost with this contact!') : '');
296
297                 $fetch_further_information = null;
298                 if ($contact['network'] == Protocol::FEED) {
299                         $fetch_further_information = [
300                                 'fetch_further_information',
301                                 $this->t('Fetch further information for feeds'),
302                                 $localRelationship->fetchFurtherInformation,
303                                 $this->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.'),
304                                 [
305                                         LocalRelationship\Entity\LocalRelationship::FFI_NONE        => $this->t('Disabled'),
306                                         LocalRelationship\Entity\LocalRelationship::FFI_INFORMATION => $this->t('Fetch information'),
307                                         LocalRelationship\Entity\LocalRelationship::FFI_KEYWORD     => $this->t('Fetch keywords'),
308                                         LocalRelationship\Entity\LocalRelationship::FFI_BOTH        => $this->t('Fetch information and keywords')
309                                 ]
310                         ];
311                 }
312
313                 $allow_remote_self = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
314                         && $this->config->get('system', 'allow_users_remote_self');
315
316                 if ($contact['network'] == Protocol::FEED) {
317                         $remote_self_options = [
318                                 Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
319                                 Contact::MIRROR_OWN_POST    => $this->t('Mirror as my own posting')
320                         ];
321                 } elseif ($contact['network'] == Protocol::ACTIVITYPUB) {
322                         $remote_self_options = [
323                                 Contact::MIRROR_DEACTIVATED    => $this->t('No mirroring'),
324                                 Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
325                         ];
326                 } elseif ($contact['network'] == Protocol::DFRN) {
327                         $remote_self_options = [
328                                 Contact::MIRROR_DEACTIVATED    => $this->t('No mirroring'),
329                                 Contact::MIRROR_OWN_POST       => $this->t('Mirror as my own posting'),
330                                 Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
331                         ];
332                 } else {
333                         $remote_self_options = [
334                                 Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
335                                 Contact::MIRROR_OWN_POST    => $this->t('Mirror as my own posting')
336                         ];
337                 }
338
339                 $poll_interval = null;
340                 if ((($contact['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) {
341                         $poll_interval = ContactSelector::pollInterval($localRelationship->priority, !$poll_enabled);
342                 }
343
344                 $contact_actions = $this->getContactActions($contact, $localRelationship);
345
346                 if ($localRelationship->rel !== Contact::NOTHING) {
347                         $lbl_info1              = $this->t('Contact Information / Notes');
348                         $contact_settings_label = $this->t('Contact Settings');
349                 } else {
350                         $lbl_info1              = null;
351                         $contact_settings_label = null;
352                 }
353
354                 $tpl = Renderer::getMarkupTemplate('contact_edit.tpl');
355                 $o .= Renderer::replaceMacros($tpl, [
356                         '$header'                    => $this->t('Contact'),
357                         '$tab_str'                   => $tab_str,
358                         '$submit'                    => $this->t('Submit'),
359                         '$lbl_info1'                 => $lbl_info1,
360                         '$lbl_info2'                 => $this->t('Their personal note'),
361                         '$reason'                    => trim($contact['reason'] ?? ''),
362                         '$infedit'                   => $this->t('Edit contact notes'),
363                         '$common_link'               => 'contact/' . $contact['id'] . '/contacts/common',
364                         '$relation_text'             => $relation_text,
365                         '$visit'                     => $this->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
366                         '$blockunblock'              => $this->t('Block/Unblock contact'),
367                         '$ignorecont'                => $this->t('Ignore contact'),
368                         '$lblrecent'                 => $this->t('View conversations'),
369                         '$lblsuggest'                => $lblsuggest,
370                         '$nettype'                   => $nettype,
371                         '$poll_interval'             => $poll_interval,
372                         '$poll_enabled'              => $poll_enabled,
373                         '$lastupdtext'               => $this->t('Last update:'),
374                         '$lost_contact'              => $lost_contact,
375                         '$updpub'                    => $this->t('Update public posts'),
376                         '$last_update'               => $last_update,
377                         '$udnow'                     => $this->t('Update now'),
378                         '$contact_id'                => $contact['id'],
379                         '$pending'                   => $localRelationship->pending   ? $this->t('Awaiting connection acknowledge') : '',
380                         '$blocked'                   => $localRelationship->blocked   ? $this->t('Currently blocked') : '',
381                         '$ignored'                   => $localRelationship->ignored   ? $this->t('Currently ignored') : '',
382                         '$collapsed'                 => $localRelationship->collapsed ? $this->t('Currently collapsed') : '',
383                         '$archived'                  => ($contact['archive'] ? $this->t('Currently archived') : ''),
384                         '$insecure'                  => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
385                         '$serverIgnored'             => $serverIgnored,
386                         '$manageServers'             => $this->t('Manage remote servers'),
387                         '$cinfo'                     => ['info', '', $localRelationship->info, ''],
388                         '$hidden'                    => ['hidden', $this->t('Hide this contact from others'), $localRelationship->hidden, $this->t('Replies/likes to your public posts <strong>may</strong> still be visible')],
389                         '$notify_new_posts'          => ['notify_new_posts', $this->t('Notification for new posts'), ($localRelationship->notifyNewPosts), $this->t('Send a notification of every new post of this contact')],
390                         '$fetch_further_information' => $fetch_further_information,
391                         '$ffi_keyword_denylist'      => ['ffi_keyword_denylist', $this->t('Keyword Deny List'), $localRelationship->ffiKeywordDenylist, $this->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')],
392                         '$photo'                     => Contact::getPhoto($contact),
393                         '$name'                      => $contact['name'],
394                         '$sparkle'                   => $sparkle,
395                         '$url'                       => $url,
396                         '$profileurllabel'           => $this->t('Profile URL'),
397                         '$profileurl'                => $contact['url'],
398                         '$account_type'              => Contact::getAccountType($contact['contact-type']),
399                         '$location'                  => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
400                         '$location_label'            => $this->t('Location:'),
401                         '$xmpp'                      => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
402                         '$xmpp_label'                => $this->t('XMPP:'),
403                         '$matrix'                    => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
404                         '$matrix_label'              => $this->t('Matrix:'),
405                         '$about'                     => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
406                         '$about_label'               => $this->t('About:'),
407                         '$keywords'                  => $contact['keywords'],
408                         '$keywords_label'            => $this->t('Tags:'),
409                         '$contact_action_button'     => $this->t('Actions'),
410                         '$contact_actions'           => $contact_actions,
411                         '$contact_status'            => $this->t('Status'),
412                         '$contact_settings_label'    => $contact_settings_label,
413                         '$contact_profile_label'     => $this->t('Profile'),
414                         '$allow_remote_self'         => $allow_remote_self,
415                         '$remote_self'               => [
416                                 'remote_self',
417                                 $this->t('Mirror postings from this contact'),
418                                 $localRelationship->remoteSelf,
419                                 $this->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
420                                 $remote_self_options
421                         ],
422                 ]);
423
424                 $arr = ['contact' => $contact, 'output' => $o];
425
426                 Hook::callAll('contact_edit', $arr);
427
428                 return $arr['output'];
429         }
430
431         /**
432          * Returns the list of available actions that can performed on the provided contact
433          *
434          * This includes actions like e.g. 'block', 'hide', 'delete' and others
435          *
436          * @param array                    $contact           Public contact row
437          * @param LocalRelationship\Entity\LocalRelationship $localRelationship
438          * @return array with contact related actions
439          * @throws HTTPException\InternalServerErrorException
440          */
441         private function getContactActions(array $contact, LocalRelationship\Entity\LocalRelationship $localRelationship): array
442         {
443                 $poll_enabled    = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
444                 $contact_actions = [];
445
446                 $formSecurityToken = self::getFormSecurityToken('contact_action');
447
448                 if ($localRelationship->rel & Contact::SHARING) {
449                         $contact_actions['unfollow'] = [
450                                 'label' => $this->t('Unfollow'),
451                                 'url'   => 'contact/unfollow?url=' . urlencode($contact['url']) . '&auto=1',
452                                 'title' => '',
453                                 'sel'   => '',
454                                 'id'    => 'unfollow',
455                         ];
456                 } else {
457                         $contact_actions['follow'] = [
458                                 'label' => $this->t('Follow'),
459                                 'url'   => 'contact/follow?url=' . urlencode($contact['url']) . '&auto=1',
460                                 'title' => '',
461                                 'sel'   => '',
462                                 'id'    => 'follow',
463                         ];
464                 }
465
466                 // Provide friend suggestion only for Friendica contacts
467                 if ($contact['network'] === Protocol::DFRN) {
468                         $contact_actions['suggest'] = [
469                                 'label' => $this->t('Suggest friends'),
470                                 'url'   => 'fsuggest/' . $contact['id'],
471                                 'title' => '',
472                                 'sel'   => '',
473                                 'id'    => 'suggest',
474                         ];
475                 }
476
477                 if ($poll_enabled) {
478                         $contact_actions['update'] = [
479                                 'label' => $this->t('Update now'),
480                                 'url'   => 'contact/' . $contact['id'] . '/update?t=' . $formSecurityToken,
481                                 'title' => '',
482                                 'sel'   => '',
483                                 'id'    => 'update',
484                         ];
485                 }
486
487                 if (Protocol::supportsProbe($contact['network'])) {
488                         $contact_actions['updateprofile'] = [
489                                 'label' => $this->t('Refetch contact data'),
490                                 'url'   => 'contact/' . $contact['id'] . '/updateprofile?t=' . $formSecurityToken,
491                                 'title' => '',
492                                 'sel'   => '',
493                                 'id'    => 'updateprofile',
494                         ];
495                 }
496
497                 $contact_actions['block'] = [
498                         'label' => $localRelationship->blocked ? $this->t('Unblock') : $this->t('Block'),
499                         'url'   => 'contact/' . $contact['id'] . '/block?t=' . $formSecurityToken,
500                         'title' => $this->t('Toggle Blocked status'),
501                         'sel'   => $localRelationship->blocked ? 'active' : '',
502                         'id'    => 'toggle-block',
503                 ];
504
505                 $contact_actions['ignore'] = [
506                         'label' => $localRelationship->ignored ? $this->t('Unignore') : $this->t('Ignore'),
507                         'url'   => 'contact/' . $contact['id'] . '/ignore?t=' . $formSecurityToken,
508                         'title' => $this->t('Toggle Ignored status'),
509                         'sel'   => $localRelationship->ignored ? 'active' : '',
510                         'id'    => 'toggle-ignore',
511                 ];
512
513                 $contact_actions['collapse'] = [
514                         'label' => $localRelationship->collapsed ? $this->t('Uncollapse') : $this->t('Collapse'),
515                         'url'   => 'contact/' . $contact['id'] . '/collapse?t=' . $formSecurityToken,
516                         'title' => $this->t('Toggle Collapsed status'),
517                         'sel'   => $localRelationship->collapsed ? 'active' : '',
518                         'id'    => 'toggle-collapse',
519                 ];
520
521                 if (Protocol::supportsRevokeFollow($contact['network']) && in_array($localRelationship->rel, [Contact::FOLLOWER, Contact::FRIEND])) {
522                         $contact_actions['revoke_follow'] = [
523                                 'label' => $this->t('Revoke Follow'),
524                                 'url'   => 'contact/' . $contact['id'] . '/revoke',
525                                 'title' => $this->t('Revoke the follow from this contact'),
526                                 'sel'   => '',
527                                 'id'    => 'revoke_follow',
528                         ];
529                 }
530
531                 return $contact_actions;
532         }
533
534         /**
535          * Updates contact from probing
536          *
537          * @param int $contact_id Id of the contact with uid != 0
538          * @return void
539          * @throws HTTPException\InternalServerErrorException
540          * @throws \ImagickException
541          */
542         private function updateContactFromProbe(int $contact_id)
543         {
544                 if (!$this->db->exists('contact', ['id' => $contact_id, 'uid' => [0, $this->session->getLocalUserId()], 'deleted' => false])) {
545                         return;
546                 }
547
548                 // Update the entry in the contact table
549                 Contact::updateFromProbe($contact_id);
550         }
551 }