]> git.mxchange.org Git - friendica.git/blob - src/Module/Contact/Profile.php
7fa64cdceeb4a1afe7f2565bdf7f353d13d28afb
[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 (isset($_POST['channel_visibility'])) {
136                         Contact\User::setChannelVisibility($cdata['user'], $this->session->getLocalUserId(), $_POST['channel_visibility']);
137                 }
138
139                 if (!Contact::update($fields, ['id' => $cdata['user'], 'uid' => $this->session->getLocalUserId()])) {
140                         $this->systemMessages->addNotice($this->t('Failed to update contact record.'));
141                 }
142         }
143
144         protected function content(array $request = []): string
145         {
146                 if (!$this->session->getLocalUserId()) {
147                         return Module\Security\Login::form($_SERVER['REQUEST_URI']);
148                 }
149
150                 // Backward compatibility: Ensure to use the public contact when the user contact is provided
151                 // Remove by version 2022.03
152                 $data = Contact::getPublicAndUserContactID(intval($this->parameters['id']), $this->session->getLocalUserId());
153                 if (empty($data)) {
154                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
155                 }
156
157                 $contact = Contact::getById($data['public']);
158                 if (!$this->db->isResult($contact)) {
159                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
160                 }
161
162                 // Don't display contacts that are about to be deleted
163                 if ($this->db->isResult($contact) && (!empty($contact['deleted']) || !empty($contact['network']) && $contact['network'] == Protocol::PHANTOM)) {
164                         throw new HTTPException\NotFoundException($this->t('Contact not found.'));
165                 }
166
167                 $localRelationship = $this->localRelationship->getForUserContact($this->session->getLocalUserId(), $contact['id']);
168
169                 if ($localRelationship->rel === Contact::SELF) {
170                         $this->baseUrl->redirect('profile/' . $contact['nick'] . '/profile');
171                 }
172
173                 if (isset($this->parameters['action'])) {
174                         self::checkFormSecurityTokenRedirectOnError('contact/' . $contact['id'], 'contact_action', 't');
175
176                         $cmd = $this->parameters['action'];
177                         if ($cmd === 'update' && $localRelationship->rel !== Contact::NOTHING) {
178                                 Module\Contact::updateContactFromPoll($contact['id']);
179                         }
180
181                         if ($cmd === 'updateprofile') {
182                                 $this->updateContactFromProbe($contact['id']);
183                         }
184
185                         if ($cmd === 'block') {
186                                 if ($localRelationship->blocked) {
187                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
188                                         Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), false);
189
190                                         $message = $this->t('Contact has been unblocked');
191                                 } else {
192                                         // @TODO Backward compatibility, replace with $localRelationship->block()
193                                         Contact\User::setBlocked($contact['id'], $this->session->getLocalUserId(), true);
194                                         $message = $this->t('Contact has been blocked');
195                                 }
196
197                                 // @TODO: add $this->localRelationship->save($localRelationship);
198                                 $this->systemMessages->addInfo($message);
199                         }
200
201                         if ($cmd === 'ignore') {
202                                 if ($localRelationship->ignored) {
203                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
204                                         Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), false);
205
206                                         $message = $this->t('Contact has been unignored');
207                                 } else {
208                                         // @TODO Backward compatibility, replace with $localRelationship->block()
209                                         Contact\User::setIgnored($contact['id'], $this->session->getLocalUserId(), true);
210                                         $message = $this->t('Contact has been ignored');
211                                 }
212
213                                 // @TODO: add $this->localRelationship->save($localRelationship);
214                                 $this->systemMessages->addInfo($message);
215                         }
216
217                         if ($cmd === 'collapse') {
218                                 if ($localRelationship->collapsed) {
219                                         // @TODO Backward compatibility, replace with $localRelationship->unblock()
220                                         Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), false);
221
222                                         $message = $this->t('Contact has been uncollapsed');
223                                 } else {
224                                         // @TODO Backward compatibility, replace with $localRelationship->block()
225                                         Contact\User::setCollapsed($contact['id'], $this->session->getLocalUserId(), true);
226                                         $message = $this->t('Contact has been collapsed');
227                                 }
228
229                                 // @TODO: add $this->localRelationship->save($localRelationship);
230                                 $this->systemMessages->addInfo($message);
231                         }
232
233                         $this->baseUrl->redirect('contact/' . $contact['id']);
234                 }
235
236                 $vcard_widget  = Widget\VCard::getHTML($contact);
237                 $circles_widget = '';
238
239                 if (!in_array($localRelationship->rel, [Contact::NOTHING, Contact::SELF])) {
240                         $circles_widget = Circle::sidebarWidget('contact', 'circle', 'full', 'everyone', $data['user']);
241                 }
242
243                 $this->page['aside'] .= $vcard_widget . $circles_widget;
244
245                 $o = '';
246                 Nav::setSelected('contact');
247
248                 $_SESSION['return_path'] = $this->args->getQueryString();
249
250                 $this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('contact_head.tpl'), [
251                 ]);
252
253                 switch ($localRelationship->rel) {
254                         case Contact::FRIEND:   $relation_text = $this->t('You are mutual friends with %s', $contact['name']); break;
255                         case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $contact['name']); break;
256                         case Contact::SHARING:  $relation_text = $this->t('%s is sharing with you', $contact['name']); break;
257                         default:
258                                 $relation_text = '';
259                 }
260
261                 if (!Protocol::supportsFollow($contact['network'])) {
262                         $relation_text = '';
263                 }
264
265                 $url = Contact::magicLinkByContact($contact);
266                 if (strpos($url, 'contact/redir/') === 0) {
267                         $sparkle = ' class="sparkle" ';
268                 } else {
269                         $sparkle = '';
270                 }
271
272                 $insecure = $this->t('Private communications are not available for this contact.');
273
274                 // @TODO: Figure out why gsid can be empty
275                 if (empty($contact['gsid'])) {
276                         $this->logger->notice('Empty gsid for contact', ['contact' => $contact]);
277                 }
278
279                 $serverIgnored =
280                         $contact['gsid'] &&
281                         $this->userGServer->isIgnoredByUser($this->session->getLocalUserId(), $contact['gsid']) ?
282                                 $this->t('This contact is on a server you ignored.')
283                                 : '';
284
285                 $last_update = (($contact['last-update'] <= DBA::NULL_DATETIME) ? $this->t('Never') : DateTimeFormat::local($contact['last-update'], 'D, j M Y, g:i A'));
286
287                 if ($contact['last-update'] > DBA::NULL_DATETIME) {
288                         $last_update .= ' ' . ($contact['failed'] ? $this->t('(Update was not successful)') : $this->t('(Update was successful)'));
289                 }
290                 $lblsuggest = (($contact['network'] === Protocol::DFRN) ? $this->t('Suggest friends') : '');
291
292                 $poll_enabled = in_array($contact['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
293
294                 $nettype = $this->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']));
295
296                 // tabs
297                 $tab_str = Module\Contact::getTabsHTML($contact, Module\Contact::TAB_PROFILE);
298
299                 $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? $this->t('Communications lost with this contact!') : '');
300
301                 $fetch_further_information = null;
302                 if ($contact['network'] == Protocol::FEED) {
303                         $fetch_further_information = [
304                                 'fetch_further_information',
305                                 $this->t('Fetch further information for feeds'),
306                                 $localRelationship->fetchFurtherInformation,
307                                 $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.'),
308                                 [
309                                         LocalRelationship\Entity\LocalRelationship::FFI_NONE        => $this->t('Disabled'),
310                                         LocalRelationship\Entity\LocalRelationship::FFI_INFORMATION => $this->t('Fetch information'),
311                                         LocalRelationship\Entity\LocalRelationship::FFI_KEYWORD     => $this->t('Fetch keywords'),
312                                         LocalRelationship\Entity\LocalRelationship::FFI_BOTH        => $this->t('Fetch information and keywords')
313                                 ]
314                         ];
315                 }
316
317                 $allow_remote_self = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::FEED, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER])
318                         && $this->config->get('system', 'allow_users_remote_self');
319
320                 if ($contact['network'] == Protocol::FEED) {
321                         $remote_self_options = [
322                                 Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
323                                 Contact::MIRROR_OWN_POST    => $this->t('Mirror as my own posting')
324                         ];
325                 } elseif ($contact['network'] == Protocol::ACTIVITYPUB) {
326                         $remote_self_options = [
327                                 Contact::MIRROR_DEACTIVATED    => $this->t('No mirroring'),
328                                 Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
329                         ];
330                 } elseif ($contact['network'] == Protocol::DFRN) {
331                         $remote_self_options = [
332                                 Contact::MIRROR_DEACTIVATED    => $this->t('No mirroring'),
333                                 Contact::MIRROR_OWN_POST       => $this->t('Mirror as my own posting'),
334                                 Contact::MIRROR_NATIVE_RESHARE => $this->t('Native reshare')
335                         ];
336                 } else {
337                         $remote_self_options = [
338                                 Contact::MIRROR_DEACTIVATED => $this->t('No mirroring'),
339                                 Contact::MIRROR_OWN_POST    => $this->t('Mirror as my own posting')
340                         ];
341                 }
342
343                 if (in_array($contact['network'], Protocol::FEDERATED)) {
344                         $channel_settings_label = $this->t('Channel Settings');
345                         $channel_visibility     = Contact\User::getChannelVisibility($contact['id'], $this->session->getLocalUserId());
346                         $channel_visibilities   = [
347                                 Contact\User::VISIBILITY_DEFAULT => $this->t('Default visibility'),
348                                 Contact\User::VISIBILITY_ALWAYS  => $this->t('Display all posts of this contact'),
349                                 Contact\User::VISIBILITY_REDUCED => $this->t('Display only few posts'),
350                                 Contact\User::VISIBILITY_NEVER   => $this->t('Never display posts from this contact'),
351                         ];
352                 } else {
353                         $channel_settings_label = null;
354                         $channel_visibility     = null;
355                         $channel_visibilities   = null;
356                 }
357
358                 $poll_interval = null;
359                 if ((($contact['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) {
360                         $poll_interval = ContactSelector::pollInterval($localRelationship->priority, !$poll_enabled);
361                 }
362
363                 $contact_actions = $this->getContactActions($contact, $localRelationship);
364
365                 if ($localRelationship->rel !== Contact::NOTHING) {
366                         $lbl_info1              = $this->t('Contact Information / Notes');
367                         $contact_settings_label = $this->t('Contact Settings');
368                 } else {
369                         $lbl_info1              = null;
370                         $contact_settings_label = null;
371                 }
372
373                 $tpl = Renderer::getMarkupTemplate('contact_edit.tpl');
374                 $o .= Renderer::replaceMacros($tpl, [
375                         '$header'                    => $this->t('Contact'),
376                         '$tab_str'                   => $tab_str,
377                         '$submit'                    => $this->t('Submit'),
378                         '$lbl_info1'                 => $lbl_info1,
379                         '$lbl_info2'                 => $this->t('Their personal note'),
380                         '$reason'                    => trim($contact['reason'] ?? ''),
381                         '$infedit'                   => $this->t('Edit contact notes'),
382                         '$common_link'               => 'contact/' . $contact['id'] . '/contacts/common',
383                         '$relation_text'             => $relation_text,
384                         '$visit'                     => $this->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
385                         '$blockunblock'              => $this->t('Block/Unblock contact'),
386                         '$ignorecont'                => $this->t('Ignore contact'),
387                         '$lblrecent'                 => $this->t('View conversations'),
388                         '$lblsuggest'                => $lblsuggest,
389                         '$nettype'                   => $nettype,
390                         '$poll_interval'             => $poll_interval,
391                         '$poll_enabled'              => $poll_enabled,
392                         '$lastupdtext'               => $this->t('Last update:'),
393                         '$lost_contact'              => $lost_contact,
394                         '$updpub'                    => $this->t('Update public posts'),
395                         '$last_update'               => $last_update,
396                         '$udnow'                     => $this->t('Update now'),
397                         '$contact_id'                => $contact['id'],
398                         '$pending'                   => $localRelationship->pending   ? $this->t('Awaiting connection acknowledge') : '',
399                         '$blocked'                   => $localRelationship->blocked   ? $this->t('Currently blocked') : '',
400                         '$ignored'                   => $localRelationship->ignored   ? $this->t('Currently ignored') : '',
401                         '$collapsed'                 => $localRelationship->collapsed ? $this->t('Currently collapsed') : '',
402                         '$archived'                  => ($contact['archive'] ? $this->t('Currently archived') : ''),
403                         '$insecure'                  => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
404                         '$serverIgnored'             => $serverIgnored,
405                         '$manageServers'             => $this->t('Manage remote servers'),
406                         '$cinfo'                     => ['info', '', $localRelationship->info, ''],
407                         '$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')],
408                         '$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')],
409                         '$fetch_further_information' => $fetch_further_information,
410                         '$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')],
411                         '$photo'                     => Contact::getPhoto($contact),
412                         '$name'                      => $contact['name'],
413                         '$sparkle'                   => $sparkle,
414                         '$url'                       => $url,
415                         '$profileurllabel'           => $this->t('Profile URL'),
416                         '$profileurl'                => $contact['url'],
417                         '$account_type'              => Contact::getAccountType($contact['contact-type']),
418                         '$location'                  => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
419                         '$location_label'            => $this->t('Location:'),
420                         '$xmpp'                      => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
421                         '$xmpp_label'                => $this->t('XMPP:'),
422                         '$matrix'                    => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['matrix']),
423                         '$matrix_label'              => $this->t('Matrix:'),
424                         '$about'                     => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
425                         '$about_label'               => $this->t('About:'),
426                         '$keywords'                  => $contact['keywords'],
427                         '$keywords_label'            => $this->t('Tags:'),
428                         '$contact_action_button'     => $this->t('Actions'),
429                         '$contact_actions'           => $contact_actions,
430                         '$contact_status'            => $this->t('Status'),
431                         '$contact_settings_label'    => $contact_settings_label,
432                         '$contact_profile_label'     => $this->t('Profile'),
433                         '$allow_remote_self'         => $allow_remote_self,
434                         '$remote_self'               => [
435                                 'remote_self',
436                                 $this->t('Mirror postings from this contact'),
437                                 $localRelationship->remoteSelf,
438                                 $this->t('Mark this contact as remote_self, this will cause friendica to repost new entries from this contact.'),
439                                 $remote_self_options
440                         ],
441                         '$channel_settings_label' => $channel_settings_label,
442                         '$channel_visibility'     => [
443                                 'channel_visibility',
444                                 $this->t('Visibility of this contact in appropriate channels'),
445                                 $channel_visibility,
446                                 $this->t('Depending on the type of the channel not all posts from contacts are displayed by default. They for example need to have a certain amount of comments to be displayed. On the other hand there can be contacts who flood the channel, so you might want to see only some of their posts. Or you don\'t want to see their content at all, but you don\'t want to block or hide the contact completely.'),
447                                 $channel_visibilities
448                         ],
449                 ]);
450
451                 $arr = ['contact' => $contact, 'output' => $o];
452
453                 Hook::callAll('contact_edit', $arr);
454
455                 return $arr['output'];
456         }
457
458         /**
459          * Returns the list of available actions that can performed on the provided contact
460          *
461          * This includes actions like e.g. 'block', 'hide', 'delete' and others
462          *
463          * @param array                    $contact           Public contact row
464          * @param LocalRelationship\Entity\LocalRelationship $localRelationship
465          * @return array with contact related actions
466          * @throws HTTPException\InternalServerErrorException
467          */
468         private function getContactActions(array $contact, LocalRelationship\Entity\LocalRelationship $localRelationship): array
469         {
470                 $poll_enabled    = in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::FEED, Protocol::MAIL]);
471                 $contact_actions = [];
472
473                 $formSecurityToken = self::getFormSecurityToken('contact_action');
474
475                 if ($localRelationship->rel & Contact::SHARING) {
476                         $contact_actions['unfollow'] = [
477                                 'label' => $this->t('Unfollow'),
478                                 'url'   => 'contact/unfollow?url=' . urlencode($contact['url']) . '&auto=1',
479                                 'title' => '',
480                                 'sel'   => '',
481                                 'id'    => 'unfollow',
482                         ];
483                 } else {
484                         $contact_actions['follow'] = [
485                                 'label' => $this->t('Follow'),
486                                 'url'   => 'contact/follow?url=' . urlencode($contact['url']) . '&auto=1',
487                                 'title' => '',
488                                 'sel'   => '',
489                                 'id'    => 'follow',
490                         ];
491                 }
492
493                 // Provide friend suggestion only for Friendica contacts
494                 if ($contact['network'] === Protocol::DFRN) {
495                         $contact_actions['suggest'] = [
496                                 'label' => $this->t('Suggest friends'),
497                                 'url'   => 'fsuggest/' . $contact['id'],
498                                 'title' => '',
499                                 'sel'   => '',
500                                 'id'    => 'suggest',
501                         ];
502                 }
503
504                 if ($poll_enabled) {
505                         $contact_actions['update'] = [
506                                 'label' => $this->t('Update now'),
507                                 'url'   => 'contact/' . $contact['id'] . '/update?t=' . $formSecurityToken,
508                                 'title' => '',
509                                 'sel'   => '',
510                                 'id'    => 'update',
511                         ];
512                 }
513
514                 if (Protocol::supportsProbe($contact['network'])) {
515                         $contact_actions['updateprofile'] = [
516                                 'label' => $this->t('Refetch contact data'),
517                                 'url'   => 'contact/' . $contact['id'] . '/updateprofile?t=' . $formSecurityToken,
518                                 'title' => '',
519                                 'sel'   => '',
520                                 'id'    => 'updateprofile',
521                         ];
522                 }
523
524                 $contact_actions['block'] = [
525                         'label' => $localRelationship->blocked ? $this->t('Unblock') : $this->t('Block'),
526                         'url'   => 'contact/' . $contact['id'] . '/block?t=' . $formSecurityToken,
527                         'title' => $this->t('Toggle Blocked status'),
528                         'sel'   => $localRelationship->blocked ? 'active' : '',
529                         'id'    => 'toggle-block',
530                 ];
531
532                 $contact_actions['ignore'] = [
533                         'label' => $localRelationship->ignored ? $this->t('Unignore') : $this->t('Ignore'),
534                         'url'   => 'contact/' . $contact['id'] . '/ignore?t=' . $formSecurityToken,
535                         'title' => $this->t('Toggle Ignored status'),
536                         'sel'   => $localRelationship->ignored ? 'active' : '',
537                         'id'    => 'toggle-ignore',
538                 ];
539
540                 $contact_actions['collapse'] = [
541                         'label' => $localRelationship->collapsed ? $this->t('Uncollapse') : $this->t('Collapse'),
542                         'url'   => 'contact/' . $contact['id'] . '/collapse?t=' . $formSecurityToken,
543                         'title' => $this->t('Toggle Collapsed status'),
544                         'sel'   => $localRelationship->collapsed ? 'active' : '',
545                         'id'    => 'toggle-collapse',
546                 ];
547
548                 if (Protocol::supportsRevokeFollow($contact['network']) && in_array($localRelationship->rel, [Contact::FOLLOWER, Contact::FRIEND])) {
549                         $contact_actions['revoke_follow'] = [
550                                 'label' => $this->t('Revoke Follow'),
551                                 'url'   => 'contact/' . $contact['id'] . '/revoke',
552                                 'title' => $this->t('Revoke the follow from this contact'),
553                                 'sel'   => '',
554                                 'id'    => 'revoke_follow',
555                         ];
556                 }
557
558                 return $contact_actions;
559         }
560
561         /**
562          * Updates contact from probing
563          *
564          * @param int $contact_id Id of the contact with uid != 0
565          * @return void
566          * @throws HTTPException\InternalServerErrorException
567          * @throws \ImagickException
568          */
569         private function updateContactFromProbe(int $contact_id)
570         {
571                 if (!$this->db->exists('contact', ['id' => $contact_id, 'uid' => [0, $this->session->getLocalUserId()], 'deleted' => false])) {
572                         return;
573                 }
574
575                 // Update the entry in the contact table
576                 Contact::updateFromProbe($contact_id);
577         }
578 }