first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,67 @@
@core @core_notes @javascript
Feature: Add notes to course participants
In order to share information with other staff
As a teacher
I need to add notes from the course particpants list
Scenario: An teacher can add multiple notes
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
# TODO MDL-57120 "Notes" and site "Participants" links are not accessible without navigation block.
Given I log in as "admin"
And I am on site homepage
And I turn editing mode on
And the following config values are set as admin:
| unaddableblocks | | theme_boost|
And I add the "Navigation" block if not present
And I configure the "Navigation" block
And I set the following fields to these values:
| Page contexts | Display throughout the entire site |
And I press "Save changes"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Participants"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Student 1')]//input[@type='checkbox']" to "1"
And I choose "Add a new note" from the participants page bulk action menu
And I set the field "bulk-note" to "Student 1 needs to pick up his game"
And I press "Add a new note to 1 person"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Student 1')]//input[@type='checkbox']" to "0"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Student 2')]//input[@type='checkbox']" to "1"
And I choose "Add a new note" from the participants page bulk action menu
And I set the field "bulk-note" to ""
And I press "Add a new note to 1 person"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Student 2')]//input[@type='checkbox']" to "0"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Student 3')]//input[@type='checkbox']" to "1"
And I choose "Add a new note" from the participants page bulk action menu
And I set the field "bulk-note" to " "
And I press "Add a new note to 1 person"
And I follow "Student 1"
And I follow "Notes"
# Student 1 has note from Teacher
Then I should see "Teacher" in the "region-main" "region"
And I should see "Student 1 needs to pick up his game"
And I follow "Participants"
And I follow "Student 2"
And I follow "Notes"
And I am on "Course 1" course homepage
And I follow "Participants"
And I follow "Notes"
Then I should see "Student 1"
And I should see "Student 1 needs to pick up his game"
# Verify Student 2 does not have a note added.
And I should not see "Student 2"
And I should not see "Student 3"
+159
View File
@@ -0,0 +1,159 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests for notes events.
*
* @package core_notes
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace core_notes\event;
/**
* Class core_notes_events_testcase
*
* Class for tests related to notes events.
*
* @package core_notes
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class events_test extends \advanced_testcase {
/** @var stdClass A note object. */
private $eventnote;
/** @var stdClass A complete record from post table */
private $noterecord;
public function setUp(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
$this->eventnote = $gen->create_instance(array('courseid' => $course->id, 'userid' => $user->id));
// Get the full record, note_load doesn't return everything.
$this->noterecord = $DB->get_record('post', array('id' => $this->eventnote->id), '*', MUST_EXIST);
}
/**
* Tests for event note_deleted.
*/
public function test_note_deleted_event(): void {
// Delete a note.
$sink = $this->redirectEvents();
note_delete($this->eventnote);
$events = $sink->get_events();
$event = array_pop($events); // Delete note event.
$sink->close();
// Validate event data.
$this->assertInstanceOf('\core\event\note_deleted', $event);
$this->assertEquals($this->eventnote->id, $event->objectid);
$this->assertEquals($this->eventnote->usermodified, $event->userid);
$this->assertEquals($this->eventnote->userid, $event->relateduserid);
$this->assertEquals('post', $event->objecttable);
$this->assertEquals(null, $event->get_url());
$this->assertEquals($this->noterecord, $event->get_record_snapshot('post', $event->objectid));
$this->assertEquals(NOTES_STATE_SITE, $event->other['publishstate']);
// Test legacy data.
$logurl = new \moodle_url('index.php',
array('course' => $this->eventnote->courseid, 'user' => $this->eventnote->userid));
$logurl->set_anchor('note-' . $this->eventnote->id);
$this->assertEventContextNotUsed($event);
}
/**
* Tests for event note_created.
*/
public function test_note_created_event(): void {
// Delete a note.
$sink = $this->redirectEvents();
$note = clone $this->eventnote;
unset($note->id);
note_save($note);
$events = $sink->get_events();
$event = array_pop($events); // Delete note event.
$sink->close();
// Validate event data.
$this->assertInstanceOf('\core\event\note_created', $event);
$this->assertEquals($note->id, $event->objectid);
$this->assertEquals($note->usermodified, $event->userid);
$this->assertEquals($note->userid, $event->relateduserid);
$this->assertEquals('post', $event->objecttable);
$this->assertEquals(NOTES_STATE_SITE, $event->other['publishstate']);
$this->assertEventContextNotUsed($event);
}
/**
* Tests for event note_updated.
*/
public function test_note_updated_event(): void {
// Delete a note.
$sink = $this->redirectEvents();
$note = clone $this->eventnote;
$note->publishstate = NOTES_STATE_DRAFT;
note_save($note);
$events = $sink->get_events();
$event = array_pop($events); // Delete note event.
$sink->close();
// Validate event data.
$this->assertInstanceOf('\core\event\note_updated', $event);
$this->assertEquals($note->id, $event->objectid);
$this->assertEquals($note->usermodified, $event->userid);
$this->assertEquals($note->userid, $event->relateduserid);
$this->assertEquals('post', $event->objecttable);
$this->assertEquals(NOTES_STATE_DRAFT, $event->other['publishstate']);
$this->assertEventContextNotUsed($event);
}
/**
* Test the notes viewed event.
*
* It's not possible to use the moodle API to simulate the viewing of notes, so here we
* simply create the event and trigger it.
*/
public function test_notes_viewed(): void {
$coursecontext = \context_course::instance($this->eventnote->courseid);
// Trigger event for notes viewed.
$event = \core\event\notes_viewed::create(array(
'context' => $coursecontext,
'relateduserid' => $this->eventnote->userid
));
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
$this->assertInstanceOf('\core\event\notes_viewed', $event);
$this->assertEquals($coursecontext, $event->get_context());
$this->assertEventContextNotUsed($event);
}
}
+478
View File
@@ -0,0 +1,478 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* External notes functions unit tests
*
* @package core_notes
* @category external
* @copyright 2012 Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_notes;
use core_external\external_api;
use core_notes_external;
use externallib_advanced_testcase;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
require_once($CFG->dirroot . '/notes/externallib.php');
class externallib_test extends externallib_advanced_testcase {
/**
* Test create_notes
*/
public function test_create_notes(): void {
global $DB, $USER;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
// Set the required capabilities by the external function.
$contextid = \context_course::instance($course->id)->id;
$roleid = $this->assignUserCapability('moodle/notes:manage', $contextid);
$this->assignUserCapability('moodle/course:view', $contextid, $roleid);
// Create test note data.
$note1 = array();
$note1['userid'] = $USER->id;
$note1['publishstate'] = 'personal';
$note1['courseid'] = $course->id;
$note1['text'] = 'the text';
$note1['clientnoteid'] = 4;
$notes = array($note1);
$creatednotes = core_notes_external::create_notes($notes);
// We need to execute the return values cleaning process to simulate the web service server.
$creatednotes = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes);
$thenote = $DB->get_record('post', array('id' => $creatednotes[0]['noteid']));
// Confirm that base note data was inserted correctly.
$this->assertEquals($thenote->userid, $note1['userid']);
$this->assertEquals($thenote->courseid, $note1['courseid']);
$this->assertEquals($thenote->publishstate, NOTES_STATE_DRAFT);
$this->assertEquals($thenote->content, $note1['text']);
$this->assertEquals($creatednotes[0]['clientnoteid'], $note1['clientnoteid']);
// Call without required capability.
$this->unassignUserCapability('moodle/notes:manage', $contextid, $roleid);
$this->expectException('\required_capability_exception');
$creatednotes = core_notes_external::create_notes($notes);
}
public function test_delete_notes(): void {
global $DB, $USER;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
// Set the required capabilities by the external function.
$contextid = \context_course::instance($course->id)->id;
$roleid = $this->assignUserCapability('moodle/notes:manage', $contextid);
$this->assignUserCapability('moodle/course:view', $contextid, $roleid);
// Create test note data.
$cnote = array();
$cnote['userid'] = $USER->id;
$cnote['publishstate'] = 'personal';
$cnote['courseid'] = $course->id;
$cnote['text'] = 'the text';
$cnote['clientnoteid'] = 4;
$cnotes = array($cnote);
$creatednotes = core_notes_external::create_notes($cnotes);
$creatednotes = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes);
$dnotes1 = array($creatednotes[0]['noteid']);
$deletednotes1 = core_notes_external::delete_notes($dnotes1);
$deletednotes1 = external_api::clean_returnvalue(core_notes_external::delete_notes_returns(), $deletednotes1);
// Confirm that base note data was deleted correctly.
$notdeletedcount = $DB->count_records_select('post', 'id = ' . $creatednotes[0]['noteid']);
$this->assertEquals(0, $notdeletedcount);
$dnotes2 = array(33); // This note does not exist.
$deletednotes2 = core_notes_external::delete_notes($dnotes2);
$deletednotes2 = external_api::clean_returnvalue(core_notes_external::delete_notes_returns(), $deletednotes2);
$this->assertEquals("note", $deletednotes2[0]["item"]);
$this->assertEquals(33, $deletednotes2[0]["itemid"]);
$this->assertEquals("badid", $deletednotes2[0]["warningcode"]);
$this->assertEquals("Note does not exist", $deletednotes2[0]["message"]);
// Call without required capability.
$creatednotes = core_notes_external::create_notes($cnotes);
$creatednotes = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes);
$dnotes3 = array($creatednotes[0]['noteid']);
$this->unassignUserCapability('moodle/notes:manage', $contextid, $roleid);
$this->expectException('\required_capability_exception');
$deletednotes = core_notes_external::delete_notes($dnotes3);
$deletednotes = external_api::clean_returnvalue(core_notes_external::delete_notes_returns(), $deletednotes);
}
public function test_get_notes(): void {
global $DB, $USER;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
// Set the required capabilities by the external function.
$contextid = \context_course::instance($course->id)->id;
$roleid = $this->assignUserCapability('moodle/notes:manage', $contextid);
$this->assignUserCapability('moodle/notes:view', $contextid, $roleid);
$this->assignUserCapability('moodle/course:view', $contextid, $roleid);
// Create test note data.
$cnote = array();
$cnote['userid'] = $USER->id;
$cnote['publishstate'] = 'personal';
$cnote['courseid'] = $course->id;
$cnote['text'] = 'the text';
$cnotes = array($cnote);
$creatednotes1 = core_notes_external::create_notes($cnotes);
$creatednotes2 = core_notes_external::create_notes($cnotes);
$creatednotes3 = core_notes_external::create_notes($cnotes);
$creatednotes1 = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes1);
$creatednotes2 = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes2);
$creatednotes3 = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes3);
// Note 33 does not exist.
$gnotes = array($creatednotes1[0]['noteid'], $creatednotes2[0]['noteid'], $creatednotes3[0]['noteid'], 33);
$getnotes = core_notes_external::get_notes($gnotes);
$getnotes = external_api::clean_returnvalue(core_notes_external::get_notes_returns(), $getnotes);
$this->unassignUserCapability('moodle/notes:manage', $contextid, $roleid);
// Confirm that base note data was retrieved correctly.
$this->assertEquals($cnote['userid'], $getnotes["notes"][0]["userid"]);
$this->assertEquals($cnote['text'], $getnotes["notes"][0]["text"]);
$this->assertEquals($cnote['userid'], $getnotes["notes"][1]["userid"]);
$this->assertEquals($cnote['text'], $getnotes["notes"][1]["text"]);
$this->assertEquals($cnote['userid'], $getnotes["notes"][2]["userid"]);
$this->assertEquals($cnote['text'], $getnotes["notes"][2]["text"]);
$this->assertEquals("note", $getnotes["warnings"][0]["item"]);
$this->assertEquals(33, $getnotes["warnings"][0]["itemid"]);
$this->assertEquals("badid", $getnotes["warnings"][0]["warningcode"]);
$this->assertEquals("Note does not exist", $getnotes["warnings"][0]["message"]);
// Call without required capability.
$this->unassignUserCapability('moodle/notes:view', $contextid, $roleid);
$this->expectException('\required_capability_exception');
$creatednotes = core_notes_external::get_notes($gnotes);
}
public function test_update_notes(): void {
global $DB, $USER;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
// Set the required capabilities by the external function.
$contextid = \context_course::instance($course->id)->id;
$roleid = $this->assignUserCapability('moodle/notes:manage', $contextid);
$this->assignUserCapability('moodle/course:view', $contextid, $roleid);
// Create test note data.
$note1 = array();
$note1['userid'] = $USER->id;
$note1['publishstate'] = 'personal';
$note1['courseid'] = $course->id;
$note1['text'] = 'the text';
$note2['userid'] = $USER->id;
$note2['publishstate'] = 'course';
$note2['courseid'] = $course->id;
$note2['text'] = 'the text';
$note3['userid'] = $USER->id;
$note3['publishstate'] = 'site';
$note3['courseid'] = $course->id;
$note3['text'] = 'the text';
$notes1 = array($note1, $note2, $note3);
$creatednotes = core_notes_external::create_notes($notes1);
$creatednotes = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes);
$note2 = array();
$note2["id"] = $creatednotes[0]['noteid'];
$note2['publishstate'] = 'personal';
$note2['text'] = 'the new text';
$note2['format'] = FORMAT_HTML;
$notes2 = array($note2);
$updatednotes = core_notes_external::update_notes($notes2);
$updatednotes = external_api::clean_returnvalue(core_notes_external::update_notes_returns(), $updatednotes);
$thenote = $DB->get_record('post', array('id' => $creatednotes[0]['noteid']));
// Confirm that base note data was updated correctly.
$this->assertEquals($thenote->publishstate, NOTES_STATE_DRAFT);
$this->assertEquals($note2['text'], $thenote->content);
// Call without required capability.
$creatednotes = core_notes_external::create_notes($notes1);
$creatednotes = external_api::clean_returnvalue(core_notes_external::create_notes_returns(), $creatednotes);
$this->unassignUserCapability('moodle/notes:manage', $contextid, $roleid);
$this->expectException('\required_capability_exception');
$note2 = array();
$note2["id"] = $creatednotes[0]['noteid'];
$note2['publishstate'] = 'personal';
$note2['text'] = 'the new text';
$note2['format'] = FORMAT_HTML;
$notes2 = array($note2);
$updatednotes = core_notes_external::update_notes($notes2);
$updatednotes = external_api::clean_returnvalue(core_notes_external::update_notes_returns(), $updatednotes);
}
/**
* Test get_course_notes
*/
public function test_get_course_notes(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
$CFG->enablenotes = true;
// Take role definitions.
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
// Create students and teachers.
$student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user();
$teacher1 = $this->getDataGenerator()->create_user();
$teacher2 = $this->getDataGenerator()->create_user();
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
// Enroll students and teachers to COURSE-1.
$this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($teacher1->id, $course1->id, $teacherrole->id);
$this->getDataGenerator()->enrol_user($teacher2->id, $course1->id, $teacherrole->id);
// Enroll students and teachers to COURSE-2 (teacher1 is not enrolled in Course 2).
$this->getDataGenerator()->enrol_user($student1->id, $course2->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($student2->id, $course2->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($teacher2->id, $course2->id, $teacherrole->id);
// Generate notes.
$gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
$this->setUser($teacher1);
// NoteA1: on student1 (Course1) by Teacher1.
$params = array('courseid' => $course1->id, 'userid' => $student1->id, 'publishstate' => NOTES_STATE_PUBLIC,
'usermodified' => $teacher1->id);
$notea1 = $gen->create_instance($params);
// NoteA2: on student1 (Course1) by Teacher1.
$params = array('courseid' => $course1->id, 'userid' => $student1->id, 'publishstate' => NOTES_STATE_PUBLIC,
'usermodified' => $teacher1->id);
$notea2 = $gen->create_instance($params);
// NoteS1: on student1 SITE-LEVEL by teacher1.
$params = array('courseid' => $course1->id, 'userid' => $student1->id, 'publishstate' => NOTES_STATE_SITE,
'usermodified' => $teacher1->id);
$notes1 = $gen->create_instance($params);
// NoteP1: on student1 PERSONAL by teacher1.
$params = array('courseid' => $course1->id, 'userid' => $student1->id, 'publishstate' => NOTES_STATE_DRAFT,
'usermodified' => $teacher1->id);
$notep1 = $gen->create_instance($params);
// NoteB1: on student1 (Course2) by teacher1.
$params = array('courseid' => $course2->id, 'userid' => $student1->id, 'publishstate' => NOTES_STATE_PUBLIC,
'usermodified' => $teacher1->id);
$noteb1 = $gen->create_instance($params);
// Retrieve notes, normal case.
$result = core_notes_external::get_course_notes($course1->id, $student1->id);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEquals($notes1->id, $result['sitenotes'][0]['id']);
$this->assertCount(2, $result['coursenotes']);
// Teacher can manage only the course notes.
$this->assertFalse($result['canmanagesystemnotes']);
$this->assertTrue($result['canmanagecoursenotes']);
foreach ($result['coursenotes'] as $coursenote) {
if ($coursenote['id'] != $notea1->id and $coursenote['id'] != $notea2->id) {
$this->fail('the returned notes ids does not match with the created ones');
}
}
$this->assertEquals($notep1->id, $result['personalnotes'][0]['id']);
// Try to get notes from a course the user is not enrolled.
try {
$result = core_notes_external::get_course_notes($course2->id, $student1->id);
$this->fail('the user is not enrolled in the course');
} catch (\require_login_exception $e) {
$this->assertEquals('requireloginerror', $e->errorcode);
}
$result = core_notes_external::get_course_notes(0, $student1->id);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEmpty($result['sitenotes']);
// Teacher can't manage system notes.
$this->assertFalse($result['canmanagesystemnotes']);
$this->assertFalse($result['canmanagecoursenotes']);
foreach ($result['coursenotes'] as $coursenote) {
if ($coursenote['id'] != $notea1->id and $coursenote['id'] != $notea2->id) {
$this->fail('the returned notes ids does not match with the created ones');
}
}
$this->assertCount(2, $result['coursenotes']);
$this->setAdminUser();
$result = core_notes_external::get_course_notes(0, $student1->id);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEquals($notes1->id, $result['sitenotes'][0]['id']);
$this->assertCount(1, $result['sitenotes']);
// Admin user can manage both system and course notes.
$this->assertTrue($result['canmanagesystemnotes']);
$this->assertTrue($result['canmanagecoursenotes']);
$this->setUser($teacher1);
$result = core_notes_external::get_course_notes(0, 0);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEmpty($result['sitenotes']);
$this->assertEmpty($result['coursenotes']);
$this->assertEmpty($result['personalnotes']);
// Teacher can't manage system notes.
$this->assertFalse($result['canmanagesystemnotes']);
$this->assertFalse($result['canmanagecoursenotes']);
$this->setUser($teacher2);
$result = core_notes_external::get_course_notes($course1->id, $student1->id);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEquals($notes1->id, $result['sitenotes'][0]['id']);
foreach ($result['coursenotes'] as $coursenote) {
if ($coursenote['id'] != $notea1->id and $coursenote['id'] != $notea2->id) {
$this->fail('the returned notes ids does not match with the created ones');
}
}
$this->assertCount(1, $result['sitenotes']);
$this->assertCount(2, $result['coursenotes']);
// Teacher can manage only the course notes.
$this->assertFalse($result['canmanagesystemnotes']);
$this->assertTrue($result['canmanagecoursenotes']);
$result = core_notes_external::get_course_notes($course1->id, 0);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEquals($notes1->id, $result['sitenotes'][0]['id']);
foreach ($result['coursenotes'] as $coursenote) {
if ($coursenote['id'] != $notea1->id and $coursenote['id'] != $notea2->id) {
$this->fail('the returned notes ids does not match with the created ones');
}
}
$this->assertCount(1, $result['sitenotes']);
$this->assertCount(2, $result['coursenotes']);
$this->setUser($teacher1);
$result = core_notes_external::get_course_notes($course1->id, 0);
$result = external_api::clean_returnvalue(core_notes_external::get_course_notes_returns(), $result);
$this->assertEquals($notep1->id, $result['personalnotes'][0]['id']);
$this->assertCount(1, $result['personalnotes']);
// Teacher can manage only the course notes.
$this->assertFalse($result['canmanagesystemnotes']);
$this->assertTrue($result['canmanagecoursenotes']);
}
/**
* Test view_notes
*/
public function test_view_notes(): void {
global $DB, $CFG;
$this->resetAfterTest(true);
$CFG->enablenotes = true;
// Take role definitions.
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
// Create students and teachers.
$student = $this->getDataGenerator()->create_user();
$teacher = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
// Enroll students and teachers to course.
$this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
// Generate notes.
$gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
$this->setUser($teacher);
// NoteA1: on student (Course) by Teacher.
$params = array('courseid' => $course->id, 'userid' => $student->id, 'publishstate' => NOTES_STATE_PUBLIC,
'usermodified' => $teacher->id);
$notea1 = $gen->create_instance($params);
$sink = $this->redirectEvents();
$result = core_notes_external::view_notes($course->id, $student->id);
$result = external_api::clean_returnvalue(core_notes_external::view_notes_returns(), $result);
$result = core_notes_external::view_notes($course->id);
$result = external_api::clean_returnvalue(core_notes_external::view_notes_returns(), $result);
$events = $sink->get_events();
$this->assertCount(2, $events);
$this->assertInstanceOf('\core\event\notes_viewed', $events[0]);
$this->assertEquals($coursecontext, $events[0]->get_context());
$this->assertEquals($student->id, $events[0]->relateduserid);
$this->assertInstanceOf('\core\event\notes_viewed', $events[1]);
$this->assertEquals($coursecontext, $events[1]->get_context());
$this->assertEquals(0, $events[1]->relateduserid);
try {
core_notes_external::view_notes(0);
$this->fail('Exception expected due to invalid permissions at system level.');
} catch (\moodle_exception $e) {
$this->assertEquals('nopermissions', $e->errorcode);
}
try {
core_notes_external::view_notes($course->id, $student->id + 100);
$this->fail('Exception expected due to invalid user id.');
} catch (\moodle_exception $e) {
$this->assertEquals('invaliduser', $e->errorcode);
}
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_notes data generator.
*
* @package core_notes
* @category test
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* core_notes data generator class.
*
* @package core_notes
* @category test
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_notes_generator extends component_generator_base {
/**
* @var number of created instances
*/
protected $instancecount = 0;
/**
* To be called from data reset code only,
* do not use in tests.
* @return void
*/
public function reset() {
$this->instancecount = 0;
}
/**
* Create a new note.
*
* @param array|stdClass $record
* @throws coding_exception
* @return stdClass activity record with extra cmid field
*/
public function create_instance($record = null) {
global $CFG, $USER;
require_once("$CFG->dirroot/notes/lib.php");
$this->instancecount++;
$i = $this->instancecount;
$record = (object)(array)$record;
if (empty($record->courseid)) {
throw new coding_exception('Module generator requires $record->courseid.');
}
if (empty($record->userid)) {
throw new coding_exception('Module generator requires $record->userid.');
}
if (!isset($record->module)) {
$record->module = 'notes';
}
if (!isset($record->groupid)) {
$record->groupid = 0;
}
if (!isset($record->moduleid)) {
$record->moduleid = 0;
}
if (!isset($record->coursemoduleid)) {
$record->coursemoduleid = 0;
}
if (!isset($record->subject)) {
$record->subject = '';
}
if (!isset($record->summary)) {
$record->summary = null;
}
if (!isset($record->content)) {
$record->content = "This is test generated note - $i .";
}
if (!isset($record->uniquehash)) {
$record->uniquehash = '';
}
if (!isset($record->rating)) {
$record->rating = 0;
}
if (!isset($record->format)) {
$record->format = FORMAT_PLAIN;
}
if (!isset($record->summaryformat)) {
$record->summaryformat = FORMAT_MOODLE;
}
if (!isset($record->attachment)) {
$record->attachment = null;
}
if (!isset($record->publishstate)) {
$record->publishstate = NOTES_STATE_SITE;
}
if (!isset($record->lastmodified)) {
$record->lastmodified = time();
}
if (!isset($record->created)) {
$record->created = time();
}
if (!isset($record->usermodified)) {
$record->usermodified = $USER->id;
}
note_save($record);
return $record;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Generator tests.
*
* @package core_notes
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_notes;
/**
* Generator tests class.
*
* @package core_notes
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class generator_test extends \advanced_testcase {
/** Test create_instance method */
public function test_create_instance(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
$this->assertFalse($DB->record_exists('post', array('courseid' => $course->id)));
$note = $gen->create_instance(array('courseid' => $course->id, 'userid' => $user->id));
$this->assertEquals(1, $DB->count_records('post', array('courseid' => $course->id, 'userid' => $user->id)));
$this->assertTrue($DB->record_exists('post', array('id' => $note->id)));
$params = array('courseid' => $course->id, 'userid' => $user->id, 'publishstate' => NOTES_STATE_DRAFT);
$note = $gen->create_instance($params);
$this->assertEquals(2, $DB->count_records('post', array('courseid' => $course->id, 'userid' => $user->id)));
$this->assertEquals(NOTES_STATE_DRAFT, $DB->get_field_select('post', 'publishstate', 'id = :id',
array('id' => $note->id)));
}
/** Test Exceptions thrown by create_instance method */
public function test_create_instance_exceptions(): void {
$this->resetAfterTest();
$gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
// Test not setting userid.
try {
$gen->create_instance(array('courseid' => 2));
$this->fail('A note should not be allowed to be created without associcated userid');
} catch (\coding_exception $e) {
$this->assertStringContainsString('Module generator requires $record->userid', $e->getMessage());
}
// Test not setting courseid.
try {
$gen->create_instance(array('userid' => 2));
$this->fail('A note should not be allowed to be created without associcated courseid');
} catch (\coding_exception $e) {
$this->assertStringContainsString('Module generator requires $record->courseid', $e->getMessage());
}
}
}
+115
View File
@@ -0,0 +1,115 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests for notes library functions.
*
* @package core_notes
* @copyright 2015 onwards Ankit agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace core_notes;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/notes/lib.php');
/**
* Class core_notes_lib_testcase
*
* @package core_notes
* @copyright 2015 onwards Ankit agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class lib_test extends \advanced_testcase {
/**
* @var stdClass The user.
*/
private $user;
/**
* @var stdClass The course.
*/
private $course;
/**
* @var \core_user\output\myprofile\tree The navigation tree.
*/
private $tree;
public function setUp(): void {
$this->user = $this->getDataGenerator()->create_user();
$this->course = $this->getDataGenerator()->create_course();
$this->tree = new \core_user\output\myprofile\tree();
$this->resetAfterTest();
}
/**
* Tests the core_notes_myprofile_navigation() function.
*/
public function test_core_notes_myprofile_navigation(): void {
global $USER;
// Set up the test.
$this->setAdminUser();
$iscurrentuser = true;
// Enable notes.
set_config('enablenotes', true);
// Check the node tree is correct.
core_notes_myprofile_navigation($this->tree, $USER, $iscurrentuser, $this->course);
$reflector = new \ReflectionObject($this->tree);
$nodes = $reflector->getProperty('nodes');
$this->assertArrayHasKey('notes', $nodes->getValue($this->tree));
}
/**
* Tests the core_notes_myprofile_navigation() function.
*/
public function test_core_notes_myprofile_navigation_as_guest(): void {
global $USER;
$this->setGuestUser();
$iscurrentuser = false;
// Check the node tree is correct.
core_notes_myprofile_navigation($this->tree, $USER, $iscurrentuser, $this->course);
$reflector = new \ReflectionObject($this->tree);
$nodes = $reflector->getProperty('nodes');
$this->assertArrayNotHasKey('notes', $nodes->getValue($this->tree));
}
/**
* Tests the core_notes_myprofile_navigation() function.
*/
public function test_core_notes_myprofile_navigation_notes_disabled(): void {
global $USER;
$this->setAdminUser();
$iscurrentuser = false;
// Disable notes.
set_config('enablenotes', false);
// Check the node tree is correct.
core_notes_myprofile_navigation($this->tree, $USER, $iscurrentuser, $this->course);
$reflector = new \ReflectionObject($this->tree);
$nodes = $reflector->getProperty('nodes');
$this->assertArrayNotHasKey('notes', $nodes->getValue($this->tree));
}
}
+585
View File
@@ -0,0 +1,585 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the core_notes implementation of the privacy API.
*
* @package core_notes
* @category test
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_notes\privacy;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . "/notes/lib.php");
use core_notes\privacy\provider;
use core_privacy\local\request\writer;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
/**
* Unit tests for the core_notes implementation of the privacy API.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core_notes\privacy\provider
*/
class provider_test extends \core_privacy\tests\provider_testcase {
/**
* Test for provider::get_contexts_for_userid().
*/
public function test_get_contexts_for_userid(): void {
global $DB;
// Test setup.
$this->resetAfterTest(true);
$this->setAdminUser();
set_config('enablenotes', true);
$teacher1 = $this->getDataGenerator()->create_user();
$this->setUser($teacher1);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
$student = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Create Courses, then enrol a teacher and a student.
$nocourses = 5;
$courses = [];
$coursecontextids = [];
for ($c = 1; $c <= $nocourses; $c++) {
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
role_assign($teacherrole->id, $teacher1->id, $coursecontext->id);
role_assign($studentrole->id, $student->id, $coursecontext->id);
// Only create private user notes (i.e. NOTES_STATE_DRAFT) for student in Course 1, 2, 3 written by the teacher.
if ($c <= 3) {
$this->help_create_user_note(
$student->id,
NOTES_STATE_DRAFT,
$course->id,
"Test private user note about the student in Course $c by the teacher"
);
}
$courses[$c] = $course;
$coursecontextids[] = $coursecontext->id;
}
// Test Teacher 1's contexts equals 3 because only 3 user notes were added for Course 1, 2, and 3.
// Course 4 and 5 does not have any notes associated with it, so the contexts should not be returned.
$contexts = provider::get_contexts_for_userid($teacher1->id);
$this->assertCount(3, $contexts->get_contextids());
// Test the Student's contexts is 0 because the notes written by the teacher are private.
$contexts = provider::get_contexts_for_userid($student->id);
$this->assertCount(0, $contexts->get_contextids());
// Add a public user note (i.e. NOTES_STATE_PUBLIC) written by the Teacher about the Student in Course 4.
$course = $courses[4];
$this->help_create_user_note(
$student->id,
NOTES_STATE_PUBLIC,
$course->id,
"Test public user note about the student in Course 4 by the teacher"
);
// Test Teacher 1's contexts equals 4 after adding a public note about a student in Course 4.
$contexts = provider::get_contexts_for_userid($teacher1->id);
$this->assertCount(4, $contexts->get_contextids());
// Test the Student's contexts is 1 for Course 4 because there is a public note written by the teacher.
$contexts = provider::get_contexts_for_userid($student->id);
$this->assertCount(1, $contexts->get_contextids());
// Add a site-wide user note (i.e. NOTES_STATE_SITE) written by the Teacher 1 about the Student in Course 3.
$course = $courses[3];
$this->help_create_user_note(
$student->id,
NOTES_STATE_SITE,
$course->id,
"Test site-wide user note about the student in Course 3 by the teacher"
);
// Test the Student's contexts is 2 for Courses 3, 4 because there is a public and site-wide note written by the Teacher.
$contexts = provider::get_contexts_for_userid($student->id);
$this->assertCount(2, $contexts->get_contextids());
// Add a site-wide user note for the Teacher 1 by another Teacher 2 in Course 5.
$teacher2 = $this->getDataGenerator()->create_user();
$this->setUser($teacher2);
$course = $courses[5];
$this->help_create_user_note(
$teacher1->id,
NOTES_STATE_SITE,
$course->id,
"Test site-wide user note about the teacher in Course 5 by another teacher"
);
// Test Teacher 1's contexts equals 5 after adding the note from another teacher.
$contextlist = provider::get_contexts_for_userid($teacher1->id);
$this->assertCount(5, $contextlist->get_contextids());
// Test Teacher 1's contexts match the contexts of the Courses associated with notes created.
$this->assertEmpty(array_diff($coursecontextids, $contextlist->get_contextids()));
}
/**
* Test for provider::export_user_data().
*/
public function test_export_user_data(): void {
global $DB;
// Test setup.
$this->resetAfterTest(true);
$this->setAdminUser();
set_config('enablenotes', true);
$teacher1 = $this->getDataGenerator()->create_user();
$this->setUser($teacher1);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
$nocourses = 5;
$nostudents = 2;
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$courses = [];
$coursecontextids = [];
for ($c = 1; $c <= $nocourses; $c++) {
// Create a Course, then enrol a teacher and enrol 2 students.
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
role_assign($teacherrole->id, $teacher1->id, $coursecontext->id);
// Only create public user notes (i.e. NOTES_STATE_PUBLIC) for students in Course 1, 2, 3 written by the teacher.
if ($c <= 3) {
for ($s = 0; $s < $nostudents; $s++) {
$student = $this->getDataGenerator()->create_user();
role_assign($studentrole->id, $student->id, $coursecontext->id);
// Create test public user note data written for students by the teacher.
$this->help_create_user_note(
$student->id,
NOTES_STATE_PUBLIC,
$course->id,
"Test public user note for student $s in Course $c by the teacher"
);
}
// Store the Course context for those which have test notes added for verification.
$coursecontextids[] = $coursecontext->id;
}
$courses[$c] = $course;
}
// Add a site-wide user note for Teacher 1 by another Teacher 2 in Course 4.
$teacher2 = $this->getDataGenerator()->create_user();
$this->setUser($teacher2);
$course = $courses[4];
$this->help_create_user_note(
$teacher1->id,
NOTES_STATE_SITE,
$course->id,
"Test site-wide user note about the teacher in Course 4 by another teacher"
);
// Store the Course context for those which have test notes added for verification.
$coursecontextids[] = \context_course::instance($course->id)->id;
// Add a private user note for Teacher 1 by another Teacher 2 in Course 5.
$course = $courses[5];
$this->help_create_user_note(
$teacher1->id,
NOTES_STATE_DRAFT,
$course->id,
"Test private user note about the teacher in Course 5 by another teacher"
);
// Test the number of contexts returned matches the Course contexts created with notes.
$contextlist = provider::get_contexts_for_userid($teacher1->id);
$this->assertEmpty(array_diff($coursecontextids, $contextlist->get_contextids()));
$approvedcontextlist = new approved_contextlist($teacher1, 'core_notes', $contextlist->get_contextids());
// Retrieve User notes created by the teacher.
provider::export_user_data($approvedcontextlist);
// Test the core_notes data is exported at the Course context level and has content.
foreach ($contextlist as $context) {
$this->assertEquals(CONTEXT_COURSE, $context->contextlevel);
/** @var \core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$this->assertTrue($writer->has_any_data());
}
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
// Test setup.
$this->resetAfterTest(true);
$this->setAdminUser();
set_config('enablenotes', true);
$teacher = $this->getDataGenerator()->create_user();
$this->setUser($teacher);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
$nocourses = 2;
$nostudents = 5;
$nonotes = 7;
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$n = 0;
for ($c = 0; $c < $nocourses; $c++) {
// Create a Course, then enrol a teacher and enrol 2 students.
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
role_assign($teacherrole->id, $teacher->id, $coursecontext->id);
for ($s = 0; $s < $nostudents; $s++) {
if ($n < $nonotes) {
$student = $this->getDataGenerator()->create_user();
role_assign($studentrole->id, $student->id, $coursecontext->id);
// Create test note data.
$this->help_create_user_note(
$student->id,
NOTES_STATE_PUBLIC,
$course->id,
"Test user note for student $s in Course $c"
);
}
$n++;
}
}
// Test the number of contexts returned equals the number of Courses created with user notes for its students.
$contextlist = provider::get_contexts_for_userid($teacher->id);
$this->assertCount($nocourses, $contextlist->get_contextids());
// Test the created user note records in mdl_post table matches the test number of user notes specified.
$notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]);
$this->assertCount($nonotes, $notes);
// Delete all user note records in mdl_post table by the specified Course context.
foreach ($contextlist->get_contexts() as $context) {
provider::delete_data_for_all_users_in_context($context);
}
// Test the core_note records in mdl_post table is equals zero.
$notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]);
$this->assertCount(0, $notes);
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user(): void {
global $DB;
// Test setup.
$this->resetAfterTest(true);
$this->setAdminUser();
set_config('enablenotes', true);
$teacher = $this->getDataGenerator()->create_user();
$this->setUser($teacher);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
$nocourses = 2;
$nostudents = 5;
$nonotes = 7;
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$n = 0;
for ($c = 0; $c < $nocourses; $c++) {
// Create a Course, then enrol a teacher and enrol 2 students.
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
role_assign($teacherrole->id, $teacher->id, $coursecontext->id);
for ($s = 0; $s < $nostudents; $s++) {
if ($n < $nonotes) {
$student = $this->getDataGenerator()->create_user();
role_assign($studentrole->id, $student->id, $coursecontext->id);
// Create test note data.
$this->help_create_user_note(
$student->id,
NOTES_STATE_PUBLIC,
$course->id,
"Test user note for student $s in Course $c"
);
}
$n++;
}
}
// Test the number of contexts returned equals the number of Courses created with user notes for its students.
$contextlist = provider::get_contexts_for_userid($teacher->id);
$this->assertCount($nocourses, $contextlist->get_contextids());
// Test the created user note records in mdl_post table matches the test number of user notes specified.
$notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]);
$this->assertCount($nonotes, $notes);
// Delete all user note records in mdl_post table created by the specified teacher.
$approvedcontextlist = new approved_contextlist($teacher, 'core_notes', $contextlist->get_contextids());
provider::delete_data_for_user($approvedcontextlist);
// Test the core_note records in mdl_post table is equals zero.
$notes = $DB->get_records('post', ['module' => 'notes', 'usermodified' => $teacher->id]);
$this->assertCount(0, $notes);
}
/**
* Test that only users within a course context are fetched.
*/
public function test_get_users_in_context(): void {
global $DB;
$this->resetAfterTest(true);
$component = 'core_notes';
// Test setup.
$this->setAdminUser();
set_config('enablenotes', true);
// Create a teacher.
$teacher1 = $this->getDataGenerator()->create_user();
$this->setUser($teacher1);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
// Create a student.
$student = $this->getDataGenerator()->create_user();
// Create student2.
$student2 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Create courses, then enrol a teacher and a student.
$nocourses = 3;
for ($c = 1; $c <= $nocourses; $c++) {
${'course' . $c} = $this->getDataGenerator()->create_course();
${'coursecontext' . $c} = \context_course::instance(${'course' . $c}->id);
role_assign($teacherrole->id, $teacher1->id, ${'coursecontext' . $c}->id);
role_assign($studentrole->id, $student->id, ${'coursecontext' . $c}->id);
role_assign($studentrole->id, $student2->id, ${'coursecontext' . $c}->id);
}
// The list of users in coursecontext1 should be empty (related data still have not been created).
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(0, $userlist1);
// The list of users in coursecontext2 should be empty (related data still have not been created).
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(0, $userlist2);
// The list of users in coursecontext3 should be empty (related data still have not been created).
$userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
// Create private user notes (i.e. NOTES_STATE_DRAFT) for student in course1 and course2 written by the teacher.
$this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course1->id,
"Test private user note about the student in Course 1 by the teacher");
$this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course2->id,
"Test private user note about the student in Course 2 by the teacher");
// The list of users in coursecontext1 should return one user (teacher1).
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
$this->assertTrue(in_array($teacher1->id, $userlist1->get_userids()));
// The list of users in coursecontext2 should return one user (teacher1).
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
$this->assertTrue(in_array($teacher1->id, $userlist2->get_userids()));
// The list of users in coursecontext3 should not return any users.
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
// Create public user note (i.e. NOTES_STATE_PUBLIC) for student in course3 written by the teacher.
$this->help_create_user_note($student->id, NOTES_STATE_PUBLIC, $course3->id,
"Test public user note about the student in Course 3 by the teacher");
// The list of users in coursecontext3 should return 2 users (teacher and student).
provider::get_users_in_context($userlist3);
$this->assertCount(2, $userlist3);
$this->assertTrue(in_array($teacher1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($student->id, $userlist3->get_userids()));
// Create site user note (i.e. NOTES_STATE_SITE) for student2 in course3 written by the teacher.
$this->help_create_user_note($student2->id, NOTES_STATE_SITE, $course3->id,
"Test site-wide user note about student2 in Course 3 by the teacher"
);
// The list of users in coursecontext3 should return 3 users (teacher, student and student2).
provider::get_users_in_context($userlist3);
$this->assertCount(3, $userlist3);
$this->assertTrue(in_array($teacher1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($student->id, $userlist3->get_userids()));
$this->assertTrue(in_array($student2->id, $userlist3->get_userids()));
// The list of users should not return any users in a different context than course context.
$contextsystem = \context_system::instance();
$userlist4 = new \core_privacy\local\request\userlist($contextsystem, $component);
provider::get_users_in_context($userlist4);
$this->assertCount(0, $userlist4);
}
/**
* Test that data for users in approved userlist is deleted.
*/
public function test_delete_data_for_users(): void {
global $DB;
$this->resetAfterTest(true);
$component = 'core_notes';
// Test setup.
$this->setAdminUser();
set_config('enablenotes', true);
// Create a teacher.
$teacher1 = $this->getDataGenerator()->create_user();
$this->setUser($teacher1);
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
// Create a student.
$student = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Create Courses, then enrol a teacher and a student.
$nocourses = 3;
for ($c = 1; $c <= $nocourses; $c++) {
${'course' . $c} = $this->getDataGenerator()->create_course();
${'coursecontext' . $c} = \context_course::instance(${'course' . $c}->id);
role_assign($teacherrole->id, $teacher1->id, ${'coursecontext' . $c}->id);
role_assign($studentrole->id, $student->id, ${'coursecontext' . $c}->id);
}
// Create private notes for student in the course1 and course2 written by the teacher.
$this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course1->id,
"Test private user note about the student in Course 1 by the teacher");
$this->help_create_user_note($student->id, NOTES_STATE_DRAFT, $course2->id,
"Test private user note about the student in Course 2 by the teacher");
// Create public notes for student in the course3 written by the teacher.
$this->help_create_user_note($student->id, NOTES_STATE_PUBLIC, $course3->id,
"Test public user note about the student in Course 3 by the teacher");
// The list of users in coursecontext1 should return one user (teacher1).
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
$this->assertTrue(in_array($teacher1->id, $userlist1->get_userids()));
// The list of users in coursecontext2 should return one user (teacher1).
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
$this->assertTrue(in_array($teacher1->id, $userlist2->get_userids()));
// The list of users in coursecontext3 should return two users (teacher1 and student).
$userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(2, $userlist3);
$this->assertTrue(in_array($teacher1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($student->id, $userlist3->get_userids()));
$approvedlist = new approved_userlist($coursecontext3, $component, [$student->id]);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in the coursecontext3.
$userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component);
// The user data in coursecontext3 should not be removed.
provider::get_users_in_context($userlist3);
$this->assertCount(2, $userlist3);
$this->assertTrue(in_array($teacher1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($student->id, $userlist3->get_userids()));
$approvedlist = new approved_userlist($coursecontext3, $component, [$teacher1->id]);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in the coursecontext3.
$userlist3 = new \core_privacy\local\request\userlist($coursecontext3, $component);
// The user data in coursecontext3 should be removed.
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
// Re-fetch users in the coursecontext1.
$userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
$approvedlist = new approved_userlist($coursecontext1, $component, [$student->id]);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in the coursecontext1.
$userlist3 = new \core_privacy\local\request\userlist($coursecontext1, $component);
// The user data in coursecontext1 should not be removed.
provider::get_users_in_context($userlist3);
$this->assertCount(1, $userlist3);
$this->assertTrue(in_array($teacher1->id, $userlist3->get_userids()));
$approvedlist = new approved_userlist($coursecontext1, $component, [$teacher1->id]);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in the coursecontext1.
$userlist3 = new \core_privacy\local\request\userlist($coursecontext1, $component);
// The user data in coursecontext1 should be removed.
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
// Re-fetch users in the coursecontext2.
$userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// The list of users should not return any users for contexts different than course context.
$systemcontext = \context_system::instance();
$userlist4 = new \core_privacy\local\request\userlist($systemcontext, $component);
provider::get_users_in_context($userlist4);
$this->assertCount(0, $userlist4);
}
/**
* Helper function to create user notes for testing.
*
* @param int $userid The ID of the User associated with the note.
* @param string $state The publish status
* @param int $courseid The ID of the Course associated with the note.
* @param string $content The note content.
*/
protected function help_create_user_note($userid, $state, $courseid, $content) {
$note = (object) [
'userid' => $userid,
'publishstate' => $state,
'courseid' => $courseid,
'content' => $content,
];
note_save($note);
}
}
@@ -0,0 +1,248 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_notes\reportbuilder\datasource;
use core_notes_generator;
use core_reportbuilder_generator;
use core_reportbuilder_testcase;
use core_reportbuilder\local\filters\{date, select, text};
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
/**
* Unit tests for notes datasource
*
* @package core_notes
* @covers \core_notes\reportbuilder\datasource\notes
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class notes_test extends core_reportbuilder_testcase {
/**
* Load required test libraries
*/
public static function setUpBeforeClass(): void {
global $CFG;
require_once("{$CFG->dirroot}/notes/lib.php");
}
/**
* Test default datasource
*/
public function test_datasource_default(): void {
$this->resetAfterTest();
/** @var core_notes_generator $notesgenerator */
$notesgenerator = $this->getDataGenerator()->get_plugin_generator('core_notes');
// Our first user will create a course note.
$course = $this->getDataGenerator()->create_course();
$userone = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe']);
$coursenote = $notesgenerator->create_instance(['courseid' => $course->id, 'userid' => $userone->id, 'content' => 'Course',
'publishstate' => NOTES_STATE_PUBLIC]);
// Our second user will create a personal and site note.
$usertwo = $this->getDataGenerator()->create_user(['firstname' => 'Amy']);
$personalnote = $notesgenerator->create_instance(['courseid' => SITEID, 'userid' => $usertwo->id, 'content' => 'Personal',
'publishstate' => NOTES_STATE_DRAFT]);
$this->waitForSecond(); // For consistent ordering we need distinct time for second user notes.
$sitenote = $notesgenerator->create_instance(['courseid' => SITEID, 'userid' => $usertwo->id, 'content' => 'Site',
'publishstate' => NOTES_STATE_SITE]);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Notes', 'source' => notes::class, 'default' => 1]);
$content = $this->get_custom_report_content($report->get('id'));
// Default columns are recipient, publishstate, course, note, time created. Sorted by recipient and time created.
$this->assertEquals([
[fullname($usertwo), 'Personal notes', '', 'Personal', userdate($personalnote->created)],
[fullname($usertwo), 'Site notes', '', 'Site', userdate($sitenote->created)],
[fullname($userone), 'Course notes', $course->fullname, 'Course', userdate($coursenote->created)],
], array_map('array_values', $content));
}
/**
* Test datasource columns that aren't added by default
*/
public function test_datasource_non_default_columns(): void {
global $DB;
$this->resetAfterTest();
$recipient = $this->getDataGenerator()->create_user();
$author = $this->getDataGenerator()->create_user();
$this->setUser($author);
/** @var core_notes_generator $notesgenerator */
$notesgenerator = $this->getDataGenerator()->get_plugin_generator('core_notes');
$note = $notesgenerator->create_instance(['courseid' => SITEID, 'publishstate' => NOTES_STATE_SITE, 'content' => 'Cool',
'userid' => $recipient->id,
]);
// Manually update the created/modified date of the note.
$note->created = 1654038000;
$note->lastmodified = $note->created + HOURSECS;
$DB->update_record('post', $note);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Notes', 'source' => notes::class, 'default' => 0]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'note:content']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'note:timecreated']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'note:timemodified']);
// Ensure we can add data from both user entities.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'recipient:fullname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'author:fullname']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(1, $content);
$this->assertEquals([
'Cool',
userdate($note->created),
userdate($note->lastmodified),
fullname($recipient),
fullname($author),
], array_values($content[0]));
}
/**
* Data provider for {@see test_datasource_filters}
*
* @return array[]
*/
public function datasource_filters_provider(): array {
return [
'Filter content' => ['content', 'Cool', 'note:content', [
'note:content_operator' => text::IS_EQUAL_TO,
'note:content_value' => 'Cool',
], true],
'Filter content (no match)' => ['content', 'Cool', 'note:content', [
'note:content_operator' => text::DOES_NOT_CONTAIN,
'note:content_value' => 'Cool',
], false],
'Filter publish state' => ['publishstate', 'site', 'note:publishstate', [
'note:publishstate_operator' => select::EQUAL_TO,
'note:publishstate_value' => 'site',
], true],
'Filter publish state (no match)' => ['publishstate', 'site', 'note:publishstate', [
'note:publishstate_operator' => select::EQUAL_TO,
'note:publishstate_value' => 'public',
], false],
'Filter time created' => ['created', 1654038000, 'note:timecreated', [
'note:timecreated_operator' => date::DATE_RANGE,
'note:timecreated_from' => 1622502000,
], true],
'Filter time created (no match)' => ['created', 1654038000, 'note:timecreated', [
'note:timecreated_operator' => date::DATE_RANGE,
'note:timecreated_to' => 1622502000,
], false],
'Filter time modified' => ['lastmodified', 1654038000, 'note:timemodified', [
'note:timemodified_operator' => date::DATE_RANGE,
'note:timemodified_from' => 1622502000,
], true],
'Filter time modified (no match)' => ['lastmodified', 1654038000, 'note:timemodified', [
'note:timemodified_operator' => date::DATE_RANGE,
'note:timemodified_to' => 1622502000,
], false],
];
}
/**
* Test datasource filters
*
* @param string $field
* @param mixed $value
* @param string $filtername
* @param array $filtervalues
* @param bool $expectmatch
*
* @dataProvider datasource_filters_provider
*/
public function test_datasource_filters(
string $field,
$value,
string $filtername,
array $filtervalues,
bool $expectmatch
): void {
global $DB;
$this->resetAfterTest();
$recipient = $this->getDataGenerator()->create_user();
/** @var core_notes_generator $notesgenerator */
$notesgenerator = $this->getDataGenerator()->get_plugin_generator('core_notes');
// Create default note, then manually override one of it's properties to use for filtering.
$note = $notesgenerator->create_instance(['courseid' => SITEID, 'userid' => $recipient->id]);
$DB->set_field('post', $field, $value, ['id' => $note->id]);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
// Create report containing single recipient column, and given filter.
$report = $generator->create_report(['name' => 'Notes', 'source' => notes::class, 'default' => 0]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'recipient:fullname']);
// Add filter, set it's values.
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
$content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
if ($expectmatch) {
$this->assertCount(1, $content);
$this->assertEquals(fullname($recipient), reset($content[0]));
} else {
$this->assertEmpty($content);
}
}
/**
* Stress test datasource
*
* In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
*/
public function test_stress_datasource(): void {
if (!PHPUNIT_LONGTEST) {
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
}
$this->resetAfterTest();
$recipient = $this->getDataGenerator()->create_user();
/** @var core_notes_generator $notesgenerator */
$notesgenerator = $this->getDataGenerator()->get_plugin_generator('core_notes');
$notesgenerator->create_instance(['courseid' => SITEID, 'userid' => $recipient->id]);
$this->datasource_stress_test_columns(notes::class);
$this->datasource_stress_test_columns_aggregation(notes::class);
$this->datasource_stress_test_conditions(notes::class, 'note:content');
}
}