ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
DatabaseDataRepository.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21namespace ILIAS\User\Profile;
22
28use ILIAS\User\Profile\Fields\ConfigurationRepository as ProfileFieldsConfigurationRepository;
31use ILIAS\User\Settings\DataRepository as SettingsDataRepository;
32use ILIAS\ResourceStorage\Services as ResourceStorage;
33
35{
36 private const string USER_BASE_TABLE = 'usr_data';
37 public const string USER_VALUES_TABLE = 'usr_profile_data';
38
39 private const string NO_AVATAR_RID = '-';
40
41 private const array SEARCH_FIELDS = [
42 'login' => true,
43 'firstname' => false,
44 'lastname' => false,
45 'email' => false,
46 'second_email' => false
47 ];
48
49 public function __construct(
50 private readonly \ilDBInterface $db,
51 private readonly ResourceStorage $irss
52 ) {
53 }
54
55 public function getDefault(): Data
56 {
57 return new Data();
58 }
59
60 public function getSingle(int $id): Data
61 {
62 $base_query = $this->db->query(
63 'SELECT * FROM ' . self::USER_BASE_TABLE . " WHERE usr_id={$id}"
64 );
65
66 $additional_query = $this->db->query(
67 'SELECT field_id, value FROM ' . self::USER_VALUES_TABLE . " WHERE usr_id = {$id}"
68 );
69
70 $base_data = $this->db->fetchObject($base_query);
71 if ($base_data === null) {
72 throw \InvalidArgumentException(
73 'This user does not exist.'
74 );
75 }
76
77 return $this->buildFromData(
78 $base_data,
79 $this->db->fetchAll(
80 $additional_query,
82 )
83 );
84
85 }
86
87 public function getMultiple(array $user_ids): \Generator
88 {
89 $query = $this->db->query(
90 'SELECT * FROM ' . self::USER_BASE_TABLE
91 . " WHERE {$this->db->in('usr_id', $user_ids, false, \ilDBConstants::T_INTEGER)}"
92 );
93
94 $prepared_query = $this->db->prepare('SELECT field_id, value FROM '
95 . self::USER_VALUES_TABLE . ' WHERE usr_id = ?');
96
97 while (($base_data = $this->db->fetchObject($query)) !== null) {
98 yield $this->buildFromData(
99 $base_data,
100 $this->db->fetchAll(
101 $this->db->execute($prepared_query, [$base_data->usr_id]),
103 )
104 );
105 }
106
107 $this->db->free($prepared_query);
108 }
109
110 public function store(Data $user_data): void
111 {
112 $system_information = $user_data->getSystemInformation();
113 $this->db->replace(
114 self::USER_BASE_TABLE,
115 [
116 'usr_id' => [
118 $user_data->getId()
119 ]
120 ],
121 [
122 'login' => [\ilDBConstants::T_TEXT, $user_data->getAlias()],
123 'firstname' => [\ilDBConstants::T_TEXT, $user_data->getFirstname()],
124 'lastname' => [\ilDBConstants::T_TEXT, $user_data->getLastname()],
125 'title' => [\ilDBConstants::T_TEXT, $user_data->getTitle()],
126 'gender' => [\ilDBConstants::T_TEXT, $user_data->getGender()?->value],
127 'rid' => [\ilDBConstants::T_TEXT, $user_data->getAvatarRid()?->serialize() ?? self::NO_AVATAR_RID],
128 'email' => [\ilDBConstants::T_TEXT, $user_data->getEmail()],
129 'second_email' => [\ilDBConstants::T_TEXT, $user_data->getSecondEmail()],
130 'hobby' => [\ilDBConstants::T_TEXT, $user_data->getHobby()],
131 'institution' => [\ilDBConstants::T_TEXT, $user_data->getInstitution()],
132 'department' => [\ilDBConstants::T_TEXT, $user_data->getDepartment()],
133 'street' => [\ilDBConstants::T_TEXT, $user_data->getStreet()],
134 'city' => [\ilDBConstants::T_TEXT, $user_data->getCity()],
135 'zipcode' => [\ilDBConstants::T_TEXT, $user_data->getZipcode()],
136 'country' => [\ilDBConstants::T_TEXT, $user_data->getCountry()],
137 'phone_office' => [\ilDBConstants::T_TEXT, $user_data->getPhoneOffice()],
138 'phone_home' => [\ilDBConstants::T_TEXT, $user_data->getPhoneHome()],
139 'phone_mobile' => [\ilDBConstants::T_TEXT, $user_data->getPhoneMobile()],
140 'fax' => [\ilDBConstants::T_TEXT, $user_data->getFax()],
141 'birthday' => [\ilDBConstants::T_DATE, $user_data->getBirthday()?->format('Y-m-d')],
142 'referral_comment' => [\ilDBConstants::T_TEXT, $user_data->getReferralComment()],
143 'matriculation' => [\ilDBConstants::T_TEXT, $user_data->getMatriculation()],
144 'latitude' => [\ilDBConstants::T_TEXT, $user_data->getGeoCoordinates()['latitude'] ?? null],
145 'longitude' => [\ilDBConstants::T_TEXT, $user_data->getGeoCoordinates()['longitude'] ?? null],
146 'loc_zoom' => [\ilDBConstants::T_INTEGER, $user_data->getGeoCoordinates()['zoom'] ?? 0],
147 'last_password_change' => [\ilDBConstants::T_INTEGER, $system_information['last_password_change']],
148 'passwd' => [\ilDBConstants::T_TEXT, $system_information['passwd']],
149 'passwd_salt' => [\ilDBConstants::T_TEXT, $system_information['passwd_salt']],
150 'passwd_enc_type' => [\ilDBConstants::T_TEXT, $system_information['passwd_enc_type']],
151 'passwd_policy_reset' => [\ilDBConstants::T_INTEGER, $system_information['passwd_policy_reset'] ? 1 : 0],
152 'client_ip' => [\ilDBConstants::T_TEXT, $system_information['client_ip']],
153 'last_login' => [
155 $system_information['last_login'] !== '' ? $system_information['last_login'] : null
156 ],
157 'first_login' => [
159 $system_information['first_login'] !== '' ? $system_information['first_login'] : null
160 ],
161 'last_profile_prompt' => [
163 $system_information['last_profile_prompt'] !== '' ? $system_information['last_profile_prompt'] : null
164 ],
165 'active' => [\ilDBConstants::T_INTEGER, $system_information['active']],
166 'approve_date' => [\ilDBConstants::T_TIMESTAMP, $system_information['approve_date']],
167 'agree_date' => [\ilDBConstants::T_TIMESTAMP, $system_information['agree_date']],
168 'inactivation_date' => [\ilDBConstants::T_TIMESTAMP, $system_information['inactivation_date']],
169 'time_limit_owner' => [\ilDBConstants::T_INTEGER, $system_information['time_limit_owner']],
170 'time_limit_unlimited' => [\ilDBConstants::T_INTEGER, $system_information['time_limit_unlimited'] ? 1 : 0],
171 'time_limit_from' => [\ilDBConstants::T_INTEGER, $system_information['time_limit_from']],
172 'time_limit_until' => [\ilDBConstants::T_INTEGER, $system_information['time_limit_until']],
173 'profile_incomplete' => [\ilDBConstants::T_INTEGER, $system_information['profile_incomplete']],
174 'auth_mode' => [\ilDBConstants::T_TEXT, $system_information['auth_mode']],
175 'ext_account' => [\ilDBConstants::T_TEXT, $system_information['ext_account']],
176 'is_self_registered' => [\ilDBConstants::T_INTEGER, $system_information['is_self_registered'] ? 1 : 0],
177 'last_update' => [\ilDBConstants::T_TIMESTAMP, date('Y-m-d H:i:s')],
178 'create_date' => [\ilDBConstants::T_TIMESTAMP, $system_information['create_date']],
179 'last_visited' => [
181 $system_information['last_visited'] === [] ? null : serialize($system_information['last_visited'])
182 ]
183 ]
184 );
185
186 $this->storeAdditionalFields($user_data);
187 }
188
189 public function deleteForFieldIdentifier(string $identifier): void
190 {
191 $this->db->manipulateF(
192 'DELETE FROM ' . self::USER_VALUES_TABLE
193 . " WHERE field_id='{$this->db->quote($identifier, \ilDBConstants::T_TEXT)}'"
194 );
195 }
196
197 public function deleteForUser(int $usr_id): void
198 {
199 $this->db->manipulate(
200 'DELETE FROM ' . self::USER_BASE_TABLE
201 . " WHERE usr_id='{$this->db->quote($usr_id, \ilDBConstants::T_INTEGER)}'"
202 );
203 $this->db->manipulate(
204 'DELETE FROM ' . self::USER_VALUES_TABLE
205 . " WHERE usr_id='{$this->db->quote($usr_id, \ilDBConstants::T_INTEGER)}'"
206 );
207 }
208
209 public function storePasswordFor(
210 int $usr_id,
211 string $password,
212 string $encoding_type,
213 ?string $salt
214 ): void {
215 $this->db->manipulateF(
216 'UPDATE ' . self::USER_BASE_TABLE . ' SET passwd = %s,' . PHP_EOL
217 . 'passwd_enc_type = %s, passwd_salt = %s WHERE usr_id = %s',
218 [\ilDBConstants::T_TEXT, \ilDBConstants::T_TEXT, \ilDBConstants::T_TEXT, \ilDBConstants::T_INTEGER],
219 [$password, $encoding_type, $salt, $usr_id]
220 );
221 }
222
223 public function storeLoginFor(
224 int $usr_id,
225 string $login
226 ): void {
227 $this->db->manipulateF(
228 'UPDATE ' . self::USER_BASE_TABLE . ' SET login = %s WHERE usr_id = %s',
229 [\ilDBConstants::T_TEXT, \ilDBConstants::T_INTEGER],
230 [$login, $usr_id]
231 );
232 }
233
234 public function storeLastVisitedFor(
235 int $usr_id,
236 array $last_visited
237 ): void {
238 $this->db->manipulateF(
239 'UPDATE ' . self::USER_BASE_TABLE . ' SET last_visited = %s WHERE usr_id = %s',
240 [\ilDBConstants::T_TEXT, \ilDBConstants::T_INTEGER],
241 [
242 $last_visited === [] ? null : serialize($last_visited),
243 $usr_id
244 ]
245 );
246 }
247
251 public function searchUsers(
252 SettingsDataRepository $settings_data_repository,
253 ProfileFieldsConfigurationRepository $profile_fields_config_repo,
254 AutocompleteQuery $autocomplete_query
255 ): array {
256 $where = $this->buildSearchUsersWhereString(
257 $profile_fields_config_repo,
258 $autocomplete_query
259 );
260
261 if ($where === null) {
262 return [];
263 }
264
265 $query = $this->db->query(
266 $settings_data_repository->getSearchSelectConditionalOnVisibility(
267 self::USER_BASE_TABLE,
268 ...array_keys(self::SEARCH_FIELDS)
269 ) . PHP_EOL
270 . $where
271 );
272
273 $results = [];
274 while (($row = $this->db->fetchObject($query)) !== null) {
276 $row->login,
277 $row->lastname ?? '',
278 $row->firstname ?? '',
279 $autocomplete_query->getUnprocessedSearchTerm()
280 );
281 }
282 return $results;
283 }
284
285 public function getProfileDataQuery(
286 array $select_fields
287 ): DataQuery {
288 return new DataQuery(
289 $this->db,
290 self::USER_BASE_TABLE,
291 self::USER_VALUES_TABLE,
292 $select_fields
293 );
294 }
295
300 DataQuery $query,
301 int $offset,
302 int $limit
303 ): array {
304 $prepared_query = $query->withAdditionalSelectAndJoinForUdfAndMultiValueFields();
305 $cnt = $this->db->fetchObject(
306 $this->db->query($prepared_query->buildCntQueryString())
307 )->cnt ?? 0;
308
309 if ($offset >= $cnt) {
310 $offset = 0;
311 }
312
313 $this->db->setLimit($limit, $offset);
314
315 return [
316 'cnt' => $cnt,
317 'set' => $this->retrieveRecordsFromQuery($prepared_query)
318 ];
319 }
320
321 private function buildFromData(
322 \stdClass $base_data,
323 array $additional_data
324 ): Data {
325 return (new Data(
326 $base_data->usr_id,
327 $base_data->login,
328 $base_data->rid !== null && $base_data->rid !== self::NO_AVATAR_RID
329 ? $this->irss->manage()->find($base_data->rid)
330 : null,
331 $base_data->firstname ?? '',
332 $base_data->lastname ?? '',
333 $base_data->title ?? '',
334 Genders::tryFrom($base_data->gender ?? ''),
335 $base_data->birthday !== null
336 ? new \DateTimeImmutable($base_data->birthday, new \DateTimeZone('UTC'))
337 : null,
338 $base_data->institution ?? '',
339 $base_data->department ?? '',
340 $base_data->street ?? '',
341 $base_data->city ?? '',
342 $base_data->zipcode ?? '',
343 $base_data->country ?? '',
344 $base_data->email ?? '',
345 $base_data->second_email,
346 $base_data->phone_office ?? '',
347 $base_data->phone_home ?? '',
348 $base_data->phone_mobile ?? '',
349 $base_data->fax ?? '',
350 $base_data->matriculation ?? '',
351 $base_data->hobby ?? '',
352 $base_data->referral_comment ?? '',
353 [
354 'latitude' => $base_data->latitude,
355 'longitude' => $base_data->longitude,
356 'zoom' => $base_data->loc_zoom
357 ],
358 array_reduce(
359 $additional_data,
360 static function (array $c, \stdClass $v): array {
361 if (!array_key_exists($v->field_id, $c)) {
362 $c[$v->field_id] = [];
363 }
364 $c[$v->field_id][] = $v->value;
365 return $c;
366 },
367 []
368 )
369 ))->withSystemInformation([
370 'last_password_change' => $base_data->last_password_change,
371 'login_attempts' => $base_data->login_attempts,
372 'passwd' => $base_data->passwd,
373 'passwd_salt' => $base_data->passwd_salt,
374 'passwd_enc_type' => $base_data->passwd_enc_type,
375 'passwd_policy_reset' => $base_data->passwd_policy_reset === 1,
376 'client_ip' => $base_data->client_ip ?? '',
377 'last_login' => $base_data->last_login ?? '',
378 'first_login' => $base_data->first_login ?? '',
379 'last_profile_prompt' => $base_data->last_profile_prompt ?? '',
380 'active' => $base_data->active,
381 'approve_date' => $base_data->approve_date,
382 'agree_date' => $base_data->agree_date,
383 'inactivation_date' => $base_data->inactivation_date,
384 'time_limit_owner' => $base_data->time_limit_owner,
385 'time_limit_unlimited' => $base_data->time_limit_unlimited === 1,
386 'time_limit_from' => $base_data->time_limit_from,
387 'time_limit_until' => $base_data->time_limit_until,
388 'profile_incomplete' => $base_data->profile_incomplete === 1,
389 'auth_mode' => $base_data->auth_mode,
390 'ext_account' => $base_data->ext_account,
391 'is_self_registered' => $base_data->is_self_registered === 1,
392 'last_update' => $base_data->last_update ?? '',
393 'create_date' => $base_data->create_date ?? '',
394 'last_visited' => $this->buildLastVisited($base_data->last_visited)
395 ]);
396 }
397
398 private function storeAdditionalFields(Data $user_data): void
399 {
400 $this->db->manipulate(
401 'DELETE FROM ' . self::USER_VALUES_TABLE
402 . " WHERE usr_id = {$user_data->getId()}"
403 );
404
405 $values_for_storage = $user_data->getAdditionalFieldsStorageValues($this->db);
406 if ($values_for_storage === '') {
407 return;
408 }
409
410 $this->db->manipulate(
411 'INSERT INTO ' . self::USER_VALUES_TABLE . ' (usr_id, field_id, value) '
412 . 'VALUES ' . $values_for_storage
413 );
414 }
415
417 ProfileFieldsConfigurationRepository $profile_fields_config_repo,
418 AutocompleteQuery $autocomplete_query
419 ): ?string {
420 $available_fields = array_filter(
421 $this->getSearchFieldsWithAvailability(
422 $profile_fields_config_repo,
423 $autocomplete_query
424 )
425 );
426
427 if ($available_fields === []) {
428 return null;
429 }
430
431 $outer_conditions = [];
432 $outer_conditions[] = 'usr_data.usr_id != ' . $this->db->quote(ANONYMOUS_USER_ID, \ilDBConstants::T_INTEGER);
433 $outer_conditions[] = 'usr_data.active != ' . $this->db->quote(0, \ilDBConstants::T_INTEGER);
434
435 if (\ilUserAccountSettings::getInstance()->isUserAccessRestricted()) {
436 $outer_conditions[] = $this->db->in(
437 'time_limit_owner',
438 \ilUserFilter::getInstance()->getFolderIds(),
439 false,
440 'integer'
441 );
442 }
443
444 $outer_conditions[] = '(' . implode(
445 ' OR ',
446 array_map(
447 fn(string $v) => $this->db->like($v, \ilDBConstants::T_TEXT, "%{$available_fields[$v]}%"),
448 array_keys($available_fields)
449 )
450 ) . ')';
451
452 return ' WHERE ' . implode(' AND ', $outer_conditions);
453 }
454
456 ProfileFieldsConfigurationRepository $profile_fields_config_repo,
457 AutocompleteQuery $autocomplete_query
458 ): array {
459 $search_term = $autocomplete_query->getSearchTermQueryString();
460 $search_term_long_enough = $autocomplete_query->checkSearchTermLength($search_term);
461 $firstname_term = $autocomplete_query->getFirstnameQueryString();
462 $lastname_term = $autocomplete_query->getLastnameQueryString();
463
464 return array_merge(
465 self::SEARCH_FIELDS,
466 [
467 'login' => $search_term_long_enough ? $search_term : null,
468 'firstname' => $profile_fields_config_repo->getByClass(FirstName::class)->isSearchable()
469 && $autocomplete_query->checkSearchTermLength($firstname_term)
470 ? $firstname_term : null,
471 'lastname' => $profile_fields_config_repo->getByClass(LastName::class)->isSearchable()
472 && $autocomplete_query->checkSearchTermLength($lastname_term)
473 ? $autocomplete_query->getLastnameQueryString() : null,
474 'email' => $profile_fields_config_repo->getByClass(Email::class)->isSearchable()
475 && $search_term_long_enough
476 ? $autocomplete_query->getSearchTermQueryString() : null,
477 'second_email' => $profile_fields_config_repo->getByClass(SecondEmail::class)->isSearchable()
478 && $search_term_long_enough
479 ? $autocomplete_query->getSearchTermQueryString() : null
480 ]
481 );
482 }
483
484 private function buildLastVisited(?string $last_visited): array
485 {
486 if ($last_visited === null) {
487 return [];
488 }
489
490 $unserialized = unserialize($last_visited, ['allowed_classes' => false]);
491
492 if (!is_array($unserialized)) {
493 return [];
494 }
495
496 return $unserialized;
497 }
498
499 private function retrieveRecordsFromQuery(DataQuery $query): array
500 {
501 $statement = $this->db->query($query->buildRecordsQueryString());
502
503 $result = [];
504 while (($row = $this->db->fetchAssoc($statement)) !== null) {
505 $row['usr_id'] = (int) $row['usr_id'];
506 $result[] = $query->explodeArrayValues($row);
507 }
508 return $result;
509 }
510}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
getAdditionalFieldsStorageValues(\ilDBInterface $db)
Definition: Data.php:359
searchUsers(SettingsDataRepository $settings_data_repository, ProfileFieldsConfigurationRepository $profile_fields_config_repo, AutocompleteQuery $autocomplete_query)
__construct(private readonly \ilDBInterface $db, private readonly ResourceStorage $irss)
getSearchFieldsWithAvailability(ProfileFieldsConfigurationRepository $profile_fields_config_repo, AutocompleteQuery $autocomplete_query)
buildSearchUsersWhereString(ProfileFieldsConfigurationRepository $profile_fields_config_repo, AutocompleteQuery $autocomplete_query)
storeLastVisitedFor(int $usr_id, array $last_visited)
getCountAndRecordsForQuery(DataQuery $query, int $offset, int $limit)
buildFromData(\stdClass $base_data, array $additional_data)
storePasswordFor(int $usr_id, string $password, string $encoding_type, ?string $salt)
This class provides some pre-processing for search terms provided by a user when searching for users.
getUnprocessedSearchTerm()
The returned search term might contain wild cards or any other input.
checkSearchTermLength(?string $search_term)
Class ilDBConstants.
const FETCHMODE_OBJECT
const ANONYMOUS_USER_ID
Definition: constants.php:27
$c
Definition: deliver.php:25
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$results
if(!file_exists('../ilias.ini.php'))