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 `success_update` >= `failure_update`
65 AND `notify` != '' $sql_extra2",
68 $contact_count = (int) $r[0]['c'];
69 } elseif ($type == 'f') {
70 // autocomplete for editor mentions of forums
71 $r = q("SELECT COUNT(*) AS c FROM `contact`
72 WHERE `uid` = %d AND NOT `self` AND NOT `deleted`
73 AND NOT `blocked` AND NOT `pending` AND NOT `archive`
74 AND (`forum` OR `prv`)
75 AND `success_update` >= `failure_update`
76 AND `notify` != '' $sql_extra2",
79 $contact_count = (int) $r[0]['c'];
80 } elseif ($type == 'm') {
81 // autocomplete for Private Messages
82 $r = q("SELECT COUNT(*) AS c FROM `contact`
83 WHERE `uid` = %d AND NOT `self` AND NOT `deleted`
84 AND NOT `blocked` AND NOT `pending` AND NOT `archive`
85 AND `success_update` >= `failure_update`
86 AND `network` IN ('%s', '%s', '%s') $sql_extra2",
88 DBA::escape(Protocol::ACTIVITYPUB),
89 DBA::escape(Protocol::DFRN),
90 DBA::escape(Protocol::DIASPORA)
92 $contact_count = (int) $r[0]['c'];
93 } elseif ($type == 'a') {
94 // autocomplete for Contacts
95 $r = q("SELECT COUNT(*) AS c FROM `contact`
96 WHERE `uid` = %d AND NOT `self`
97 AND NOT `pending` AND NOT `deleted` $sql_extra2",
100 $contact_count = (int) $r[0]['c'];
103 $tot = $group_count + $contact_count;
108 if ($type == '' || $type == 'g') {
109 /// @todo We should cache this query.
110 // This can be done when we can delete cache entries via wildcard
111 $r = q("SELECT `group`.`id`, `group`.`name`, GROUP_CONCAT(DISTINCT `group_member`.`contact-id` SEPARATOR ',') AS uids
113 INNER JOIN `group_member` ON `group_member`.`gid`=`group`.`id`
114 WHERE NOT `group`.`deleted` AND `group`.`uid` = %d
116 GROUP BY `group`.`name`, `group`.`id`
117 ORDER BY `group`.`name`
119 intval(local_user()),
127 'photo' => 'images/twopeople.png',
128 'name' => htmlspecialchars($g['name']),
129 'id' => intval($g['id']),
130 'uids' => array_map('intval', explode(',', $g['uids'])),
135 if ((count($groups) > 0) && ($search == '')) {
136 $groups[] = ['separator' => true];
142 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv`, (`prv` OR `forum`) AS `frm` FROM `contact`
143 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
144 AND `success_update` >= `failure_update` AND NOT (`network` IN ('%s', '%s'))
146 ORDER BY `name` ASC ",
147 intval(local_user()),
148 DBA::escape(Protocol::OSTATUS),
149 DBA::escape(Protocol::STATUSNET)
151 } elseif ($type == 'c') {
152 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
153 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
154 AND `success_update` >= `failure_update` AND NOT (`network` IN ('%s'))
156 ORDER BY `name` ASC ",
157 intval(local_user()),
158 DBA::escape(Protocol::STATUSNET)
160 } elseif ($type == 'f') {
161 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
162 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
163 AND `success_update` >= `failure_update` AND NOT (`network` IN ('%s'))
164 AND (`forum` OR `prv`)
166 ORDER BY `name` ASC ",
167 intval(local_user()),
168 DBA::escape(Protocol::STATUSNET)
170 } elseif ($type == 'm') {
171 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr` FROM `contact`
172 WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive`
173 AND `success_update` >= `failure_update` AND `network` IN ('%s', '%s', '%s')
175 ORDER BY `name` ASC ",
176 intval(local_user()),
177 DBA::escape(Protocol::ACTIVITYPUB),
178 DBA::escape(Protocol::DFRN),
179 DBA::escape(Protocol::DIASPORA)
181 } elseif ($type == 'a') {
182 $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
183 WHERE `uid` = %d AND NOT `deleted` AND NOT `pending` AND `success_update` >= `failure_update`
185 ORDER BY `name` ASC ",
188 } elseif ($type == 'x') {
189 // autocomplete for global contact search (e.g. navbar search)
190 $search = Strings::escapeTags(trim($_REQUEST['search']));
191 $mode = $_REQUEST['smode'];
193 $r = ACL::contactAutocomplete($search, $mode);
198 'photo' => ProxyUtils::proxifyUrl($g['photo'], false, ProxyUtils::SIZE_MICRO),
199 'name' => htmlspecialchars($g['name']),
200 'nick' => defaults($g, 'addr', $g['url']),
201 'network' => $g['network'],
203 'forum' => !empty($g['community']) ? 1 : 0,
209 'items' => $contacts,
211 echo json_encode($o);
215 if (DBA::isResult($r)) {
220 'photo' => ProxyUtils::proxifyUrl($g['micro'], false, ProxyUtils::SIZE_MICRO),
221 'name' => htmlspecialchars($g['name']),
222 'id' => intval($g['id']),
223 'network' => $g['network'],
225 'nick' => htmlentities(defaults($g, 'attag', $g['nick'])),
226 'addr' => htmlentities(defaults($g, 'addr', $g['url'])),
227 'forum' => !empty($g['forum']) || !empty($g['prv']) ? 1 : 0,
229 if ($entry['forum']) {
232 $contacts[] = $entry;
235 if (count($forums) > 0) {
237 $forums[] = ['separator' => true];
239 $contacts = array_merge($forums, $contacts);
243 $items = array_merge($groups, $contacts);
246 // In multi threaded posts the conv_id is not the parent of the whole thread
247 $parent_item = Item::selectFirst(['parent'], ['id' => $conv_id]);
248 if (DBA::isResult($parent_item)) {
249 $conv_id = $parent_item['parent'];
253 * if $conv_id is set, get unknown contacts in thread
254 * but first get known contacts url to filter them out
256 $known_contacts = array_map(function ($i) {
260 $unknown_contacts = [];
262 $condition = ["`parent` = ?", $conv_id];
263 $params = ['order' => ['author-name' => true]];
264 $authors = Item::selectForUser(local_user(), ['author-link'], $condition, $params);
266 while ($author = Item::fetch($authors)) {
267 $item_authors[$author['author-link']] = $author['author-link'];
269 DBA::close($authors);
271 foreach ($item_authors as $author) {
272 if (in_array($author, $known_contacts)) {
276 $contact = Contact::getDetailsByURL($author);
278 if (count($contact) > 0) {
279 $unknown_contacts[] = [
281 'photo' => ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO),
282 'name' => htmlspecialchars($contact['name']),
283 'id' => intval($contact['cid']),
284 'network' => $contact['network'],
285 'link' => $contact['url'],
286 'nick' => htmlentities(defaults($contact, 'nick', $contact['addr'])),
287 'addr' => htmlentities(defaults($contact, 'addr', $contact['url'])),
288 'forum' => $contact['forum']
293 $items = array_merge($items, $unknown_contacts);
294 $tot += count($unknown_contacts);
302 'contacts' => $contacts,
308 Hook::callAll('acl_lookup_end', $results);
311 'tot' => $results['tot'],
312 'start' => $results['start'],
313 'count' => $results['count'],
314 'items' => $results['items'],
317 echo json_encode($o);