ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilCertificateMigrationJob.php
Go to the documentation of this file.
1 <?php
2 /*
3  +----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2001 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +----------------------------------------------------------------------------+
22 */
23 
30 
36 {
38  protected $db_table;
39 
41  protected $user_id;
42 
44  protected $tree;
45 
47  protected $db;
48 
50  protected $event_handler;
51 
53  protected $task_factory;
54 
56  private $logger;
57 
58  private $types = [
59  'test',
60  'scorm',
61  'exercise',
62  'course'
63  ];
64 
71  public function run(array $input, Observer $observer) : \ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\IntegerValue
72  {
73  global $DIC;
74 
75  $this->user_id = $input[0]->getValue();
76  $user = new ilObjUser($this->user_id);
77 
78  $this->tree = $DIC->repositoryTree();
79  $this->db = $DIC->database();
81  $this->event_handler = $DIC['ilAppEventHandler'];
82  $this->task_factory = $DIC->backgroundTasks()->taskFactory();
83  $this->logger = $DIC->logger()->cert();
84 
85  $certificates = [];
86  $output = new IntegerValue();
87 
88  $this->logger->debug('Started at ' . ($st_time = date('d.m.Y H:i:s')) . ' for user with id: ' . $this->user_id);
89 
90  $task_informations = $this->getTaskInformations();
91  if (empty($task_informations)) {
92  $this->initTask();
93  }
94  if ($task_informations['state'] === \ilCertificateMigrationJobDefinitions::CERT_MIGRATION_STATE_RUNNING) {
95  $this->logger->info('Parallel execution protection. Stopped task for user ' . $this->user_id . ', because it is already running.');
97  return $output;
98  }
99  if ($task_informations['lock'] == true) {
100  // we should never get here if no fatal error occures
101  $this->logger->info('Parallel execution protection. Stopped task for user ' . $this->user_id . ', because it is locked.');
103  return $output;
104  }
105 
106  $this->updateTask([
107  'lock' => true,
109  ]);
110 
111  $found_items = 0;
112 
113  try {
114  // collect all data
115  $this->logger->info('Start collection certificate data for user: ' . $this->user_id);
116 
117  foreach ($this->types as $type) {
118  if ($type === 'scorm') {
120  } elseif ($type === 'test') {
122  } elseif ($type === 'exercise') {
124  } elseif ($type === 'course') {
126  }
127 
128  $found_items += count($certificates[$type]);
129  $observer->heartbeat();
130  }
131 
132  $this->updateTask(['found_items' => $found_items]);
133  $this->logger->debug('Found overall ' . $found_items . ' items for user with id: ' . $this->user_id);
134  $this->logger->info('Finished collecting certificate data for user: ' . $this->user_id);
135 
136  $processed_items = 0;
137 
138  $template_repository = new ilCertificateTemplateRepository($this->db);
139  $repository = new ilUserCertificateRepository($this->db);
140  $value_replacement = new ilCertificateValueReplacement();
141 
142  $this->logger->info('Start migrating certificates for user: ' . $user->getId());
143 
144  foreach ($this->types as $type) {
145  $this->logger->info(sprintf('Start preparing "%s" certificates', $type));
146 
147  switch ($type) {
148  case 'test':
149  $class = ilTestPlaceholderValues::class;
150  break;
151 
152  case 'scorm':
153  $class = ilScormPlaceholderValues::class;
154  break;
155 
156  case 'exercise':
157  $class = ilExercisePlaceholderValues::class;
158  break;
159 
160  case 'course':
161  $class = ilCoursePlaceholderValues::class;
162  break;
163  }
164  $placeholder_values = new $class();
165 
166  foreach ($certificates[$type] as $certificate_id) {
167  if (\ilCertificate::isObjectActive($certificate_id)) {
168  try {
169  switch (true) {
170  case $type == 'course':
171  $acquireDate = new ilDateTime(
172  ilCourseParticipants::getDateTimeOfPassed($certificate_id, $user->getId()),
174  );
175  $acquireTimestamp = $acquireDate->get(IL_CAL_UNIX);
176  break;
177  case $type == 'test' && !ilObjUserTracking::_enabledLearningProgress():
178  $test = new ilObjTest($certificate_id, false);
179  $acquireTimestamp = $test
180  ->getTestResult($test->getActiveIdOfUser($user->getId()))['test']['result_tstamp'];
181  break;
182  default:
183  $acquireDate = new ilDateTime(
184  ilLPStatus::_lookupStatusChanged($certificate_id, $user->getId()),
186  );
187  $acquireTimestamp = $acquireDate->get(IL_CAL_UNIX);
188  }
189  } catch (Exception $exception) {
190  $this->logger->warning(sprintf('Unable to gather aquired timestamp for certificate %s', $certificate_id));
191  $acquireTimestamp = null;
192  }
193  if ($acquireTimestamp === null || 0 === (int) $acquireTimestamp) {
194  $acquireTimestamp = time();
195  }
196  $template = null;
197  try {
198  $template = $template_repository->fetchFirstCreatedTemplate($certificate_id);
199  } catch (Exception $exception) {
200  $this->logger->warning(sprintf('Unable to gather template for certificate %s. Skipped.', $certificate_id));
201  }
202  if ($template) {
204  $template->getId(),
205  $certificate_id,
206  $type,
207  $user->getId(),
208  $user->getFullname(),
209  $acquireTimestamp,
210  $value_replacement->replace(
211  $placeholder_values->getPlaceholderValues($user->getId(), $certificate_id),
212  $template->getCertificateContent()
213  ) ?? '',
214  json_encode($placeholder_values->getPlaceholderValues($user->getId(), $certificate_id)) ?? '',
215  null,
216  $template->getVersion(),
218  true,
219  $template->getBackgroundImagePath() ?? '',
220  $template->getThumbnailImagePath() ?? ''
221  );
222  $repository->save($certificate);
223  $processed_items++;
224  }
225  }
226  $this->updateTask([
227  'processed_items' => $processed_items,
228  'progress' => ($processed_items / $found_items) * 100
229  ]);
230 
231  $observer->heartbeat();
232  }
233  $this->logger->info(sprintf('Finished preparing "%s" certificates', $type));
234  }
235 
236  $this->logger->info('Finished migrating certificates for user: ' . $user->getId());
237  } catch (\Exception $e) {
238  $this->logger->error($e->getMessage(), 'error');
239  $this->updateTask([
240  'lock' => false,
241  'processed_items' => $processed_items,
243  ]);
244  $output->setValue((int) $e->getCode());
245  return $output;
246  }
247 
249 
250  $this->logger->debug('Finished at ' . ($f_time = date('d.m.Y H:i:s')) . ' after ' . (strtotime($f_time) - strtotime($st_time)) . ' seconds');
251  $this->updateTask([
252  'lock' => false,
253  'finished_ts' => $f_time,
254  'processed_items' => $processed_items,
256  ]);
257 
258  $user->writePref('cert_migr_finished', 1);
259 
260  return $output;
261  }
262 
267  public function isStateless() : bool
268  {
269  return true;
270  }
271 
278  public function getExpectedTimeOfTaskInSeconds() : int
279  {
280  return 100;
281  }
282 
286  public function getInputTypes() : array
287  {
288  return [
289  new SingleType(IntegerValue::class),
290  ];
291  }
292 
296  public function getOutputType() : SingleType
297  {
298  return new SingleType(IntegerValue::class);
299  }
300 
304  public function getTaskInformations() : array
305  {
306  global $DIC;
307 
308  $db = $DIC->database();
309 
310  $result = $db->queryF(
311  'SELECT * FROM ' . $this->db_table . ' WHERE usr_id = %s',
312  ['integer'],
313  [$this->user_id]
314  );
315  if ($result->numRows() == 1) {
316  $data = $db->fetchAssoc($result);
317  return $data;
318  }
319  return [];
320  }
321 
325  protected function initTask()
326  {
327  $this->logger->debug('Insert new entry for user with id: ' . $this->user_id);
328  $this->db->insert($this->db_table, [
329  'id' => ['integer', $this->db->nextId($this->db_table)],
330  'usr_id' => ['integer', $this->user_id],
331  'lock' => ['integer', false],
332  'found_items' => ['integer', 0],
333  'processed_items' => ['integer', 0],
334  'migrated_items' => ['integer', 0],
335  'progress' => ['integer', 0],
337  'started_ts' => ['integer', strtotime('now')],
338  'finished_ts' => null,
339  ]);
340  }
341 
346  protected function updateTask(array $data)
347  {
348  $updata = [];
349  if (array_key_exists('lock', $data)) {
350  $updata['lock'] = ['integer', $data['lock']];
351  }
352  if (array_key_exists('found_items', $data)) {
353  $updata['found_items'] = ['integer', $data['found_items']];
354  }
355  if (array_key_exists('processed_items', $data)) {
356  $updata['processed_items'] = ['integer', $data['processed_items']];
357  }
358  if (array_key_exists('migrated_items', $data)) {
359  $updata['migrated_items'] = ['integer', $data['migrated_items']];
360  }
361  if (array_key_exists('progress', $data)) {
362  $updata['progress'] = ['integer', $data['progress']];
363  }
364  if (array_key_exists('state', $data)) {
365  $updata['state'] = ['text', $data['state']];
366  }
367  if (array_key_exists('finished_ts', $data)) {
368  if (is_string($data['finished_ts'])) {
369  $data['finished_ts'] = strtotime($data['finished_ts']);
370  }
371  $updata['finished_ts'] = ['integer', $data['finished_ts']];
372  }
373  $this->logger->debug('Update data: ' . json_encode($updata));
374  if (!empty($updata)) {
375  $this->logger->debug('Update entry for user with id: ' . $this->user_id);
376  $this->db->update($this->db_table, $updata, ['usr_id' => ['integer', $this->user_id] ]);
377  }
378  }
379 
384  protected function getScormCertificates() : array
385  {
386  $this->logger->info('Trying to get scorm certificates for user: ' . $this->user_id);
387 
388  $data = array();
389 
390  if (\ilCertificate::isActive()) {
392  $obj_ids = array();
393  $root = $this->tree->getNodeData($this->tree->getRootId());
394  foreach ($this->tree->getSubTree($root, true, "sahs") as $node) {
395  $obj_ids[] = $node["obj_id"];
396  }
397  if ($obj_ids) {
398  foreach (\ilCertificate::areObjectsActive($obj_ids) as $objectId => $active) {
399  if ($active) {
401 
402  $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
403  if (!$object || !($object instanceof \ilObjSAHSLearningModule)) {
404  $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
405  continue;
406  }
407  $object->setSubType($type);
408 
409  $lpdata = $completed = false;
410  if ($lp_active) {
411  $completed = ilLPStatus::_hasUserCompleted($objectId, $this->user_id);
412  $lpdata = true;
413  }
414 
415  if (!$lpdata) {
416  switch ($type) {
417  case 'scorm':
419  $objectId,
420  $this->user_id
421  );
422  break;
423 
424  case 'scorm2004':
426  $objectId,
427  $this->user_id
428  );
429  break;
430  }
431  }
432 
433  if (!$completed) {
434  $this->logger->info(sprintf(
435  'User did not complete SCORM object with obj_id %s, ignoring object for migration ...',
436  $objectId
437  ));
438  continue;
439  }
440 
441  $this->logger->info(sprintf(
442  'User completed SCORM object with obj_id %s, considering object for migration ...',
443  $objectId
444  ));
445 
446  $data[] = $objectId;
447  }
448  }
449  }
450  }
451 
452  $this->logger->info('Got ' . count($data) . ' scorm certificates for user: ' . $this->user_id);
453  return $data;
454  }
455 
460  protected function getTestCertificates() : array
461  {
462  $this->logger->info('Trying to get test certificates for user: ' . $this->user_id);
463 
464  $data = array();
465 
466  foreach (\ilObjTest::getTestObjIdsWithActiveForUserId($this->user_id) as $objectId) {
467  $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
468  if (!$object || !($object instanceof \ilObjTest)) {
469  $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
470  continue;
471  }
472 
473  $session = new \ilTestSessionFactory($object);
474  $session = $session->getSession(null);
475  if ($object->canShowCertificate($session, $session->getUserId(), $session->getActiveId())) {
476  $data[] = $objectId;
477  }
478  }
479 
480  $this->logger->info('Got ' . count($data) . ' test certificates for user: ' . $this->user_id);
481  return $data;
482  }
483 
488  protected function getExerciseCertificates() : array
489  {
490  $this->logger->info('Trying to get exercise certificates for user: ' . $this->user_id);
491 
492  $data = array();
493 
494  foreach (\ilObjExercise::_lookupFinishedUserExercises($this->user_id) as $objectId => $passed) {
495  $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
496  if (!$object || !($object instanceof \ilObjExercise)) {
497  $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
498  continue;
499  }
500 
501  $adapter = new \ilExerciseCertificateAdapter($object);
502  if ($adapter->hasUserCertificate($this->user_id)) {
503  $data[] = $objectId;
504  }
505  }
506 
507  $this->logger->info('Got ' . count($data) . ' exercise certificates for user: ' . $this->user_id);
508  return $data;
509  }
510 
515  protected function getCourseCertificates() : array
516  {
517  $this->logger->info('Trying to get course certificates for user: ' . $this->user_id);
518 
519  $data = array();
520 
521  $obj_ids = \ilCourseParticipants::_getMembershipByType($this->user_id, "crs");
522  if ($obj_ids) {
523  \ilCourseCertificateAdapter::_preloadListData([$this->user_id], $obj_ids);
524 
525  foreach ($obj_ids as $objectId) {
526  if (\ilCourseCertificateAdapter::_hasUserCertificate($this->user_id, $objectId)) {
527  $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
528  if (!$object || !($object instanceof \ilObjCourse)) {
529  $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
530  continue;
531  }
532 
533  $data[] = $objectId;
534  }
535  }
536  }
537 
538  $this->logger->info('Got ' . count($data) . ' course certificates for user: ' . $this->user_id);
539  return $data;
540  }
541 }
const IL_CAL_DATETIME
$result
const ILIAS_VERSION_NUMERIC
$template
$type
global $DIC
Definition: saml.php:7
static _getCourseCompletionForUser($a_id, $a_user)
Get the completion of a SCORM module for a given user.
static getDateTimeOfPassed($a_obj_id, $a_usr_id)
heartbeat()
I&#39;m still alive! If your calculation takes a really long time don&#39;t forget to use the heartbeat...
Class BaseForm.
$session
static _getMembershipByType($a_usr_id, $a_type, $a_only_member_role=false)
get membership by type Get course or group membership
$certificates
Definition: metarefresh.php:39
getTestCertificates()
Get certificates for test objects.
const IL_CAL_UNIX
static _lookupSubType($a_obj_id)
lookup subtype id (scorm, )
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $certificate
Definition: example_052.php:77
static _preloadListData($a_usr_ids, $a_obj_ids)
Get certificate/passed status for all given objects and users.
static _enabledLearningProgress()
check wether learing progress is enabled or not
Class ilObjExercise.
Class ilObjCourse.
$f_time
Definition: buildRTE.php:82
getScormCertificates()
Get certificates for scorm objects.
static _hasUserCompleted($a_obj_id, $a_user_id)
Lookup user object completion.
static _hasUserCertificate($a_usr_id, $a_obj_id)
Check if user has certificate for course.
Date and time handling
static _lookupStatusChanged($a_obj_id, $a_user_id)
Lookup status changed.
$root
Definition: sabredav.php:45
$user
Definition: migrateto20.php:57
static getInstanceByObjId($a_obj_id, $stop_on_error=true)
get an instance of an Ilias object by object id
getExerciseCertificates()
Get certificates for exercise objects.
getCourseCertificates()
Get certificates for course objects.
static _lookupFinishedUserExercises($a_user_id)
Get all exercises for user.
static isObjectActive($a_obj_id)
run(array $input, Observer $observer)
static getTestObjIdsWithActiveForUserId($userId)
Class ilCertificateMigrationJob.
static _getCourseCompletionForUser($a_id, $a_user)
Get the completion of a SCORM module for a given user.
Class ilObjSCORMLearningModule.
$test
Definition: Utf8Test.php:84
$data
Definition: bench.php:6