diff --git a/backup/moodle2/backup_vpl_stepslib.php b/backup/moodle2/backup_vpl_stepslib.php index 8cd7fcd81c9b367428906a0332487b467c5f4160..ac0879f59058f3a98fc699451860bc1c2f3be832 100644 --- a/backup/moodle2/backup_vpl_stepslib.php +++ b/backup/moodle2/backup_vpl_stepslib.php @@ -219,14 +219,22 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step */ protected $overridefields = array ( 'vpl', - 'userids', - 'groupids', 'startdate', 'duedate', 'freeevaluations', 'reductionbyevaluation' ); + /** + * @var array Assigned Overrides table fields list + */ + protected $assioverridefields = array ( + 'vpl', + 'userid', + 'groupid', + 'override' + ); + /** * Define the full structure of a VPL instance with user data * {@inheritDoc} @@ -256,6 +264,10 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step $this->asivariationfields ); $overrides = new backup_nested_element( 'overrides' ); $override = new backup_nested_element( 'override', $idfield, $this->overridefields ); + $assignedoverrides = new backup_nested_element( 'assigned_overrides' ); + $assignedoverride = new backup_nested_element( 'assigned_override', + $idfield, + $this->assioverridefields ); $submissions = new backup_nested_element( 'submissions' ); $submission = new backup_nested_element( 'submission', $idfield, $this->submissionfields ); $submissionfiles = new backup_nested_element( 'submission_files' ); @@ -266,6 +278,7 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step $vpl->add_child( $executionfiles ); $vpl->add_child( $variations ); $vpl->add_child( $overrides ); + $vpl->add_child( $assignedoverrides ); $vpl->add_child( $submissions ); $requiredfiles->add_child( $requiredfile ); $corrected_files->add_child( $corrected_file ); @@ -274,6 +287,7 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step $variation->add_child( $asignedvariations ); $asignedvariations->add_child( $asignedvariation ); $overrides->add_child( $override ); + $assignedoverrides->add_child( $assignedoverride ); $submissions->add_child( $submission ); $submission->add_child( $submissionfiles ); $submissionfiles->add_child( $submissionfile ); @@ -286,9 +300,10 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step $query .= ' WHERE s.id = ?'; $vpl->set_source_sql( $query, array ( backup::VAR_ACTIVITYID ) ); $variation->set_source_table( 'vpl_variations', $parmvplid ); + $override->set_source_table( 'vpl_overrides', $parmvplid ); if ($userinfo) { $asignedvariation->set_source_table( 'vpl_assigned_variations', $parmvplid ); - $override->set_source_table( 'vpl_overrides', $parmvplid ); + $assignedoverride->set_source_table( 'vpl_assigned_overrides', $parmvplid ); /* * Uncomment next line and comment nexts to backup all student's submissions, not only last one. * $submission->set_source_table('vpl_submissions', $parmvplid); @@ -305,6 +320,8 @@ class backup_vpl_activity_structure_step extends backup_activity_structure_step $vpl->annotate_ids( 'scale', 'grade' ); $vpl->annotate_ids( 'vpl', 'basedon' ); $asignedvariation->annotate_ids( 'user', 'userid' ); + $assignedoverride->annotate_ids( 'user', 'userid' ); + $assignedoverride->annotate_ids( 'group', 'groupid' ); $submission->annotate_ids( 'user', 'userid' ); $submission->annotate_ids( 'user', 'grader' ); $submission->annotate_ids( 'group', 'groupid' ); diff --git a/backup/moodle2/restore_vpl_stepslib.php b/backup/moodle2/restore_vpl_stepslib.php index ecca1d14538eeefa0c58c741accd1741566a9e52..6b4a65d310d63dbe3dfa0ad8c71e0a14cda9735b 100644 --- a/backup/moodle2/restore_vpl_stepslib.php +++ b/backup/moodle2/restore_vpl_stepslib.php @@ -80,9 +80,10 @@ class restore_vpl_activity_structure_step extends restore_activity_structure_ste $paths [] = new restore_path_element ( 'corrected_file', '/activity/vpl/corrected_files/corrected_file' ); $paths [] = new restore_path_element ( 'execution_file', '/activity/vpl/execution_files/execution_file' ); $paths [] = new restore_path_element ( 'variation', '/activity/vpl/variations/variation' ); + $paths [] = new restore_path_element ( 'override', '/activity/vpl/overrides/override' ); if ($userinfo) { $paths [] = new restore_path_element ( 'assigned_variation', '/activity/vpl/assigned_variations/assigned_variation' ); - $paths [] = new restore_path_element ( 'override', '/activity/vpl/overrides/override' ); + $paths [] = new restore_path_element ( 'assigned_override', '/activity/vpl/assigned_overrides/assigned_override' ); $paths [] = new restore_path_element ( 'submission', '/activity/vpl/submissions/submission' ); $paths [] = new restore_path_element ( 'submission_file', @@ -203,7 +204,25 @@ class restore_vpl_activity_structure_step extends restore_activity_structure_ste $data->vpl = $this->get_new_parentid ( 'vpl' ); $data->startdate = $this->apply_date_offset ( $data->startdate ); $data->duedate = $this->apply_date_offset ( $data->duedate ); - $DB->insert_record ( 'vpl_overrides', $data ); + $newid = $DB->insert_record ( 'vpl_overrides', $data ); + $this->set_mapping('override', $data->id, $newid); // Map new id to be used by process_assigned_override(). + } + + /** + * Restore an override assignation + * @param array $data assigned override instance + */ + protected function process_assigned_override($data) { + global $DB; + $data = ( object ) $data; + $newid = $this->get_mappingid('override', $data->override, null); // Fetch new override id. + if ($newid !== null) { + $data->vpl = $this->get_new_parentid ( 'vpl' ); + $data->override = $newid; + $data->userid = $this->get_mappingid ( 'user', $data->userid, null ); + $data->groupid = $this->get_mappingid ( 'group', $data->groupid, null ); + $DB->insert_record ( 'vpl_assigned_overrides', $data ); + } } /** @@ -255,12 +274,5 @@ class restore_vpl_activity_structure_step extends restore_activity_structure_ste */ protected function after_execute() { $this->add_related_files('mod_vpl', 'intro', null); - global $DB; - require_once(dirname(__FILE__).'/../../vpl.class.php'); - $overrides = $DB->get_records( 'vpl_overrides', array('vpl' => $this->get_new_parentid ( 'vpl' )) ); - foreach ($overrides as $override) { - $vpl = new mod_vpl(null, $override->vpl); - $vpl->update_override_calendar_events($override, null); - } } } \ No newline at end of file diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index eeb0e5df1caf5f98ba407286191de980ea014de6..c3707006742dbf2724a87c6ac76f70d655e34448 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -32,6 +32,7 @@ use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\contextlist; use core_privacy\local\request\transform; use core_privacy\local\request\writer; +use \core_privacy\local\request\content_writer; use core_privacy\local\request\userlist; use \core_privacy\local\request\approved_userlist; @@ -57,34 +58,37 @@ class provider implements \core_privacy\local\metadata\provider, 'name' => 'privacy:metadata:vpl:name', ]; $submisionsfields = [ - 'vpl' => 'privacy:metadata:vpl_assigned_variations:vplid', 'userid' => 'privacy:metadata:vpl_submissions:userid', 'groupid' => 'privacy:metadata:vpl_submissions:groupid', 'datesubmitted' => 'privacy:metadata:vpl_submissions:datesubmitted', 'comments' => 'privacy:metadata:vpl_submissions:studentcomments', - 'dategraded' => 'privacy:metadata:vpl_submissions:dategraded', - 'grade' => 'privacy:metadata:vpl_submissions:grade', 'nevaluations' => 'privacy:metadata:vpl_submissions:nevaluations', - 'submission' => 'privacy:metadata:vpl_submissions:filessubmitted', - 'gradercomments' => 'privacy:metadata:vpl_submissions:gradercomments', - ]; - $evaluationfields = [ - 'vpl' => 'privacy:metadata:vpl_assigned_variations:vplid', - 'grader' => 'privacy:metadata:vpl_submissions:graderid', - 'datesubmitted' => 'privacy:metadata:vpl_submissions:datesubmitted', 'dategraded' => 'privacy:metadata:vpl_submissions:dategraded', 'grade' => 'privacy:metadata:vpl_submissions:grade', + 'grader' => 'privacy:metadata:vpl_submissions:graderid', 'gradercomments' => 'privacy:metadata:vpl_submissions:gradercomments', ]; $variationsfields = [ 'userid' => 'privacy:metadata:vpl_assigned_variations:userid', 'vpl' => 'privacy:metadata:vpl_assigned_variations:vplid', - 'variation' => 'privacy:metadata:vpl_assigned_variations:variationdescription', + 'variation' => 'privacy:metadata:vpl_assigned_variations:description', + ]; + $overridesfields = [ + 'vpl' => 'privacy:metadata:vpl_assigned_overrides:vplid', + 'userid' => 'privacy:metadata:vpl_assigned_overrides:userid', + 'override' => 'privacy:metadata:vpl_assigned_overrides:overrideid', + ]; + $runningfields = [ + 'userid' => 'privacy:metadata:vpl_running_processes:userid', + 'vpl' => 'privacy:metadata:vpl_running_processes:vplid', + 'server' => 'privacy:metadata:vpl_running_processes:server', + 'start_time' => 'privacy:metadata:vpl_running_processes:starttime', ]; $collection->add_database_table('vpl', $vplfields, 'privacy:metadata:vpl'); $collection->add_database_table('vpl_submissions', $submisionsfields, 'privacy:metadata:vpl_submissions'); - $collection->add_database_table('vpl_evaluations', $evaluationfields, 'privacy:metadata:vpl_evaluations'); $collection->add_database_table('vpl_assigned_variations', $variationsfields, 'privacy:metadata:vpl_assigned_variations'); + $collection->add_database_table('vpl_assigned_overrides', $overridesfields, 'privacy:metadata:vpl_assigned_overrides'); + $collection->add_database_table('vpl_running_processes', $runningfields, 'privacy:metadata:vpl_running_processes'); // IDE user preferences. $collection->add_user_preference('vpl_editor_fontsize', 'privacy:metadata:vpl_editor_fontsize'); $collection->add_user_preference('vpl_acetheme', 'privacy:metadata:vpl_acetheme'); @@ -106,6 +110,8 @@ class provider implements \core_privacy\local\metadata\provider, self::add_contexts_for_submissions($contextlist, $userid); self::add_contexts_for_evaluations($contextlist, $userid); self::add_contexts_for_variations($contextlist, $userid); + self::add_contexts_for_overrides($contextlist, $userid); + self::add_contexts_for_running($contextlist, $userid); return $contextlist; } @@ -129,44 +135,120 @@ class provider implements \core_privacy\local\metadata\provider, continue; } - $vpl = self::get_vpl_by_context($context); - if ($vpl === null) { + $vplinstance = self::get_vpl_by_context($context); + if ($vplinstance === null) { continue; } + $vplid = $vplinstance->id; $contentwriter = writer::with_context($context); + self::export_vpl_data($contentwriter, $vplinstance); + self::export_user_assigned_variation_data($contentwriter, $vplid, $userid); + self::export_user_assigned_override_data($contentwriter, $vplid, $userid); + self::export_user_submissions_data($contentwriter, $vplid, $userid); + self::export_user_running_processes_data($contentwriter, $vplid, $userid); + } + } + + /** + * Export vpl description for related personal data. + * + * @param content_writer $contentwriter data writer object. + * @param object $vplinstance vpl instance + */ + public static function export_vpl_data(content_writer $contentwriter, $vplinstance) { + // Get vpl details object for output. + $vploutput = self::get_vpl_output($vplinstance); + $contentwriter->export_data([], $vploutput); + } + /** + * Export variation for related personal data. + * + * @param content_writer $contentwriter data writer object. + * @param int $vplid vpl DB id + * @param int $userid user DB id + */ + public static function export_user_assigned_variation_data(content_writer $contentwriter, int $vplid, int $userid) { + // Get assigned variation related to the user if any. + $variation = self::get_assigned_variation_by_vpl_and_user($vplid, $userid); + if (count($variation) == 1) { + $variationoutput = self::get_vpl_assigned_variation_output(reset($variation)); + $contentwriter->export_data([get_string('privacy:variationpath', 'vpl')], $variationoutput); + } + } - // Get vpl details object for output. - $vploutput = self::get_vpl_output($vpl); - $contentwriter->export_data([], $vploutput); - - // Get the vpl submissions of a vpl related to a user. - $submissions = self::get_vpl_submissions_by_vpl_and_user($vpl->id, $userid); - - foreach ($submissions as $submission) { - if ($submission->userid == $userid) { - $subcontexts = [ - $submission->id - ]; - $dataoutput = self::get_vpl_submission_output($submission); - $contentwriter->export_data($subcontexts, $dataoutput); - // TODO read files - $files = []; - foreach ($files as $filename => $filecontent) { - $contentwriter->export_custom_file($subcontexts, $filename, $filecontent); - } + /** + * Export override for related personal data. + * + * @param content_writer $contentwriter data writer object. + * @param int $vplid vpl DB id + * @param int $userid user DB id + */ + public static function export_user_assigned_override_data(content_writer $contentwriter, int $vplid, int $userid) { + // Get assigned override related to the user if any. + $override = self::get_assigned_override_by_vpl_and_user($vplid, $userid); + if (count($override) == 1) { + $overrideoutput = self::get_vpl_assigned_override_output(reset($override)); + $contentwriter->export_data([get_string('privacy:overridepath', 'vpl')], $overrideoutput); + } + } + + /** + * Export submissions personal data. + * + * @param content_writer $contentwriter data writer object. + * @param int $vplid vpl DB id + * @param int $userid user DB id + */ + public static function export_user_submissions_data(content_writer $contentwriter, int $vplid, int $userid) { + // Get the vpl submissions related to the user. + $vpl = new \mod_vpl(false, $vplid); + $submissions = self::get_vpl_submissions_by_vpl_and_user($vplid, $userid); + $zipfilename = get_string('submission', 'vpl') . '.zip'; + $subcontextsecuence = 1; + foreach ($submissions as $submissioninstance) { + $subcontext = [ get_string('privacy:submissionpath', 'vpl', $subcontextsecuence++) ]; + $submission = new \mod_vpl_submission_CE($vpl, $submissioninstance); + $nograder = $submissioninstance->userid == $userid; + if ( $submissioninstance->dategraded > 0) { + $submissioninstance->gradercomments = $submission->get_grade_comments($nograder); + if ( $nograder ) { + unset($submissioninstance->grader); + } else { + unset($submissioninstance->userid); } - if ($submission->grader == $userid) { - $subcontexts = [ - $submission->id - ]; - $dataoutput = self::get_vpl_evaluation_output($submission); - writer::with_context($context)->export_data($subcontexts, $dataoutput); + } + $dataoutput = self::get_vpl_submission_output($submissioninstance); + $contentwriter->export_data($subcontext, $dataoutput); + if ($nograder) { + $tempfilename = $submission->get_submitted_fgm()->generate_zip_file(); + if ($tempfilename !== false) { + $filecontent = file_get_contents($tempfilename); + $contentwriter->export_custom_file($subcontext, $zipfilename, $filecontent); + unlink($tempfilename); } } } } + /** + * Export running processes personal data. + * + * @param content_writer $contentwriter data writer object. + * @param int $vplid vpl DB id + * @param int $userid user DB id + */ + public static function export_user_running_processes_data(content_writer $contentwriter, int $vplid, int $userid) { + // Get the vpl submissions related to the user. + $runningprocesses = self::get_running_processes_by_vpl_and_user($vplid, $userid); + $subcontextsecuence = 1; + foreach ($runningprocesses as $runningprocess) { + $subcontext = [ get_string('privacy:runningprocesspath', 'vpl', $subcontextsecuence++) ]; + $dataoutput = self::get_vpl_running_process_output($runningprocess); + $contentwriter->export_data($subcontext, $dataoutput); + } + } + /** * Exports user preferences of mod_vpl. * @@ -175,14 +257,10 @@ class provider implements \core_privacy\local\metadata\provider, public static function export_user_preferences(int $userid) { $context = \context_system::instance(); - $preferences = ['vpl_editor_fontsize', 'vpl_acetheme', 'vpl_terminaltheme']; - foreach ($preferences as $key) { - $value = get_user_preferences($key, null, $userid); - if (isset($value)) { - $str = get_string('privacy:metadata:' . $key, 'mod_vpl'); - writer::with_context($context) - ->export_user_preference('mod_vpl', $key, $value, $str); - } + $preferences = self::get_user_preferences($userid); + foreach ($preferences as $key => $value) { + $str = get_string('privacy:metadata:' . $key, 'mod_vpl'); + writer::with_context($context)->export_user_preference('mod_vpl', $key, $value, $str); } } @@ -244,9 +322,18 @@ class provider implements \core_privacy\local\metadata\provider, list($insql, $inparams) = $DB->get_in_or_equal($evaluationids); $sql = "UPDATE {vpl_submissions} s SET s.grader = 0 - WHERE s.id $insql;"; + WHERE s.id $insql"; $DB->execute($sql, $inparams); } + + // Delete asigned variations. + self::delete_assigned_variations_by_contextlist($contextlist, $userid); + + // Delete asigned overrides. + self::delete_assigned_overrides_by_contextlist($contextlist, $userid); + + // Delete running processes. + self::delete_running_processes_by_contextlist($contextlist, $userid); } /** @@ -256,6 +343,7 @@ class provider implements \core_privacy\local\metadata\provider, * @return void */ public static function get_users_in_context(userlist $userlist): void { + global $DB; $context = $userlist->get_context(); if (!($context instanceof \context_module)) { @@ -272,7 +360,7 @@ class provider implements \core_privacy\local\metadata\provider, FROM {vpl_submissions} s JOIN {course_modules} cm ON s.vpl = cm.instance JOIN {modules} m ON m.id = cm.module - WHERE cm.id = :instanceid AND m.name = :modulename;"; + WHERE cm.id = :instanceid AND m.name = :modulename"; $userlist->add_from_sql('userid', $sql, $params); // Graders. @@ -280,7 +368,7 @@ class provider implements \core_privacy\local\metadata\provider, FROM {vpl_submissions} s JOIN {course_modules} cm ON s.vpl = cm.instance JOIN {modules} m ON m.id = cm.module - WHERE s.grader > 0 AND cm.id = :instanceid AND m.name = :modulename;"; + WHERE s.grader > 0 AND cm.id = :instanceid AND m.name = :modulename"; $userlist->add_from_sql('grader', $sql, $params); // Variations assigned. @@ -288,7 +376,23 @@ class provider implements \core_privacy\local\metadata\provider, FROM {vpl_assigned_variations} av JOIN {course_modules} cm ON av.vpl = cm.instance JOIN {modules} m ON m.id = cm.module - WHERE cm.id = :instanceid AND m.name = :modulename;"; + WHERE cm.id = :instanceid AND m.name = :modulename"; + $userlist->add_from_sql('userid', $sql, $params); + + // Assigned overrides. + $sql = "SELECT DISTINCT ao.userid + FROM {vpl_assigned_overrides} ao + JOIN {course_modules} cm ON ao.vpl = cm.instance + JOIN {modules} m ON m.id = cm.module + WHERE cm.id = :instanceid AND m.name = :modulename"; + $userlist->add_from_sql('userid', $sql, $params); + + // Running process. + $sql = "SELECT DISTINCT rp.userid + FROM {vpl_running_processes} rp + JOIN {course_modules} cm ON rp.vpl = cm.instance + JOIN {modules} m ON m.id = cm.module + WHERE cm.id = :instanceid AND m.name = :modulename"; $userlist->add_from_sql('userid', $sql, $params); } @@ -300,46 +404,51 @@ class provider implements \core_privacy\local\metadata\provider, public static function delete_data_for_users(approved_userlist $userlist):void { global $DB, $CFG; - $context = $userlist->get_context(); - list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); - - $cm = $DB->get_record('course_modules', ['id' => $context->instanceid]); - - $params = array_merge(['chatid' => $chat->id], $userinparams); - $sql = "chatid = :chatid AND userid {$userinsql}"; - - // For each context retrieve VPL and remove user submissions and related directories. - $submissions = self::get_vpl_submissions_by_contextlist($contextlist, $userid); - // Submisions ids of the $userid. - $submissionids = []; - // Submisions ids of evaluations of the $userid. - $evaluationids = []; - // VPL ids of the submisisions to delete - $vplids = []; - foreach ($submissions as $submission) { - if ($submission->userid == $userid) { - $submissionids[] = $submission->id; - $vplids[$submission->vpl] = true; - } - if ($submission->grader == $userid) { - $evaluationids[] = $submission->id; - } + if ($userlist->count() === 0) { + return; } - // Delete submissions - $DB->delete_records_list('vpl_submissions', 'id', $submissionids); - foreach (array_keys($vplids) as $vplid) { - fulldelete( $CFG->dataroot . '/vpl_data/'. $vplid . '/usersdata/' . $userid ); + $vplinstace = self::get_vpl_by_context($userlist->get_context()); + if ($vplinstace === null) { + return; } + $vplid = $vplinstace->id; + $userids = $userlist->get_userids(); - // Change grader as 0 - if (count($evaluationids) > 0) { - list($insql, $inparams) = $DB->get_in_or_equal($evaluationids); - $sql = "UPDATE {vpl_submissions} s - SET s.grader = 0 - WHERE s.id $insql;"; - $DB->execute($sql, $inparams); + $params = [ + 'vplid' => $vplid, + ]; + // Get sql partial where of users ids. + list($userssql, $usersparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + + // Delete selected submissions. + $sql = "DELETE + FROM {vpl_submissions} + WHERE vpl = :vplid AND userid {$userssql}"; + $DB->execute($sql, $params + $usersparams); + foreach ($userids as $userid) { + fulldelete( $CFG->dataroot . '/vpl_data/'. $vplid . '/usersdata/' . $userid ); } + // Anonymizes graders identification. + $sql = "UPDATE {vpl_submissions} + SET grader = 0 + WHERE vpl = :vplid AND grader {$userssql}"; + $DB->execute($sql, $params + $usersparams); + // Delete related assigned variations. + $sql = "DELETE + FROM {vpl_assigned_variations} + WHERE vpl = :vplid AND userid {$userssql}"; + $DB->execute($sql, $params + $usersparams); + // Delete related assigned overrides. + $sql = "DELETE + FROM {vpl_assigned_overrides} + WHERE vpl = :vplid AND userid {$userssql}"; + $DB->execute($sql, $params + $usersparams); + // Delete related running processes. + $sql = "DELETE + FROM {vpl_running_processes} + WHERE vpl = :vplid AND userid {$userssql}"; + $DB->execute($sql, $params + $usersparams); } // Start of helper functions. @@ -352,8 +461,7 @@ class provider implements \core_privacy\local\metadata\provider, * @return void. */ protected static function add_contexts_for_submissions(contextlist $list, int $userid) : void { - $sql = "SELECT DISTINCT - ctx.id + $sql = "SELECT DISTINCT ctx.id FROM {context} ctx JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule JOIN {modules} m ON cm.module = m.id AND m.name = :modulename @@ -418,24 +526,68 @@ class provider implements \core_privacy\local\metadata\provider, } /** - * Return if a user has graded submissions for a given VPL activity. + * Add contexts for assigned overrides to the specified user. * - * @param int $vplid The id of the VPL to check. - * @param int $userid The id of the user. - * @return bool If user has graded submissions returns true, otherwise false. - * @throws \dml_exception + * @param contextlist $list the list of context. + * @param int $userid the userid. + * @return void. */ - protected static function has_graded_vpl_submissions($vplid, $userid) { - global $DB; - if ( $userid < 1 ) { - return false; - } + protected static function add_contexts_for_overrides(contextlist $list, int $userid) : void { + $sql = "SELECT DISTINCT ctx.id + FROM {context} ctx + JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {vpl_assigned_overrides} ao ON ao.vpl = cm.instance + WHERE ao.userid = :userid"; + $params = [ - 'vpl' => $vplid, - 'grader' => $userid + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, ]; - $marks = $DB->count_records('vpl_submissions', $params); - return $marks > 0; + + $list->add_from_sql($sql, $params); + } + + /** + * Adds contexts of running process for the specified user. + * + * @param contextlist $list the list of context. + * @param int $userid the userid. + * @return void. + */ + protected static function add_contexts_for_running(contextlist $list, int $userid): void { + $sql = "SELECT DISTINCT ctx.id + FROM {context} ctx + JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {vpl_running_processes} rp ON rp.vpl = cm.instance + WHERE rp.userid = :userid"; + + $params = [ + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, + ]; + + $list->add_from_sql($sql, $params); + } + + /** + * Returns preference key => value for the user + * + * @param int $userid The userid of the preferences to return + */ + protected static function get_user_preferences(int $userid): array { + $pref = array(); + $preferences = ['vpl_editor_fontsize', 'vpl_acetheme', 'vpl_terminaltheme']; + foreach ($preferences as $key) { + $value = get_user_preferences($key, null, $userid); + if (isset($value)) { + $pref[$key] = $value; + } + } + return $pref; } /** @@ -450,11 +602,10 @@ class provider implements \core_privacy\local\metadata\provider, $params = [ 'modulename' => 'vpl', - 'contextmodule' => CONTEXT_MODULE, 'coursemoduleid' => $context->instanceid ]; - $sql = "SELECT * + $sql = "SELECT v.* FROM {vpl} v JOIN {course_modules} cm ON v.id = cm.instance AND cm.id = :coursemoduleid JOIN {modules} m ON m.id = cm.module AND m.name = :modulename;"; @@ -473,30 +624,117 @@ class provider implements \core_privacy\local\metadata\provider, */ protected static function get_vpl_submissions_by_contextlist($contextlist, $userid) { global $DB; -//TODO Finish - // Get vplids for submissions search. + // Get sql partial where of contexts. list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); $params = [ - 'contextmodule' => CONTEXT_MODULE, - 'modulename' => 'vpl', + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, + 'grader' => $userid, ]; - $sql = "SELECT vpl.id as id - FROM {context} ctx - JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule - JOIN {modules} m ON cm.module = m.id AND m.name = :modulename - JOIN {vpl} vpl ON cm.instance = a.id"; - $sql .= " WHERE ctx.id {$contextsql}"; + $sql = " SELECT s.id, s.vpl, s.userid, s.grader + FROM {vpl_submissions} s + JOIN {vpl} v ON v.id = s.vpl + JOIN {course_modules} cm ON cm.instance = v.id + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + WHERE (s.userid = :userid OR s.grader = :grader) AND ctx.id {$contextsql}"; $params += $contextparams; - $vplids = $DB->get_records_sql($sql, $params); - if ($teacher == true) { - $sql .= " OR s.teacher = :teacher"; - $params['teacher'] = $userid; - } return $DB->get_records_sql($sql, $params); } + /** + * Delete the assigned variations for the user and their contextlist. + * + * @param object $contextlist Object with the contexts related to a userid. + * @param int $userid The user ID. + */ + protected static function delete_assigned_variations_by_contextlist($contextlist, $userid) { + global $DB; + // Get sql partial where of contexts. + list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + + $params = [ + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, + ]; + + $sql = "DELETE + FROM {vpl_assigned_variations} + WHERE userid = :userid AND + vpl IN ( + SELECT cm.instance + FROM {course_modules} cm + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + WHERE ctx.id {$contextsql} )"; + $params += $contextparams; + $DB->execute($sql, $params); + } + + /** + * Delete the assigned overrides for the user and their contextlist. + * + * @param object $contextlist Object with the contexts related to a userid. + * @param int $userid The user ID. + */ + protected static function delete_assigned_overrides_by_contextlist($contextlist, $userid) { + global $DB; + // Get sql partial where of contexts. + list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + + $params = [ + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, + ]; + + $sql = "DELETE + FROM {vpl_assigned_overrides} + WHERE userid = :userid AND + vpl IN ( + SELECT cm.instance + FROM {course_modules} cm + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + WHERE ctx.id {$contextsql} )"; + $params += $contextparams; + $DB->execute($sql, $params); + } + + /** + * Delete running processes for the user and their contextlist. + * + * @param object $contextlist Object with the contexts related to a userid. + * @param int $userid The user ID. + */ + protected static function delete_running_processes_by_contextlist($contextlist, $userid) { + global $DB; + // Get sql partial where of contexts. + list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + + $params = [ + 'contextmodule' => CONTEXT_MODULE, + 'modulename' => 'vpl', + 'userid' => $userid, + ]; + + $sql = "DELETE + FROM {vpl_running_processes} + WHERE userid = :userid AND + vpl IN ( + SELECT cm.instance + FROM {course_modules} cm + JOIN {modules} m ON cm.module = m.id AND m.name = :modulename + JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule + WHERE ctx.id {$contextsql} )"; + $params += $contextparams; + $DB->execute($sql, $params); + } + /** * Helper function to retrieve vpl submissions related with user (submitted or graded). * @@ -511,7 +749,7 @@ class provider implements \core_privacy\local\metadata\provider, $params = [ 'vplid' => $vplid, 'userid' => $userid, - 'graderid' => $userid + 'grader' => $userid ]; $sql = "SELECT * @@ -521,6 +759,100 @@ class provider implements \core_privacy\local\metadata\provider, return $DB->get_records_sql($sql, $params); } + /** + * Helper function to retrieve assigned variation related with user. + * + * @param int $vplid The vpl id to retrieve variation. + * @param int $userid The user id to retrieve assigned variation. + * @return array Array of assigned variation details. + * @throws \dml_exception + */ + protected static function get_assigned_variation_by_vpl_and_user($vplid, $userid) { + global $DB; + $params = [ + 'vplid' => $vplid, + 'userid' => $userid, + ]; + $sql = "SELECT * + FROM {vpl_variations} v + JOIN {vpl_assigned_variations} av ON v.id = av.variation + WHERE av.vpl = :vplid AND av.userid = :userid"; + return $DB->get_records_sql($sql, $params); + } + + /** + * Helper function to retrieve assigned override related with user. + * + * @param int $vplid The vpl id to retrieve override. + * @param int $userid The user id to retrieve assigned override. + * @return array Array of assigned override details. + * @throws \dml_exception + */ + protected static function get_assigned_override_by_vpl_and_user($vplid, $userid) { + global $DB; + $params = [ + 'vplid' => $vplid, + 'userid' => $userid, + ]; + $sql = "SELECT ao.id as aoid, ao.userid, o.* + FROM {vpl_assigned_overrides} ao + JOIN {vpl_overrides} o ON ao.override = o.id + WHERE ao.vpl = :vplid AND ao.userid = :userid"; + return $DB->get_records_sql($sql, $params); + } + + /** + * Helper function to retrieve running processes of a user. + * + * @param int $vplid The vpl id to retrieve running processes. + * @param int $userid The user id to retrieve running processes. + * @return array Array of running processes details. + * @throws \dml_exception + */ + protected static function get_running_processes_by_vpl_and_user($vplid, $userid) { + global $DB; + $params = [ + 'vplid' => $vplid, + 'userid' => $userid, + ]; + $sql = "SELECT * + FROM {vpl_running_processes} rp + WHERE rp.vpl = :vplid AND rp.userid = :userid"; + return $DB->get_records_sql($sql, $params); + } + + /** + * Helper function to copy object fields + * + * @param object $from Object containing data. + * @param object $to Object to modify. + * @param array $fiels List of fields to copy. + * @return void + */ + protected static function copy_fields($from, $to, $fields) { + foreach ($fields as $field) { + if (isset($from->$field)) { + $to->$field = $from->$field; + } + } + } + + /** + * Helper function to copy and convert object date fields + * + * @param object $from Object containing data. + * @param object $to Object to modify. + * @param array $fiels List of fields to copy. + * @return void + */ + protected static function copy_date_fields($from, $to, $datefields) { + foreach ($datefields as $field) { + if (isset($from->$field) && $from->$field > 0) { + $to->$field = transform::datetime($from->$field); + } + } + } + /** * Helper function generate vpl output object for exporting. * @@ -528,9 +860,25 @@ class provider implements \core_privacy\local\metadata\provider, * @return object Formatted vpl output object for exporting. */ protected static function get_vpl_output($vpldata) { - $vpl = (object) [ - 'name' => $vpldata->name, - ]; + $vpl = new \stdClass; + $fields = ['id', 'course', 'name', 'shortdescription']; + $datefields = ['startdate', 'duedate']; + self::copy_fields($vpldata, $vpl, $fields); + if ($vpldata->grade != 0) { // If 0 then NO GRADE. + if ($vpldata->grade > 0) { + $vpl->grade = get_string('grademax', 'core_grades') + . ': ' . format_float($vpldata->grade, 5, true, true); + } else { + $vpl->grade = get_string( 'typescale', 'core_grades' ); + } + if ($vpldata->reductionbyevaluation != 0) { // If penalizaions for automatic evaluation requests. + $vpl->reductionbyevaluation = $vpldata->reductionbyevaluation; + $vpl->freeevaluations = $vpldata->freeevaluations; + } + } else { + $vpl->grade = get_string('nograde'); + } + self::copy_date_fields($vpldata, $vpl, $datefields); return $vpl; } @@ -541,13 +889,59 @@ class provider implements \core_privacy\local\metadata\provider, * @return object Formatted vpl submission output for exporting. */ protected static function get_vpl_submission_output($submission) { + $subfields = ['userid', 'groupid', 'comments', 'nevaluations']; + $gradefields = ['grade', 'gradercomments']; + $datefields = ['datesubmitted', 'dategraded']; + $data = new \stdClass(); + self::copy_fields($submission, $data, $subfields); + self::copy_date_fields($submission, $data, $datefields); + if ($submission->dategraded > 0) { + self::copy_fields($submission, $data, $gradefields); + } + return $data; } + /** - * Helper function generate vpl evaluation output object for exporting. + * Helper function generate assigned variation output object for exporting. * - * @param object $submission Object containing an instance record of vpl submission. - * @return object Formatted vpl evaluation output for exporting. + * @param object $assignedvariation Object containing an instance record of assigned variation. + * @return object Formatted assigned variation output for exporting. + */ + protected static function get_vpl_assigned_variation_output($assignedvariation) { + $fields = ['userid', 'vpl']; + $data = new \stdClass(); + self::copy_fields($assignedvariation, $data, $fields); + $data->variation = $assignedvariation->description; + return $data; + } + + /** + * Helper function generate assigned override output object for exporting. + * + * @param object $assignedoverride Object containing an instance record of assigned override. + * @return object Formatted assigned override output for exporting. + */ + protected static function get_vpl_assigned_override_output($assignedoverride) { + $fields = ['userid', 'vpl', 'reductionbyevaluation', 'freeevaluations']; + $datefields = ['startdate', 'duedate']; + $data = new \stdClass(); + self::copy_fields($assignedoverride, $data, $fields); + self::copy_date_fields($assignedoverride, $data, $datefields); + return $data; + } + + /** + * Helper function generate running process output object for exporting. + * + * @param object $assignedvariation Object containing an instance record of running process. + * @return object Formatted running process output for exporting. */ - protected static function get_vpl_evaluation_output($submission) { + protected static function get_vpl_running_process_output($runningprocess) { + $fields = ['userid', 'vpl', 'server']; + $data = new \stdClass(); + self::copy_fields($runningprocess, $data, $fields); + self::copy_date_fields($runningprocess, $data, ['start_time']); + $data->server = parse_url($data->server, PHP_URL_HOST); + return $data; } } \ No newline at end of file diff --git a/db/install.xml b/db/install.xml index a6398f9e749c25e52bf6c46c6e4615502c9be82b..fe5c69a5c5ce66674aab95b9412192077acc97dc 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<XMLDB PATH="mod/vpl/db" VERSION="20210526" COMMENT="XMLDB file for Moodle mod/vpl" +<XMLDB PATH="mod/vpl/db" VERSION="20210616" COMMENT="XMLDB file for Moodle mod/vpl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" > @@ -132,12 +132,10 @@ <INDEX NAME="userid_id" UNIQUE="true" FIELDS="userid, id" COMMENT="Index for userid id"/> </INDEXES> </TABLE> - <TABLE NAME="vpl_overrides" COMMENT="Overrides to VPL settings assigned to users"> + <TABLE NAME="vpl_overrides" COMMENT="Overrides to VPL settings"> <FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <FIELD NAME="vpl" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="VPL id"/> - <FIELD NAME="userids" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Users this override applies to"/> - <FIELD NAME="groupids" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Groups this override applies to"/> <FIELD NAME="startdate" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="If set, date the vpl will be available"/> <FIELD NAME="duedate" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="If set, after this date this vpl instance will not be available"/> <FIELD NAME="freeevaluations" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="If set, number of automatic evaluations that do not reduce final score"/> @@ -146,6 +144,25 @@ <KEYS> <KEY NAME="primary" TYPE="primary" FIELDS="id"/> </KEYS> + <INDEXES> + <INDEX NAME="vpl" UNIQUE="false" FIELDS="vpl"/> + </INDEXES> + </TABLE> + <TABLE NAME="vpl_assigned_overrides" COMMENT="Overrides assignations to users and groups"> + <FIELDS> + <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> + <FIELD NAME="vpl" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="VPL id"/> + <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="User this override applies to"/> + <FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Group this override applies to"/> + <FIELD NAME="override" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Override id"/> + </FIELDS> + <KEYS> + <KEY NAME="primary" TYPE="primary" FIELDS="id"/> + </KEYS> + <INDEXES> + <INDEX NAME="vpl" UNIQUE="false" FIELDS="vpl"/> + <INDEX NAME="override" UNIQUE="false" FIELDS="override"/> + </INDEXES> </TABLE> </TABLES> </XMLDB> \ No newline at end of file diff --git a/db/upgrade.php b/db/upgrade.php index c75f00ea1d05a8e8d6de11e6b8783bcbe7ddf879..f4fcc6aa0f6e0d4fb587c78849687d8627f8406c 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -349,5 +349,63 @@ function xmldb_vpl_upgrade($oldversion = 0) { // Vpl savepoint reached. upgrade_mod_savepoint(true, 2021052600, 'vpl'); } + if ($oldversion < 2021061400) { + + // Define index vpl (not unique) to be added to vpl_overrides. + $table = new xmldb_table('vpl_overrides'); + $index = new xmldb_index('vpl', XMLDB_INDEX_NOTUNIQUE, ['vpl']); + + // Conditionally launch add index vpl. + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + + // Vpl savepoint reached. + upgrade_mod_savepoint(true, 2021061400, 'vpl'); + } + if ($oldversion < 2021061600) { + + // Define table vpl_assigned_overrides to be created. + $table = new xmldb_table('vpl_assigned_overrides'); + + // Adding fields to table vpl_assigned_overrides. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('vpl', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('groupid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('override', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + + // Adding keys to table vpl_assigned_overrides. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table vpl_assigned_overrides. + $table->add_index('vpl', XMLDB_INDEX_NOTUNIQUE, ['vpl']); + $table->add_index('override', XMLDB_INDEX_NOTUNIQUE, ['override']); + + // Conditionally launch create table for vpl_assigned_overrides. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define field userids to be dropped from vpl_overrides. + $table = new xmldb_table('vpl_overrides'); + $field = new xmldb_field('userids'); + + // Conditionally launch drop field userids. + if ($dbman->field_exists($table, $field)) { + $dbman->drop_field($table, $field); + } + + // Define field groupids to be dropped from vpl_overrides. + $field = new xmldb_field('groupids'); + + // Conditionally launch drop field groupids. + if ($dbman->field_exists($table, $field)) { + $dbman->drop_field($table, $field); + } + + // Vpl savepoint reached. + upgrade_mod_savepoint(true, 2021061600, 'vpl'); + } return true; } diff --git a/forms/overrides.php b/forms/overrides.php index 60a65ac6fd507cdb0bd78cb6729de032bc8e1e87..23012158e2faedd14bdba6c639565f626d88523f 100644 --- a/forms/overrides.php +++ b/forms/overrides.php @@ -157,7 +157,17 @@ $vpl = new mod_vpl( $id ); $vpl->require_capability( VPL_MANAGE_CAPABILITY ); $vpl->prepare_page( 'forms/overrides.php', array( 'id' => $id ) ); -$overrides = $DB->get_records( VPL_OVERRIDES, array('vpl' => $id) ); +$vplid = $vpl->get_instance()->id; + + +$sql = 'SELECT o.*, GROUP_CONCAT(ao.userid) as userids, GROUP_CONCAT(ao.groupid) as groupids + FROM {vpl_overrides} o + LEFT JOIN {vpl_assigned_overrides} ao ON ao.override = o.id + WHERE o.vpl = :vplid + GROUP BY o.id + ORDER BY o.id ASC'; +$overrides = $DB->get_records_sql($sql, array('vplid' => $vplid)); + $fields = array('startdate', 'duedate', 'reductionbyevaluation', 'freeevaluations'); // Prepare forms if we are editing or submitting an override. @@ -208,12 +218,13 @@ if ($edit !== null || $update !== null) { if ($delete !== null) { $overrideid = $delete; - $override = $DB->get_record( VPL_OVERRIDES, array('id' => $overrideid) ); - if ($override !== false) { + if (isset($overrides[$overrideid])) { + $override = $overrides[$overrideid]; // Delete associated calendar events. $vpl->update_override_calendar_events($override, null, true); // Delete the override. $DB->delete_records( VPL_OVERRIDES, array('id' => $overrideid) ); + $DB->delete_records( VPL_ASSIGNED_OVERRIDES, array('override' => $overrideid) ); \mod_vpl\event\override_deleted::log($vpl, $overrideid); } // Properly reload the page. @@ -236,7 +247,11 @@ if ($update !== null) { if (empty($override->groupids)) { $override->groupids = null; } - $override->vpl = $id; + $override->vpl = $vplid; + $old = array( + 'userids' => array(), + 'groupids' => array() + ); if ($update == 0) { // Create the override. $newid = $DB->insert_record( VPL_OVERRIDES, $override ); @@ -246,10 +261,63 @@ if ($update !== null) { } else { // Update the override. $override->id = $update; - $oldoverride = $DB->get_record( VPL_OVERRIDES, array('id' => $override->id) ); + if (isset($overrides[$override->id])) { + $oldoverride = $overrides[$override->id]; + if (!empty($oldoverride->userids)) { + $old['userids'] = explode(',', $oldoverride->userids); + } + if (!empty($oldoverride->groupids)) { + $old['groupids'] = explode(',', $oldoverride->groupids); + } + } else { + $oldoverride = null; + } $DB->update_record( VPL_OVERRIDES, $override ); \mod_vpl\event\override_updated::log($vpl, $update); } + + $record = array( + 'vpl' => $override->vpl, + 'override' => $override->id + ); + // Process users and groups for the updated override, to update assigned overrides table. + foreach (array('userid', 'groupid') as $key) { + $record['userid'] = null; + $record['groupid'] = null; + $ids = $key . 's'; + sort($old[$ids]); + if (!empty($override->$ids)) { + $newids = explode(',', $override->$ids); + } else { + $newids = array(); + } + sort($newids); + $i = 0; + $n = count($old[$ids]); + $j = 0; + $m = count($newids); + // Walk simultaneously through both arrays. + while ($i < $n || $j < $m) { + if ($i == $n || ($j < $m && $old[$ids][$i] > $newids[$j])) { + // Insert new user/group. + $record[$key] = $newids[$j]; + $DB->insert_record( VPL_ASSIGNED_OVERRIDES, $record); + $j++; + } else if ($j == $m || ($newids[$j] > $old[$ids][$i])) { + // Remove old user/group. + $DB->delete_records( VPL_ASSIGNED_OVERRIDES, array( + 'vpl' => $override->vpl, + 'override' => $override->id, + $key => $old[$ids][$i] + )); + $i++; + } else { + // This user/group was and is still there, skip. + $i++; + $j++; + } + } + } // Create or update associated calendar events. $vpl->update_override_calendar_events($override, $oldoverride); } diff --git a/lang/en/vpl.php b/lang/en/vpl.php index ff7230450f9c0b75f0638026e65a8ddfb2688225..03c910faf02f8e0c01361c10d9c83ffdfbb116b4 100644 --- a/lang/en/vpl.php +++ b/lang/en/vpl.php @@ -240,24 +240,42 @@ $string ['previoussubmissionslist'] = 'Previous submissions list'; $string ['primarycolor'] = 'Primary color:'; $string ['print'] = 'Print'; $string ['privacy:metadata:vpl'] = 'Information of the activity'; -$string ['privacy:metadata:vpl_submissions'] = 'Information of the attempt/submission and on its evaluation if it was done'; +$string ['privacy:metadata:vpl_submissions'] = 'Information of the attempts/submissions and on its evaluation'; $string ['privacy:metadata:vpl_editor_fontsize'] = 'The user preference for the font size of the IDE'; $string ['privacy:metadata:vpl_acetheme'] = 'The user preference for the editor theme of the IDE'; $string ['privacy:metadata:vpl_terminaltheme'] = 'The user preference for the terminal color combination'; -$string ['privacy:metadata:vpl:assignedvariationdescription'] = 'Variation description if one assigned'; $string ['privacy:metadata:vpl:id'] = 'Activity identification number'; $string ['privacy:metadata:vpl:name'] = 'Activity name'; $string ['privacy:metadata:vpl:shortdescription'] = 'Activity short description'; $string ['privacy:metadata:vpl:grade'] = 'Activity grade'; $string ['privacy:metadata:vpl:reductionbyevaluation'] = 'Penalization on the mark for each student request of automatic evaluation'; $string ['privacy:metadata:vpl:freeevaluations'] = 'Number of free automatic evaluations (without penalization)'; +$string ['privacy:metadata:vpl_submissions:userid'] = 'User DB id'; +$string ['privacy:metadata:vpl_submissions:groupid'] = 'Group DB id'; $string ['privacy:metadata:vpl_submissions:datesubmitted'] = 'Date and time of submission'; $string ['privacy:metadata:vpl_submissions:studentcomments'] = 'Comments written by the student about the submission'; $string ['privacy:metadata:vpl_submissions:nevaluations'] = 'Number of requested automatic evaluation by the student until this submission'; $string ['privacy:metadata:vpl_submissions:dategraded'] = 'Date and time of the evaluation of the submission'; $string ['privacy:metadata:vpl_submissions:grade'] = 'The mark for this submission. This value may no match the value in the grade book.'; +$string ['privacy:metadata:vpl_submissions:graderid'] = 'grader user DB id'; $string ['privacy:metadata:vpl_submissions:gradercomments'] = 'Comments of the grader about this submission'; +$string ['privacy:metadata:vpl_assigned_variations'] = 'Information of the activity variation assigned, if any'; +$string ['privacy:metadata:vpl_assigned_variations:userid'] = 'User DB id.'; +$string ['privacy:metadata:vpl_assigned_variations:vplid'] = 'VPL DB id'; +$string ['privacy:metadata:vpl_assigned_variations:description'] = 'Description of the assigned variation'; +$string ['privacy:metadata:vpl_assigned_overrides'] = 'Information of the activity settings overrides assigned, if any'; +$string ['privacy:metadata:vpl_assigned_overrides:vplid'] = 'VPL DB id'; +$string ['privacy:metadata:vpl_assigned_overrides:userid'] = 'User DB id'; +$string ['privacy:metadata:vpl_assigned_overrides:overrideid'] = 'Assigned override id'; +$string ['privacy:metadata:vpl_running_processes'] = 'Information of user\'s running processes on this activity '; +$string ['privacy:metadata:vpl_running_processes:userid'] = 'User DB id.'; +$string ['privacy:metadata:vpl_running_processes:vplid'] = 'VPL DB id'; +$string ['privacy:metadata:vpl_running_processes:server'] = 'Server that runs the task'; +$string ['privacy:metadata:vpl_running_processes:starttime'] = 'Date the task starts running'; +$string ['privacy:overridepath'] = 'assigned_override'; +$string ['privacy:runningprocesspath'] = 'running_process_{$a}'; $string ['privacy:submissionpath'] = 'submission_{$a}'; +$string ['privacy:variationpath'] = 'assigned_variation'; $string ['proposedgrade'] = 'Proposed grade: {$a}'; $string ['proxy'] = 'proxy'; $string ['proxy_description'] = 'Proxy from Moodle to execution servers'; diff --git a/lib.php b/lib.php index 281db05a6ce3a775a80e32ac81dc17eee609bd32..f1b751b5907aaa3dcfda86f5f4f92ef03c2258b5 100644 --- a/lib.php +++ b/lib.php @@ -244,7 +244,9 @@ function vpl_delete_instance( $id ) { $tables = [ VPL_SUBMISSIONS, VPL_VARIATIONS, - VPL_ASSIGNED_VARIATIONS + VPL_ASSIGNED_VARIATIONS, + VPL_OVERRIDES, + VPL_ASSIGNED_OVERRIDES ]; foreach ($tables as $table) { $DB->delete_records( $table, array ('vpl' => $id) ); @@ -756,6 +758,21 @@ function vpl_reset_instance_userdata($vplid) { $DB->delete_records( VPL_ASSIGNED_VARIATIONS, array ( 'vpl' => $vplid ) ); + // Delete overrides and associated events. + require_once(dirname(__FILE__) . '/vpl.class.php'); + $vpl = new mod_vpl(null, $vplid); + $sql = 'SELECT o.*, GROUP_CONCAT(ao.userid) as userids, GROUP_CONCAT(ao.groupid) as groupids + FROM {vpl_overrides} o + LEFT JOIN {vpl_assigned_overrides} ao ON ao.override = o.id + WHERE o.vpl = :vplid + GROUP BY o.id'; + $overrides = $DB->get_records_sql($sql, array('vplid' => $vplid)); + foreach ($overrides as $override) { + $vpl->update_override_calendar_events($override, null, true); + } + $DB->delete_records( VPL_ASSIGNED_OVERRIDES, array ( + 'vpl' => $vplid + ) ); // Delete submission, execution and evaluation files. fulldelete( $CFG->dataroot . '/vpl_data/'. $vplid . '/usersdata' ); diff --git a/locallib.php b/locallib.php index 457952a4ac7686a229213fc3a951873ab9cb9462..110f4626cd228ecf53921d5506ca292f30d0258e 100644 --- a/locallib.php +++ b/locallib.php @@ -31,6 +31,7 @@ define( 'VPL_RUNNING_PROCESSES', 'vpl_running_processes' ); define( 'VPL_VARIATIONS', 'vpl_variations' ); define( 'VPL_ASSIGNED_VARIATIONS', 'vpl_assigned_variations' ); define( 'VPL_OVERRIDES', 'vpl_overrides' ); +define( 'VPL_ASSIGNED_OVERRIDES', 'vpl_assigned_overrides' ); define( 'VPL_GRADE_CAPABILITY', 'mod/vpl:grade' ); define( 'VPL_VIEW_CAPABILITY', 'mod/vpl:view' ); define( 'VPL_SUBMIT_CAPABILITY', 'mod/vpl:submit' ); diff --git a/tests/base_test.php b/tests/base_test.php index 0dbf731d9ac51e54b33bfd06d25f46d018f64761..1b4a3f6452e2f5f4882451ec61034d94907265aa 100644 --- a/tests/base_test.php +++ b/tests/base_test.php @@ -354,42 +354,64 @@ class mod_vpl_base_testcase extends advanced_testcase { // - Teacher 1 has due date (due date is disabled) overriden (by group and by user, latter should prevail). $override = new stdClass(); - $override->vpl = $this->vploverrides->get_course_module()->id; - $override->userids = $this->students[1]->id . ',' . $this->students[2]->id; - $override->groupids = null; + $override->vpl = $this->vploverrides->get_instance()->id; $override->startdate = $now - 3600; $override->duedate = $baseduedate + DAYSECS; $override->freeevaluations = 10; $override->reductionbyevaluation = 1; $override->id = $DB->insert_record( VPL_OVERRIDES, $override ); - $override->override_startdate = 1; - $override->override_duedate = 1; - $override->override_freeevaluations = 1; - $override->override_reductionbyevaluation = 1; + $assignedoverride = new stdClass(); + $assignedoverride->vpl = $override->vpl; + $assignedoverride->override = $override->id; + $userids = array($this->students[1]->id, $this->students[2]->id); + foreach ($userids as $userid) { + $assignedoverride->userid = $userid; + $assignedoverride->groupid = null; + $DB->insert_record( VPL_ASSIGNED_OVERRIDES, $assignedoverride ); + } + $override->userids = implode(',', $userids); + $override->groupids = null; $this->vploverrides->update_override_calendar_events($override); $override = new stdClass(); - $override->vpl = $this->vploverrides->get_course_module()->id; - $override->userids = $this->students[3]->id; - $override->groupids = $this->groups[2]->id . ',' . $this->groups[3]->id; + $override->vpl = $this->vploverrides->get_instance()->id; $override->startdate = null; $override->duedate = $baseduedate + 2 * DAYSECS; $override->freeevaluations = null; $override->reductionbyevaluation = null; $override->id = $DB->insert_record( VPL_OVERRIDES, $override ); - $override->override_duedate = 1; + $assignedoverride = new stdClass(); + $assignedoverride->vpl = $override->vpl; + $assignedoverride->override = $override->id; + $assignedoverride->userid = $this->students[3]->id; + $assignedoverride->groupid = null; + $DB->insert_record( VPL_ASSIGNED_OVERRIDES, $assignedoverride ); + $groupids = array($this->groups[2]->id, $this->groups[3]->id); + foreach ($groupids as $groupid) { + $assignedoverride->userid = null; + $assignedoverride->groupid = $groupid; + $DB->insert_record( VPL_ASSIGNED_OVERRIDES, $assignedoverride ); + } + $override->userids = $this->students[3]->id; + $override->groupids = implode(',', $groupids); $this->vploverrides->update_override_calendar_events($override); $override = new stdClass(); - $override->vpl = $this->vploverrides->get_course_module()->id; - $override->userids = $this->teachers[1]->id; + $override->vpl = $this->vploverrides->get_instance()->id; $override->groupids = null; $override->startdate = null; $override->duedate = 0; $override->freeevaluations = null; $override->reductionbyevaluation = null; $override->id = $DB->insert_record( VPL_OVERRIDES, $override ); - $override->override_duedate = 1; + $assignedoverride = new stdClass(); + $assignedoverride->vpl = $override->vpl; + $assignedoverride->override = $override->id; + $assignedoverride->userid = $this->teachers[1]->id; + $assignedoverride->groupid = null; + $DB->insert_record( VPL_ASSIGNED_OVERRIDES, $assignedoverride ); + $override->userids = $this->teachers[1]->id; + $override->groupids = null; $this->vploverrides->update_override_calendar_events($override); } diff --git a/tests/lib_test.php b/tests/lib_test.php index 9f33222eadd814045ab2694e5e89de407eb66ec8..eeb31e1bb5aa2ca010f2c9ff11971a4a74a4617a 100644 --- a/tests/lib_test.php +++ b/tests/lib_test.php @@ -274,7 +274,9 @@ class mod_vpl_lib_testcase extends mod_vpl_base_testcase { VPL_SUBMISSIONS, VPL_VARIATIONS, VPL_ASSIGNED_VARIATIONS, - VPL_RUNNING_PROCESSES + VPL_RUNNING_PROCESSES, + VPL_OVERRIDES, + VPL_ASSIGNED_OVERRIDES ]; $parms = array('vpl' => $instance->id); foreach ($tables as $table) { @@ -388,6 +390,9 @@ class mod_vpl_lib_testcase extends mod_vpl_base_testcase { $parms = array( 'vpl' => $instance->id); $count = $DB->count_records(VPL_ASSIGNED_VARIATIONS, $parms); $this->assertEquals(0, $count, $instance->name); + $parms = array( 'vpl' => $instance->id); + $count = $DB->count_records(VPL_ASSIGNED_OVERRIDES, $parms); + $this->assertEquals(0, $count, $instance->name); $directory = $CFG->dataroot . '/vpl_data/'. $instance->id . '/usersdata'; $this->assertFalse(file_exists($directory) && is_dir($directory), $instance->name); } diff --git a/tests/privacy_provider_test.php b/tests/privacy_provider_test.php index db29e9fd5e16d7c0ab611258819109ff120bfce9..b1e437a9ebf74261f02952a058cc3559010f6738 100644 --- a/tests/privacy_provider_test.php +++ b/tests/privacy_provider_test.php @@ -108,8 +108,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $users = [$this->students[0], $this->students[1], $this->students[2], $this->editingteachers[0], $this->students[5]]; $usersvpls = [ [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [$this->vplonefile, $this->vplteamwork], [], ]; @@ -206,7 +206,13 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $this->assertEquals('', $data->shortdescription); $gradestr = get_string('grademax', 'core_grades') . ': ' . format_float(10, 5, true, true); $this->assertEquals($gradestr, $data->grade); - $this->assertTrue(strpos($data->variation, 'variation ') === 0); + + $data = $writer->get_data([get_string('privacy:variationpath', 'vpl')]); + $userid = $this->students[2]->id; + $res = $this->vplvariations->get_variation($userid); + $this->assertEquals($res->vpl, $data->vpl); + $this->assertEquals($userid, $data->userid); + $this->assertEquals($res->description, $data->variation); } /** @@ -237,6 +243,37 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $this->assertEquals([], $data); } + /** + * Method to test export user data with running processes. + */ + public function test_export_user_data_with_running_processes() { + global $DB; + $instance = $this->vplonefile->get_instance(); + $vplid = $instance->id; + $userid = $this->students[0]->id; + for ($i = 1; $i < 4; $i++) { + $parms = array( + 'userid' => $userid, + 'vpl' => $vplid, + 'server' => 'https://www.server' . $i . '.com', + 'start_time' => time(), + 'adminticket' => 'secret', + ); + $DB->insert_record( VPL_RUNNING_PROCESSES, $parms); + } + $context = $this->vplonefile->get_context(); + $approved = new \core_privacy\local\request\approved_contextlist($this->students[0], 'mod_vpl', array($context->id)); + $this->provider->export_user_data($approved); + $writer = \core_privacy\local\request\writer::with_context($context); + for ($i = 1; $i < 4; $i++) { + $data = $writer->get_data([get_string('privacy:runningprocesspath', 'vpl', $i) ]); + $this->assertInstanceOf('stdClass', $data); + $this->assertEquals($vplid, $data->vpl); + $this->assertEquals($userid, $data->userid); + $this->assertEquals('www.server' . $i . '.com', $data->server); + } + } + /** * Method to test export_user_preferences. */ @@ -281,8 +318,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $users = [$this->students[0], $this->students[1], $this->students[2], $this->editingteachers[0], $this->students[5]]; $usersvpls = [ [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [$this->vplonefile, $this->vplteamwork], [] ]; @@ -312,8 +349,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $usersvpls = [ [$this->vplonefile, $this->vplteamwork], [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [] ]; @@ -366,8 +403,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $usersvpls = [ [$this->vplonefile, $this->vplteamwork], [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [] ]; @@ -400,8 +437,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $users = [$this->students[0], $this->students[1], $this->students[2], $this->editingteachers[0], $this->students[5]]; $usersvpls = [ [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [$this->vplonefile, $this->vplteamwork], [] ]; @@ -436,8 +473,8 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $users = [$this->students[0], $this->students[1], $this->students[2], $this->editingteachers[0], $this->students[5]]; $usersvpls = [ [$this->vplonefile, $this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplmultifile, $this->vplvariations, $this->vplteamwork], - [$this->vplvariations], + [$this->vplmultifile, $this->vplvariations, $this->vplteamwork, $this->vploverrides], + [$this->vplvariations, $this->vploverrides], [$this->vplonefile, $this->vplteamwork], [] ]; @@ -451,7 +488,11 @@ class mod_vpl_privacy_provider_testcase extends mod_vpl_base_testcase { $expecteduserids[] = $users[$i]->id; } } - $this->assertEqualsCanonicalizing($expecteduserids, $userlist->get_userids()); + $a1 = $expecteduserids; + $a2 = $userlist->get_userids(); + sort($a1); + sort($a2); + $this->assertEquals($a1, $a2); } } diff --git a/tests/vpl_test.php b/tests/vpl_test.php index af0eb4bc4a409273a6c6c382f9b6c05a3c072cac..e9a487647a0d3748f9a84bd8a0af9bdf3cb8025a 100644 --- a/tests/vpl_test.php +++ b/tests/vpl_test.php @@ -70,7 +70,9 @@ class mod_vpl_class_testcase extends mod_vpl_base_testcase { VPL_SUBMISSIONS, VPL_VARIATIONS, VPL_ASSIGNED_VARIATIONS, - VPL_RUNNING_PROCESSES + VPL_RUNNING_PROCESSES, + VPL_OVERRIDES, + VPL_ASSIGNED_OVERRIDES ]; $parms = array('vpl' => $instance->id); foreach ($tables as $table) { diff --git a/tests/webservice_test.php b/tests/webservice_test.php index 730e8cfa51a82a6a8f83a9a7750035baf0ad6547..a4cce21f1c6446d6d1c997d4cf5eb1b63304e995 100644 --- a/tests/webservice_test.php +++ b/tests/webservice_test.php @@ -134,7 +134,7 @@ class mod_vpl_webservice_testcase extends mod_vpl_base_testcase { $res = mod_vpl_webservice::info($this->vplnotavailable->get_course_module()->id, 'boberĂa'); $this->fail('Exception expected'); } catch (Exception $e) { - $this->assertStringContainsString('password', $e->getMessage()); + $this->assertFalse(strpos($e->getMessage(), 'password') === false); } } } diff --git a/version.php b/version.php index af640262af060d948af7f5a4b0526244ee489e47..67ff48c7f0b851d9d28323159f072a4da92bce2a 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2021053001; +$plugin->version = 2021061600; $plugin->requires = 2014051200; // Moodle 2.7! $plugin->maturity = MATURITY_STABLE; $plugin->release = '3.4'; diff --git a/vpl.class.php b/vpl.class.php index b42c0c14fcedd2d284217172f79ebf4bf1be8dcd..baa57440d29110a76e992893d4f21b7203a02190 100644 --- a/vpl.class.php +++ b/vpl.class.php @@ -2023,20 +2023,24 @@ class mod_vpl { } if (!isset(self::$overridensettings[$this->cm->id][$userid])) { self::$overridensettings[$this->cm->id][$userid] = new stdClass(); - $overrides = $DB->get_records( VPL_OVERRIDES, array('vpl' => $this->cm->id), 'id DESC' ); + + $sql = 'SELECT ao.id as aoid, ao.override, ao.userid, ao.groupid, o.* + FROM {vpl_assigned_overrides} ao + JOIN {vpl_overrides} o ON ao.override = o.id + WHERE o.vpl = :vplid AND (ao.userid = :userid OR ao.groupid IS NOT NULL) + ORDER BY ao.override DESC'; + $overrides = $DB->get_records_sql($sql, array('vplid' => $this->instance->id, 'userid' => $userid)); + foreach ($overrides as $override) { - if (!empty($override->groupids)) { - foreach (explode(',', $override->groupids) as $groupid) { - if (groups_is_member($groupid, $userid)) { - foreach ($fields as $field) { - self::$overridensettings[$this->cm->id][$userid]->$field = $override->$field; - } - } + if (!empty($override->userid)) { + // Found record for user. + foreach ($fields as $field) { + self::$overridensettings[$this->cm->id][$userid]->$field = $override->$field; } + break; // User overrides take priority, do not search further. } - } - foreach ($overrides as $override) { - if (!empty($override->userids) && in_array($userid, explode(',', $override->userids))) { + + if (groups_is_member($override->groupid, $userid)) { foreach ($fields as $field) { self::$overridensettings[$this->cm->id][$userid]->$field = $override->$field; } @@ -2054,6 +2058,7 @@ class mod_vpl { /** * Update calendar events for duedate overrides. * @param stdClass $override The override being created / updated / deleted. + * It should contain joint data from vpl_overrides and vpl_assigned_overrides tables. * @param stdClass $oldoverride The old override data (in case of an update). * @param boolean $delete If true, simply delete all related events. */ @@ -2080,7 +2085,7 @@ class mod_vpl { foreach (explode(',', $override->{$target . 's'}) as $userorgroupid) { // Loop over users or groups. $params[$target] = $userorgroupid; $currenteventid = $DB->get_field( 'event', 'id', $params ); // Get current calendar event. - if (isset($override->override_duedate) && !$delete) { + if (isset($override->duedate) && !$delete) { if ($target == 'userid') { $userorgroupname = fullname($DB->get_record( 'user', array('id' => $userorgroupid) )); } else {