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