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