Differences Between: [Versions 39 and 310]
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 * Data provider tests. 19 * 20 * @package mod_lesson 21 * @category test 22 * @copyright 2018 Frédéric Massart 23 * @author Frédéric Massart <fred@branchup.tech> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 global $CFG; 29 30 use core_privacy\tests\provider_testcase; 31 use core_privacy\local\request\approved_contextlist; 32 use core_privacy\local\request\transform; 33 use core_privacy\local\request\writer; 34 use mod_lesson\privacy\provider; 35 36 /** 37 * Data provider testcase class. 38 * 39 * @package mod_lesson 40 * @category test 41 * @copyright 2018 Frédéric Massart 42 * @author Frédéric Massart <fred@branchup.tech> 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 */ 45 class mod_lesson_privacy_testcase extends provider_testcase { 46 47 public function setUp(): void { 48 global $PAGE; 49 $this->setAdminUser(); // The data generator complains without this. 50 $this->resetAfterTest(); 51 $PAGE->get_renderer('core'); 52 } 53 54 public function test_get_contexts_for_userid() { 55 $dg = $this->getDataGenerator(); 56 $c1 = $dg->create_course(); 57 58 $u1 = $dg->create_user(); 59 $u2 = $dg->create_user(); 60 $u3 = $dg->create_user(); 61 $u4 = $dg->create_user(); 62 $u5 = $dg->create_user(); 63 $u6 = $dg->create_user(); 64 65 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 66 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 67 $cm3 = $dg->create_module('lesson', ['course' => $c1]); 68 $cm1ctx = context_module::instance($cm1->cmid); 69 $cm2ctx = context_module::instance($cm2->cmid); 70 $cm3ctx = context_module::instance($cm3->cmid); 71 72 $this->create_attempt($cm1, $u1); 73 $this->create_grade($cm2, $u2); 74 $this->create_timer($cm3, $u3); 75 $this->create_branch($cm2, $u4); 76 $this->create_override($cm1, $u5); 77 78 $this->create_attempt($cm2, $u6); 79 $this->create_grade($cm2, $u6); 80 $this->create_timer($cm1, $u6); 81 $this->create_branch($cm2, $u6); 82 $this->create_override($cm3, $u6); 83 84 $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids(); 85 $this->assertCount(1, $contextids); 86 $this->assertTrue(in_array($cm1ctx->id, $contextids)); 87 88 $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids(); 89 $this->assertCount(1, $contextids); 90 $this->assertTrue(in_array($cm2ctx->id, $contextids)); 91 92 $contextids = provider::get_contexts_for_userid($u3->id)->get_contextids(); 93 $this->assertCount(1, $contextids); 94 $this->assertTrue(in_array($cm3ctx->id, $contextids)); 95 96 $contextids = provider::get_contexts_for_userid($u4->id)->get_contextids(); 97 $this->assertCount(1, $contextids); 98 $this->assertTrue(in_array($cm2ctx->id, $contextids)); 99 100 $contextids = provider::get_contexts_for_userid($u5->id)->get_contextids(); 101 $this->assertCount(1, $contextids); 102 $this->assertTrue(in_array($cm1ctx->id, $contextids)); 103 104 $contextids = provider::get_contexts_for_userid($u6->id)->get_contextids(); 105 $this->assertCount(3, $contextids); 106 $this->assertTrue(in_array($cm1ctx->id, $contextids)); 107 $this->assertTrue(in_array($cm2ctx->id, $contextids)); 108 $this->assertTrue(in_array($cm3ctx->id, $contextids)); 109 } 110 111 /* 112 * Test for provider::get_users_in_context(). 113 */ 114 public function test_get_users_in_context() { 115 $dg = $this->getDataGenerator(); 116 $c1 = $dg->create_course(); 117 $component = 'mod_lesson'; 118 119 $u1 = $dg->create_user(); 120 $u2 = $dg->create_user(); 121 $u3 = $dg->create_user(); 122 $u4 = $dg->create_user(); 123 $u5 = $dg->create_user(); 124 $u6 = $dg->create_user(); 125 126 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 127 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 128 129 $cm1ctx = context_module::instance($cm1->cmid); 130 $cm2ctx = context_module::instance($cm2->cmid); 131 132 $this->create_attempt($cm1, $u1); 133 $this->create_grade($cm1, $u2); 134 $this->create_timer($cm1, $u3); 135 $this->create_branch($cm1, $u4); 136 $this->create_override($cm1, $u5); 137 138 $this->create_attempt($cm2, $u6); 139 $this->create_grade($cm2, $u6); 140 $this->create_timer($cm2, $u6); 141 $this->create_branch($cm2, $u6); 142 $this->create_override($cm2, $u6); 143 144 $context = context_module::instance($cm1->cmid); 145 $userlist = new \core_privacy\local\request\userlist($context, $component); 146 provider::get_users_in_context($userlist); 147 $userids = $userlist->get_userids(); 148 149 $this->assertCount(5, $userids); 150 $expected = [$u1->id, $u2->id, $u3->id, $u4->id, $u5->id]; 151 $actual = $userids; 152 sort($expected); 153 sort($actual); 154 $this->assertEquals($expected, $actual); 155 156 $context = context_module::instance($cm2->cmid); 157 $userlist = new \core_privacy\local\request\userlist($context, $component); 158 provider::get_users_in_context($userlist); 159 $userids = $userlist->get_userids(); 160 161 $this->assertCount(1, $userids); 162 $this->assertEquals([$u6->id], $userids); 163 } 164 165 public function test_delete_data_for_all_users_in_context() { 166 global $DB; 167 $dg = $this->getDataGenerator(); 168 $c1 = $dg->create_course(); 169 $u1 = $dg->create_user(); 170 $u2 = $dg->create_user(); 171 172 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 173 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 174 $cm3 = $dg->create_module('lesson', ['course' => $c1]); 175 176 $c1ctx = context_course::instance($c1->id); 177 $cm1ctx = context_module::instance($cm1->cmid); 178 $cm2ctx = context_module::instance($cm2->cmid); 179 $cm3ctx = context_module::instance($cm3->cmid); 180 181 $this->create_attempt($cm1, $u1); 182 $this->create_grade($cm1, $u1); 183 $this->create_timer($cm1, $u1); 184 $this->create_branch($cm1, $u1); 185 $this->create_override($cm1, $u1); 186 187 $this->create_attempt($cm1, $u2); 188 $this->create_grade($cm1, $u2); 189 $this->create_timer($cm1, $u2); 190 $this->create_branch($cm1, $u2); 191 $this->create_override($cm1, $u2); 192 193 $this->create_attempt($cm2, $u1); 194 $this->create_grade($cm2, $u1); 195 $this->create_timer($cm2, $u1); 196 $this->create_branch($cm2, $u1); 197 $this->create_override($cm2, $u1); 198 $this->create_attempt($cm2, $u2); 199 $this->create_grade($cm2, $u2); 200 $this->create_timer($cm2, $u2); 201 $this->create_branch($cm2, $u2); 202 $this->create_override($cm2, $u2); 203 204 $assertcm1nochange = function() use ($DB, $u1, $u2, $cm1) { 205 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 206 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 207 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 208 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 209 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 210 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 211 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 212 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 213 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 214 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 215 }; 216 $assertcm2nochange = function() use ($DB, $u1, $u2, $cm2) { 217 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 218 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 219 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 220 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 221 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 222 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 223 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 224 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 225 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 226 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 227 }; 228 229 // Confirm existing state. 230 $assertcm1nochange(); 231 $assertcm2nochange(); 232 233 // Delete the course: no change. 234 provider::delete_data_for_all_users_in_context(context_course::instance($c1->id)); 235 $assertcm1nochange(); 236 $assertcm2nochange(); 237 238 // Delete another module: no change. 239 provider::delete_data_for_all_users_in_context(context_module::instance($cm3->cmid)); 240 $assertcm1nochange(); 241 $assertcm2nochange(); 242 243 // Delete cm1: no change in cm2. 244 provider::delete_data_for_all_users_in_context(context_module::instance($cm1->cmid)); 245 $assertcm2nochange(); 246 $this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 247 $this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 248 $this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 249 $this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 250 $this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 251 $this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 252 $this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 253 $this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 254 $this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 255 $this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 256 } 257 258 public function test_delete_data_for_user() { 259 global $DB; 260 $dg = $this->getDataGenerator(); 261 $c1 = $dg->create_course(); 262 $u1 = $dg->create_user(); 263 $u2 = $dg->create_user(); 264 265 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 266 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 267 $cm3 = $dg->create_module('lesson', ['course' => $c1]); 268 269 $c1ctx = context_course::instance($c1->id); 270 $cm1ctx = context_module::instance($cm1->cmid); 271 $cm2ctx = context_module::instance($cm2->cmid); 272 $cm3ctx = context_module::instance($cm3->cmid); 273 274 $this->create_attempt($cm1, $u1); 275 $this->create_grade($cm1, $u1); 276 $this->create_timer($cm1, $u1); 277 $this->create_branch($cm1, $u1); 278 $this->create_override($cm1, $u1); 279 $this->create_attempt($cm1, $u2); 280 $this->create_grade($cm1, $u2); 281 $this->create_timer($cm1, $u2); 282 $this->create_branch($cm1, $u2); 283 $this->create_override($cm1, $u2); 284 285 $this->create_attempt($cm2, $u1); 286 $this->create_grade($cm2, $u1); 287 $this->create_timer($cm2, $u1); 288 $this->create_branch($cm2, $u1); 289 $this->create_override($cm2, $u1); 290 $this->create_attempt($cm2, $u2); 291 $this->create_grade($cm2, $u2); 292 $this->create_timer($cm2, $u2); 293 $this->create_branch($cm2, $u2); 294 $this->create_override($cm2, $u2); 295 296 $assertu1nochange = function() use ($DB, $u1, $cm1, $cm2) { 297 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 298 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 299 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 300 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 301 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 302 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 303 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 304 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 305 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 306 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 307 }; 308 $assertu2nochange = function() use ($DB, $u2, $cm1, $cm2) { 309 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 310 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 311 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 312 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 313 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id])); 314 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 315 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 316 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 317 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 318 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm2->id])); 319 }; 320 321 // Confirm existing state. 322 $assertu1nochange(); 323 $assertu2nochange(); 324 325 // Delete the course: no change. 326 provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [context_course::instance($c1->id)->id])); 327 $assertu1nochange(); 328 $assertu2nochange(); 329 330 // Delete another module: no change. 331 provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [context_module::instance($cm3->cmid)->id])); 332 $assertu1nochange(); 333 $assertu2nochange(); 334 335 // Delete u1 in cm1: no change for u2 and in cm2. 336 provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [context_module::instance($cm1->cmid)->id])); 337 $assertu2nochange(); 338 $this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 339 $this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 340 $this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 341 $this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 342 $this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id])); 343 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 344 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 345 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 346 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 347 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id])); 348 } 349 350 /* 351 * Test for provider::delete_data_for_users(). 352 */ 353 public function test_delete_data_for_users() { 354 global $DB; 355 $dg = $this->getDataGenerator(); 356 $c1 = $dg->create_course(); 357 $u1 = $dg->create_user(); 358 $u2 = $dg->create_user(); 359 360 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 361 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 362 $cm3 = $dg->create_module('lesson', ['course' => $c1]); 363 $context1 = context_module::instance($cm1->cmid); 364 $context3 = context_module::instance($cm3->cmid); 365 366 $this->create_attempt($cm1, $u1); 367 $this->create_grade($cm1, $u1); 368 $this->create_timer($cm1, $u1); 369 $this->create_branch($cm1, $u1); 370 $this->create_override($cm1, $u1); 371 $this->create_attempt($cm1, $u2); 372 $this->create_grade($cm1, $u2); 373 $this->create_timer($cm1, $u2); 374 $this->create_branch($cm1, $u2); 375 $this->create_override($cm1, $u2); 376 377 $this->create_attempt($cm2, $u1); 378 $this->create_grade($cm2, $u1); 379 $this->create_timer($cm2, $u1); 380 $this->create_branch($cm2, $u1); 381 $this->create_override($cm2, $u1); 382 $this->create_attempt($cm2, $u2); 383 $this->create_grade($cm2, $u2); 384 $this->create_timer($cm2, $u2); 385 $this->create_branch($cm2, $u2); 386 $this->create_override($cm2, $u2); 387 388 $assertnochange = function($user, $cm) use ($DB) { 389 $this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id])); 390 $this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id])); 391 $this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id])); 392 $this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id])); 393 $this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id])); 394 }; 395 396 $assertdeleted = function($user, $cm) use ($DB) { 397 $this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id])); 398 $this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id])); 399 $this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id])); 400 $this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id])); 401 $this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id])); 402 }; 403 404 // Confirm existing state. 405 $assertnochange($u1, $cm1); 406 $assertnochange($u1, $cm2); 407 $assertnochange($u2, $cm1); 408 $assertnochange($u2, $cm2); 409 410 // Delete another module: no change. 411 $approveduserlist = new core_privacy\local\request\approved_userlist($context3, 'mod_lesson', [$u1->id]); 412 provider::delete_data_for_users($approveduserlist); 413 414 $assertnochange($u1, $cm1); 415 $assertnochange($u1, $cm2); 416 $assertnochange($u2, $cm1); 417 $assertnochange($u2, $cm2); 418 419 // Delete cm1 for u1: no change for u2 and in cm2. 420 $approveduserlist = new core_privacy\local\request\approved_userlist($context1, 'mod_lesson', [$u1->id]); 421 provider::delete_data_for_users($approveduserlist); 422 423 $assertdeleted($u1, $cm1); 424 $assertnochange($u1, $cm2); 425 $assertnochange($u2, $cm1); 426 $assertnochange($u2, $cm2); 427 } 428 429 public function test_export_data_for_user_overrides() { 430 $dg = $this->getDataGenerator(); 431 $c1 = $dg->create_course(); 432 $u1 = $dg->create_user(); 433 $u2 = $dg->create_user(); 434 435 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 436 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 437 $cm1ctx = context_module::instance($cm1->cmid); 438 $cm2ctx = context_module::instance($cm2->cmid); 439 440 $now = time(); 441 $this->create_override($cm1, $u1); // All null. 442 $this->create_override($cm2, $u1, [ 443 'available' => $now - 3600, 444 'deadline' => $now + 3600, 445 'timelimit' => 123, 446 'review' => 1, 447 'maxattempts' => 1, 448 'retake' => 0, 449 'password' => '1337 5p34k' 450 ]); 451 $this->create_override($cm1, $u2, [ 452 'available' => $now - 1230, 453 'timelimit' => 456, 454 'maxattempts' => 5, 455 'retake' => 1, 456 ]); 457 458 provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 459 $data = writer::with_context($cm1ctx)->get_data([]); 460 $this->assertNotEmpty($data); 461 $data = writer::with_context($cm1ctx)->get_related_data([], 'overrides'); 462 $this->assertNull($data->available); 463 $this->assertNull($data->deadline); 464 $this->assertNull($data->timelimit); 465 $this->assertNull($data->review); 466 $this->assertNull($data->maxattempts); 467 $this->assertNull($data->retake); 468 $this->assertNull($data->password); 469 470 $data = writer::with_context($cm2ctx)->get_data([]); 471 $this->assertNotEmpty($data); 472 $data = writer::with_context($cm2ctx)->get_related_data([], 'overrides'); 473 $this->assertEquals(transform::datetime($now - 3600), $data->available); 474 $this->assertEquals(transform::datetime($now + 3600), $data->deadline); 475 $this->assertEquals(format_time(123), $data->timelimit); 476 $this->assertEquals(transform::yesno(true), $data->review); 477 $this->assertEquals(1, $data->maxattempts); 478 $this->assertEquals(transform::yesno(false), $data->retake); 479 $this->assertEquals('1337 5p34k', $data->password); 480 481 writer::reset(); 482 provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 483 $data = writer::with_context($cm1ctx)->get_data([]); 484 $this->assertNotEmpty($data); 485 $data = writer::with_context($cm1ctx)->get_related_data([], 'overrides'); 486 $this->assertEquals(transform::datetime($now - 1230), $data->available); 487 $this->assertNull($data->deadline); 488 $this->assertEquals(format_time(456), $data->timelimit); 489 $this->assertNull($data->review); 490 $this->assertEquals(5, $data->maxattempts); 491 $this->assertEquals(transform::yesno(true), $data->retake); 492 $this->assertNull($data->password); 493 494 $data = writer::with_context($cm2ctx)->get_data([]); 495 $this->assertNotEmpty($data); 496 $data = writer::with_context($cm2ctx)->get_related_data([], 'overrides'); 497 $this->assertEmpty($data); 498 } 499 500 public function test_export_data_for_user_grades() { 501 $dg = $this->getDataGenerator(); 502 $c1 = $dg->create_course(); 503 $u1 = $dg->create_user(); 504 $u2 = $dg->create_user(); 505 506 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 507 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 508 $cm1ctx = context_module::instance($cm1->cmid); 509 $cm2ctx = context_module::instance($cm2->cmid); 510 511 $now = time(); 512 $this->create_grade($cm2, $u1, ['grade' => 33.33, 'completed' => $now - 3600]); 513 $this->create_grade($cm2, $u1, ['grade' => 50, 'completed' => $now - 1600]); 514 $this->create_grade($cm2, $u1, ['grade' => 81.23, 'completed' => $now - 100]); 515 $this->create_grade($cm1, $u2, ['grade' => 99.98, 'completed' => $now - 86400]); 516 517 provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 518 $data = writer::with_context($cm1ctx)->get_related_data([], 'grades'); 519 $this->assertEmpty($data); 520 $data = writer::with_context($cm2ctx)->get_related_data([], 'grades'); 521 $this->assertNotEmpty($data); 522 $this->assertCount(3, $data->grades); 523 $this->assertEquals(33.33, $data->grades[0]->grade); 524 $this->assertEquals(50, $data->grades[1]->grade); 525 $this->assertEquals(81.23, $data->grades[2]->grade); 526 $this->assertEquals(transform::datetime($now - 3600), $data->grades[0]->completed); 527 $this->assertEquals(transform::datetime($now - 1600), $data->grades[1]->completed); 528 $this->assertEquals(transform::datetime($now - 100), $data->grades[2]->completed); 529 530 writer::reset(); 531 provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 532 $data = writer::with_context($cm2ctx)->get_related_data([], 'grades'); 533 $this->assertEmpty($data); 534 $data = writer::with_context($cm1ctx)->get_related_data([], 'grades'); 535 $this->assertNotEmpty($data); 536 $this->assertCount(1, $data->grades); 537 $this->assertEquals(99.98, $data->grades[0]->grade); 538 $this->assertEquals(transform::datetime($now - 86400), $data->grades[0]->completed); 539 } 540 541 public function test_export_data_for_user_timers() { 542 $dg = $this->getDataGenerator(); 543 $c1 = $dg->create_course(); 544 $u1 = $dg->create_user(); 545 $u2 = $dg->create_user(); 546 547 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 548 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 549 $cm1ctx = context_module::instance($cm1->cmid); 550 $cm2ctx = context_module::instance($cm2->cmid); 551 552 $now = time(); 553 $this->create_timer($cm2, $u1, ['starttime' => $now - 2000, 'lessontime' => $now + 3600, 'completed' => 0, 554 'timemodifiedoffline' => $now - 7000]); 555 $this->create_timer($cm2, $u1, ['starttime' => $now - 1000, 'lessontime' => $now + 1600, 'completed' => 0]); 556 $this->create_timer($cm2, $u1, ['starttime' => $now - 500, 'lessontime' => $now + 100, 'completed' => 1]); 557 $this->create_timer($cm1, $u2, ['starttime' => $now - 1000, 'lessontime' => $now + 1800, 'completed' => 1]); 558 559 provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 560 $data = writer::with_context($cm1ctx)->get_related_data([], 'timers'); 561 $this->assertEmpty($data); 562 $data = writer::with_context($cm2ctx)->get_related_data([], 'timers'); 563 $this->assertNotEmpty($data); 564 $this->assertCount(3, $data->timers); 565 $this->assertEquals(transform::datetime($now - 2000), $data->timers[0]->starttime); 566 $this->assertEquals(transform::datetime($now + 3600), $data->timers[0]->lastactivity); 567 $this->assertEquals(transform::yesno(false), $data->timers[0]->completed); 568 $this->assertEquals(transform::datetime($now - 7000), $data->timers[0]->timemodifiedoffline); 569 570 $this->assertEquals(transform::datetime($now - 1000), $data->timers[1]->starttime); 571 $this->assertEquals(transform::datetime($now + 1600), $data->timers[1]->lastactivity); 572 $this->assertEquals(transform::yesno(false), $data->timers[1]->completed); 573 $this->assertNull($data->timers[1]->timemodifiedoffline); 574 575 $this->assertEquals(transform::datetime($now - 500), $data->timers[2]->starttime); 576 $this->assertEquals(transform::datetime($now + 100), $data->timers[2]->lastactivity); 577 $this->assertEquals(transform::yesno(true), $data->timers[2]->completed); 578 $this->assertNull($data->timers[2]->timemodifiedoffline); 579 580 writer::reset(); 581 provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 582 $data = writer::with_context($cm2ctx)->get_related_data([], 'timers'); 583 $this->assertEmpty($data); 584 $data = writer::with_context($cm1ctx)->get_related_data([], 'timers'); 585 $this->assertCount(1, $data->timers); 586 $this->assertEquals(transform::datetime($now - 1000), $data->timers[0]->starttime); 587 $this->assertEquals(transform::datetime($now + 1800), $data->timers[0]->lastactivity); 588 $this->assertEquals(transform::yesno(true), $data->timers[0]->completed); 589 $this->assertNull($data->timers[0]->timemodifiedoffline); 590 } 591 592 public function test_export_data_for_user_attempts() { 593 global $DB; 594 $dg = $this->getDataGenerator(); 595 $lg = $dg->get_plugin_generator('mod_lesson'); 596 597 $c1 = $dg->create_course(); 598 $u1 = $dg->create_user(); 599 $u2 = $dg->create_user(); 600 601 $cm1 = $dg->create_module('lesson', ['course' => $c1]); 602 $cm2 = $dg->create_module('lesson', ['course' => $c1]); 603 $cm1ctx = context_module::instance($cm1->cmid); 604 $cm2ctx = context_module::instance($cm2->cmid); 605 606 $page1 = $lg->create_content($cm1); 607 $page2 = $lg->create_question_truefalse($cm1); 608 $page3 = $lg->create_question_multichoice($cm1); 609 $page4 = $lg->create_question_multichoice($cm1, [ 610 'qoption' => 1, 611 'answer_editor' => [ 612 ['text' => 'Cats', 'format' => FORMAT_PLAIN, 'score' => 1], 613 ['text' => 'Dogs', 'format' => FORMAT_PLAIN, 'score' => 1], 614 ['text' => 'Birds', 'format' => FORMAT_PLAIN, 'score' => 0], 615 ], 616 'jumpto' => [LESSON_NEXTPAGE, LESSON_NEXTPAGE, LESSON_THISPAGE] 617 ]); 618 $page4answers = array_keys($DB->get_records('lesson_answers', ['pageid' => $page4->id], 'id')); 619 $page5 = $lg->create_question_matching($cm1, [ 620 'answer_editor' => [ 621 2 => ['text' => 'The plural of cat', 'format' => FORMAT_PLAIN], 622 3 => ['text' => 'The plural of dog', 'format' => FORMAT_PLAIN], 623 4 => ['text' => 'The plural of bird', 'format' => FORMAT_PLAIN], 624 ], 625 'response_editor' => [ 626 2 => 'Cats', 627 3 => 'Dogs', 628 4 => 'Birds', 629 ] 630 ]); 631 $page6 = $lg->create_question_shortanswer($cm1); 632 $page7 = $lg->create_question_numeric($cm1); 633 $page8 = $lg->create_question_essay($cm1); 634 $page9 = $lg->create_content($cm1); 635 636 $pageb1 = $lg->create_content($cm2); 637 $pageb2 = $lg->create_question_truefalse($cm2); 638 $pageb3 = $lg->create_question_truefalse($cm2); 639 640 $this->create_branch($cm1, $u1, ['pageid' => $page1->id, 'nextpageid' => $page2->id]); 641 $this->create_attempt($cm1, $u1, ['pageid' => $page2->id, 'useranswer' => 'This is true']); 642 $this->create_attempt($cm1, $u1, ['pageid' => $page3->id, 'useranswer' => 'A', 'correct' => 1]); 643 $this->create_attempt($cm1, $u1, ['pageid' => $page4->id, 644 'useranswer' => implode(',', array_slice($page4answers, 0, 2))]); 645 $this->create_attempt($cm1, $u1, ['pageid' => $page5->id, 'useranswer' => 'Cats,Birds,Dogs']); 646 $this->create_attempt($cm1, $u1, ['pageid' => $page6->id, 'useranswer' => 'Hello world!']); 647 $this->create_attempt($cm1, $u1, ['pageid' => $page7->id, 'useranswer' => '1337']); 648 $this->create_attempt($cm1, $u1, ['pageid' => $page8->id, 'useranswer' => serialize((object) [ 649 'sent' => 0, 'graded' => 0, 'score' => 0, 'answer' => 'I like cats', 'answerformat' => FORMAT_PLAIN, 650 'response' => 'Me too!', 'responseformat' => FORMAT_PLAIN 651 ])]); 652 $this->create_branch($cm1, $u1, ['pageid' => $page9->id, 'nextpageid' => 0]); 653 654 provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 655 $data = writer::with_context($cm2ctx)->get_related_data([], 'attempts'); 656 $this->assertEmpty($data); 657 $data = writer::with_context($cm1ctx)->get_related_data([], 'attempts'); 658 $this->assertNotEmpty($data); 659 $this->assertCount(1, $data->attempts); 660 $this->assertEquals(1, $data->attempts[0]->number); 661 $this->assertCount(2, $data->attempts[0]->jumps); 662 $this->assertCount(7, $data->attempts[0]->answers); 663 $jump = $data->attempts[0]->jumps[0]; 664 $this->assert_attempt_page($page1, $jump); 665 $this->assertTrue(strpos($jump['went_to'], $page2->title) !== false); 666 $jump = $data->attempts[0]->jumps[1]; 667 $this->assert_attempt_page($page9, $jump); 668 $this->assertEquals(get_string('endoflesson', 'mod_lesson'), $jump['went_to']); 669 $answer = $data->attempts[0]->answers[0]; 670 $this->assert_attempt_page($page2, $answer); 671 $this->assertEquals(transform::yesno(false), $answer['correct']); 672 $this->assertEquals('This is true', $answer['answer']); 673 $answer = $data->attempts[0]->answers[1]; 674 $this->assert_attempt_page($page3, $answer); 675 $this->assertEquals(transform::yesno(true), $answer['correct']); 676 $this->assertEquals('A', $answer['answer']); 677 $answer = $data->attempts[0]->answers[2]; 678 $this->assert_attempt_page($page4, $answer); 679 $this->assertEquals(transform::yesno(false), $answer['correct']); 680 $this->assertCount(2, $answer['answer']); 681 $this->assertTrue(in_array('Cats', $answer['answer'])); 682 $this->assertTrue(in_array('Dogs', $answer['answer'])); 683 $answer = $data->attempts[0]->answers[3]; 684 $this->assert_attempt_page($page5, $answer); 685 $this->assertEquals(transform::yesno(false), $answer['correct']); 686 $this->assertCount(3, $answer['answer']); 687 $this->assertEquals('The plural of cat', $answer['answer'][0]['label']); 688 $this->assertEquals('Cats', $answer['answer'][0]['matched_with']); 689 $this->assertEquals('The plural of dog', $answer['answer'][1]['label']); 690 $this->assertEquals('Birds', $answer['answer'][1]['matched_with']); 691 $this->assertEquals('The plural of bird', $answer['answer'][2]['label']); 692 $this->assertEquals('Dogs', $answer['answer'][2]['matched_with']); 693 $answer = $data->attempts[0]->answers[4]; 694 $this->assert_attempt_page($page6, $answer); 695 $this->assertEquals(transform::yesno(false), $answer['correct']); 696 $this->assertEquals('Hello world!', $answer['answer']); 697 $answer = $data->attempts[0]->answers[5]; 698 $this->assert_attempt_page($page7, $answer); 699 $this->assertEquals(transform::yesno(false), $answer['correct']); 700 $this->assertEquals('1337', $answer['answer']); 701 $answer = $data->attempts[0]->answers[6]; 702 $this->assert_attempt_page($page8, $answer); 703 $this->assertEquals(transform::yesno(false), $answer['correct']); 704 $this->assertEquals('I like cats', $answer['answer']); 705 $this->assertEquals('Me too!', $answer['response']); 706 707 writer::reset(); 708 provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 709 $data = writer::with_context($cm1ctx)->get_related_data([], 'attempts'); 710 $this->assertEmpty($data); 711 $data = writer::with_context($cm2ctx)->get_related_data([], 'attempts'); 712 $this->assertEmpty($data); 713 714 // Let's mess with the data by creating an additional attempt for u1, and create data for u1 and u2 in the other cm. 715 $this->create_branch($cm1, $u1, ['pageid' => $page1->id, 'nextpageid' => $page3->id, 'retry' => 1]); 716 $this->create_attempt($cm1, $u1, ['pageid' => $page3->id, 'useranswer' => 'B', 'retry' => 1]); 717 718 $this->create_branch($cm2, $u1, ['pageid' => $pageb1->id, 'nextpageid' => $pageb2->id]); 719 $this->create_attempt($cm2, $u1, ['pageid' => $pageb2->id, 'useranswer' => 'Abc']); 720 721 $this->create_branch($cm2, $u2, ['pageid' => $pageb1->id, 'nextpageid' => $pageb3->id]); 722 $this->create_attempt($cm2, $u2, ['pageid' => $pageb3->id, 'useranswer' => 'Def']); 723 724 writer::reset(); 725 provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 726 $data = writer::with_context($cm1ctx)->get_related_data([], 'attempts'); 727 $this->assertNotEmpty($data); 728 $this->assertCount(2, $data->attempts); 729 $this->assertEquals(1, $data->attempts[0]->number); 730 $this->assertCount(2, $data->attempts[0]->jumps); 731 $this->assertCount(7, $data->attempts[0]->answers); 732 $attempt = $data->attempts[1]; 733 $this->assertEquals(2, $attempt->number); 734 $this->assertCount(1, $attempt->jumps); 735 $this->assertCount(1, $attempt->answers); 736 $this->assert_attempt_page($page1, $attempt->jumps[0]); 737 $this->assertTrue(strpos($attempt->jumps[0]['went_to'], $page3->title) !== false); 738 $this->assert_attempt_page($page3, $attempt->answers[0]); 739 $this->assertEquals('B', $attempt->answers[0]['answer']); 740 741 $data = writer::with_context($cm2ctx)->get_related_data([], 'attempts'); 742 $this->assertCount(1, $data->attempts); 743 $attempt = $data->attempts[0]; 744 $this->assertEquals(1, $attempt->number); 745 $this->assertCount(1, $attempt->jumps); 746 $this->assertCount(1, $attempt->answers); 747 $this->assert_attempt_page($pageb1, $attempt->jumps[0]); 748 $this->assertTrue(strpos($attempt->jumps[0]['went_to'], $pageb2->title) !== false); 749 $this->assert_attempt_page($pageb2, $attempt->answers[0]); 750 $this->assertEquals('Abc', $attempt->answers[0]['answer']); 751 752 writer::reset(); 753 provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id])); 754 $data = writer::with_context($cm1ctx)->get_related_data([], 'attempts'); 755 $this->assertEmpty($data); 756 757 $data = writer::with_context($cm2ctx)->get_related_data([], 'attempts'); 758 $this->assertCount(1, $data->attempts); 759 $attempt = $data->attempts[0]; 760 $this->assertEquals(1, $attempt->number); 761 $this->assertCount(1, $attempt->jumps); 762 $this->assertCount(1, $attempt->answers); 763 $this->assert_attempt_page($pageb1, $attempt->jumps[0]); 764 $this->assertTrue(strpos($attempt->jumps[0]['went_to'], $pageb3->title) !== false); 765 $this->assert_attempt_page($pageb3, $attempt->answers[0]); 766 $this->assertEquals('Def', $attempt->answers[0]['answer']); 767 } 768 769 /** 770 * Assert the page details of an attempt. 771 * 772 * @param object $page The expected page info. 773 * @param array $attempt The exported attempt details. 774 * @return void 775 */ 776 protected function assert_attempt_page($page, $attempt) { 777 $this->assertEquals($page->id, $attempt['id']); 778 $this->assertEquals($page->title, $attempt['page']); 779 $this->assertEquals(format_text($page->contents, $page->contentsformat), $attempt['contents']); 780 } 781 782 /** 783 * Create an attempt (answer to a question). 784 * 785 * @param object $lesson The lesson. 786 * @param object $user The user. 787 * @param array $options Options. 788 * @return object 789 */ 790 protected function create_attempt($lesson, $user, array $options = []) { 791 global $DB; 792 $record = (object) array_merge([ 793 'lessonid' => $lesson->id, 794 'userid' => $user->id, 795 'pageid' => 0, 796 'answerid' => 0, 797 'retry' => 0, 798 'correct' => 0, 799 'useranswer' => '', 800 'timeseen' => time(), 801 ], $options); 802 $record->id = $DB->insert_record('lesson_attempts', $record); 803 return $record; 804 } 805 806 /** 807 * Create a grade. 808 * 809 * @param object $lesson The lesson. 810 * @param object $user The user. 811 * @param array $options Options. 812 * @return object 813 */ 814 protected function create_grade($lesson, $user, array $options = []) { 815 global $DB; 816 $record = (object) array_merge([ 817 'lessonid' => $lesson->id, 818 'userid' => $user->id, 819 'late' => 0, 820 'grade' => 50.0, 821 'completed' => time(), 822 ], $options); 823 $record->id = $DB->insert_record('lesson_grades', $record); 824 return $record; 825 } 826 827 /** 828 * Create a timer. 829 * 830 * @param object $lesson The lesson. 831 * @param object $user The user. 832 * @param array $options Options. 833 * @return object 834 */ 835 protected function create_timer($lesson, $user, array $options = []) { 836 global $DB; 837 $record = (object) array_merge([ 838 'lessonid' => $lesson->id, 839 'userid' => $user->id, 840 'starttime' => time() - 600, 841 'lessontime' => time(), 842 'completed' => 1, 843 'timemodifiedoffline' => 0, 844 ], $options); 845 $record->id = $DB->insert_record('lesson_timer', $record); 846 return $record; 847 } 848 849 /** 850 * Create a branch (choice on page). 851 * 852 * @param object $lesson The lesson. 853 * @param object $user The user. 854 * @param array $options Options. 855 * @return object 856 */ 857 protected function create_branch($lesson, $user, array $options = []) { 858 global $DB; 859 $record = (object) array_merge([ 860 'lessonid' => $lesson->id, 861 'userid' => $user->id, 862 'pageid' => 0, 863 'retry' => 0, 864 'flag' => 0, 865 'timeseen' => time(), 866 'nextpageid' => 0, 867 ], $options); 868 $record->id = $DB->insert_record('lesson_branch', $record); 869 return $record; 870 } 871 872 /** 873 * Create an override. 874 * 875 * @param object $lesson The lesson. 876 * @param object $user The user. 877 * @param array $options Options. 878 * @return object 879 */ 880 protected function create_override($lesson, $user, array $options = []) { 881 global $DB; 882 $record = (object) array_merge([ 883 'lessonid' => $lesson->id, 884 'userid' => $user->id, 885 ], $options); 886 $record->id = $DB->insert_record('lesson_overrides', $record); 887 return $record; 888 } 889 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body