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