Differences Between: [Versions 311 and 402] [Versions 311 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Unit tests for the core_notes implementation of the privacy API. 19 * 20 * @package core_notes 21 * @category test 22 * @copyright 2018 Zig Tan <zig@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 namespace core_notes\privacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 global $CFG; 29 30 require_once($CFG->dirroot . "/notes/lib.php"); 31 32 use core_notes\privacy\provider; 33 use core_privacy\local\request\writer; 34 use core_privacy\local\request\approved_contextlist; 35 use core_privacy\local\request\approved_userlist; 36 37 /** 38 * Unit tests for the core_notes implementation of the privacy API. 39 * 40 * @copyright 2018 Zig Tan <zig@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class provider_test extends \core_privacy\tests\provider_testcase { 44 45 /** 46 * Test for provider::get_contexts_for_userid(). 47 */ 48 public function test_get_contexts_for_userid() { 49 global $DB; 50 51 // Test setup. 52 $this->resetAfterTest(true); 53 $this->setAdminUser(); 54 set_config('enablenotes', true); 55 56 $teacher1 = $this->getDataGenerator()->create_user(); 57 $this->setUser($teacher1); 58 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 59 60 $student = $this->getDataGenerator()->create_user(); 61 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 62 63 // Create Courses, then enrol a teacher and a student. 64 $nocourses = 5; 65 $courses = []; 66 $coursecontextids = []; 67 for ($c = 1; $c <= $nocourses; $c++) { 68 $course = $this->getDataGenerator()->create_course(); 69 $coursecontext = \context_course::instance($course->id); 70 71 role_assign($teacherrole->id, $teacher1->id, $coursecontext->id); 72 role_assign($studentrole->id, $student->id, $coursecontext->id); 73 74 // Only create private user notes (i.e. NOTES_STATE_DRAFT) for student in Course 1, 2, 3 written by the teacher. 75 if ($c <= 3) { 76 $this->help_create_user_note( 77 $student->id, 78 NOTES_STATE_DRAFT, 79 $course->id, 80 "Test private user note about the student in Course $c by the teacher" 81 ); 82 } 83 84 $courses[$c] = $course; 85 $coursecontextids[] = $coursecontext->id; 86 } 87 88 // Test Teacher 1's contexts equals 3 because only 3 user notes were added for Course 1, 2, and 3. 89 // Course 4 and 5 does not have any notes associated with it, so the contexts should not be returned. 90 $contexts = provider::get_contexts_for_userid($teacher1->id); 91 $this->assertCount(3, $contexts->get_contextids()); 92 93 // Test the Student's contexts is 0 because the notes written by the teacher are private. 94 $contexts = provider::get_contexts_for_userid($student->id); 95 $this->assertCount(0, $contexts->get_contextids()); 96 97 // Add a public user note (i.e. NOTES_STATE_PUBLIC) written by the Teacher about the Student in Course 4. 98 $course = $courses[4]; 99 $this->help_create_user_note( 100 $student->id, 101 NOTES_STATE_PUBLIC, 102 $course->id, 103 "Test public user note about the student in Course 4 by the teacher" 104 ); 105 106 // Test Teacher 1's contexts equals 4 after adding a public note about a student in Course 4. 107 $contexts = provider::get_contexts_for_userid($teacher1->id); 108 $this->assertCount(4, $contexts->get_contextids()); 109 110 // Test the Student's contexts is 1 for Course 4 because there is a public note written by the teacher. 111 $contexts = provider::get_contexts_for_userid($student->id); 112 $this->assertCount(1, $contexts->get_contextids()); 113 114 // Add a site-wide user note (i.e. NOTES_STATE_SITE) written by the Teacher 1 about the Student in Course 3. 115 $course = $courses[3]; 116 $this->help_create_user_note( 117 $student->id, 118 NOTES_STATE_SITE, 119 $course->id, 120 "Test site-wide user note about the student in Course 3 by the teacher" 121 ); 122 123 // Test the Student's contexts is 2 for Courses 3, 4 because there is a public and site-wide note written by the Teacher. 124 $contexts = provider::get_contexts_for_userid($student->id); 125 $this->assertCount(2, $contexts->get_contextids()); 126 127 // Add a site-wide user note for the Teacher 1 by another Teacher 2 in Course 5. 128 $teacher2 = $this->getDataGenerator()->create_user(); 129 $this->setUser($teacher2); 130 131 $course = $courses[5]; 132 $this->help_create_user_note( 133 $teacher1->id, 134 NOTES_STATE_SITE, 135 $course->id, 136 "Test site-wide user note about the teacher in Course 5 by another teacher" 137 ); 138 139 // Test Teacher 1's contexts equals 5 after adding the note from another teacher. 140 $contextlist = provider::get_contexts_for_userid($teacher1->id); 141 $this->assertCount(5, $contextlist->get_contextids()); 142 143 // Test Teacher 1's contexts match the contexts of the Courses associated with notes created. 144 $this->assertEmpty(array_diff($coursecontextids, $contextlist->get_contextids())); 145 } 146 147 /** 148 * Test for provider::export_user_data(). 149 */ 150 public function test_export_user_data() { 151 global $DB; 152 153 // Test setup. 154 $this->resetAfterTest(true); 155 $this->setAdminUser(); 156 set_config('enablenotes', true); 157 158 $teacher1 = $this->getDataGenerator()->create_user(); 159 $this->setUser($teacher1); 160 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 161 162 $nocourses = 5; 163 $nostudents = 2; 164 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 165 166 $courses = []; 167 $coursecontextids = []; 168 for ($c = 1; $c <= $nocourses; $c++) { 169 // Create a Course, then enrol a teacher and enrol 2 students. 170 $course = $this->getDataGenerator()->create_course(); 171 $coursecontext = \context_course::instance($course->id); 172 173 role_assign($teacherrole->id, $teacher1->id, $coursecontext->id); 174 175 // Only create public user notes (i.e. NOTES_STATE_PUBLIC) for students in Course 1, 2, 3 written by the teacher. 176 if ($c <= 3) { 177 for ($s = 0; $s < $nostudents; $s++) { 178 $student = $this->getDataGenerator()->create_user(); 179 role_assign($studentrole->id, $student->id, $coursecontext->id); 180 181 // Create test public user note data written for students by the teacher. 182 $this->help_create_user_note( 183 $student->id, 184 NOTES_STATE_PUBLIC, 185 $course->id, 186 "Test public user note for student $s in Course $c by the teacher" 187 ); 188 } 189 // Store the Course context for those which have test notes added for verification. 190 $coursecontextids[] = $coursecontext->id; 191 } 192 193 $courses[$c] = $course; 194 } 195 196 // Add a site-wide user note for Teacher 1 by another Teacher 2 in Course 4. 197 $teacher2 = $this->getDataGenerator()->create_user(); 198 $this->setUser($teacher2); 199 200 $course = $courses[4]; 201 $this->help_create_user_note( 202 $teacher1->id, 203 NOTES_STATE_SITE, 204 $course->id, 205 "Test site-wide user note about the teacher in Course 4 by another teacher" 206 ); 207 // Store the Course context for those which have test notes added for verification. 208 $coursecontextids[] = \context_course::instance($course->id)->id; 209 210 // Add a private user note for Teacher 1 by another Teacher 2 in Course 5. 211 $course = $courses[5]; 212 $this->help_create_user_note( 213 $teacher1->id, 214 NOTES_STATE_DRAFT, 215 $course->id, 216 "Test private user note about the teacher in Course 5 by another teacher" 217 ); 218 219 // Test the number of contexts returned matches the Course contexts created with notes. 220 $contextlist = provider::get_contexts_for_userid($teacher1->id); 221 $this->assertEmpty(array_diff($coursecontextids, $contextlist->get_contextids())); 222 223 $approvedcontextlist = new approved_contextlist($teacher1, 'core_notes', $contextlist->get_contextids()); 224 225 // Retrieve User notes created by the teacher. 226 provider::export_user_data($approvedcontextlist); 227 228 // Test the core_notes data is exported at the Course context level and has content. 229 foreach ($contextlist as $context) { 230 $this->assertEquals(CONTEXT_COURSE, $context->contextlevel); 231 232 $writer = writer::with_context($context); 233 $this->assertTrue($writer->has_any_data()); 234 } 235 } 236 237 /** 238 * Test for provider::delete_data_for_all_users_in_context(). 239 */ 240 public function test_delete_data_for_all_users_in_context() { 241 global $DB; 242 243 // Test setup. 244 $this->resetAfterTest(true); 245 $this->setAdminUser(); 246 set_config('enablenotes', true); 247 248 $teacher = $this->getDataGenerator()->create_user(); 249 $this->setUser($teacher); 250 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 251 252 $nocourses = 2; 253 $nostudents = 5; 254 $nonotes = 7; 255 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 256 257 $n = 0; 258 for ($c = 0; $c < $nocourses; $c++) { 259 // Create a Course, then enrol a teacher and enrol 2 students. 260 $course = $this->getDataGenerator()->create_course(); 261 $coursecontext = \context_course::instance($course->id); 262 263 role_assign($teacherrole->id, $teacher->id, $coursecontext->id); 264 265 for ($s = 0; $s < $nostudents; $s++) { 266 if ($n < $nonotes) { 267 $student = $this->getDataGenerator()->create_user(); 268 role_assign($studentrole->id, $student->id, $coursecontext->id); 269 270 // Create test note data. 271 $this->help_create_user_note( 272 $student->id, 273 NOTES_STATE_PUBLIC, 274 $course->id, 275 "Test user note for student $s in Course $c" 276 ); 277 } 278 $n++; 279 } 280 } 281 282 // Test the number of contexts returned equals the number of Courses created with user notes for its students. 283 $contextlist = provider::get_contexts_for_userid($teacher->id); 284 $this->assertCount($nocourses, $contextlist->get_contextids()); 285 286 // Test the created user note records in mdl_post table matches the test number of user notes specified. 287 $notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]); 288 $this->assertCount($nonotes, $notes); 289 290 // Delete all user note records in mdl_post table by the specified Course context. 291 foreach ($contextlist->get_contexts() as $context) { 292 provider::delete_data_for_all_users_in_context($context); 293 } 294 295 // Test the core_note records in mdl_post table is equals zero. 296 $notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]); 297 $this->assertCount(0, $notes); 298 } 299 300 /** 301 * Test for provider::delete_data_for_user(). 302 */ 303 public function test_delete_data_for_user() { 304 global $DB; 305 306 // Test setup. 307 $this->resetAfterTest(true); 308 $this->setAdminUser(); 309 set_config('enablenotes', true); 310 311 $teacher = $this->getDataGenerator()->create_user(); 312 $this->setUser($teacher); 313 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 314 315 $nocourses = 2; 316 $nostudents = 5; 317 $nonotes = 7; 318 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 319 320 $n = 0; 321 for ($c = 0; $c < $nocourses; $c++) { 322 // Create a Course, then enrol a teacher and enrol 2 students. 323 $course = $this->getDataGenerator()->create_course(); 324 $coursecontext = \context_course::instance($course->id); 325 326 role_assign($teacherrole->id, $teacher->id, $coursecontext->id); 327 328 for ($s = 0; $s < $nostudents; $s++) { 329 if ($n < $nonotes) { 330 $student = $this->getDataGenerator()->create_user(); 331 role_assign($studentrole->id, $student->id, $coursecontext->id); 332 333 // Create test note data. 334 $this->help_create_user_note( 335 $student->id, 336 NOTES_STATE_PUBLIC, 337 $course->id, 338 "Test user note for student $s in Course $c" 339 ); 340 } 341 $n++; 342 } 343 } 344 345 // Test the number of contexts returned equals the number of Courses created with user notes for its students. 346 $contextlist = provider::get_contexts_for_userid($teacher->id); 347 $this->assertCount($nocourses, $contextlist->get_contextids()); 348 349 // Test the created user note records in mdl_post table matches the test number of user notes specified. 350 $notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]); 351 $this->assertCount($nonotes, $notes); 352 353 // Delete all user note records in mdl_post table created by the specified teacher. 354 $approvedcontextlist = new approved_contextlist($teacher, 'core_notes', $contextlist->get_contextids()); 355 provider::delete_data_for_user($approvedcontextlist); 356 357 // Test the core_note records in mdl_post table is equals zero. 358 $notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]); 359 $this->assertCount(0, $notes); 360 } 361 362 /** 363 * Test that only users within a course context are fetched. 364 */ 365 public function test_get_users_in_context() { 366 global $DB; 367 368 $this->resetAfterTest(true); 369 370 $component = 'core_notes'; 371 // Test setup. 372 $this->setAdminUser(); 373 set_config('enablenotes', true); 374 // Create a teacher. 375 $teacher1 = $this->getDataGenerator()->create_user(); 376 $this->setUser($teacher1); 377 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 378 // Create a student. 379 $student = $this->getDataGenerator()->create_user(); 380 // Create student2. 381 $student2 = $this->getDataGenerator()->create_user(); 382 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 383 384 // Create courses, then enrol a teacher and a student. 385 $nocourses = 3; 386 for ($c = 1; $c <= $nocourses; $c++) { 387 ${'course' . $c} = $this->getDataGenerator()->create_course(); 388 ${'coursecontext' . $c} = \context_course::instance(${'course' . $c}->id); 389 390 role_assign($teacherrole->id, $teacher1->id, ${'coursecontext' . $c}->id); 391 role_assign($studentrole->id, $student->id, ${'coursecontext' . $c}->id); 392 role_assign($studentrole->id, $student2->id, ${'coursecontext' . $c}->id); 393 } 394 // The list of users in coursecontext1 should be empty (related data still have not been created). 395 $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component); 396 provider::get_users_in_context($userlist1); 397 $this->assertCount(0, $userlist1); 398 // The list of users in coursecontext2 should be empty (related data still have not been created). 399 $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component); 400 provider::get_users_in_context($userlist2); 401 $this->assertCount(0, $userlist2); 402 // The list of users in coursecontext3 should be empty (related data still have not been created). 403 $userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component); 404 provider::get_users_in_context($userlist3); 405 $this->assertCount(0, $userlist3); 406 407 // Create private user notes (i.e. NOTES_STATE_DRAFT) for student in course1 and course2 written by the teacher. 408 $this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course1->id, 409 "Test private user note about the student in Course 1 by the teacher"); 410 $this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course2->id, 411 "Test private user note about the student in Course 2 by the teacher"); 412 413 // The list of users in coursecontext1 should return one user (teacher1). 414 provider::get_users_in_context($userlist1); 415 $this->assertCount(1, $userlist1); 416 $this->assertTrue(in_array($teacher1->id, $userlist1->get_userids())); 417 // The list of users in coursecontext2 should return one user (teacher1). 418 provider::get_users_in_context($userlist2); 419 $this->assertCount(1, $userlist2); 420 $this->assertTrue(in_array($teacher1->id, $userlist2->get_userids())); 421 // The list of users in coursecontext3 should not return any users. 422 provider::get_users_in_context($userlist3); 423 $this->assertCount(0, $userlist3); 424 425 // Create public user note (i.e. NOTES_STATE_PUBLIC) for student in course3 written by the teacher. 426 $this->help_create_user_note($student->id, NOTES_STATE_PUBLIC, $course3->id, 427 "Test public user note about the student in Course 3 by the teacher"); 428 429 // The list of users in coursecontext3 should return 2 users (teacher and student). 430 provider::get_users_in_context($userlist3); 431 $this->assertCount(2, $userlist3); 432 $this->assertTrue(in_array($teacher1->id, $userlist3->get_userids())); 433 $this->assertTrue(in_array($student->id, $userlist3->get_userids())); 434 435 // Create site user note (i.e. NOTES_STATE_SITE) for student2 in course3 written by the teacher. 436 $this->help_create_user_note($student2->id, NOTES_STATE_SITE, $course3->id, 437 "Test site-wide user note about student2 in Course 3 by the teacher" 438 ); 439 440 // The list of users in coursecontext3 should return 3 users (teacher, student and student2). 441 provider::get_users_in_context($userlist3); 442 $this->assertCount(3, $userlist3); 443 $this->assertTrue(in_array($teacher1->id, $userlist3->get_userids())); 444 $this->assertTrue(in_array($student->id, $userlist3->get_userids())); 445 $this->assertTrue(in_array($student2->id, $userlist3->get_userids())); 446 447 // The list of users should not return any users in a different context than course context. 448 $contextsystem = \context_system::instance(); 449 $userlist4 = new \core_privacy\local\request\userlist($contextsystem, $component); 450 provider::get_users_in_context($userlist4); 451 $this->assertCount(0, $userlist4); 452 } 453 454 /** 455 * Test that data for users in approved userlist is deleted. 456 */ 457 public function test_delete_data_for_users() { 458 global $DB; 459 460 $this->resetAfterTest(true); 461 462 $component = 'core_notes'; 463 // Test setup. 464 $this->setAdminUser(); 465 set_config('enablenotes', true); 466 // Create a teacher. 467 $teacher1 = $this->getDataGenerator()->create_user(); 468 $this->setUser($teacher1); 469 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); 470 // Create a student. 471 $student = $this->getDataGenerator()->create_user(); 472 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 473 474 // Create Courses, then enrol a teacher and a student. 475 $nocourses = 3; 476 for ($c = 1; $c <= $nocourses; $c++) { 477 ${'course' . $c} = $this->getDataGenerator()->create_course(); 478 ${'coursecontext' . $c} = \context_course::instance(${'course' . $c}->id); 479 480 role_assign($teacherrole->id, $teacher1->id, ${'coursecontext' . $c}->id); 481 role_assign($studentrole->id, $student->id, ${'coursecontext' . $c}->id); 482 } 483 484 // Create private notes for student in the course1 and course2 written by the teacher. 485 $this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course1->id, 486 "Test private user note about the student in Course 1 by the teacher"); 487 $this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course2->id, 488 "Test private user note about the student in Course 2 by the teacher"); 489 // Create public notes for student in the course3 written by the teacher. 490 $this->help_create_user_note($student->id, NOTES_STATE_PUBLIC, $course3->id, 491 "Test public user note about the student in Course 3 by the teacher"); 492 493 // The list of users in coursecontext1 should return one user (teacher1). 494 $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component); 495 provider::get_users_in_context($userlist1); 496 $this->assertCount(1, $userlist1); 497 $this->assertTrue(in_array($teacher1->id, $userlist1->get_userids())); 498 // The list of users in coursecontext2 should return one user (teacher1). 499 $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component); 500 provider::get_users_in_context($userlist2); 501 $this->assertCount(1, $userlist2); 502 $this->assertTrue(in_array($teacher1->id, $userlist2->get_userids())); 503 // The list of users in coursecontext3 should return two users (teacher1 and student). 504 $userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component); 505 provider::get_users_in_context($userlist3); 506 $this->assertCount(2, $userlist3); 507 $this->assertTrue(in_array($teacher1->id, $userlist3->get_userids())); 508 $this->assertTrue(in_array($student->id, $userlist3->get_userids())); 509 510 $approvedlist = new approved_userlist($coursecontext3, $component, [$student->id]); 511 // Delete using delete_data_for_user. 512 provider::delete_data_for_users($approvedlist); 513 // Re-fetch users in the coursecontext3. 514 $userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component); 515 // The user data in coursecontext3 should not be removed. 516 provider::get_users_in_context($userlist3); 517 $this->assertCount(2, $userlist3); 518 $this->assertTrue(in_array($teacher1->id, $userlist3->get_userids())); 519 $this->assertTrue(in_array($student->id, $userlist3->get_userids())); 520 521 $approvedlist = new approved_userlist($coursecontext3, $component, [$teacher1->id]); 522 // Delete using delete_data_for_user. 523 provider::delete_data_for_users($approvedlist); 524 // Re-fetch users in the coursecontext3. 525 $userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component); 526 // The user data in coursecontext3 should be removed. 527 provider::get_users_in_context($userlist3); 528 $this->assertCount(0, $userlist3); 529 530 // Re-fetch users in the coursecontext1. 531 $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component); 532 provider::get_users_in_context($userlist1); 533 $this->assertCount(1, $userlist1); 534 535 $approvedlist = new approved_userlist($coursecontext1, $component, [$student->id]); 536 // Delete using delete_data_for_user. 537 provider::delete_data_for_users($approvedlist); 538 // Re-fetch users in the coursecontext1. 539 $userlist3 = new \core_privacy\local\request\userlist($coursecontext1, $component); 540 // The user data in coursecontext1 should not be removed. 541 provider::get_users_in_context($userlist3); 542 $this->assertCount(1, $userlist3); 543 $this->assertTrue(in_array($teacher1->id, $userlist3->get_userids())); 544 545 $approvedlist = new approved_userlist($coursecontext1, $component, [$teacher1->id]); 546 // Delete using delete_data_for_user. 547 provider::delete_data_for_users($approvedlist); 548 // Re-fetch users in the coursecontext1. 549 $userlist3 = new \core_privacy\local\request\userlist($coursecontext1, $component); 550 // The user data in coursecontext1 should be removed. 551 provider::get_users_in_context($userlist3); 552 $this->assertCount(0, $userlist3); 553 554 // Re-fetch users in the coursecontext2. 555 $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component); 556 provider::get_users_in_context($userlist2); 557 $this->assertCount(1, $userlist2); 558 559 // The list of users should not return any users for contexts different than course context. 560 $systemcontext = \context_system::instance(); 561 $userlist4 = new \core_privacy\local\request\userlist($systemcontext, $component); 562 provider::get_users_in_context($userlist4); 563 $this->assertCount(0, $userlist4); 564 } 565 566 /** 567 * Helper function to create user notes for testing. 568 * 569 * @param int $userid The ID of the User associated with the note. 570 * @param string $state The publish status 571 * @param int $courseid The ID of the Course associated with the note. 572 * @param string $content The note content. 573 */ 574 protected function help_create_user_note($userid, $state, $courseid, $content) { 575 $note = (object) [ 576 'userid' => $userid, 577 'publishstate' => $state, 578 'courseid' => $courseid, 579 'content' => $content, 580 ]; 581 note_save($note); 582 } 583 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body