ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilCronDeleteInactiveUserAccounts.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
26
34{
35 private const DEFAULT_INACTIVITY_PERIOD = 365;
36 private const DEFAULT_REMINDER_PERIOD = 0;
37
38 private const ACTION_USER_NONE = 0;
40 private const ACTION_USER_DELETED = 2;
41
42 private int $delete_period;
43 private int $reminder_period;
45 private array $include_roles;
48 private Language $lng;
52 private \ILIAS\HTTP\GlobalHttpState $http;
53 private \ILIAS\Refinery\Factory $refinery;
55 private \ilGlobalTemplateInterface $main_tpl;
56
57 public function __construct()
58 {
60 global $DIC;
61
62 if (isset($DIC['ilDB'])) {
63 $this->cron_delete_reminder_mail = new ilCronDeleteInactiveUserReminderMail($DIC['ilDB']);
64 }
65
66 if (isset($DIC['tpl'])) {
67 $this->main_tpl = $DIC['tpl'];
68 }
69 if (isset($DIC['http'])) {
70 $this->http = $DIC['http'];
71 }
72
73 if (isset($DIC['lng'])) {
74 $this->lng = $DIC['lng'];
75 }
76
77 if (isset($DIC['ilLog'])) {
78 $this->log = $DIC['ilLog'];
79 }
80
81 if (isset($DIC['refinery'])) {
82 $this->refinery = $DIC['refinery'];
83 }
84
85 if (isset($DIC['ilObjDataCache'])) {
86 $this->objectDataCache = $DIC['ilObjDataCache'];
87 }
88
89 if (isset($DIC['rbacreview'])) {
90 $this->rbac_review = $DIC['rbacreview'];
91 }
92
93 if (isset($DIC['cron.repository'])) {
94 $this->cronRepository = $DIC['cron.repository'];
95 }
96
97 if (isset($DIC['ilSetting'])) {
98 $this->settings = $DIC['ilSetting'];
99 $this->loadSettings();
100 }
101 }
102
103 private function loadSettings(): void
104 {
105 $include_roles = $this->settings->get(
106 'cron_inactive_user_delete_include_roles',
107 null
108 );
109 if ($include_roles === null) {
110 $this->include_roles = [];
111 } else {
112 $this->include_roles = array_filter(array_map('intval', explode(',', $include_roles)));
113 }
114
115 $this->delete_period = (int) $this->settings->get(
116 'cron_inactive_user_delete_period',
118 );
119 $this->reminder_period = (int) $this->settings->get(
120 'cron_inactive_user_reminder_period',
122 );
123 }
124
128 protected function isDecimal($number): bool
129 {
130 $number_as_string = (string) $number;
131
132 return strpos($number_as_string, ',') || strpos($number_as_string, '.');
133 }
134
135 protected function getTimeDifferenceBySchedule(JobScheduleType $schedule_time, int $multiplier): int
136 {
137 $time_difference = 0;
138
139 switch ($schedule_time) {
140 case JobScheduleType::DAILY:
141 $time_difference = 86400;
142 break;
143 case JobScheduleType::IN_MINUTES:
144 $time_difference = 60 * $multiplier;
145 break;
146 case JobScheduleType::IN_HOURS:
147 $time_difference = 3600 * $multiplier;
148 break;
149 case JobScheduleType::IN_DAYS:
150 $time_difference = 86400 * $multiplier;
151 break;
152 case JobScheduleType::WEEKLY:
153 $time_difference = 604800;
154 break;
155 case JobScheduleType::MONTHLY:
156 $time_difference = 2629743;
157 break;
158 case JobScheduleType::QUARTERLY:
159 $time_difference = 7889229;
160 break;
162 $time_difference = 31556926;
163 break;
164 }
165
166 return $time_difference;
167 }
168
169 public function getId(): string
170 {
171 return "user_inactive";
172 }
173
174 public function getTitle(): string
175 {
176 return $this->lng->txt("delete_inactive_user_accounts");
177 }
178
179 public function getDescription(): string
180 {
181 return $this->lng->txt("delete_inactive_user_accounts_desc");
182 }
183
185 {
186 return JobScheduleType::DAILY;
187 }
188
189 public function getDefaultScheduleValue(): ?int
190 {
191 return null;
192 }
193
194 public function hasAutoActivation(): bool
195 {
196 return false;
197 }
198
199 public function hasFlexibleSchedule(): bool
200 {
201 return true;
202 }
203
204 public function hasCustomSettings(): bool
205 {
206 return true;
207 }
208
209 public function run(): JobResult
210 {
211 $status = JobResult::STATUS_NO_ACTION;
212 $check_mail = $this->delete_period - $this->reminder_period;
213 $usr_ids = ilObjUser::getUserIdsByInactivityPeriod($check_mail);
214 $counters = [
215 self::ACTION_USER_NONE => 0,
216 self::ACTION_USER_REMINDER_MAIL_SENT => 0,
217 self::ACTION_USER_DELETED => 0
218 ];
219 foreach ($usr_ids as $usr_id) {
220 if ($usr_id === ANONYMOUS_USER_ID || $usr_id === SYSTEM_USER_ID) {
221 continue;
222 }
223
224 foreach ($this->include_roles as $role_id) {
225 if ($this->rbac_review->isAssigned($usr_id, $role_id)) {
226 $action_taken = $this->deleteUserOrSendReminderMail($usr_id);
227 $counters[$action_taken]++;
228 break;
229 }
230 }
231 }
232
233 if ($counters[self::ACTION_USER_REMINDER_MAIL_SENT] > 0
234 || $counters[self::ACTION_USER_DELETED] > 0) {
235 $status = JobResult::STATUS_OK;
236 }
237
238 $this->cron_delete_reminder_mail->removeEntriesFromTableIfLastLoginIsNewer();
239 $this->log->write(
240 'CRON - ilCronDeleteInactiveUserAccounts::run(), deleted '
241 . "=> {$counters[self::ACTION_USER_DELETED]} User(s), sent reminder "
242 . "mail to {$counters[self::ACTION_USER_REMINDER_MAIL_SENT]} User(s)"
243 );
244
245 $result = new JobResult();
246 $result->setStatus($status);
247
248 return $result;
249 }
250
251 private function deleteUserOrSendReminderMail($usr_id): int
252 {
254 $timestamp_last_login = strtotime($user->getLastLogin());
255 $grace_period_over = time() - ($this->delete_period * 24 * 60 * 60);
256
257 if ($timestamp_last_login < $grace_period_over) {
258 $user->delete();
260 }
261
262 if ($this->reminder_period > 0) {
263 $timestamp_for_deletion = $timestamp_last_login - $grace_period_over;
264 $account_will_be_deleted_on = $this->calculateDeletionData($timestamp_for_deletion);
265 if (
266 $this->cron_delete_reminder_mail->sendReminderMailIfNeeded(
267 $user,
268 $this->reminder_period,
269 $account_will_be_deleted_on
270 )
271 ) {
273 }
274 }
275
277 }
278
279 protected function calculateDeletionData(int $date_for_deletion): int
280 {
281 $cron_timing = $this->cronRepository->getCronJobData($this->getId());
282 $time_difference = 0;
283 $multiplier = 1;
284
285 if (!is_array($cron_timing) || !isset($cron_timing[0]) || !is_array($cron_timing[0])) {
286 return time() + $date_for_deletion + $time_difference;
287 }
288
289 if (array_key_exists('schedule_type', $cron_timing[0])) {
290 if ($cron_timing[0]['schedule_value'] !== null) {
291 $multiplier = (int) $cron_timing[0]['schedule_value'];
292 }
293 $time_difference = $this->getTimeDifferenceBySchedule(
294 JobScheduleType::from((int) $cron_timing[0]['schedule_type']),
295 $multiplier
296 );
297 }
298 return time() + $date_for_deletion + $time_difference;
299 }
300
301 public function addCustomSettingsToForm(ilPropertyFormGUI $a_form): void
302 {
303 $this->lng->loadLanguageModule("user");
304
305 $schedule = $a_form->getItemByPostVar('type');
306 $schedule->setTitle($this->lng->txt('delete_inactive_user_accounts_frequency'));
307 $schedule->setInfo($this->lng->txt('delete_inactive_user_accounts_frequency_desc'));
308
309 $sub_mlist = new ilMultiSelectInputGUI(
310 $this->lng->txt('delete_inactive_user_accounts_include_roles'),
311 'cron_inactive_user_delete_include_roles'
312 );
313 $sub_mlist->setInfo($this->lng->txt('delete_inactive_user_accounts_include_roles_desc'));
314 $roles = [];
315 foreach ($this->rbac_review->getGlobalRoles() as $role_id) {
316 if ($role_id !== ANONYMOUS_ROLE_ID) {
317 $roles[$role_id] = $this->objectDataCache->lookupTitle($role_id);
318 }
319 }
320 $sub_mlist->setOptions($roles);
321 $setting = $this->settings->get('cron_inactive_user_delete_include_roles', null);
322 if ($setting === null) {
323 $setting = [];
324 } else {
325 $setting = explode(',', $setting);
326 }
327 $sub_mlist->setValue($setting);
328 $sub_mlist->setWidth(300);
329 $a_form->addItem($sub_mlist);
330
331 $default_setting = (string) self::DEFAULT_INACTIVITY_PERIOD;
332
333 $sub_text = new ilNumberInputGUI(
334 $this->lng->txt('delete_inactive_user_accounts_period'),
335 'cron_inactive_user_delete_period'
336 );
337 $sub_text->allowDecimals(false);
338 $sub_text->setInfo($this->lng->txt('delete_inactive_user_accounts_period_desc'));
339 $sub_text->setValue($this->settings->get("cron_inactive_user_delete_period", $default_setting));
340 $sub_text->setSize(4);
341 $sub_text->setMaxLength(4);
342 $sub_text->setRequired(true);
343 $a_form->addItem($sub_text);
344
345 $sub_period = new ilNumberInputGUI(
346 $this->lng->txt('send_mail_to_inactive_users'),
347 'cron_inactive_user_reminder_period'
348 );
349 $sub_period->allowDecimals(false);
350 $sub_period->setInfo($this->lng->txt("send_mail_to_inactive_users_desc"));
351 $sub_period->setValue($this->settings->get("cron_inactive_user_reminder_period", $default_setting));
352 $sub_period->setSuffix($this->lng->txt("send_mail_to_inactive_users_suffix"));
353 $sub_period->setSize(4);
354 $sub_period->setMaxLength(4);
355 $sub_period->setRequired(false);
356 $sub_period->setMinValue(0);
357 $a_form->addItem($sub_period);
358 }
359
360 public function saveCustomSettings(ilPropertyFormGUI $a_form): bool
361 {
362 $this->lng->loadLanguageModule("user");
363
364 $valid = true;
365
366 $cron_period = JobScheduleType::from($this->http->wrapper()->post()->retrieve(
367 'type',
368 $this->refinery->kindlyTo()->int()
369 ));
370
371 $cron_period_custom = 0;
372 $delete_period = 0;
373 $reminder_period = '';
374
375 $empty_string_trafo = $this->refinery->custom()->transformation(static function ($value): string {
376 if ($value === '') {
377 return '';
378 }
379
380 throw new Exception('The value to be transformed is not an empty string');
381 });
382
383 if ($this->http->wrapper()->post()->has('sdyi')) {
384 $cron_period_custom = $this->http->wrapper()->post()->retrieve(
385 'sdyi',
386 $this->refinery->byTrying([
387 $this->refinery->kindlyTo()->int(),
388 $empty_string_trafo
389 ])
390 );
391 }
392
393 if ($this->http->wrapper()->post()->has('cron_inactive_user_delete_period')) {
394 $delete_period = $this->http->wrapper()->post()->retrieve(
395 'cron_inactive_user_delete_period',
396 $this->refinery->byTrying([
397 $this->refinery->kindlyTo()->int(),
398 $this->refinery->in()->series([
399 $this->refinery->kindlyTo()->float(),
400 $this->refinery->kindlyTo()->int()
401 ])
402 ])
403 );
404 }
405
406 if ($this->http->wrapper()->post()->has('cron_inactive_user_reminder_period')) {
407 $reminder_period = $this->http->wrapper()->post()->retrieve(
408 'cron_inactive_user_reminder_period',
409 $this->refinery->byTrying([
410 $empty_string_trafo,
411 $this->refinery->byTrying([
412 $this->refinery->kindlyTo()->int(),
413 $this->refinery->in()->series([
414 $this->refinery->kindlyTo()->float(),
415 $this->refinery->kindlyTo()->int()
416 ])
417 ])
418 ])
419 );
420 }
421
422 if ($this->isDecimal($delete_period)) {
423 $valid = false;
424 $a_form->getItemByPostVar('cron_inactive_user_delete_period')->setAlert(
425 $this->lng->txt('send_mail_to_inactive_users_numbers_only')
426 );
427 }
428
429 if ($this->isDecimal($reminder_period)) {
430 $valid = false;
431 $a_form->getItemByPostVar('cron_inactive_user_reminder_period')->setAlert(
432 $this->lng->txt('send_mail_to_inactive_users_numbers_only')
433 );
434 }
435
436 if ($reminder_period >= $delete_period) {
437 $valid = false;
438 $a_form->getItemByPostVar('cron_inactive_user_reminder_period')->setAlert(
439 $this->lng->txt('send_mail_to_inactive_users_must_be_smaller_than')
440 );
441 }
442
443 if ($cron_period->value >= JobScheduleType::IN_DAYS->value &&
444 $cron_period->value <= JobScheduleType::YEARLY->value && $reminder_period > 0) {
445 $logic = true;
446 $check_window_logic = $delete_period - $reminder_period;
447 if ($cron_period === JobScheduleType::IN_DAYS) {
448 if ($check_window_logic < $cron_period_custom) {
449 $logic = false;
450 }
451 } elseif ($cron_period === JobScheduleType::WEEKLY) {
452 if ($check_window_logic <= 7) {
453 $logic = false;
454 }
455 } elseif ($cron_period === JobScheduleType::MONTHLY) {
456 if ($check_window_logic <= 31) {
457 $logic = false;
458 }
459 } elseif ($cron_period === JobScheduleType::QUARTERLY) {
460 if ($check_window_logic <= 92) {
461 $logic = false;
462 }
463 } elseif ($cron_period === JobScheduleType::YEARLY) {
464 if ($check_window_logic <= 366) {
465 $logic = false;
466 }
467 }
468
469 if (!$logic) {
470 $valid = false;
471 $a_form->getItemByPostVar('cron_inactive_user_reminder_period')->setAlert(
472 $this->lng->txt('send_mail_reminder_window_too_small')
473 );
474 }
475 }
476
477 if ($delete_period > 0) {
478 $roles = implode(',', $this->http->wrapper()->post()->retrieve(
479 'cron_inactive_user_delete_include_roles',
480 $this->refinery->byTrying([
481 $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()),
482 $this->refinery->always([])
483 ])
484 ));
485
486 $this->settings->set('cron_inactive_user_delete_include_roles', $roles);
487 $this->settings->set('cron_inactive_user_delete_period', (string) $delete_period);
488 }
489
490 if ($this->reminder_period > $reminder_period) {
491 $this->cron_delete_reminder_mail->flushDataTable();
492 }
493
494 $this->settings->set('cron_inactive_user_reminder_period', (string) $reminder_period);
495
496 if (!$valid) {
497 $this->main_tpl->setOnScreenMessage('failure', $this->lng->txt("form_input_not_valid"));
498 return false;
499 }
500
501 return true;
502 }
503}
Component logger with individual log levels by component id.
hasAutoActivation()
Is to be activated on "installation", does only work for ILIAS core cron jobs.
ilCronDeleteInactiveUserReminderMail $cron_delete_reminder_mail
getTimeDifferenceBySchedule(JobScheduleType $schedule_time, int $multiplier)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a multi selection list property in a property form.
This class represents a number property in a property form.
static getUserIdsByInactivityPeriod(int $periodInDays)
class ilObjectDataCache
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
This class represents a property form user interface.
getItemByPostVar(string $a_post_var)
class ilRbacReview Contains Review functions of core Rbac.
ILIAS Setting Class.
const ANONYMOUS_ROLE_ID
Definition: constants.php:28
const SYSTEM_USER_ID
This file contains constants for PHPStan analyis, see: https://phpstan.org/config-reference#constants...
Definition: constants.php:26
const ANONYMOUS_USER_ID
Definition: constants.php:27
$valid
static http()
Fetches the global http state from ILIAS.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $DIC
Definition: shib_login.php:26