]> git.mxchange.org Git - friendica.git/blob - src/Core/ACL.php
ccc2b34d5d2d8a86d907db4b8d3bb13d8f5acb46
[friendica.git] / src / Core / ACL.php
1 <?php
2
3 /**
4  * @file src/Core/Acl.php
5  */
6
7 namespace Friendica\Core;
8
9 use Friendica\BaseObject;
10 use Friendica\Content\Feature;
11 use Friendica\Database\DBA;
12 use Friendica\Model\Contact;
13 use Friendica\Model\GContact;
14 use Friendica\Core\Session;
15 use Friendica\Util\Network;
16 use Friendica\Model\Group;
17
18 /**
19  * Handle ACL management and display
20  *
21  * @author Hypolite Petovan <hypolite@mrpetovan.com>
22  */
23 class ACL extends BaseObject
24 {
25         /**
26          * Returns a select input tag with all the contact of the local user
27          *
28          * @param string $selname     Name attribute of the select input tag
29          * @param string $selclass    Class attribute of the select input tag
30          * @param array  $options     Available options:
31          *                            - size: length of the select box
32          *                            - mutual_friends: Only used for the hook
33          *                            - single: Only used for the hook
34          *                            - exclude: Only used for the hook
35          * @param array  $preselected Contact ID that should be already selected
36          * @return string
37          * @throws \Exception
38          */
39         public static function getSuggestContactSelectHTML($selname, $selclass, array $options = [], array $preselected = [])
40         {
41                 $a = self::getApp();
42
43                 $networks = null;
44
45                 $size = ($options['size'] ?? 0) ?: 4;
46                 $mutual = !empty($options['mutual_friends']);
47                 $single = !empty($options['single']) && empty($options['multiple']);
48                 $exclude = $options['exclude'] ?? false;
49
50                 switch (($options['networks'] ?? '') ?: Protocol::PHANTOM) {
51                         case 'DFRN_ONLY':
52                                 $networks = [Protocol::DFRN];
53                                 break;
54
55                         case 'PRIVATE':
56                                 $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA];
57                                 break;
58
59                         case 'TWO_WAY':
60                                 if (!empty($a->user['prvnets'])) {
61                                         $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA];
62                                 } else {
63                                         $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA, Protocol::OSTATUS];
64                                 }
65                                 break;
66
67                         default: /// @TODO Maybe log this call?
68                                 break;
69                 }
70
71                 $x = ['options' => $options, 'size' => $size, 'single' => $single, 'mutual' => $mutual, 'exclude' => $exclude, 'networks' => $networks];
72
73                 Hook::callAll('contact_select_options', $x);
74
75                 $o = '';
76
77                 $sql_extra = '';
78
79                 if (!empty($x['mutual'])) {
80                         $sql_extra .= sprintf(" AND `rel` = %d ", intval(Contact::FRIEND));
81                 }
82
83                 if (!empty($x['exclude'])) {
84                         $sql_extra .= sprintf(" AND `id` != %d ", intval($x['exclude']));
85                 }
86
87                 if (!empty($x['networks'])) {
88                         /// @TODO rewrite to foreach()
89                         array_walk($x['networks'], function (&$value) {
90                                 $value = "'" . DBA::escape($value) . "'";
91                         });
92                         $str_nets = implode(',', $x['networks']);
93                         $sql_extra .= " AND `network` IN ( $str_nets ) ";
94                 }
95
96                 $tabindex = (!empty($options['tabindex']) ? 'tabindex="' . $options["tabindex"] . '"' : '');
97
98                 if (!empty($x['single'])) {
99                         $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"" . $x['size'] . "\" $tabindex >\r\n";
100                 } else {
101                         $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"" . $x['size'] . "$\" $tabindex >\r\n";
102                 }
103
104                 $stmt = DBA::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
105                         WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
106                         $sql_extra
107                         ORDER BY `name` ASC ", intval(local_user())
108                 );
109
110                 $contacts = DBA::toArray($stmt);
111
112                 $arr = ['contact' => $contacts, 'entry' => $o];
113
114                 // e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
115                 Hook::callAll($a->module . '_pre_' . $selname, $arr);
116
117                 if (DBA::isResult($contacts)) {
118                         foreach ($contacts as $contact) {
119                                 if (in_array($contact['id'], $preselected)) {
120                                         $selected = ' selected="selected" ';
121                                 } else {
122                                         $selected = '';
123                                 }
124
125                                 $trimmed = mb_substr($contact['name'], 0, 20);
126
127                                 $o .= "<option value=\"{$contact['id']}\" $selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
128                         }
129                 }
130
131                 $o .= '</select>' . PHP_EOL;
132
133                 Hook::callAll($a->module . '_post_' . $selname, $o);
134
135                 return $o;
136         }
137
138         /**
139          * Returns a select input tag with all the contact of the local user
140          *
141          * @param string $selname     Name attribute of the select input tag
142          * @param string $selclass    Class attribute of the select input tag
143          * @param array  $preselected Contact IDs that should be already selected
144          * @param int    $size        Length of the select box
145          * @param int    $tabindex    Select input tag tabindex attribute
146          * @return string
147          * @throws \Exception
148          */
149         public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
150         {
151                 $a = self::getApp();
152
153                 $o = '';
154
155                 // When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
156                 // to one recipient. By default our selector allows multiple selects amongst all contacts.
157                 $sql_extra = sprintf(" AND `rel` = %d ", intval(Contact::FRIEND));
158                 $sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", Protocol::DFRN, Protocol::DIASPORA);
159
160                 $tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
161
162                 $hidepreselected = '';
163                 if ($preselected) {
164                         $sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
165                         $hidepreselected = ' style="display: none;"';
166                 }
167
168                 $o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
169
170                 $stmt = DBA::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
171                         WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
172                         $sql_extra
173                         ORDER BY `name` ASC ", intval(local_user())
174                 );
175
176                 $contacts = DBA::toArray($stmt);
177
178                 $arr = ['contact' => $contacts, 'entry' => $o];
179
180                 // e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
181                 Hook::callAll($a->module . '_pre_' . $selname, $arr);
182
183                 $receiverlist = [];
184
185                 if (DBA::isResult($contacts)) {
186                         foreach ($contacts as $contact) {
187                                 if (in_array($contact['id'], $preselected)) {
188                                         $selected = ' selected="selected"';
189                                 } else {
190                                         $selected = '';
191                                 }
192
193                                 $trimmed = Protocol::formatMention($contact['url'], $contact['name']);
194
195                                 $receiverlist[] = $trimmed;
196
197                                 $o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
198                         }
199                 }
200
201                 $o .= '</select>' . PHP_EOL;
202
203                 if ($preselected) {
204                         $o .= implode(', ', $receiverlist);
205                 }
206
207                 Hook::callAll($a->module . '_post_' . $selname, $o);
208
209                 return $o;
210         }
211
212         private static function fixACL(&$item)
213         {
214                 $item = intval(str_replace(['<', '>'], ['', ''], $item));
215         }
216
217         /**
218          * Return the default permission of the provided user array
219          *
220          * @param array $user
221          * @return array Hash of contact id lists
222          * @throws \Exception
223          */
224         public static function getDefaultUserPermissions(array $user = null)
225         {
226                 $matches = [];
227
228                 $acl_regex = '/<([0-9]+)>/i';
229
230                 preg_match_all($acl_regex, $user['allow_cid'] ?? '', $matches);
231                 $allow_cid = $matches[1];
232                 preg_match_all($acl_regex, $user['allow_gid'] ?? '', $matches);
233                 $allow_gid = $matches[1];
234                 preg_match_all($acl_regex, $user['deny_cid'] ?? '', $matches);
235                 $deny_cid = $matches[1];
236                 preg_match_all($acl_regex, $user['deny_gid'] ?? '', $matches);
237                 $deny_gid = $matches[1];
238
239                 // Reformats the ACL data so that it is accepted by the JS frontend
240                 array_walk($allow_cid, 'self::fixACL');
241                 array_walk($allow_gid, 'self::fixACL');
242                 array_walk($deny_cid, 'self::fixACL');
243                 array_walk($deny_gid, 'self::fixACL');
244
245                 Contact::pruneUnavailable($allow_cid);
246
247                 return [
248                         'allow_cid' => $allow_cid,
249                         'allow_gid' => $allow_gid,
250                         'deny_cid' => $deny_cid,
251                         'deny_gid' => $deny_gid,
252                 ];
253         }
254
255         /**
256          * Returns the ACL list of contacts for a given user id
257          *
258          * @param int $user_id
259          * @return array
260          * @throws \Exception
261          */
262         public static function getContactListByUserId(int $user_id)
263         {
264                 $acl_contacts = Contact::selectToArray(
265                         ['id', 'name', 'addr', 'micro'],
266                         ['uid' => $user_id, 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]
267                 );
268                 array_walk($acl_contacts, function (&$value) {
269                         $value['type'] = 'contact';
270                 });
271
272                 return $acl_contacts;
273         }
274
275         /**
276          * Returns the ACL list of groups (including meta-groups) for a given user id
277          *
278          * @param int $user_id
279          * @return array
280          */
281         public static function getGroupListByUserId(int $user_id)
282         {
283                 $acl_groups = [
284                         [
285                                 'id' => Group::FOLLOWERS,
286                                 'name' => L10n::t('Followers'),
287                                 'addr' => '',
288                                 'micro' => 'images/twopeople.png',
289                                 'type' => 'group',
290                         ],
291                         [
292                                 'id' => Group::MUTUALS,
293                                 'name' => L10n::t('Mutuals'),
294                                 'addr' => '',
295                                 'micro' => 'images/twopeople.png',
296                                 'type' => 'group',
297                         ]
298                 ];
299                 foreach (Group::getByUserId($user_id) as $group) {
300                         $acl_groups[] = [
301                                 'id' => $group['id'],
302                                 'name' => $group['name'],
303                                 'addr' => '',
304                                 'micro' => 'images/twopeople.png',
305                                 'type' => 'group',
306                         ];
307                 }
308
309                 return $acl_groups;
310         }
311
312         /**
313          * Return the full jot ACL selector HTML
314          *
315          * @param array $user                User array
316          * @param bool  $show_jotnets
317          * @param array $default_permissions Static defaults permission array: ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']
318          * @return string
319          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
320          */
321         public static function getFullSelectorHTML(array $user = null, $show_jotnets = false, array $default_permissions = [])
322         {
323                 // Defaults user permissions
324                 if (empty($default_permissions)) {
325                         $default_permissions = self::getDefaultUserPermissions($user);
326                 }
327
328                 $jotnets_fields = [];
329                 if ($show_jotnets) {
330                         $mail_enabled = false;
331                         $pubmail_enabled = false;
332
333                         if (function_exists('imap_open') && !Config::get('system', 'imap_disabled')) {
334                                 $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
335                                 if (DBA::isResult($mailacct)) {
336                                         $mail_enabled = true;
337                                         $pubmail_enabled = !empty($mailacct['pubmail']);
338                                 }
339                         }
340
341                         if (empty($default_permissions['hidewall'])) {
342                                 if ($mail_enabled) {
343                                         $jotnets_fields[] = [
344                                                 'type' => 'checkbox',
345                                                 'field' => [
346                                                         'pubmail_enable',
347                                                         L10n::t('Post to Email'),
348                                                         $pubmail_enabled
349                                                 ]
350                                         ];
351                                 }
352
353                                 Hook::callAll('jot_networks', $jotnets_fields);
354                         }
355                 }
356
357                 $tpl = Renderer::getMarkupTemplate('acl_selector.tpl');
358                 $o = Renderer::replaceMacros($tpl, [
359                         '$showall' => L10n::t('Visible to everybody'),
360                         '$show' => L10n::t('show'),
361                         '$hide' => L10n::t('don\'t show'),
362                         '$allowcid' => json_encode(($default_permissions['allow_cid'] ?? '') ?: []), // We need arrays for
363                         '$allowgid' => json_encode(($default_permissions['allow_gid'] ?? '') ?: []), // Javascript since we
364                         '$denycid'  => json_encode(($default_permissions['deny_cid']  ?? '') ?: []), // call .remove() and
365                         '$denygid'  => json_encode(($default_permissions['deny_gid']  ?? '') ?: []), // .push() on these values
366                         '$networks' => $show_jotnets,
367                         '$emailcc' => L10n::t('CC: email addresses'),
368                         '$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
369                         '$jotnets_enabled' => empty($default_permissions['hidewall']),
370                         '$jotnets_summary' => L10n::t('Connectors'),
371                         '$jotnets_fields' => $jotnets_fields,
372                         '$jotnets_disabled_label' => L10n::t('Connectors disabled, since "%s" is enabled.', L10n::t('Hide your profile details from unknown viewers?')),
373                         '$aclModalTitle' => L10n::t('Permissions'),
374                         '$aclModalDismiss' => L10n::t('Close'),
375                         '$features' => [
376                                 'aclautomention' => !empty($user['uid']) && Feature::isEnabled($user['uid'], 'aclautomention') ? 'true' : 'false'
377                         ],
378                 ]);
379
380                 return $o;
381         }
382 }