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