ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 public function __construct()
91 {
92 global $DIC;
93
94 $this->result_field = 'login';
95
96 $this->setSearchType(self::SEARCH_TYPE_LIKE);
97 $this->setPrivacyMode(self::PRIVACY_MODE_IGNORE_USER_SETTING);
98
99 $this->logger = $DIC->logger()->user();
100 }
101
105 public function respectMinimumSearchCharacterCount($a_status)
106 {
107 $this->respect_min_search_character_count = $a_status;
108 }
109
114 {
116 }
117
118
119 public function setLimit($a_limit)
120 {
121 $this->limit = $a_limit;
122 }
123
124 public function getLimit()
125 {
126 return $this->limit;
127 }
128
133 {
134 $this->search_type = $search_type;
135 }
136
140 public function getSearchType()
141 {
142 return $this->search_type;
143 }
144
149 {
150 $this->privacy_mode = $privacy_mode;
151 }
152
156 public function getPrivacyMode()
157 {
158 return $this->privacy_mode;
159 }
160
164 public function setUser($user)
165 {
166 $this->user = $user;
167 }
168
172 public function getUser()
173 {
174 return $this->user;
175 }
176
181 public function enableFieldSearchableCheck($a_status)
182 {
183 $this->searchable_check = $a_status;
184 }
185
191 {
193 }
194
200 public function enableUserAccessCheck($a_status)
201 {
202 $this->user_access_check = $a_status;
203 }
204
209 public function isUserAccessCheckEnabled()
210 {
212 }
213
218 public function setSearchFields($a_fields)
219 {
220 $this->possible_fields = $a_fields;
221 }
222
227 public function getSearchFields()
228 {
230 }
231
236 protected function getFields()
237 {
238 if (!$this->isFieldSearchableCheckEnabled()) {
239 return $this->getSearchFields();
240 }
241 $available_fields = array();
242 foreach ($this->getSearchFields() as $field) {
243 include_once 'Services/Search/classes/class.ilUserSearchOptions.php';
245 $available_fields[] = $field;
246 }
247 }
248 return $available_fields;
249 }
250
255 public function setResultField($a_field)
256 {
257 $this->result_field = $a_field;
258 }
259
265 public function getList($a_str)
266 {
270 global $ilDB;
271 $parsed_query = $this->parseQueryString($a_str);
272
273 if (ilStr::strLen($parsed_query['query']) < ilQueryParser::MIN_WORD_LENGTH) {
274 $result_json['items'] = [];
275 $result_json['hasMoreResults'] = false;
276 $this->logger->debug('Autocomplete search rejected: minimum characters count.');
277 return json_encode($result_json);
278 }
279
280
281 $select_part = $this->getSelectPart();
282 $where_part = $this->getWherePart($parsed_query);
283 $order_by_part = $this->getOrderByPart();
284 $query = implode(" ", array(
285 'SELECT ' . $select_part,
286 'FROM ' . $this->getFromPart(),
287 $where_part ? 'WHERE ' . $where_part : '',
288 $order_by_part ? 'ORDER BY ' . $order_by_part : ''
289 ));
290
291 $this->logger->debug('Query: ' . $query);
292
293 $res = $ilDB->query($query);
294
295 // add email only if it is "searchable"
296 $add_email = true;
297 include_once 'Services/Search/classes/class.ilUserSearchOptions.php';
299 $add_email = false;
300 }
301
302 $add_second_email = true;
303 if ($this->isFieldSearchableCheckEnabled() && !ilUserSearchOptions::_isEnabled("second_email")) {
304 $add_second_email = false;
305 }
306
307 include_once './Services/Search/classes/class.ilSearchSettings.php';
308 $max = $this->getLimit() ? $this->getLimit() : ilSearchSettings::getInstance()->getAutoCompleteLength();
309 $cnt = 0;
310 $more_results = false;
311 $result = array();
312 while (($rec = $ilDB->fetchAssoc($res)) && $cnt < ($max + 1)) {
313 if ($cnt >= $max && $this->isMoreLinkAvailable()) {
314 $more_results = true;
315 break;
316 }
317
318 if (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || in_array($rec['profile_value'], ['y','g'])) {
319 $label = $rec['lastname'] . ', ' . $rec['firstname'] . ' [' . $rec['login'] . ']';
320 } else {
321 $label = '[' . $rec['login'] . ']';
322 }
323
324 if ($add_email && $rec['email'] && (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || 'y' == $rec['email_value'])) {
325 $label .= ', ' . $rec['email'];
326 }
327
328 if ($add_second_email && $rec['second_email'] && (self::PRIVACY_MODE_RESPECT_USER_SETTING != $this->getPrivacyMode() || 'y' == $rec['second_email_value'])) {
329 $label .= ', ' . $rec['second_email'];
330 }
331
332 $result[$cnt]['value'] = (string) $rec[$this->result_field];
333 $result[$cnt]['label'] = $label;
334 $result[$cnt]['id'] = $rec['usr_id'];
335 $cnt++;
336 }
337
338 include_once 'Services/JSON/classes/class.ilJsonUtil.php';
339
340 $result_json['items'] = $result;
341 $result_json['hasMoreResults'] = $more_results;
342
343 $this->logger->dump($result_json);
344
345 return ilJsonUtil::encode($result_json);
346 }
347
351 protected function getSelectPart()
352 {
353 $fields = array(
354 'ud.usr_id',
355 'ud.login',
356 'ud.firstname',
357 'ud.lastname',
358 'ud.email',
359 'ud.second_email'
360 );
361
362 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
363 $fields[] = 'profpref.value profile_value';
364 $fields[] = 'pubemail.value email_value';
365 $fields[] = 'pubsecondemail.value second_email_value';
366 }
367
368 return implode(', ', $fields);
369 }
370
374 protected function getFromPart()
375 {
379 global $ilDB;
380
381 $joins = array();
382
383 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
384 $joins[] = 'LEFT JOIN usr_pref profpref
385 ON profpref.usr_id = ud.usr_id
386 AND profpref.keyword = ' . $ilDB->quote('public_profile', 'text');
387
388 $joins[] = 'LEFT JOIN usr_pref pubemail
389 ON pubemail.usr_id = ud.usr_id
390 AND pubemail.keyword = ' . $ilDB->quote('public_email', 'text');
391
392 $joins[] = 'LEFT JOIN usr_pref pubsecondemail
393 ON pubsecondemail.usr_id = ud.usr_id
394 AND pubsecondemail.keyword = ' . $ilDB->quote('public_second_email', 'text');
395 }
396
397 if ($joins) {
398 return 'usr_data ud ' . implode(' ', $joins);
399 } else {
400 return 'usr_data ud';
401 }
402 }
403
408 protected function getWherePart(array $search_query)
409 {
414 global $ilDB, $ilSetting;
415
416 $outer_conditions = array();
417
418 // In 'anonymous' context with respected user privacy, only users with globally published profiles should be found.
419 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode() &&
420 $this->getUser() instanceof ilObjUser &&
421 $this->getUser()->isAnonymous()
422 ) {
423 if (!$ilSetting->get('enable_global_profiles', 0)) {
424 // If 'Enable User Content Publishing' is not set in the administration, no user should be found for 'anonymous' context.
425 return '1 = 2';
426 } else {
427 // Otherwise respect the profile activation setting of every user (as a global (outer) condition in the where clause).
428 $outer_conditions[] = 'profpref.value = ' . $ilDB->quote('g', 'text');
429 }
430 }
431
432 $outer_conditions[] = 'ud.usr_id != ' . $ilDB->quote(ANONYMOUS_USER_ID, 'integer');
433
434 $field_conditions = array();
435 foreach ($this->getFields() as $field) {
436 $field_condition = $this->getQueryConditionByFieldAndValue($field, $search_query);
437
438 if ('email' == $field && self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
439 // If privacy should be respected, the profile setting of every user concerning the email address has to be
440 // respected (in every user context, no matter if the user is 'logged in' or 'anonymous').
441 $email_query = array();
442 $email_query[] = $field_condition;
443 $email_query[] = 'pubemail.value = ' . $ilDB->quote('y', 'text');
444 $field_conditions[] = '(' . implode(' AND ', $email_query) . ')';
445 } elseif ('second_email' == $field && self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode()) {
446 // If privacy should be respected, the profile setting of every user concerning the email address has to be
447 // respected (in every user context, no matter if the user is 'logged in' or 'anonymous').
448 $email_query = array();
449 $email_query[] = $field_condition;
450 $email_query[] = 'pubsecondemail.value = ' . $ilDB->quote('y', 'text');
451 $field_conditions[] = '(' . implode(' AND ', $email_query) . ')';
452 } else {
453 $field_conditions[] = $field_condition;
454 }
455 }
456
457 // If the current user context ist 'logged in' and privacy should be respected, all fields >>>except the login<<<
458 // should only be searchable if the users' profile is published (y oder g)
459 // In 'anonymous' context we do not need this additional conditions,
460 // because we checked the privacy setting in the condition above: profile = 'g'
461 if (self::PRIVACY_MODE_RESPECT_USER_SETTING == $this->getPrivacyMode() &&
462 $this->getUser() instanceof ilObjUser && !$this->getUser()->isAnonymous() &&
463 $field_conditions
464 ) {
465 $fields = '(' . implode(' OR ', $field_conditions) . ')';
466
467 $field_conditions = [
468 '(' . implode(' AND ', array(
469 $fields,
470 $ilDB->in('profpref.value', array('y', 'g'), false, 'text')
471 )) . ')'
472 ];
473 }
474
475 // The login field must be searchable regardless (for 'logged in' users) of any privacy settings.
476 // We handled the general condition for 'anonymous' context above: profile = 'g'
477 $field_conditions[] = $this->getQueryConditionByFieldAndValue('login', $search_query);
478
479 include_once 'Services/User/classes/class.ilUserAccountSettings.php';
480 if (ilUserAccountSettings::getInstance()->isUserAccessRestricted()) {
481 include_once './Services/User/classes/class.ilUserFilter.php';
482 $outer_conditions[] = $ilDB->in('time_limit_owner', ilUserFilter::getInstance()->getFolderIds(), false, 'integer');
483 }
484
485 if ($field_conditions) {
486 $outer_conditions[] = '(' . implode(' OR ', $field_conditions) . ')';
487 }
488
489 include_once './Services/Search/classes/class.ilSearchSettings.php';
490 $settings = ilSearchSettings::getInstance();
491
492 if (!$settings->isInactiveUserVisible() && $this->getUserLimitations()) {
493 $outer_conditions[] = "ud.active = " . $ilDB->quote(1, 'integer');
494 }
495
496 if (!$settings->isLimitedUserVisible() && $this->getUserLimitations()) {
497 $unlimited = "ud.time_limit_unlimited = " . $ilDB->quote(1, 'integer');
498 $from = "ud.time_limit_from < " . $ilDB->quote(time(), 'integer');
499 $until = "ud.time_limit_until > " . $ilDB->quote(time(), 'integer');
500
501 $outer_conditions[] = '(' . $unlimited . ' OR (' . $from . ' AND ' . $until . '))';
502 }
503
504 return implode(' AND ', $outer_conditions);
505 }
506
510 protected function getOrderByPart()
511 {
512 return 'login ASC';
513 }
514
520 protected function getQueryConditionByFieldAndValue($field, $query)
521 {
525 global $ilDB;
526
527 $query_strings = array($query['query']);
528
529 if (array_key_exists($field, $query)) {
530 $query_strings = array($query[$field]);
531 } elseif (array_key_exists('parts', $query)) {
532 $query_strings = $query['parts'];
533 }
534
535 $query_condition = '( ';
536 $num = 0;
537 foreach ($query_strings as $query_string) {
538 if ($num++ > 0) {
539 $query_condition .= ' OR ';
540 }
541 if (self::SEARCH_TYPE_LIKE == $this->getSearchType()) {
542 $query_condition .= $ilDB->like($field, 'text', $query_string . '%');
543 } else {
544 $query_condition .= $ilDB->like($field, 'text', $query_string);
545 }
546 }
547 $query_condition .= ')';
548 return $query_condition;
549 }
550
556 public function setUserLimitations($a_limitations)
557 {
558 $this->user_limitations = (bool) $a_limitations;
559 }
560
565 public function getUserLimitations()
566 {
568 }
569
573 public function isMoreLinkAvailable()
574 {
576 }
577
584 {
585 $this->more_link_available = $more_link_available;
586 }
587
593 public function parseQueryString($a_query)
594 {
595 $query = array();
596
597 if (!stristr($a_query, '\\')) {
598 $a_query = str_replace('%', '\%', $a_query);
599 $a_query = str_replace('_', '\_', $a_query);
600 }
601
602 $query['query'] = trim($a_query);
603
604 // "," means fixed search for lastname, firstname
605 if (strpos($a_query, ',')) {
606 $comma_separated = (array) explode(',', $a_query);
607
608 if (count($comma_separated) == 2) {
609 if (trim($comma_separated[0])) {
610 $query['lastname'] = trim($comma_separated[0]);
611 }
612 if (trim($comma_separated[1])) {
613 $query['firstname'] = trim($comma_separated[1]);
614 }
615 }
616 } else {
617 $whitespace_separated = (array) explode(' ', $a_query);
618 foreach ($whitespace_separated as $part) {
619 if (trim($part)) {
620 $query['parts'][] = trim($part);
621 }
622 }
623 }
624
625 $this->logger->dump($query, ilLogLevel::DEBUG);
626
627 return $query;
628 }
629}
$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.
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