ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilUserAutoComplete.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
8{
9 const MAX_ENTRIES = 1000;
10
11
16
21
26
31
35 private $logger = null;
36
40 private $searchable_check = false;
41
45 private $user_access_check = true;
46
50 private $possible_fields = array();
51
56
60 private $search_type;
61
66
70 private $user;
71
72
73 private $limit = 0;
74
75 private $user_limitations = true;
76
81
85 private $more_link_available = false;
86
90 protected $user_filter = null;
91
95 public function __construct()
96 {
97 global $DIC;
98
99 $this->result_field = 'login';
100
101 $this->setSearchType(self::SEARCH_TYPE_LIKE);
102 $this->setPrivacyMode(self::PRIVACY_MODE_IGNORE_USER_SETTING);
103
104 $this->logger = $DIC->logger()->user();
105 }
106
110 public function respectMinimumSearchCharacterCount($a_status)
111 {
112 $this->respect_min_search_character_count = $a_status;
113 }
114
119 {
121 }
122
123
134 {
135 $this->user_filter = $user_filter;
136 }
137
138 public function setLimit($a_limit)
139 {
140 $this->limit = $a_limit;
141 }
142
143 public function getLimit()
144 {
145 return $this->limit;
146 }
147
152 {
153 $this->search_type = $search_type;
154 }
155
159 public function getSearchType()
160 {
161 return $this->search_type;
162 }
163
168 {
169 $this->privacy_mode = $privacy_mode;
170 }
171
175 public function getPrivacyMode()
176 {
177 return $this->privacy_mode;
178 }
179
183 public function setUser($user)
184 {
185 $this->user = $user;
186 }
187
191 public function getUser()
192 {
193 return $this->user;
194 }
195
200 public function enableFieldSearchableCheck($a_status)
201 {
202 $this->searchable_check = $a_status;
203 }
204
210 {
212 }
213
219 public function enableUserAccessCheck($a_status)
220 {
221 $this->user_access_check = $a_status;
222 }
223
228 public function isUserAccessCheckEnabled()
229 {
231 }
232
237 public function setSearchFields($a_fields)
238 {
239 $this->possible_fields = $a_fields;
240 }
241
246 public function getSearchFields()
247 {
249 }
250
255 protected function getFields()
256 {
257 if (!$this->isFieldSearchableCheckEnabled()) {
258 return $this->getSearchFields();
259 }
260 $available_fields = array();
261 foreach ($this->getSearchFields() as $field) {
262 include_once 'Services/Search/classes/class.ilUserSearchOptions.php';
264 $available_fields[] = $field;
265 }
266 }
267 return $available_fields;
268 }
269
274 public function setResultField($a_field)
275 {
276 $this->result_field = $a_field;
277 }
278
284 public function getList($a_str)
285 {
289 global $DIC;
290
291 $ilDB = $DIC['ilDB'];
292
293 $parsed_query = $this->parseQueryString($a_str);
294
295 if (ilStr::strLen($parsed_query['query']) < ilQueryParser::MIN_WORD_LENGTH) {
296 $result_json['items'] = [];
297 $result_json['hasMoreResults'] = false;
298 $this->logger->debug('Autocomplete search rejected: minimum characters count.');
299 return json_encode($result_json);
300 }
301
302
303 $select_part = $this->getSelectPart();
304 $where_part = $this->getWherePart($parsed_query);
305 $order_by_part = $this->getOrderByPart();
306 $query = implode(" ", array(
307 'SELECT ' . $select_part,
308 'FROM ' . $this->getFromPart(),
309 $where_part ? 'WHERE ' . $where_part : '',
310 $order_by_part ? 'ORDER BY ' . $order_by_part : ''
311 ));
312
313 $this->logger->debug('Query: ' . $query);
314
315 $res = $ilDB->query($query);
316
317 // add email only if it is "searchable"
318 $add_email = true;
319 include_once 'Services/Search/classes/class.ilUserSearchOptions.php';
321 $add_email = false;
322 }
323
324 $add_second_email = true;
325 if ($this->isFieldSearchableCheckEnabled() && !ilUserSearchOptions::_isEnabled("second_email")) {
326 $add_second_email = false;
327 }
328
329 include_once './Services/Search/classes/class.ilSearchSettings.php';
330 $max = $this->getLimit() ? $this->getLimit() : ilSearchSettings::getInstance()->getAutoCompleteLength();
331 $cnt = 0;
332 $more_results = false;
333 $result = array();
334 $recs = array();
335 $usrIds = array();
336 while (($rec = $ilDB->fetchAssoc($res)) && $cnt < ($max + 1)) {
337 if ($cnt >= $max && $this->isMoreLinkAvailable()) {
338 $more_results = true;
339 break;
340 }
341 $recs[$rec['usr_id']] = $rec;
342 $usrIds[] = $rec['usr_id'];
343 }
344 if (is_callable($this->user_filter, true, $callable_name = '')) {
345 $usrIds = call_user_func_array($this->user_filter, [$usrIds]);
346 }
347 foreach ($usrIds as $usr_id) {
348 $rec = $recs[$usr_id];
349
350 if (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || in_array($rec['profile_value'], ['y','g'])) {
351 $label = $rec['lastname'] . ', ' . $rec['firstname'] . ' [' . $rec['login'] . ']';
352 } else {
353 $label = '[' . $rec['login'] . ']';
354 }
355
356 if ($add_email && $rec['email'] && (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || 'y' == $rec['email_value'])) {
357 $label .= ', ' . $rec['email'];
358 }
359
360 if ($add_second_email && $rec['second_email'] && (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || 'y' == $rec['second_email_value'])) {
361 $label .= ', ' . $rec['second_email'];
362 }
363
364 $result[$cnt]['value'] = (string) $rec[$this->result_field];
365 $result[$cnt]['label'] = $label;
366 $result[$cnt]['id'] = $rec['usr_id'];
367 $cnt++;
368 }
369
370 include_once 'Services/JSON/classes/class.ilJsonUtil.php';
371
372 $result_json['items'] = $result;
373 $result_json['hasMoreResults'] = $more_results;
374
375 $this->logger->dump($result_json, ilLogLevel::DEBUG);
376
377 return ilJsonUtil::encode($result_json);
378 }
379
383 protected function getSelectPart()
384 {
385 $fields = array(
386 'ud.usr_id',
387 'ud.login',
388 'ud.firstname',
389 'ud.lastname',
390 'ud.email',
391 'ud.second_email'
392 );
393
394 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
395 $fields[] = 'profpref.value profile_value';
396 $fields[] = 'pubemail.value email_value';
397 $fields[] = 'pubsecondemail.value second_email_value';
398 }
399
400 return implode(', ', $fields);
401 }
402
406 protected function getFromPart()
407 {
411 global $DIC;
412
413 $ilDB = $DIC['ilDB'];
414
415 $joins = array();
416
417 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
418 $joins[] = 'LEFT JOIN usr_pref profpref
419 ON profpref.usr_id = ud.usr_id
420 AND profpref.keyword = ' . $ilDB->quote('public_profile', 'text');
421
422 $joins[] = 'LEFT JOIN usr_pref pubemail
423 ON pubemail.usr_id = ud.usr_id
424 AND pubemail.keyword = ' . $ilDB->quote('public_email', 'text');
425
426 $joins[] = 'LEFT JOIN usr_pref pubsecondemail
427 ON pubsecondemail.usr_id = ud.usr_id
428 AND pubsecondemail.keyword = ' . $ilDB->quote('public_second_email', 'text');
429 }
430
431 if ($joins) {
432 return 'usr_data ud ' . implode(' ', $joins);
433 } else {
434 return 'usr_data ud';
435 }
436 }
437
442 protected function getWherePart(array $search_query)
443 {
448 global $DIC;
449
450 $ilDB = $DIC['ilDB'];
451 $ilSetting = $DIC['ilSetting'];
452
453 $outer_conditions = array();
454
455 // In 'anonymous' context with respected user privacy, only users with globally published profiles should be found.
456 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode() &&
457 $this->getUser() instanceof ilObjUser &&
458 $this->getUser()->isAnonymous()
459 ) {
460 if (!$ilSetting->get('enable_global_profiles', 0)) {
461 // If 'Enable User Content Publishing' is not set in the administration, no user should be found for 'anonymous' context.
462 return '1 = 2';
463 } else {
464 // Otherwise respect the profile activation setting of every user (as a global (outer) condition in the where clause).
465 $outer_conditions[] = 'profpref.value = ' . $ilDB->quote('g', 'text');
466 }
467 }
468
469 $outer_conditions[] = 'ud.usr_id != ' . $ilDB->quote(ANONYMOUS_USER_ID, 'integer');
470
471 $field_conditions = array();
472 foreach ($this->getFields() as $field) {
473 $field_condition = $this->getQueryConditionByFieldAndValue($field, $search_query);
474
475 if ('email' == $field && self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
476 // If privacy should be respected, the profile setting of every user concerning the email address has to be
477 // respected (in every user context, no matter if the user is 'logged in' or 'anonymous').
478 $email_query = array();
479 $email_query[] = $field_condition;
480 $email_query[] = 'pubemail.value = ' . $ilDB->quote('y', 'text');
481 $field_conditions[] = '(' . implode(' AND ', $email_query) . ')';
482 } elseif ('second_email' == $field && self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
483 // If privacy should be respected, the profile setting of every user concerning the email address has to be
484 // respected (in every user context, no matter if the user is 'logged in' or 'anonymous').
485 $email_query = array();
486 $email_query[] = $field_condition;
487 $email_query[] = 'pubsecondemail.value = ' . $ilDB->quote('y', 'text');
488 $field_conditions[] = '(' . implode(' AND ', $email_query) . ')';
489 } else {
490 $field_conditions[] = $field_condition;
491 }
492 }
493
494 // If the current user context ist 'logged in' and privacy should be respected, all fields >>>except the login<<<
495 // should only be searchable if the users' profile is published (y oder g)
496 // In 'anonymous' context we do not need this additional conditions,
497 // because we checked the privacy setting in the condition above: profile = 'g'
498 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode() &&
499 $this->getUser() instanceof ilObjUser && !$this->getUser()->isAnonymous() &&
500 $field_conditions
501 ) {
502 $fields = '(' . implode(' OR ', $field_conditions) . ')';
503
504 $field_conditions = [
505 '(' . implode(' AND ', array(
506 $fields,
507 $ilDB->in('profpref.value', array('y', 'g'), false, 'text')
508 )) . ')'
509 ];
510 }
511
512 // The login field must be searchable regardless (for 'logged in' users) of any privacy settings.
513 // We handled the general condition for 'anonymous' context above: profile = 'g'
514 $field_conditions[] = $this->getQueryConditionByFieldAndValue('login', $search_query);
515
516 include_once 'Services/User/classes/class.ilUserAccountSettings.php';
517 if (ilUserAccountSettings::getInstance()->isUserAccessRestricted()) {
518 include_once './Services/User/classes/class.ilUserFilter.php';
519 $outer_conditions[] = $ilDB->in('time_limit_owner', ilUserFilter::getInstance()->getFolderIds(), false, 'integer');
520 }
521
522 if ($field_conditions) {
523 $outer_conditions[] = '(' . implode(' OR ', $field_conditions) . ')';
524 }
525
526 include_once './Services/Search/classes/class.ilSearchSettings.php';
527 $settings = ilSearchSettings::getInstance();
528
529 if (!$settings->isInactiveUserVisible() && $this->getUserLimitations()) {
530 $outer_conditions[] = "ud.active = " . $ilDB->quote(1, 'integer');
531 }
532
533 if (!$settings->isLimitedUserVisible() && $this->getUserLimitations()) {
534 $unlimited = "ud.time_limit_unlimited = " . $ilDB->quote(1, 'integer');
535 $from = "ud.time_limit_from < " . $ilDB->quote(time(), 'integer');
536 $until = "ud.time_limit_until > " . $ilDB->quote(time(), 'integer');
537
538 $outer_conditions[] = '(' . $unlimited . ' OR (' . $from . ' AND ' . $until . '))';
539 }
540
541 return implode(' AND ', $outer_conditions);
542 }
543
547 protected function getOrderByPart()
548 {
549 return 'login ASC';
550 }
551
557 protected function getQueryConditionByFieldAndValue($field, $query)
558 {
562 global $DIC;
563
564 $ilDB = $DIC['ilDB'];
565
566 $query_strings = array($query['query']);
567
568 if (array_key_exists($field, $query)) {
569 $query_strings = array($query[$field]);
570 } elseif (array_key_exists('parts', $query)) {
571 $query_strings = $query['parts'];
572 }
573
574 $query_condition = '( ';
575 $num = 0;
576 foreach ($query_strings as $query_string) {
577 if ($num++ > 0) {
578 $query_condition .= ' OR ';
579 }
580 if (self::SEARCH_TYPE_LIKE == $this->getSearchType()) {
581 $query_condition .= $ilDB->like($field, 'text', $query_string . '%');
582 } else {
583 $query_condition .= $ilDB->like($field, 'text', $query_string);
584 }
585 }
586 $query_condition .= ')';
587 return $query_condition;
588 }
589
595 public function setUserLimitations($a_limitations)
596 {
597 $this->user_limitations = (bool) $a_limitations;
598 }
599
604 public function getUserLimitations()
605 {
607 }
608
612 public function isMoreLinkAvailable()
613 {
615 }
616
623 {
624 $this->more_link_available = $more_link_available;
625 }
626
632 public function parseQueryString($a_query)
633 {
634 $query = array();
635
636 if (!stristr($a_query, '\\')) {
637 $a_query = str_replace('%', '\%', $a_query);
638 $a_query = str_replace('_', '\_', $a_query);
639 }
640
641 $query['query'] = trim($a_query);
642
643 // "," means fixed search for lastname, firstname
644 if (strpos($a_query, ',')) {
645 $comma_separated = (array) explode(',', $a_query);
646
647 if (count($comma_separated) == 2) {
648 if (trim($comma_separated[0])) {
649 $query['lastname'] = trim($comma_separated[0]);
650 }
651 if (trim($comma_separated[1])) {
652 $query['firstname'] = trim($comma_separated[1]);
653 }
654 }
655 } else {
656 $whitespace_separated = (array) explode(' ', $a_query);
657 foreach ($whitespace_separated as $part) {
658 if (trim($part)) {
659 $query['parts'][] = trim($part);
660 }
661 }
662 }
663
664 $this->logger->dump($query, ilLogLevel::DEBUG);
665
666 return $query;
667 }
668}
$result
user()
Definition: user.php:4
An exception for terminatinating execution or to throw for unit testing.
static encode($mixed, $suppress_native=false)
const MIN_WORD_LENGTH
Minimum of characters required for search.
static strLen($a_string)
Definition: class.ilStr.php:78
static getInstance()
Singelton get instance.
Auto completion class for user lists.
getFields()
Get searchable fields.
parseQueryString($a_query)
Parse query string.
addUserAccessFilterCallable(callable $user_filter)
Closure for filtering users e.g $rep_search_gui->addUserAccessFilterCallable(function($user_ids) use(...
setUserLimitations($a_limitations)
allow user limitations like inactive and access limitations
__construct()
Default constructor.
getUserLimitations()
allow user limitations like inactive and access limitations
isUserAccessCheckEnabled()
Check if user access check is enabled.
isFieldSearchableCheckEnabled()
Searchable check enabled.
setResultField($a_field)
Set result field.
getSearchFields()
get possible search fields
enableFieldSearchableCheck($a_status)
Enable the check whether the field is searchable in Administration -> Settings -> Standard Fields.
setSearchFields($a_fields)
Set searchable fields.
enableUserAccessCheck($a_status)
Enable user access check.
respectMinimumSearchCharacterCount($a_status)
setMoreLinkAvailable($more_link_available)
IMPORTANT: remember to read request parameter 'fetchall' to use this function.
static getInstance()
Singelton get instance.
global $ilSetting
Definition: privfeed.php:17
$query
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$from