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 
15  const SEARCH_TYPE_LIKE = 1;
16 
20  const SEARCH_TYPE_EQUALS = 2;
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 
55  private $result_field;
56 
60  private $search_type;
61 
65  private $privacy_mode;
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 
132  public function setSearchType($search_type)
133  {
134  $this->search_type = $search_type;
135  }
136 
140  public function getSearchType()
141  {
142  return $this->search_type;
143  }
144 
148  public function setPrivacyMode($privacy_mode)
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  {
229  return $this->possible_fields;
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';
244  if (ilUserSearchOptions::_isEnabled($field)) {
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 }
respectMinimumSearchCharacterCount($a_status)
isFieldSearchableCheckEnabled()
Searchable check enabled.
Add rich text string
static strLen($a_string)
Definition: class.ilStr.php:78
$result
global $DIC
Definition: saml.php:7
setUserLimitations($a_limitations)
allow user limitations like inactive and access limitations
setMoreLinkAvailable($more_link_available)
IMPORTANT: remember to read request parameter &#39;fetchall&#39; to use this function.
$from
static getInstance()
Singelton get instance.
user()
Definition: user.php:4
Auto completion class for user lists.
const MIN_WORD_LENGTH
Minimum of characters required for search.
getFields()
Get searchable fields.
static encode($mixed, $suppress_native=false)
enableUserAccessCheck($a_status)
Enable user access check.
foreach($_POST as $key=> $value) $res
getSearchFields()
get possible search fields
getUserLimitations()
allow user limitations like inactive and access limitations
$query
Create styles array
The data for the language used.
parseQueryString($a_query)
Parse query string.
setResultField($a_field)
Set result field.
isUserAccessCheckEnabled()
Check if user access check is enabled.
enableFieldSearchableCheck($a_status)
Enable the check whether the field is searchable in Administration -> Settings -> Standard Fields...
__construct()
Default constructor.
global $ilSetting
Definition: privfeed.php:17
static getInstance()
Singelton get instance.
global $ilDB
setSearchFields($a_fields)
Set searchable fields.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.