See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Privacy class for requesting user data. 19 * 20 * @package mod_assign 21 * @copyright 2018 Adrian Greeve <adrian@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_assign\privacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 require_once($CFG->dirroot . '/mod/assign/locallib.php'); 30 31 use \core_privacy\local\metadata\collection; 32 use \core_privacy\local\request\contextlist; 33 use \core_privacy\local\request\writer; 34 use \core_privacy\local\request\approved_contextlist; 35 use \core_privacy\local\request\transform; 36 use \core_privacy\local\request\helper; 37 use \core_privacy\local\request\userlist; 38 use \core_privacy\local\request\approved_userlist; 39 use \core_privacy\manager; 40 41 /** 42 * Privacy class for requesting user data. 43 * 44 * @package mod_assign 45 * @copyright 2018 Adrian Greeve <adrian@moodle.com> 46 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 47 */ 48 class provider implements 49 \core_privacy\local\metadata\provider, 50 \core_privacy\local\request\plugin\provider, 51 \core_privacy\local\request\user_preference_provider, 52 \core_privacy\local\request\core_userlist_provider { 53 54 /** Interface for all assign submission sub-plugins. */ 55 const ASSIGNSUBMISSION_INTERFACE = 'mod_assign\privacy\assignsubmission_provider'; 56 57 /** Interface for all assign submission sub-plugins. This allows for deletion of users with a context. */ 58 const ASSIGNSUBMISSION_USER_INTERFACE = 'mod_assign\privacy\assignsubmission_user_provider'; 59 60 /** Interface for all assign feedback sub-plugins. This allows for deletion of users with a context. */ 61 const ASSIGNFEEDBACK_USER_INTERFACE = 'mod_assign\privacy\assignfeedback_user_provider'; 62 63 /** Interface for all assign feedback sub-plugins. */ 64 const ASSIGNFEEDBACK_INTERFACE = 'mod_assign\privacy\assignfeedback_provider'; 65 66 /** 67 * Provides meta data that is stored about a user with mod_assign 68 * 69 * @param collection $collection A collection of meta data items to be added to. 70 * @return collection Returns the collection of metadata. 71 */ 72 public static function get_metadata(collection $collection) : collection { 73 $assigngrades = [ 74 'userid' => 'privacy:metadata:userid', 75 'timecreated' => 'privacy:metadata:timecreated', 76 'timemodified' => 'timemodified', 77 'grader' => 'privacy:metadata:grader', 78 'grade' => 'privacy:metadata:grade', 79 'attemptnumber' => 'attemptnumber' 80 ]; 81 $assignoverrides = [ 82 'groupid' => 'privacy:metadata:groupid', 83 'userid' => 'privacy:metadata:userid', 84 'allowsubmissionsfromdate' => 'allowsubmissionsfromdate', 85 'duedate' => 'duedate', 86 'cutoffdate' => 'cutoffdate' 87 ]; 88 $assignsubmission = [ 89 'userid' => 'privacy:metadata:userid', 90 'timecreated' => 'privacy:metadata:timecreated', 91 'timemodified' => 'timemodified', 92 'status' => 'gradingstatus', 93 'groupid' => 'privacy:metadata:groupid', 94 'attemptnumber' => 'attemptnumber', 95 'latest' => 'privacy:metadata:latest' 96 ]; 97 $assignuserflags = [ 98 'userid' => 'privacy:metadata:userid', 99 'assignment' => 'privacy:metadata:assignmentid', 100 'locked' => 'locksubmissions', 101 'mailed' => 'privacy:metadata:mailed', 102 'extensionduedate' => 'extensionduedate', 103 'workflowstate' => 'markingworkflowstate', 104 'allocatedmarker' => 'allocatedmarker' 105 ]; 106 $assignusermapping = [ 107 'assignment' => 'privacy:metadata:assignmentid', 108 'userid' => 'privacy:metadata:userid' 109 ]; 110 $collection->add_database_table('assign_grades', $assigngrades, 'privacy:metadata:assigngrades'); 111 $collection->add_database_table('assign_overrides', $assignoverrides, 'privacy:metadata:assignoverrides'); 112 $collection->add_database_table('assign_submission', $assignsubmission, 'privacy:metadata:assignsubmissiondetail'); 113 $collection->add_database_table('assign_user_flags', $assignuserflags, 'privacy:metadata:assignuserflags'); 114 $collection->add_database_table('assign_user_mapping', $assignusermapping, 'privacy:metadata:assignusermapping'); 115 $collection->add_user_preference('assign_perpage', 'privacy:metadata:assignperpage'); 116 $collection->add_user_preference('assign_filter', 'privacy:metadata:assignfilter'); 117 $collection->add_user_preference('assign_markerfilter', 'privacy:metadata:assignmarkerfilter'); 118 $collection->add_user_preference('assign_workflowfilter', 'privacy:metadata:assignworkflowfilter'); 119 $collection->add_user_preference('assign_quickgrading', 'privacy:metadata:assignquickgrading'); 120 $collection->add_user_preference('assign_downloadasfolders', 'privacy:metadata:assigndownloadasfolders'); 121 122 // Link to subplugins. 123 $collection->add_plugintype_link('assignsubmission', [],'privacy:metadata:assignsubmissionpluginsummary'); 124 $collection->add_plugintype_link('assignfeedback', [], 'privacy:metadata:assignfeedbackpluginsummary'); 125 $collection->add_subsystem_link('core_message', [], 'privacy:metadata:assignmessageexplanation'); 126 127 return $collection; 128 } 129 130 /** 131 * Returns all of the contexts that has information relating to the userid. 132 * 133 * @param int $userid The user ID. 134 * @return contextlist an object with the contexts related to a userid. 135 */ 136 public static function get_contexts_for_userid(int $userid) : contextlist { 137 $params = ['modulename' => 'assign', 138 'contextlevel' => CONTEXT_MODULE, 139 'userid' => $userid, 140 'graderid' => $userid, 141 'aouserid' => $userid, 142 'asnuserid' => $userid, 143 'aufuserid' => $userid, 144 'aumuserid' => $userid]; 145 146 $sql = "SELECT ctx.id 147 FROM {course_modules} cm 148 JOIN {modules} m ON cm.module = m.id AND m.name = :modulename 149 JOIN {assign} a ON cm.instance = a.id 150 JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel 151 JOIN {assign_grades} ag ON a.id = ag.assignment AND (ag.userid = :userid OR ag.grader = :graderid)"; 152 153 $contextlist = new contextlist(); 154 $contextlist->add_from_sql($sql, $params); 155 156 $sql = "SELECT ctx.id 157 FROM {course_modules} cm 158 JOIN {modules} m ON cm.module = m.id AND m.name = :modulename 159 JOIN {assign} a ON cm.instance = a.id 160 JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel 161 JOIN {assign_overrides} ao ON a.id = ao.assignid 162 WHERE ao.userid = :aouserid"; 163 164 $contextlist->add_from_sql($sql, $params); 165 166 $sql = "SELECT ctx.id 167 FROM {course_modules} cm 168 JOIN {modules} m ON cm.module = m.id AND m.name = :modulename 169 JOIN {assign} a ON cm.instance = a.id 170 JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel 171 JOIN {assign_submission} asn ON a.id = asn.assignment 172 WHERE asn.userid = :asnuserid"; 173 174 $contextlist->add_from_sql($sql, $params); 175 176 $sql = "SELECT ctx.id 177 FROM {course_modules} cm 178 JOIN {modules} m ON cm.module = m.id AND m.name = :modulename 179 JOIN {assign} a ON cm.instance = a.id 180 JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel 181 JOIN {assign_user_flags} auf ON a.id = auf.assignment 182 WHERE auf.userid = :aufuserid"; 183 184 $contextlist->add_from_sql($sql, $params); 185 186 $sql = "SELECT ctx.id 187 FROM {course_modules} cm 188 JOIN {modules} m ON cm.module = m.id AND m.name = :modulename 189 JOIN {assign} a ON cm.instance = a.id 190 JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel 191 JOIN {assign_user_mapping} aum ON a.id = aum.assignment 192 WHERE aum.userid = :aumuserid"; 193 194 $contextlist->add_from_sql($sql, $params); 195 196 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE, 197 'get_context_for_userid_within_feedback', [$userid, $contextlist]); 198 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE, 199 'get_context_for_userid_within_submission', [$userid, $contextlist]); 200 201 return $contextlist; 202 } 203 204 /** 205 * Get the list of users who have data within a context. 206 * 207 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 208 */ 209 public static function get_users_in_context(userlist $userlist) { 210 211 $context = $userlist->get_context(); 212 if ($context->contextlevel != CONTEXT_MODULE) { 213 return; 214 } 215 216 $params = [ 217 'modulename' => 'assign', 218 'contextid' => $context->id, 219 'contextlevel' => CONTEXT_MODULE 220 ]; 221 222 $sql = "SELECT g.userid, g.grader 223 FROM {context} ctx 224 JOIN {course_modules} cm ON cm.id = ctx.instanceid 225 JOIN {modules} m ON m.id = cm.module AND m.name = :modulename 226 JOIN {assign} a ON a.id = cm.instance 227 JOIN {assign_grades} g ON a.id = g.assignment 228 WHERE ctx.id = :contextid AND ctx.contextlevel = :contextlevel"; 229 $userlist->add_from_sql('userid', $sql, $params); 230 $userlist->add_from_sql('grader', $sql, $params); 231 232 $sql = "SELECT o.userid 233 FROM {context} ctx 234 JOIN {course_modules} cm ON cm.id = ctx.instanceid 235 JOIN {modules} m ON m.id = cm.module AND m.name = :modulename 236 JOIN {assign} a ON a.id = cm.instance 237 JOIN {assign_overrides} o ON a.id = o.assignid 238 WHERE ctx.id = :contextid AND ctx.contextlevel = :contextlevel"; 239 $userlist->add_from_sql('userid', $sql, $params); 240 241 $sql = "SELECT s.userid 242 FROM {context} ctx 243 JOIN {course_modules} cm ON cm.id = ctx.instanceid 244 JOIN {modules} m ON m.id = cm.module AND m.name = :modulename 245 JOIN {assign} a ON a.id = cm.instance 246 JOIN {assign_submission} s ON a.id = s.assignment 247 WHERE ctx.id = :contextid AND ctx.contextlevel = :contextlevel"; 248 $userlist->add_from_sql('userid', $sql, $params); 249 250 $sql = "SELECT uf.userid 251 FROM {context} ctx 252 JOIN {course_modules} cm ON cm.id = ctx.instanceid 253 JOIN {modules} m ON m.id = cm.module AND m.name = :modulename 254 JOIN {assign} a ON a.id = cm.instance 255 JOIN {assign_user_flags} uf ON a.id = uf.assignment 256 WHERE ctx.id = :contextid AND ctx.contextlevel = :contextlevel"; 257 $userlist->add_from_sql('userid', $sql, $params); 258 259 $sql = "SELECT um.userid 260 FROM {context} ctx 261 JOIN {course_modules} cm ON cm.id = ctx.instanceid 262 JOIN {modules} m ON m.id = cm.module AND m.name = :modulename 263 JOIN {assign} a ON a.id = cm.instance 264 JOIN {assign_user_mapping} um ON a.id = um.assignment 265 WHERE ctx.id = :contextid AND ctx.contextlevel = :contextlevel"; 266 $userlist->add_from_sql('userid', $sql, $params); 267 268 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_USER_INTERFACE, 269 'get_userids_from_context', [$userlist]); 270 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_USER_INTERFACE, 271 'get_userids_from_context', [$userlist]); 272 } 273 274 /** 275 * Write out the user data filtered by contexts. 276 * 277 * @param approved_contextlist $contextlist contexts that we are writing data out from. 278 */ 279 public static function export_user_data(approved_contextlist $contextlist) { 280 foreach ($contextlist->get_contexts() as $context) { 281 // Check that the context is a module context. 282 if ($context->contextlevel != CONTEXT_MODULE) { 283 continue; 284 } 285 $user = $contextlist->get_user(); 286 $assigndata = helper::get_context_data($context, $user); 287 helper::export_context_files($context, $user); 288 289 writer::with_context($context)->export_data([], $assigndata); 290 $assign = new \assign($context, null, null); 291 292 // I need to find out if I'm a student or a teacher. 293 if ($userids = self::get_graded_users($user->id, $assign)) { 294 // Return teacher info. 295 $currentpath = [get_string('privacy:studentpath', 'mod_assign')]; 296 foreach ($userids as $studentuserid) { 297 $studentpath = array_merge($currentpath, [$studentuserid->id]); 298 static::export_submission($assign, $studentuserid, $context, $studentpath, true); 299 } 300 } 301 302 static::export_overrides($context, $assign, $user); 303 static::export_submission($assign, $user, $context, []); 304 // Meta data. 305 self::store_assign_user_flags($context, $assign, $user->id); 306 if ($assign->is_blind_marking()) { 307 $uniqueid = $assign->get_uniqueid_for_user_static($assign->get_instance()->id, $contextlist->get_user()->id); 308 if ($uniqueid) { 309 writer::with_context($context) 310 ->export_metadata([get_string('blindmarking', 'mod_assign')], 'blindmarkingid', $uniqueid, 311 get_string('privacy:blindmarkingidentifier', 'mod_assign')); 312 } 313 } 314 } 315 } 316 317 /** 318 * Delete all use data which matches the specified context. 319 * 320 * @param \context $context The module context. 321 */ 322 public static function delete_data_for_all_users_in_context(\context $context) { 323 global $DB; 324 325 if ($context->contextlevel == CONTEXT_MODULE) { 326 $cm = get_coursemodule_from_id('assign', $context->instanceid); 327 if ($cm) { 328 // Get the assignment related to this context. 329 $assign = new \assign($context, null, null); 330 // What to do first... Get sub plugins to delete their stuff. 331 $requestdata = new assign_plugin_request_data($context, $assign); 332 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE, 333 'delete_submission_for_context', [$requestdata]); 334 $requestdata = new assign_plugin_request_data($context, $assign); 335 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE, 336 'delete_feedback_for_context', [$requestdata]); 337 $DB->delete_records('assign_grades', ['assignment' => $assign->get_instance()->id]); 338 339 // Delete advanced grading information. 340 $gradingmanager = get_grading_manager($context, 'mod_assign', 'submissions'); 341 $controller = $gradingmanager->get_active_controller(); 342 if (isset($controller)) { 343 \core_grading\privacy\provider::delete_instance_data($context); 344 } 345 346 // Time to roll my own method for deleting overrides. 347 static::delete_overrides_for_users($assign); 348 $DB->delete_records('assign_submission', ['assignment' => $assign->get_instance()->id]); 349 $DB->delete_records('assign_user_flags', ['assignment' => $assign->get_instance()->id]); 350 $DB->delete_records('assign_user_mapping', ['assignment' => $assign->get_instance()->id]); 351 } 352 } 353 } 354 355 /** 356 * Delete all user data for the specified user, in the specified contexts. 357 * 358 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 359 */ 360 public static function delete_data_for_user(approved_contextlist $contextlist) { 361 global $DB; 362 363 $user = $contextlist->get_user(); 364 365 foreach ($contextlist as $context) { 366 if ($context->contextlevel != CONTEXT_MODULE) { 367 continue; 368 } 369 // Get the assign object. 370 $assign = new \assign($context, null, null); 371 $assignid = $assign->get_instance()->id; 372 373 $submissions = $DB->get_records('assign_submission', ['assignment' => $assignid, 'userid' => $user->id]); 374 foreach ($submissions as $submission) { 375 $requestdata = new assign_plugin_request_data($context, $assign, $submission, [], $user); 376 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE, 377 'delete_submission_for_userid', [$requestdata]); 378 } 379 380 $grades = $DB->get_records('assign_grades', ['assignment' => $assignid, 'userid' => $user->id]); 381 $gradingmanager = get_grading_manager($context, 'mod_assign', 'submissions'); 382 $controller = $gradingmanager->get_active_controller(); 383 foreach ($grades as $grade) { 384 $requestdata = new assign_plugin_request_data($context, $assign, $grade, [], $user); 385 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE, 386 'delete_feedback_for_grade', [$requestdata]); 387 // Delete advanced grading information. 388 if (isset($controller)) { 389 \core_grading\privacy\provider::delete_instance_data($context, $grade->id); 390 } 391 } 392 393 static::delete_overrides_for_users($assign, [$user->id]); 394 $DB->delete_records('assign_user_flags', ['assignment' => $assignid, 'userid' => $user->id]); 395 $DB->delete_records('assign_user_mapping', ['assignment' => $assignid, 'userid' => $user->id]); 396 $DB->delete_records('assign_grades', ['assignment' => $assignid, 'userid' => $user->id]); 397 $DB->delete_records('assign_submission', ['assignment' => $assignid, 'userid' => $user->id]); 398 } 399 } 400 401 /** 402 * Delete multiple users within a single context. 403 * 404 * @param approved_userlist $userlist The approved context and user information to delete information for. 405 */ 406 public static function delete_data_for_users(approved_userlist $userlist) { 407 global $DB; 408 409 $context = $userlist->get_context(); 410 if ($context->contextlevel != CONTEXT_MODULE) { 411 return; 412 } 413 414 $userids = $userlist->get_userids(); 415 416 $assign = new \assign($context, null, null); 417 $assignid = $assign->get_instance()->id; 418 $requestdata = new assign_plugin_request_data($context, $assign); 419 $requestdata->set_userids($userids); 420 $requestdata->populate_submissions_and_grades(); 421 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_USER_INTERFACE, 'delete_submissions', 422 [$requestdata]); 423 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_USER_INTERFACE, 'delete_feedback_for_grades', 424 [$requestdata]); 425 426 // Update this function to delete advanced grading information. 427 $gradingmanager = get_grading_manager($context, 'mod_assign', 'submissions'); 428 $controller = $gradingmanager->get_active_controller(); 429 if (isset($controller)) { 430 $gradeids = $requestdata->get_gradeids(); 431 // Careful here, if no gradeids are provided then all data is deleted for the context. 432 if (!empty($gradeids)) { 433 \core_grading\privacy\provider::delete_data_for_instances($context, $gradeids); 434 } 435 } 436 437 static::delete_overrides_for_users($assign, $userids); 438 list($sql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); 439 $params['assignment'] = $assignid; 440 $DB->delete_records_select('assign_user_flags', "assignment = :assignment AND userid $sql", $params); 441 $DB->delete_records_select('assign_user_mapping', "assignment = :assignment AND userid $sql", $params); 442 $DB->delete_records_select('assign_grades', "assignment = :assignment AND userid $sql", $params); 443 $DB->delete_records_select('assign_submission', "assignment = :assignment AND userid $sql", $params); 444 } 445 446 /** 447 * Deletes assignment overrides in bulk 448 * 449 * @param \assign $assign The assignment object 450 * @param array $userids An array of user IDs 451 */ 452 protected static function delete_overrides_for_users(\assign $assign, array $userids = []) { 453 global $DB; 454 $assignid = $assign->get_instance()->id; 455 456 $usersql = ''; 457 $params = ['assignid' => $assignid]; 458 if (!empty($userids)) { 459 list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); 460 $params = array_merge($params, $userparams); 461 $overrides = $DB->get_records_select('assign_overrides', "assignid = :assignid AND userid $usersql", $params); 462 } else { 463 $overrides = $DB->get_records('assign_overrides', $params); 464 } 465 if (!empty($overrides)) { 466 $params = ['modulename' => 'assign', 'instance' => $assignid]; 467 if (!empty($userids)) { 468 $params = array_merge($params, $userparams); 469 $DB->delete_records_select('event', "modulename = :modulename AND instance = :instance AND userid $usersql", 470 $params); 471 // Setting up for the next query. 472 $params = $userparams; 473 $usersql = "AND userid $usersql"; 474 } else { 475 $DB->delete_records('event', $params); 476 // Setting up for the next query. 477 $params = []; 478 } 479 list($overridesql, $overrideparams) = $DB->get_in_or_equal(array_keys($overrides), SQL_PARAMS_NAMED); 480 $params = array_merge($params, $overrideparams); 481 $DB->delete_records_select('assign_overrides', "id $overridesql $usersql", $params); 482 } 483 } 484 485 /** 486 * Find out if this user has graded any users. 487 * 488 * @param int $userid The user ID (potential teacher). 489 * @param assign $assign The assignment object. 490 * @return array If successful an array of objects with userids that this user graded, otherwise false. 491 */ 492 protected static function get_graded_users(int $userid, \assign $assign) { 493 $params = ['grader' => $userid, 'assignid' => $assign->get_instance()->id]; 494 495 $sql = "SELECT DISTINCT userid AS id 496 FROM {assign_grades} 497 WHERE grader = :grader AND assignment = :assignid"; 498 499 $useridlist = new useridlist($userid, $assign->get_instance()->id); 500 $useridlist->add_from_sql($sql, $params); 501 502 // Call sub-plugins to see if they have information not already collected. 503 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE, 'get_student_user_ids', 504 [$useridlist]); 505 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE, 'get_student_user_ids', [$useridlist]); 506 507 $userids = $useridlist->get_userids(); 508 return ($userids) ? $userids : false; 509 } 510 511 /** 512 * Writes out various user meta data about the assignment. 513 * 514 * @param \context $context The context of this assignment. 515 * @param \assign $assign The assignment object. 516 * @param int $userid The user ID 517 */ 518 protected static function store_assign_user_flags(\context $context, \assign $assign, int $userid) { 519 $datatypes = ['locked' => get_string('locksubmissions', 'mod_assign'), 520 'mailed' => get_string('privacy:metadata:mailed', 'mod_assign'), 521 'extensionduedate' => get_string('extensionduedate', 'mod_assign'), 522 'workflowstate' => get_string('markingworkflowstate', 'mod_assign'), 523 'allocatedmarker' => get_string('allocatedmarker_help', 'mod_assign')]; 524 $userflags = (array)$assign->get_user_flags($userid, false); 525 526 foreach ($datatypes as $key => $description) { 527 if (isset($userflags[$key]) && !empty($userflags[$key])) { 528 $value = $userflags[$key]; 529 if ($key == 'locked' || $key == 'mailed') { 530 $value = transform::yesno($value); 531 } else if ($key == 'extensionduedate') { 532 $value = transform::datetime($value); 533 } 534 writer::with_context($context)->export_metadata([], $key, $value, $description); 535 } 536 } 537 } 538 539 /** 540 * Formats and then exports the user's grade data. 541 * 542 * @param \stdClass $grade The assign grade object 543 * @param \context $context The context object 544 * @param array $currentpath Current directory path that we are exporting to. 545 */ 546 protected static function export_grade_data(\stdClass $grade, \context $context, array $currentpath) { 547 $gradedata = (object)[ 548 'timecreated' => transform::datetime($grade->timecreated), 549 'timemodified' => transform::datetime($grade->timemodified), 550 'grader' => transform::user($grade->grader), 551 'grade' => $grade->grade, 552 'attemptnumber' => ($grade->attemptnumber + 1) 553 ]; 554 writer::with_context($context) 555 ->export_data(array_merge($currentpath, [get_string('privacy:gradepath', 'mod_assign')]), $gradedata); 556 } 557 558 /** 559 * Formats and then exports the user's submission data. 560 * 561 * @param \stdClass $submission The assign submission object 562 * @param \context $context The context object 563 * @param array $currentpath Current directory path that we are exporting to. 564 */ 565 protected static function export_submission_data(\stdClass $submission, \context $context, array $currentpath) { 566 $submissiondata = (object)[ 567 'timecreated' => transform::datetime($submission->timecreated), 568 'timemodified' => transform::datetime($submission->timemodified), 569 'status' => get_string('submissionstatus_' . $submission->status, 'mod_assign'), 570 'groupid' => $submission->groupid, 571 'attemptnumber' => ($submission->attemptnumber + 1), 572 'latest' => transform::yesno($submission->latest) 573 ]; 574 writer::with_context($context) 575 ->export_data(array_merge($currentpath, [get_string('privacy:submissionpath', 'mod_assign')]), $submissiondata); 576 } 577 578 /** 579 * Stores the user preferences related to mod_assign. 580 * 581 * @param int $userid The user ID that we want the preferences for. 582 */ 583 public static function export_user_preferences(int $userid) { 584 $context = \context_system::instance(); 585 $assignpreferences = [ 586 'assign_perpage' => ['string' => get_string('privacy:metadata:assignperpage', 'mod_assign'), 'bool' => false], 587 'assign_filter' => ['string' => get_string('privacy:metadata:assignfilter', 'mod_assign'), 'bool' => false], 588 'assign_markerfilter' => ['string' => get_string('privacy:metadata:assignmarkerfilter', 'mod_assign'), 'bool' => true], 589 'assign_workflowfilter' => ['string' => get_string('privacy:metadata:assignworkflowfilter', 'mod_assign'), 590 'bool' => true], 591 'assign_quickgrading' => ['string' => get_string('privacy:metadata:assignquickgrading', 'mod_assign'), 'bool' => true], 592 'assign_downloadasfolders' => ['string' => get_string('privacy:metadata:assigndownloadasfolders', 'mod_assign'), 593 'bool' => true] 594 ]; 595 foreach ($assignpreferences as $key => $preference) { 596 $value = get_user_preferences($key, null, $userid); 597 if ($preference['bool']) { 598 $value = transform::yesno($value); 599 } 600 if (isset($value)) { 601 writer::with_context($context)->export_user_preference('mod_assign', $key, $value, $preference['string']); 602 } 603 } 604 } 605 606 /** 607 * Export overrides for this assignment. 608 * 609 * @param \context $context Context 610 * @param \assign $assign The assign object. 611 * @param \stdClass $user The user object. 612 */ 613 public static function export_overrides(\context $context, \assign $assign, \stdClass $user) { 614 615 $overrides = $assign->override_exists($user->id); 616 // Overrides returns an array with data in it, but an override with actual data will have the assign ID set. 617 if (isset($overrides->assignid)) { 618 $data = new \stdClass(); 619 if (!empty($overrides->duedate)) { 620 $data->duedate = transform::datetime($overrides->duedate); 621 } 622 if (!empty($overrides->cutoffdate)) { 623 $data->cutoffdate = transform::datetime($overrides->cutoffdate); 624 } 625 if (!empty($overrides->allowsubmissionsfromdate)) { 626 $data->allowsubmissionsfromdate = transform::datetime($overrides->allowsubmissionsfromdate); 627 } 628 if (!empty($data)) { 629 writer::with_context($context)->export_data([get_string('overrides', 'mod_assign')], $data); 630 } 631 } 632 } 633 634 /** 635 * Exports assignment submission data for a user. 636 * 637 * @param \assign $assign The assignment object 638 * @param \stdClass $user The user object 639 * @param \context_module $context The context 640 * @param array $path The path for exporting data 641 * @param bool|boolean $exportforteacher A flag for if this is exporting data as a teacher. 642 */ 643 protected static function export_submission(\assign $assign, \stdClass $user, \context_module $context, array $path, 644 bool $exportforteacher = false) { 645 $submissions = $assign->get_all_submissions($user->id); 646 $teacher = ($exportforteacher) ? $user : null; 647 $gradingmanager = get_grading_manager($context, 'mod_assign', 'submissions'); 648 $controller = $gradingmanager->get_active_controller(); 649 foreach ($submissions as $submission) { 650 // Attempt numbers start at zero, which is fine for programming, but doesn't make as much sense 651 // for users. 652 $submissionpath = array_merge($path, 653 [get_string('privacy:attemptpath', 'mod_assign', ($submission->attemptnumber + 1))]); 654 655 $params = new assign_plugin_request_data($context, $assign, $submission, $submissionpath ,$teacher); 656 manager::plugintype_class_callback('assignsubmission', self::ASSIGNSUBMISSION_INTERFACE, 657 'export_submission_user_data', [$params]); 658 if (!isset($teacher)) { 659 self::export_submission_data($submission, $context, $submissionpath); 660 } 661 $grade = $assign->get_user_grade($user->id, false, $submission->attemptnumber); 662 if ($grade) { 663 $params = new assign_plugin_request_data($context, $assign, $grade, $submissionpath, $teacher); 664 manager::plugintype_class_callback('assignfeedback', self::ASSIGNFEEDBACK_INTERFACE, 'export_feedback_user_data', 665 [$params]); 666 667 self::export_grade_data($grade, $context, $submissionpath); 668 // Check for advanced grading and retrieve that information. 669 if (isset($controller)) { 670 \core_grading\privacy\provider::export_item_data($context, $grade->id, $submissionpath); 671 } 672 } 673 } 674 } 675 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body