diff --git a/db/migrations/5.3.18_cw_unit_adjustments.php b/db/migrations/5.3.18_cw_unit_adjustments.php new file mode 100644 index 0000000000000000000000000000000000000000..131c81c7558921959efbcee6264eec0af3d4832c --- /dev/null +++ b/db/migrations/5.3.18_cw_unit_adjustments.php @@ -0,0 +1,108 @@ +<?php + +class CwUnitAdjustments extends Migration +{ + public function description() + { + return 'adjust courseware config to units'; + } + + public function up() + { + // Add column for storing per-unit configuration. + DBManager::get()->exec( + "ALTER TABLE `cw_units` ADD `config` TEXT NOT NULL DEFAULT '' AFTER `withdraw_date`" + ); + + // Which fields in config are relevant for this migration? + $fields = [ + 'COURSEWARE_SEQUENTIAL_PROGRESSION', + 'COURSEWARE_EDITING_PERMISSION', + 'COURSEWARE_CERTIFICATE_SETTINGS', + 'COURSEWARE_REMINDER_SETTINGS', + 'COURSEWARE_RESET_PROGRESS_SETTINGS', + 'COURSEWARE_LAST_REMINDER', + 'COURSEWARE_LAST_PROGRESS_RESET' + ]; + + // Which courses do have custom courseware settings and need to be migrated? + $ranges = DBManager::get()->fetchFirst( + "SELECT DISTINCT `range_id` FROM `config_values` WHERE `field` IN (:fields)", + ['fields' => $fields] + ); + + $update = DBManager::get()->prepare("UPDATE `cw_units` SET `config` = :config WHERE `id` = :unit"); + + // Get courseware settings per course as stored in config_values, + foreach ($ranges as $course) { + $global = DBManager::get()->fetchAll( + "SELECT `field`, `value` FROM `config_values` WHERE `range_id` = :range AND `field` IN (:fields)", + ['range' => $course, 'fields' => $fields] + ); + + // Build configuration per unit. + $config = []; + // Convert values. + foreach ($global as $one) { + + $decoded = json_decode($one['value'], true); + + foreach ($decoded as $unit_id => $settings) { + switch ($one['field']) { + case 'COURSEWARE_SEQUENTIAL_PROGRESSION': + $config[$unit_id]['sequential_progression'] = $settings; + break; + case 'COURSEWARE_EDITING_PERMISSION': + $config[$unit_id]['editing_permission'] = $settings; + break; + case 'COURSEWARE_CERTIFICATE_SETTINGS': + $config[$unit_id]['certificate'] = $settings; + break; + case 'COURSEWARE_REMINDER_SETTINGS': + $config[$unit_id]['reminder'] = $settings; + break; + case 'COURSEWARE_RESET_PROGRESS_SETTINGS': + $config[$unit_id]['reset_progress'] = $settings; + break; + case 'COURSEWARE_LAST_REMINDER': + $config[$unit_id]['last_reminder'] = $settings; + break; + case 'COURSEWARE_LAST_PROGRESS_RESET': + $config[$unit_id]['last_progress_reset'] = $settings; + break; + } + } + } + + // Now write per-unit configurations to database. + foreach ($config as $unit => $config) { + $update->execute(['config' => json_encode($config), 'unit' => $unit]); + } + + } + + // Drop old values from global config. + DBManager::get()->execute( + "DELETE FROM `config` WHERE `field` IN (:fields)", + ['fields' => $fields] + ); + DBManager::get()->execute( + "DELETE FROM `config_values` WHERE `field` IN (:fields)", + ['fields' => $fields] + ); + + // Add column for storing unit_id with certificate date. + DBManager::get()->exec( + "ALTER TABLE `cw_certificates` ADD `unit_id` INT NOT NULL AFTER `course_id`" + ); + DBManager::get()->exec("ALTER TABLE `cw_certificates` DROP INDEX `index_course_id`, DROP INDEX `index_user_ourse`"); + DBManager::get()->exec("ALTER TABLE `cw_certificates` ADD INDEX index_unit_id (`unit_id`)"); + } + + public function down() + { + // Drop columns for storing per-unit configuration. + DBManager::get()->exec("ALTER TABLE `cw_units` DROP `config`"); + DBManager::get()->exec("ALTER TABLE `cw_certificates` DROP `unit_id`"); + } +} diff --git a/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php b/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php index e48588413cfe8af382d508ea426f3912c931c1f1..fff4608cdebffa3453d6618ee3fe9c419725ced4 100644 --- a/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php +++ b/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php @@ -133,6 +133,9 @@ class CoursewareInstancesUpdate extends JsonApiController $resetProgressSettings = $get('data.attributes.reset-progress-settings'); $instance->setResetProgressSettings($resetProgressSettings); + // Store changes in unit configuration. + $instance->getUnit()->store(); + return $instance; } } diff --git a/lib/cronjobs/courseware.php b/lib/cronjobs/courseware.php index 16e0ad96475180150386cb3696cb5123135a6e69..37259c6b309b00c72fdeb67b39ef641ab87fb614 100644 --- a/lib/cronjobs/courseware.php +++ b/lib/cronjobs/courseware.php @@ -41,200 +41,176 @@ class CoursewareCronjob extends CronJob $verbose = $parameters['verbose']; /* - * Fetch all courses that have some relevant settings. + * Fetch all units that have some relevant settings. */ - $todo = DBManager::get()->fetchAll( - "SELECT c.`range_id`, c. `field`, c.`value` - FROM `config_values` c - JOIN `seminare` s ON (s.`Seminar_id` = c.`range_id`) - WHERE c.`field` IN (:fields)", - ['fields' => [ - // Send certificate when this progress is reached - 'COURSEWARE_CERTIFICATE_SETTINGS', - // Remind all users about courseware - 'COURSEWARE_REMINDER_SETTINGS', - // Reset user progress to 0 - 'COURSEWARE_RESET_PROGRESS_SETTINGS' - ] - ] + $todo = Courseware\Unit::findBySQL( + "`range_type` = 'course' AND (`config` LIKE (:cert) OR `config` LIKE (:reminder) OR `config` LIKE (:reset))", + ['cert' => '%"certificate":%', 'reminder' => '%"reminder":%', 'reset' => '%"reset_progress":%'] ); if (count($todo) > 0) { if ($verbose) { - echo sprintf("Found %u courses to process.\n", count($todo)); + printf("Found %u units to process.\n", count($todo)); } $timezone = Config::get()->DEFAULT_TIMEZONE; // Process all found entries... - foreach ($todo as $one) { - - // Fetch all courseware blocks belonging to the current course. - $blocks = DBManager::get()->fetchFirst( - "SELECT DISTINCT b.`id` - FROM `cw_blocks` b - JOIN `cw_containers` c ON (c.`id` = b.`container_id`) - JOIN `cw_structural_elements` e ON (e.`id` = c.`structural_element_id`) - WHERE e.`range_id` = :course", - ['course' => $one['range_id']] - ); - - // extract details from JSON - $settings = json_decode($one['value'], true); - - // differentiate by setting type - switch ($one['field']) { - // Send certificates to those who have progressed far enough and have not yet gotten one. - case 'COURSEWARE_CERTIFICATE_SETTINGS': - - if ($verbose) { - echo sprintf("Generating certificates for course %s.\n", - $one['range_id']); - } - - // Fetch accumulated progress values for all users in this course. - $progresses = DBManager::get()->fetchAll( - "SELECT DISTINCT p.`user_id`, SUM(p.`grade`) AS progress - FROM `cw_user_progresses` p - WHERE `block_id` IN (:blocks) - AND NOT EXISTS ( - SELECT `id` FROM `cw_certificates` WHERE `user_id` = p.`user_id` AND `course_id` = :course - ) - GROUP BY `user_id`", - ['blocks' => $blocks, 'course' => $one['range_id']] - ); - - // Calculate percentual progress and send certificates if necessary. - foreach ($progresses as $progress) { - $percent = ($progress['progress'] / count($blocks)) * 100; - if ($percent >= $settings['threshold']) { - if ($verbose) { - echo sprintf("User %s will get a certificate for course %s.\n", - $progress['user_id'], $one['range_id']); - } - - $this->sendCertificate($one['range_id'], $progress['user_id'], - $percent, $settings); - - /* - * Insert a new entry into database for tracking who already got a certificate. - * This can be useful if certificates get a validity time or such. - */ - $entry = new Courseware\Certificate(); - $entry->user_id = $progress['user_id']; - $entry->course_id = $one['range_id']; - $entry->store(); - } - } - - break; - - // Send reminders to all course participants. - case 'COURSEWARE_REMINDER_SETTINGS': - - // Check when the last reminder was sent... - $now = new DateTime('', new DateTimeZone($timezone)); - - // What would be the minimum date for the last reminder? - $minReminder = clone $now; - - // The last reminder has been sent at? - $lastReminder = new DateTime('', new DateTimeZone($timezone)); - $lastReminder->setTimestamp( - UserConfig::get($one['range_id'])->COURSEWARE_LAST_REMINDER ?: 0 - ); - - // Check if the settings specify a start and/or end date for reminders - $start = new DateTime($settings['startDate'] ?: '1970-01-01', - new DateTimeZone($timezone)); - $end = new DateTime($settings['endDate'] ?: '2199-12-31', - new DateTimeZone($timezone)); - - $interval = new DateInterval('P1D'); - switch ($settings['interval']) { - case 7: - $interval = new DateInterval('P7D'); - break; - case 14: - $interval = new DateInterval('P14D'); - break; - case 30: - $interval = new DateInterval('P1M'); - break; - case 90: - $interval = new DateInterval('P3M'); - break; - case 180: - $interval = new DateInterval('P6M'); - break; - case 365: - $interval = new DateInterval('P1Y'); - break; - } - $minReminder->sub($interval); - - // ... and send a new one if necessary. - if ($lastReminder <= $minReminder && $now >= $start && $now <= $end) { + foreach ($todo as $unit) { + + // Fetch all courseware block IDs belonging to the current unit. + $instance = new Courseware\Instance($unit->structural_element); + $blocks = array_column($instance->findAllBlocks(), 'id'); + + // Send certificates to those who have progressed far enough and have not yet gotten one. + if (isset($unit->config['certificate'])) { + if ($verbose) { + printf("Generating certificates for course %s, unit %u.\n", + $unit->range_id, $unit->id); + } + + // Fetch accumulated progress values for all users in this course. + $progresses = DBManager::get()->fetchAll( + "SELECT DISTINCT p.`user_id`, SUM(p.`grade`) AS progress + FROM `cw_user_progresses` p + WHERE `block_id` IN (:blocks) + AND NOT EXISTS ( + SELECT `id` FROM `cw_certificates` WHERE `user_id` = p.`user_id` AND `unit_id` = :unit + ) + GROUP BY `user_id`", + ['blocks' => $blocks, 'unit' => $unit->id] + ); + + // Calculate percentual progress and send certificates if necessary. + foreach ($progresses as $progress) { + $percent = ($progress['progress'] / count($blocks)) * 100; + printf("User %s has progress %u.\n", $progress['user_id'], $percent); + if ($percent >= $unit->config['certificate']['threshold']) { if ($verbose) { - echo sprintf("Sending reminders for course %s.\n", - $one['range_id']); + printf("User %s will get a certificate for course %s and unit %u.\n", + $progress['user_id'], $unit->range_id, $unit->id); } - if ($this->sendReminders($one['range_id'], $settings)) { - UserConfig::get($one['range_id'])->store('COURSEWARE_LAST_REMINDER', - $now->getTimestamp() - ); + if (!$this->sendCertificate($unit, $progress['user_id'], $percent, + $unit->config['certificate']['image'])) { + printf("Could not send certificate for course %s and unit %u to user %s.\n", + $unit->range_id, $unit->id, $progress['user_id']); } } + } + } - break; - - // Reset courseware progress to 0 for all course participants. - case 'COURSEWARE_RESET_PROGRESS_SETTINGS': - - // Check when the last reset was performed... - $now = new DateTime('', new DateTimeZone($timezone)); - $checkLast = clone $now; - $lastReset = new DateTime('', new DateTimeZone($timezone)); - $lastReset->setTimestamp( - UserConfig::get($one['range_id'])->COURSEWARE_LAST_PROGRESS_RESET ?: 0 - ); - - $interval = new DateInterval('P1D'); - switch ($one['value']) { - case 14: - $interval = new DateInterval('P14D'); - break; - case 30: - $interval = new DateInterval('P1M'); - break; - case 90: - $interval = new DateInterval('P3M'); - break; - case 180: - $interval = new DateInterval('P6M'); - break; - case 365: - $interval = new DateInterval('P1Y'); - break; + // Send reminder messages to participants if necessary. + if (isset($unit->config['reminder'])) { + // Check when the last reminder was sent... + $now = new DateTime('', new DateTimeZone($timezone)); + + // What would be the minimum date for the last reminder? + $minReminder = clone $now; + + // The last reminder has been sent at? + $lastReminder = new DateTime('', new DateTimeZone($timezone)); + $lastReminder->setTimestamp($unit->config['last_reminder'] ?? 0); + + // Check if the settings specify a start and/or end date for reminders + $start = new DateTime($unit->config['reminder']['startDate'] ?? '1970-01-01', + new DateTimeZone($timezone)); + $end = new DateTime($unit->config['reminder']['endDate'] ?? '2199-12-31', + new DateTimeZone($timezone)); + + $interval = new DateInterval('P1D'); + switch ($unit->config['reminder']['interval']) { + case 7: + $interval = new DateInterval('P7D'); + break; + case 14: + $interval = new DateInterval('P14D'); + break; + case 30: + $interval = new DateInterval('P1M'); + break; + case 90: + $interval = new DateInterval('P3M'); + break; + case 180: + $interval = new DateInterval('P6M'); + break; + case 365: + $interval = new DateInterval('P1Y'); + break; + } + $minReminder->sub($interval); + + // ... and send a new one if necessary. + if ($lastReminder <= $minReminder && $now >= $start && $now <= $end) { + if ($verbose) { + printf("Sending reminders for course %s and unit %u.\n", + $unit->range_id, $unit->id); } - // ... and reset again if necessary. - if ($lastReset <= $checkLast->sub($interval)) { - if ($verbose) { - echo sprintf("Resetting all progress for courseware in course %s.\n", - $one['range_id']); - } + if ($this->sendReminders($unit)) { + $unit->config['last_reminder'] = time(); + } + } + } - // Remove all progress in the given blocks. - $this->resetProgress($one['range_id'], $blocks, $settings); + // Reset progress if necessary. + if (isset($unit->config['reset_progress'])) { + // Check when the last rest took place... + $now = new DateTime('', new DateTimeZone($timezone)); + + // What would be the minimum date for the last reset? + $minReset = clone $now; + + // The last reset was done at: + $lastReset = new DateTime('', new DateTimeZone($timezone)); + $lastReset->setTimestamp($unit->config['last_progress_reset'] ?? 0); + + // Check if the settings specify a start and/or end date for reminders + $start = new DateTime($unit->config['reset_progress']['startDate'] ?? '1970-01-01', + new DateTimeZone($timezone)); + $end = new DateTime($unit->config['reset_progress']['endDate'] ?? '2199-12-31', + new DateTimeZone($timezone)); + + $interval = new DateInterval('P1D'); + switch ($unit->config['reset_progress']['interval']) { + case 7: + $interval = new DateInterval('P7D'); + break; + case 14: + $interval = new DateInterval('P14D'); + break; + case 30: + $interval = new DateInterval('P1M'); + break; + case 90: + $interval = new DateInterval('P3M'); + break; + case 180: + $interval = new DateInterval('P6M'); + break; + case 365: + $interval = new DateInterval('P1Y'); + break; + } + $minReset->sub($interval); + + // ... and send a new one if necessary. + if ($lastReset <= $minReset && $now >= $start && $now <= $end) { + if ($verbose) { + printf("Resetting progress for course %s and unit %u.\n", + $unit->range_id, $unit->id); + } - UserConfig::get($one['range_id'])->store('COURSEWARE_LAST_PROGRESS_RESET', - $now->getTimestamp() - ); + if ($this->resetProgress($unit, $blocks, $unit->config['reset_progress']['mailText'])) { + $unit->config['last_progress_reset'] = time(); } + } } + + // Store config back, saving timestamps for reminders and progress reset. + $unit->store(); } } else if ($verbose) { @@ -242,10 +218,10 @@ class CoursewareCronjob extends CronJob } } - private function sendCertificate($course_id, $user_id, $progress, $settings) + private function sendCertificate($unit, $user_id, $progress, $image = '') { $user = User::find($user_id); - $course = Course::find($course_id); + $course = Course::find($unit->range_id); $template = $GLOBALS['template_factory']->open('courseware/mails/certificate'); $html = $template->render( @@ -253,7 +229,7 @@ class CoursewareCronjob extends CronJob ); // Generate the PDF. - $pdf = new CoursewarePDFCertificate($settings['image']); + $pdf = new CoursewarePDFCertificate($image); $pdf->AddPage(); $pdf->writeHTML($html, true, false, true, false, ''); $pdf_file_name = $user->nachname . '_' . $course->name . '_' . _('Zertifikat') . '.pdf'; @@ -265,12 +241,12 @@ class CoursewareCronjob extends CronJob $message = sprintf( _('Anbei erhalten Sie Ihr Courseware-Zertifikat zur Veranstaltung %1$s, in der Sie einen Fortschritt ' . - 'von %2$u %% erreicht haben.'), $course->getFullname(), $progress); + 'von %2$u %% im Lernmaterial "%s" erreicht haben.'), + $course->getFullname(), $progress, $unit->structural_element->title); $message .= "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' . - URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id, - 'redirect_to' => 'dispatch.php/course/courseware']); + URLHelper::getURL('dispatch.php/course/courseware/courseware/' . $unit->id, ['cid' => $course->id]); - $mail->addRecipient($user->email, $user->getFullname()) + $sent = $mail->addRecipient($user->email, $user->getFullname()) ->setSubject(_('Courseware: Zertifikat') . ' - ' . $course->getFullname()) ->setBodyText($message) ->addFileAttachment($filename, $pdf_file_name) @@ -279,12 +255,20 @@ class CoursewareCronjob extends CronJob @unlink($filename); // Add database entry for the certificate. - + if ($sent) { + $cert = new Courseware\Certificate(); + $cert->user_id = $user_id; + $cert->course_id = $course->id; + $cert->unit_id = $unit->id; + return $cert->store(); + } else { + return false; + } } - private function sendReminders($course_id, $settings) + private function sendReminders($unit) { - $course = Course::find($course_id); + $course = Course::find($unit->range_id); $recipients = $course->getMembersWithStatus('autor', true); @@ -298,19 +282,19 @@ class CoursewareCronjob extends CronJob ); } - $message = $settings['mailText'] . "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' . - URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id, - 'redirect_to' => 'dispatch.php/course/courseware']); + $message = $unit->config['reminder']['mailText'] . "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' . + URLHelper::getURL('dispatch.php/course/courseware/courseware/' . $unit->id, ['cid' => $course->id]); - $mail->setSubject(_('Courseware: Erinnerung') . ' - ' . $course->getFullname()) + $mail->setSubject(_('Courseware: Erinnerung') . ' - ' . $course->getFullname() . + ', ' . $unit->structural_element->title) ->setBodyText($message); return $mail->send(); } - private function resetProgress($course_id, $block_ids, $settings) + private function resetProgress($unit, $block_ids) { - $course = Course::find($course_id); + $course = Course::find($unit->range_id); DBManager::get()->execute( "DELETE FROM `cw_user_progresses` WHERE `block_id` IN (:blocks)", @@ -329,9 +313,9 @@ class CoursewareCronjob extends CronJob ); } - $message = $settings['mailText'] . "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' . - URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id, - 'redirect_to' => 'dispatch.php/course/courseware']); + $message = $unit->config['reset_progress']['mailText'] . "\n\n" . + _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' . + URLHelper::getURL('dispatch.php/course/courseware/courseware/' . $unit->id, ['cid' => $course->id]); $mail->setSubject(_('Courseware: Fortschritt zurückgesetzt') . ' - ' . $course->getFullname()) ->setBodyText($message); diff --git a/lib/models/Courseware/Instance.php b/lib/models/Courseware/Instance.php index 63bd34bd8aa91fc9830193c69a48abbf16689d75..bee37016af28eb928758b6907b79ef02f90c9383 100644 --- a/lib/models/Courseware/Instance.php +++ b/lib/models/Courseware/Instance.php @@ -34,9 +34,6 @@ class Instance $instance = new self($root); - $range->getConfiguration()->delete('COURSEWARE_SEQUENTIAL_PROGRESSION'); - $range->getConfiguration()->delete('COURSEWARE_EDITING_PERMISSION'); - $last_element_configs = \ConfigValue::findBySQL('field = ? AND value LIKE ?', [ 'COURSEWARE_LAST_ELEMENT', '%' . $range->getRangeId() . '%', @@ -62,6 +59,11 @@ class Instance */ private $root; + /** + * @var Unit + */ + private $unit; + /** * Create a new representation of a a courseware instance. * @@ -73,6 +75,7 @@ class Instance public function __construct(StructuralElement $root) { $this->root = $root; + $this->unit = $root->findUnit(); } /** @@ -85,6 +88,16 @@ class Instance return $this->root; } + /** + * Returns the unit belonging to this courseware instance. + * + * @return Unit the unit belonging this courseware instance + */ + public function getUnit(): Unit + { + return $this->unit; + } + /** * Returns the range this courseware instance belongs to. * @@ -164,9 +177,7 @@ class Instance */ public function getSequentialProgression(): bool { - $range = $this->getRange(); - $root = $this->getRoot(); - $sequentialProgression = $range->getConfiguration()->COURSEWARE_SEQUENTIAL_PROGRESSION[$root->id]; + $sequentialProgression = $this->unit->config['sequential_progression'] ?? false; return (bool) $sequentialProgression; } @@ -178,11 +189,7 @@ class Instance */ public function setSequentialProgression(bool $isSequentialProgression): void { - $range = $this->getRange(); - $root = $this->getRoot(); - $progressions = $range->getConfiguration()->getValue('COURSEWARE_SEQUENTIAL_PROGRESSION'); - $progressions[$root->id] = $isSequentialProgression ? 1 : 0; - $range->getConfiguration()->store('COURSEWARE_SEQUENTIAL_PROGRESSION', $progressions); + $this->unit->config['sequential_progression'] = $isSequentialProgression ? 1 : 0; } const EDITING_PERMISSION_DOZENT = 'dozent'; @@ -195,10 +202,8 @@ class Instance */ public function getEditingPermissionLevel(): string { - $range = $this->getRange(); - $root = $this->getRoot(); /** @var string $editingPermissionLevel */ - $editingPermissionLevel = $range->getConfiguration()->COURSEWARE_EDITING_PERMISSION[$root->id]; + $editingPermissionLevel = $this->unit->config['editing_permission']; if ($editingPermissionLevel) { $this->validateEditingPermissionLevel($editingPermissionLevel); return $editingPermissionLevel; @@ -216,11 +221,7 @@ class Instance public function setEditingPermissionLevel(string $editingPermissionLevel): void { $this->validateEditingPermissionLevel($editingPermissionLevel); - $range = $this->getRange(); - $root = $this->getRoot(); - $permissions = $range->getConfiguration()->getValue('COURSEWARE_EDITING_PERMISSION'); - $permissions[$root->id] = $editingPermissionLevel; - $range->getConfiguration()->store('COURSEWARE_EDITING_PERMISSION', $permissions); + $this->unit->config['editing_permission'] = $editingPermissionLevel; } /** @@ -250,13 +251,10 @@ class Instance */ public function getCertificateSettings(): array { - $range = $this->getRange(); - $root = $this->getRoot(); /** @var array $certificateSettings */ - $certificateSettings = json_decode( - $range->getConfiguration()->COURSEWARE_CERTIFICATE_SETTINGS[$root->id], - true - )?: []; + $certificateSettings = isset($this->unit->config['certificate']) + ? $this->unit->config['certificate']->getArrayCopy() + : []; $this->validateCertificateSettings($certificateSettings); return $certificateSettings; @@ -269,27 +267,27 @@ class Instance */ public function setCertificateSettings(array $certificateSettings): void { - $this->validateCertificateSettings($certificateSettings); - $range = $this->getRange(); - $root = $this->getRoot(); - $settings = $range->getConfiguration()->getValue('COURSEWARE_CERTIFICATE_SETTINGS'); - $settings[$root->id] = count($certificateSettings) > 0 ? json_encode($certificateSettings) : null; - $range->getConfiguration()->store('COURSEWARE_CERTIFICATE_SETTINGS', $settings); + if (count($certificateSettings) > 0) { + $this->validateCertificateSettings($certificateSettings); + $this->unit->config['certificate'] = $certificateSettings; + } else { + unset($this->unit->config['certificate']); + } } /** * Validates certificate settings. * - * @param array $certificateSettings settings for certificate creation + * @param \JSONArrayObject $certificateSettings settings for certificate creation * * @return bool true if all given values are valid, false otherwise */ - public function isValidCertificateSettings(array $certificateSettings): bool + public function isValidCertificateSettings($certificateSettings): bool { return (int) $certificateSettings['threshold'] >= 0 && (int) $certificateSettings['threshold'] <= 100; } - private function validateCertificateSettings(array $certificateSettings): void + private function validateCertificateSettings($certificateSettings): void { if (!$this->isValidCertificateSettings($certificateSettings)) { throw new \InvalidArgumentException('Invalid certificate settings given.'); @@ -303,13 +301,10 @@ class Instance */ public function getReminderSettings(): array { - $range = $this->getRange(); - $root = $this->getRoot(); - /** @var int $reminderInterval */ - $reminderSettings = json_decode( - $range->getConfiguration()->COURSEWARE_REMINDER_SETTINGS[$root->id], - true - )?: []; + /** @var array $reminderSettings */ + $reminderSettings = isset($this->unit->config['reminder']) + ? $this->unit->config['reminder']->getArrayCopy() + : []; $this->validateReminderSettings($reminderSettings); return $reminderSettings; @@ -318,33 +313,34 @@ class Instance /** * Sets the reminder message settings this courseware instance. * - * @param array $reminderSettings an array of parameters + * @param \JSONArrayObject $reminderSettings an array of parameters */ - public function setReminderSettings(array $reminderSettings): void + public function setReminderSettings($reminderSettings): void { - $this->validateReminderSettings($reminderSettings); - $range = $this->getRange(); - $root = $this->getRoot(); - $settings = $range->getConfiguration()->getValue('COURSEWARE_REMINDER_SETTINGS'); - $settings[$root->id] = count($reminderSettings) > 0 ? json_encode($reminderSettings) : null; - $range->getConfiguration()->store('COURSEWARE_REMINDER_SETTINGS', $settings); + if (count($reminderSettings) > 0) { + $this->validateReminderSettings($reminderSettings); + $this->unit->config['reminder'] = $reminderSettings; + } else { + unset($this->unit->config['reminder']); + unset($this->unit->config['last_reminder']); + } } /** * Validates reminder message settings. * - * @param array $reminderSettings settings for reminder mail sending + * @param \JSONArrayObject $reminderSettings settings for reminder mail sending * * @return bool true if all given values are valid, false otherwise */ - public function isValidReminderSettings(array $reminderSettings): bool + public function isValidReminderSettings($reminderSettings): bool { $valid = in_array($reminderSettings['interval'], [0, 7, 14, 30, 90, 180, 365]); return $valid; } - private function validateReminderSettings(array $reminderSettings): void + private function validateReminderSettings($reminderSettings): void { if (!$this->isValidReminderSettings($reminderSettings)) { throw new \InvalidArgumentException('Invalid reminder settings given.'); @@ -358,13 +354,10 @@ class Instance */ public function getResetProgressSettings(): array { - $range = $this->getRange(); - $root = $this->getRoot(); - /** @var int $reminderInterval */ - $resetProgressSettings = json_decode( - $range->getConfiguration()->COURSEWARE_RESET_PROGRESS_SETTINGS[$root->id], - true - )?: []; + /** @var array $resetProgressSettings */ + $resetProgressSettings = isset($this->unit->config['reset_progress']) + ? $this->unit->config['reset_progress']->getArrayCopy() + : []; $this->validateResetProgressSettings($resetProgressSettings); return $resetProgressSettings; @@ -373,33 +366,34 @@ class Instance /** * Sets the progress resetting settings this courseware instance. * - * @param array $reminderSettings an array of parameters + * @param \JSONArrayObject $resetProgressSettings an array of parameters */ - public function setResetProgressSettings(array $resetProgressSettings): void + public function setResetProgressSettings($resetProgressSettings): void { - $this->validateResetProgressSettings($resetProgressSettings); - $range = $this->getRange(); - $root = $this->getRoot(); - $settings = $range->getConfiguration()->getValue('COURSEWARE_RESET_PROGRESS_SETTINGS'); - $settings[$root->id] = count($resetProgressSettings) > 0 ? json_encode($resetProgressSettings) : null; - $range->getConfiguration()->store('COURSEWARE_RESET_PROGRESS_SETTINGS', $settings); + if (count($resetProgressSettings) > 0) { + $this->validateResetProgressSettings($resetProgressSettings); + $this->unit->config['reset_progress'] = $resetProgressSettings; + } else { + unset($this->unit->config['reset_progress']); + unset($this->unit->config['last_progress_reset']); + } } /** * Validates progress resetting settings. * - * @param array $resetProgressSettings settings for progress resetting + * @param \JSONArrayObject $resetProgressSettings settings for progress resetting * * @return bool true if all given values are valid, false otherwise */ - public function isValidResetProgressSettings(array $resetProgressSettings): bool + public function isValidResetProgressSettings($resetProgressSettings): bool { $valid = in_array($resetProgressSettings['interval'], [0, 14, 30, 90, 180, 365]); return $valid; } - private function validateResetProgressSettings(array $resetProgressSettings): void + private function validateResetProgressSettings($resetProgressSettings): void { if (!$this->isValidResetProgressSettings($resetProgressSettings)) { throw new \InvalidArgumentException('Invalid progress resetting settings given.'); @@ -491,4 +485,5 @@ class Instance return $data; } + } diff --git a/lib/models/Courseware/Unit.php b/lib/models/Courseware/Unit.php index 5782d11c06306978b211b82a487c882924ddafaa..5c470c901ff893aa92819151d49a21dad7f58c23 100644 --- a/lib/models/Courseware/Unit.php +++ b/lib/models/Courseware/Unit.php @@ -11,7 +11,7 @@ use User; * @license GPL2 or any later version * * @since Stud.IP 5.3 - * + * * @property int $id database column * @property string $range_id database column * @property string $range_type database column @@ -21,11 +21,12 @@ use User; * @property string $creator_id database column * @property int $release_date database column * @property int $withdraw_date database column + * @property \JSONArrayObject $config database column * @property int $mkdate database column * @property int $chdate database column * @property \User $creator belongs_to User * @property \Courseware\StructuralElement $structural_element belongs_to Courseware\StructuralElement - * + * * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ @@ -36,6 +37,8 @@ class Unit extends \SimpleORMap { $config['db_table'] = 'cw_units'; + $config['serialized_fields']['config'] = 'JSONArrayObject'; + $config['has_one']['structural_element'] = [ 'class_name' => StructuralElement::class, 'foreign_key' => 'structural_element_id', @@ -102,7 +105,7 @@ class Unit extends \SimpleORMap 'release_date' => null, 'withdraw_date' => null, ]); - + $newUnit->store(); return $newUnit;