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