]> git.mxchange.org Git - friendica.git/blob - src/Core/ACL.php
Merge pull request #8263 from annando/remote-follow
[friendica.git] / src / Core / ACL.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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\Core;
23
24 use Friendica\App\Page;
25 use Friendica\Database\DBA;
26 use Friendica\DI;
27 use Friendica\Model\Contact;
28 use Friendica\Model\Group;
29
30 /**
31  * Handle ACL management and display
32  */
33 class ACL
34 {
35         /**
36          * Returns a select input tag with all the contact of the local user
37          *
38          * @param string $selname     Name attribute of the select input tag
39          * @param string $selclass    Class attribute of the select input tag
40          * @param array  $preselected Contact IDs that should be already selected
41          * @param int    $size        Length of the select box
42          * @param int    $tabindex    Select input tag tabindex attribute
43          * @return string
44          * @throws \Exception
45          */
46         public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
47         {
48                 $a = DI::app();
49
50                 $o = '';
51
52                 // When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
53                 // to one recipient. By default our selector allows multiple selects amongst all contacts.
54                 $sql_extra = sprintf(" AND `rel` = %d ", intval(Contact::FRIEND));
55                 $sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", Protocol::DFRN, Protocol::DIASPORA);
56
57                 $tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
58
59                 $hidepreselected = '';
60                 if ($preselected) {
61                         $sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
62                         $hidepreselected = ' style="display: none;"';
63                 }
64
65                 $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
66
67                 $stmt = DBA::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
68                         WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
69                         $sql_extra
70                         ORDER BY `name` ASC ", intval(local_user())
71                 );
72
73                 $contacts = DBA::toArray($stmt);
74
75                 $arr = ['contact' => $contacts, 'entry' => $o];
76
77                 // e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
78                 Hook::callAll(DI::module()->getName() . '_pre_' . $selname, $arr);
79
80                 $receiverlist = [];
81
82                 if (DBA::isResult($contacts)) {
83                         foreach ($contacts as $contact) {
84                                 if (in_array($contact['id'], $preselected)) {
85                                         $selected = ' selected="selected"';
86                                 } else {
87                                         $selected = '';
88                                 }
89
90                                 $trimmed = Protocol::formatMention($contact['url'], $contact['name']);
91
92                                 $receiverlist[] = $trimmed;
93
94                                 $o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
95                         }
96                 }
97
98                 $o .= '</select>' . PHP_EOL;
99
100                 if ($preselected) {
101                         $o .= implode(', ', $receiverlist);
102                 }
103
104                 Hook::callAll(DI::module()->getName() . '_post_' . $selname, $o);
105
106                 return $o;
107         }
108
109         /**
110          * Return the default permission of the provided user array
111          *
112          * @param array $user
113          * @return array Hash of contact id lists
114          * @throws \Exception
115          */
116         public static function getDefaultUserPermissions(array $user = null)
117         {
118                 $aclFormatter = DI::aclFormatter();
119
120                 return [
121                         'allow_cid' => Contact::pruneUnavailable($aclFormatter->expand($user['allow_cid'] ?? '')),
122                         'allow_gid' => $aclFormatter->expand($user['allow_gid'] ?? ''),
123                         'deny_cid'  => $aclFormatter->expand($user['deny_cid']  ?? ''),
124                         'deny_gid'  => $aclFormatter->expand($user['deny_gid']  ?? ''),
125                 ];
126         }
127
128         /**
129          * Returns the ACL list of contacts for a given user id
130          *
131          * @param int   $user_id
132          * @param array $condition Additional contact lookup table conditions
133          * @return array
134          * @throws \Exception
135          */
136         public static function getContactListByUserId(int $user_id, array $condition = [])
137         {
138                 $fields = ['id', 'name', 'addr', 'micro'];
139                 $params = ['order' => ['name']];
140                 $acl_contacts = Contact::selectToArray(
141                         $fields,
142                         array_merge([
143                                 'uid' => $user_id,
144                                 'self' => false,
145                                 'blocked' => false,
146                                 'archive' => false,
147                                 'deleted' => false,
148                                 'pending' => false,
149                                 'rel' => [Contact::FOLLOWER, Contact::FRIEND]
150                         ], $condition),
151                         $params
152                 );
153
154                 $acl_yourself = Contact::selectFirst($fields, ['uid' => $user_id, 'self' => true]);
155                 $acl_yourself['name'] = DI::l10n()->t('Yourself');
156
157                 $acl_contacts[] = $acl_yourself;
158
159                 $acl_forums = Contact::selectToArray($fields,
160                         ['uid' => $user_id, 'self' => false, 'blocked' => false, 'archive' => false, 'deleted' => false,
161                         'pending' => false, 'contact-type' => Contact::TYPE_COMMUNITY], $params
162                 );
163
164                 $acl_contacts = array_merge($acl_forums, $acl_contacts);
165
166                 array_walk($acl_contacts, function (&$value) {
167                         $value['type'] = 'contact';
168                 });
169
170                 return $acl_contacts;
171         }
172
173         /**
174          * Returns the ACL list of groups (including meta-groups) for a given user id
175          *
176          * @param int $user_id
177          * @return array
178          */
179         public static function getGroupListByUserId(int $user_id)
180         {
181                 $acl_groups = [
182                         [
183                                 'id' => Group::FOLLOWERS,
184                                 'name' => DI::l10n()->t('Followers'),
185                                 'addr' => '',
186                                 'micro' => 'images/twopeople.png',
187                                 'type' => 'group',
188                         ],
189                         [
190                                 'id' => Group::MUTUALS,
191                                 'name' => DI::l10n()->t('Mutuals'),
192                                 'addr' => '',
193                                 'micro' => 'images/twopeople.png',
194                                 'type' => 'group',
195                         ]
196                 ];
197                 foreach (Group::getByUserId($user_id) as $group) {
198                         $acl_groups[] = [
199                                 'id' => $group['id'],
200                                 'name' => $group['name'],
201                                 'addr' => '',
202                                 'micro' => 'images/twopeople.png',
203                                 'type' => 'group',
204                         ];
205                 }
206
207                 return $acl_groups;
208         }
209
210         /**
211          * Return the full jot ACL selector HTML
212          *
213          * @param Page   $page
214          * @param array  $user                  User array
215          * @param bool   $for_federation
216          * @param array  $default_permissions   Static defaults permission array:
217          *                                      [
218          *                                      'allow_cid' => [],
219          *                                      'allow_gid' => [],
220          *                                      'deny_cid' => [],
221          *                                      'deny_gid' => [],
222          *                                      'hidewall' => true/false
223          *                                      ]
224          * @param array  $condition
225          * @param string $form_prefix
226          * @return string
227          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
228          */
229         public static function getFullSelectorHTML(
230                 Page $page,
231                 array $user = null,
232                 bool $for_federation = false,
233                 array $default_permissions = [],
234                 array $condition = [],
235                 $form_prefix = ''
236         ) {
237                 if (empty($user['uid'])) {
238                         return '';
239                 }
240
241                 static $input_group_id = 0;
242
243                 $input_group_id++;
244
245                 $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
246                 $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
247                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
248                 $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
249
250                 // Defaults user permissions
251                 if (empty($default_permissions)) {
252                         $default_permissions = self::getDefaultUserPermissions($user);
253                 }
254
255                 $default_permissions = [
256                         'allow_cid' => $default_permissions['allow_cid'] ?? [],
257                         'allow_gid' => $default_permissions['allow_gid'] ?? [],
258                         'deny_cid'  => $default_permissions['deny_cid']  ?? [],
259                         'deny_gid'  => $default_permissions['deny_gid']  ?? [],
260                         'hidewall'  => $default_permissions['hidewall']  ?? false,
261                 ];
262
263                 if (count($default_permissions['allow_cid'])
264                         + count($default_permissions['allow_gid'])
265                         + count($default_permissions['deny_cid'])
266                         + count($default_permissions['deny_gid'])) {
267                         $visibility = 'custom';
268                 } else {
269                         $visibility = 'public';
270                         // Default permission display for custom panel
271                         $default_permissions['allow_gid'] = [Group::FOLLOWERS];
272                 }
273
274                 $jotnets_fields = [];
275                 if ($for_federation) {
276                         $mail_enabled = false;
277                         $pubmail_enabled = false;
278
279                         if (function_exists('imap_open') && !DI::config()->get('system', 'imap_disabled')) {
280                                 $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', $user['uid']]);
281                                 if (DBA::isResult($mailacct)) {
282                                         $mail_enabled = true;
283                                         $pubmail_enabled = !empty($mailacct['pubmail']);
284                                 }
285                         }
286
287                         if (!$default_permissions['hidewall']) {
288                                 if ($mail_enabled) {
289                                         $jotnets_fields[] = [
290                                                 'type' => 'checkbox',
291                                                 'field' => [
292                                                         'pubmail_enable',
293                                                         DI::l10n()->t('Post to Email'),
294                                                         $pubmail_enabled
295                                                 ]
296                                         ];
297                                 }
298
299                                 Hook::callAll('jot_networks', $jotnets_fields);
300                         }
301                 }
302
303                 $acl_contacts = self::getContactListByUserId($user['uid'], $condition);
304
305                 $acl_groups = self::getGroupListByUserId($user['uid']);
306
307                 $acl_list = array_merge($acl_groups, $acl_contacts);
308
309                 $input_names = [
310                         'visibility'    => $form_prefix ? $form_prefix . '[visibility]'    : 'visibility',
311                         'group_allow'   => $form_prefix ? $form_prefix . '[group_allow]'   : 'group_allow',
312                         'contact_allow' => $form_prefix ? $form_prefix . '[contact_allow]' : 'contact_allow',
313                         'group_deny'    => $form_prefix ? $form_prefix . '[group_deny]'    : 'group_deny',
314                         'contact_deny'  => $form_prefix ? $form_prefix . '[contact_deny]'  : 'contact_deny',
315                         'emailcc'       => $form_prefix ? $form_prefix . '[emailcc]'       : 'emailcc',
316                 ];
317
318                 $tpl = Renderer::getMarkupTemplate('acl_selector.tpl');
319                 $o = Renderer::replaceMacros($tpl, [
320                         '$public_title'   => DI::l10n()->t('Public'),
321                         '$public_desc'    => DI::l10n()->t('This content will be shown to all your followers and can be seen in the community pages and by anyone with its link.'),
322                         '$custom_title'   => DI::l10n()->t('Limited/Private'),
323                         '$custom_desc'    => DI::l10n()->t('This content will be shown only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.'),
324                         '$allow_label'    => DI::l10n()->t('Show to:'),
325                         '$deny_label'     => DI::l10n()->t('Except to:'),
326                         '$emailcc'        => DI::l10n()->t('CC: email addresses'),
327                         '$emtitle'        => DI::l10n()->t('Example: bob@example.com, mary@example.com'),
328                         '$jotnets_summary' => DI::l10n()->t('Connectors'),
329                         '$jotnets_disabled_label' => DI::l10n()->t('Connectors disabled, since "%s" is enabled.', DI::l10n()->t('Hide your profile details from unknown viewers?')),
330                         '$visibility'     => $visibility,
331                         '$acl_contacts'   => $acl_contacts,
332                         '$acl_groups'     => $acl_groups,
333                         '$acl_list'       => $acl_list,
334                         '$contact_allow'  => implode(',', $default_permissions['allow_cid']),
335                         '$group_allow'    => implode(',', $default_permissions['allow_gid']),
336                         '$contact_deny'   => implode(',', $default_permissions['deny_cid']),
337                         '$group_deny'     => implode(',', $default_permissions['deny_gid']),
338                         '$for_federation' => $for_federation,
339                         '$jotnets_fields' => $jotnets_fields,
340                         '$user_hidewall'  => $default_permissions['hidewall'],
341                         '$input_names'    => $input_names,
342                         '$input_group_id' => $input_group_id,
343                 ]);
344
345                 return $o;
346         }
347 }