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