3 /* ACL selector json backend */
6 use Friendica\Content\Widget;
7 use Friendica\Core\ACL;
8 use Friendica\Core\Hook;
9 use Friendica\Core\Logger;
10 use Friendica\Core\Protocol;
11 use Friendica\Database\DBA;
12 use Friendica\Model\Contact;
13 use Friendica\Model\Item;
14 use Friendica\Util\Proxy as ProxyUtils;
15 use Friendica\Util\Strings;
17 function acl_content(App $a)
23 $start = defaults($_REQUEST, 'start' , 0);
24 $count = defaults($_REQUEST, 'count' , 100);
25 $search = defaults($_REQUEST, 'search' , '');
26 $type = defaults($_REQUEST, 'type' , '');
27 $conv_id = defaults($_REQUEST, 'conversation', null);
29 // For use with jquery.textcomplete for private mail completion
30 if (!empty($_REQUEST['query'])) {
34 $search = $_REQUEST['query'];
37 Logger::info('ACL {action} - {subaction}', ['module' => 'acl', 'action' => 'content', 'subaction' => 'search', 'search' => $search, 'type' => $type, 'conversation' => $conv_id]);
40 $sql_extra = "AND `name` LIKE '%%" . DBA::escape($search) . "%%'";
41 $sql_extra2 = "AND (`attag` LIKE '%%" . DBA::escape($search) . "%%' OR `name` LIKE '%%" . DBA::escape($search) . "%%' OR `nick` LIKE '%%" . DBA::escape($search) . "%%')";
43 /// @TODO Avoid these needless else blocks by putting variable-initialization atop of if()
44 $sql_extra = $sql_extra2 = '';
47 // count groups and contacts
49 if ($type == '' || $type == 'g') {
50 $r = q("SELECT COUNT(*) AS g FROM `group` WHERE NOT `deleted` AND `uid` = %d $sql_extra",
53 $group_count = (int) $r[0]['g'];
56 $sql_extra2 .= ' ' . Widget::unavailableNetworks();
59 if ($type == '' || $type == 'c') {
60 // autocomplete for editor mentions
61 $r = q("SELECT COUNT(*) AS c FROM `contact`
62 WHERE `uid` = %d AND NOT `self` AND NOT `deleted`
63 AND NOT `blocked` AND NOT `pending` AND NOT `archive`
64 AND `notify` != '' $sql_extra2",
67 $contact_count = (int) $r[0]['c'];
68 } elseif ($type == 'f') {
69 // autocomplete for editor mentions of forums
70 $r = q("SELECT COUNT(*) AS c FROM `contact`
71 WHERE `uid` = %d AND NOT `self` AND NOT `deleted`
72 AND NOT `blocked` AND NOT `pending` AND NOT `archive`
73 AND (`forum` OR `prv`)
74 AND `notify` != '' $sql_extra2",
77 $contact_count = (int) $r[0]['c'];
78 } elseif ($type == 'm') {
79 // autocomplete for Private Messages
80 $r = q("SELECT COUNT(*) AS c FROM `contact`
81 WHERE `uid` = %d AND NOT `self` AND NOT `deleted`
82 AND NOT `blocked` AND NOT `pending` AND NOT `archive`
83 AND `network` IN ('%s', '%s', '%s') $sql_extra2",
85 DBA::escape(Protocol::ACTIVITYPUB),
86 DBA::escape(Protocol::DFRN),
87 DBA::escape(Protocol::DIASPORA)
89 $contact_count = (int) $r[0]['c'];
90 } elseif ($type == 'a') {
91 // autocomplete for Contacts
92 $r = q("SELECT COUNT(*) AS c FROM `contact`
93 WHERE `uid` = %d AND NOT `self`
94 AND NOT `pending` AND NOT `deleted` $sql_extra2",
97 $contact_count = (int) $r[0]['c'];
100 $tot = $group_count + $contact_count;
105 if ($type == '' || $type == 'g') {
106 /// @todo We should cache this query.
107 // This can be done when we can delete cache entries via wildcard
108 $r = q("SELECT `group`.`id`, `group`.`name`, GROUP_CONCAT(DISTINCT `group_member`.`contact-id` SEPARATOR ',') AS uids
110 INNER JOIN `group_member` ON `group_member`.`gid`=`group`.`id`
111 WHERE NOT `group`.`deleted` AND `group`.`uid` = %d
113 GROUP BY `group`.`name`, `group`.`id`
114 ORDER BY `group`.`name`
116 intval(local_user()),
124 'photo' => 'images/twopeople.png',
125 'name' => htmlspecialchars($g['name']),
126 'id' => intval($g['id']),
127 'uids' => array_map('intval', explode(',', $g['uids'])),
132 if ((count($groups) > 0) && ($search == '')) {
133 $groups[] = ['separator' => true];
139 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv`, (`prv` OR `forum`) AS `frm` FROM `contact`
140 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
141 AND NOT (`network` IN ('%s', '%s'))
143 ORDER BY `name` ASC ",
144 intval(local_user()),
145 DBA::escape(Protocol::OSTATUS),
146 DBA::escape(Protocol::STATUSNET)
148 } elseif ($type == 'c') {
149 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
150 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
151 AND NOT (`network` IN ('%s'))
153 ORDER BY `name` ASC ",
154 intval(local_user()),
155 DBA::escape(Protocol::STATUSNET)
157 } elseif ($type == 'f') {
158 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
159 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
160 AND NOT (`network` IN ('%s'))
161 AND (`forum` OR `prv`)
163 ORDER BY `name` ASC ",
164 intval(local_user()),
165 DBA::escape(Protocol::STATUSNET)
167 } elseif ($type == 'm') {
168 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr` FROM `contact`
169 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive`
170 AND `network` IN ('%s', '%s', '%s')
172 ORDER BY `name` ASC ",
173 intval(local_user()),
174 DBA::escape(Protocol::ACTIVITYPUB),
175 DBA::escape(Protocol::DFRN),
176 DBA::escape(Protocol::DIASPORA)
178 } elseif ($type == 'a') {
179 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
180 WHERE `uid` = %d AND NOT `deleted` AND NOT `pending` AND NOT `archive`
182 ORDER BY `name` ASC ",
185 } elseif ($type == 'x') {
186 // autocomplete for global contact search (e.g. navbar search)
187 $search = Strings::escapeTags(trim($_REQUEST['search']));
188 $mode = $_REQUEST['smode'];
189 $page = $_REQUEST['page'] ?? 1;
191 $r = ACL::contactAutocomplete($search, $mode, $page);
196 'photo' => ProxyUtils::proxifyUrl($g['photo'], false, ProxyUtils::SIZE_MICRO),
197 'name' => htmlspecialchars($g['name']),
198 'nick' => defaults($g, 'addr', $g['url']),
199 'network' => $g['network'],
201 'forum' => !empty($g['community']) ? 1 : 0,
207 'items' => $contacts,
209 echo json_encode($o);
213 if (DBA::isResult($r)) {
218 'photo' => ProxyUtils::proxifyUrl($g['micro'], false, ProxyUtils::SIZE_MICRO),
219 'name' => htmlspecialchars($g['name']),
220 'id' => intval($g['id']),
221 'network' => $g['network'],
223 'nick' => htmlentities(defaults($g, 'attag', $g['nick'])),
224 'addr' => htmlentities(defaults($g, 'addr', $g['url'])),
225 'forum' => !empty($g['forum']) || !empty($g['prv']) ? 1 : 0,
227 if ($entry['forum']) {
230 $contacts[] = $entry;
233 if (count($forums) > 0) {
235 $forums[] = ['separator' => true];
237 $contacts = array_merge($forums, $contacts);
241 $items = array_merge($groups, $contacts);
244 // In multi threaded posts the conv_id is not the parent of the whole thread
245 $parent_item = Item::selectFirst(['parent'], ['id' => $conv_id]);
246 if (DBA::isResult($parent_item)) {
247 $conv_id = $parent_item['parent'];
251 * if $conv_id is set, get unknown contacts in thread
252 * but first get known contacts url to filter them out
254 $known_contacts = array_map(function ($i) {
258 $unknown_contacts = [];
260 $condition = ["`parent` = ?", $conv_id];
261 $params = ['order' => ['author-name' => true]];
262 $authors = Item::selectForUser(local_user(), ['author-link'], $condition, $params);
264 while ($author = Item::fetch($authors)) {
265 $item_authors[$author['author-link']] = $author['author-link'];
267 DBA::close($authors);
269 foreach ($item_authors as $author) {
270 if (in_array($author, $known_contacts)) {
274 $contact = Contact::getDetailsByURL($author);
276 if (count($contact) > 0) {
277 $unknown_contacts[] = [
279 'photo' => ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO),
280 'name' => htmlspecialchars($contact['name']),
281 'id' => intval($contact['cid']),
282 'network' => $contact['network'],
283 'link' => $contact['url'],
284 'nick' => htmlentities(defaults($contact, 'nick', $contact['addr'])),
285 'addr' => htmlentities(defaults($contact, 'addr', $contact['url'])),
286 'forum' => $contact['forum']
291 $items = array_merge($items, $unknown_contacts);
292 $tot += count($unknown_contacts);
300 'contacts' => $contacts,
306 Hook::callAll('acl_lookup_end', $results);
309 'tot' => $results['tot'],
310 'start' => $results['start'],
311 'count' => $results['count'],
312 'items' => $results['items'],
315 echo json_encode($o);