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
+295
View File
@@ -0,0 +1,295 @@
<?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/>.
/**
* Privacy Subsystem implementation for core_notes.
*
* @package core_notes
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_notes\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use core_privacy\local\request\userlist;
use \core_privacy\local\request\approved_userlist;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/notes/lib.php');
/**
* Implementation of the privacy subsystem plugin provider for core_notes.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $items a reference to the collection to use to store the metadata.
* @return collection the updated collection of metadata items.
*/
public static function get_metadata(collection $items): collection {
// The core_notes components utilises the shared mdl_post table.
$items->add_database_table(
'post',
[
'content' => 'privacy:metadata:core_notes:content',
'courseid' => 'privacy:metadata:core_notes:courseid',
'created' => 'privacy:metadata:core_notes:created',
'lastmodified' => 'privacy:metadata:core_notes:lastmodified',
'publishstate' => 'privacy:metadata:core_notes:publishstate',
'userid' => 'privacy:metadata:core_notes:userid'
],
'privacy:metadata:core_notes'
);
return $items;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid the userid.
* @return contextlist the list of contexts containing user info for the user.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
global $DB;
$contextlist = new contextlist();
$publishstates = [
NOTES_STATE_PUBLIC,
NOTES_STATE_SITE
];
list($publishstatesql, $publishstateparams) = $DB->get_in_or_equal($publishstates, SQL_PARAMS_NAMED);
// Retrieve all the Course contexts associated with notes written by the user, and also written about the user.
// Only notes written about the user that are public or site wide will be exported.
$sql = "SELECT c.id
FROM {context} c
INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcoursewrittenby
WHERE p.module = 'notes'
AND p.usermodified = :usermodified";
$params = [
'contextcoursewrittenby' => CONTEXT_COURSE,
'usermodified' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "SELECT c.id
FROM {context} c
INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcoursewrittenfor
WHERE p.module = 'notes'
AND p.userid = :userid
AND p.publishstate {$publishstatesql}";
$params = [
'contextcoursewrittenfor' => CONTEXT_COURSE,
'userid' => $userid
];
$params += $publishstateparams;
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if (!$context instanceof \context_course) {
return;
}
$params = [
'instanceid' => $context->instanceid
];
$sql = "SELECT usermodified as userid
FROM {post}
WHERE module = 'notes'
AND courseid = :instanceid";
$userlist->add_from_sql('userid', $sql, $params);
$publishstates = [
NOTES_STATE_PUBLIC,
NOTES_STATE_SITE
];
list($publishstatesql, $publishstateparams) = $DB->get_in_or_equal($publishstates, SQL_PARAMS_NAMED);
$params += $publishstateparams;
$sql = "SELECT userid
FROM {post}
WHERE module = 'notes'
AND courseid = :instanceid
AND publishstate {$publishstatesql}";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export personal data for the given approved_contextlist.
* User and context information is contained within the contextlist.
*
* @param approved_contextlist $contextlist a list of contexts approved for export.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
// Export all notes written by and written about the user, and organize it by the associated Course context(s).
$sql = "SELECT p.courseid as courseid,
p.content as content,
p.publishstate as publishstate,
p.userid as userid,
p.usermodified as usermodified,
p.created as datecreated,
p.lastmodified as datemodified
FROM {context} c
INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcourse
WHERE p.module = 'notes'
AND (p.usermodified = :usermodified OR p.userid = :userid)
AND c.id {$contextsql}";
$params = [
'contextcourse' => CONTEXT_COURSE,
'usermodified' => $userid,
'userid' => $userid
];
$params += $contextparams;
$notes = $DB->get_recordset_sql($sql, $params);
foreach ($notes as $note) {
$contextcourse = \context_course::instance($note->courseid);
// The exported notes will be organized in {Course Context}/Notes/{publishstate}/usernote-{userid}.json.
$subcontext = [
get_string('notes', 'notes'),
$note->publishstate
];
$name = 'usernote-' . transform::user($note->userid);
$notecontent = (object) [
'content' => $note->content,
'publishstate' => $note->publishstate,
'userid' => transform::user($note->userid),
'usermodified' => transform::user($note->usermodified),
'datecreated' => transform::datetime($note->datecreated),
'datemodified' => transform::datetime($note->datemodified)
];
writer::with_context($contextcourse)->export_related_data($subcontext, $name, $notecontent);
}
$notes->close();
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context the context to delete in.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
if ($context->contextlevel != CONTEXT_COURSE) {
return;
}
$DB->delete_records('post', ['module' => 'notes', 'courseid' => $context->instanceid]);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if ($context->contextlevel != CONTEXT_COURSE) {
return;
}
$userids = $userlist->get_userids();
if (empty($userids)) {
return;
}
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$select = "module = :module AND courseid = :courseid AND usermodified {$usersql}";
$params = ['module' => 'notes', 'courseid' => $context->instanceid] + $userparams;
$DB->delete_records_select('post', $select, $params);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist a list of contexts approved for deletion.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
foreach ($contextlist->get_contexts() as $context) {
$conditions = [
'module' => 'notes',
'courseid' => $context->instanceid,
'usermodified' => $userid
];
$DB->delete_records('post', $conditions);
}
}
}
@@ -0,0 +1,144 @@
<?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 lang_string;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\{course, user};
use core_notes\reportbuilder\local\entities\note;
defined('MOODLE_INTERNAL') || die;
global $CFG;
require_once("{$CFG->dirroot}/notes/lib.php");
/**
* Notes datasource
*
* @package core_notes
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class notes extends datasource {
/**
* Return user friendly name of the report source
*
* @return string
*/
public static function get_name(): string {
return get_string('notes', 'core_notes');
}
/**
* Initialise report
*/
protected function initialise(): void {
$noteentity = new note();
$postalias = $noteentity->get_table_alias('post');
$this->set_main_table('post', $postalias);
$this->add_base_condition_simple("{$postalias}.module", 'notes');
$this->add_entity($noteentity);
// Join the user entity to represent the note recipient.
$recipiententity = (new user())
->set_entity_name('recipient')
->set_entity_title(new lang_string('recipient', 'core_notes'));
$recipientalias = $recipiententity->get_table_alias('user');
$this->add_entity($recipiententity->add_join("
LEFT JOIN {user} {$recipientalias}
ON {$recipientalias}.id = {$postalias}.userid")
);
// Join the user entity to represent the note author.
$authorentity = (new user())
->set_entity_name('author')
->set_entity_title(new lang_string('author', 'core_notes'));
$authoralias = $authorentity->get_table_alias('user');
$this->add_entity($authorentity->add_join("
LEFT JOIN {user} {$authoralias}
ON {$authoralias}.id = {$postalias}.usermodified")
);
// Join the course entity for course notes.
$courseentity = new course();
$coursealias = $courseentity->get_table_alias('course');
$this->add_entity($courseentity->add_join("
LEFT JOIN {course} {$coursealias}
ON {$coursealias}.id = {$postalias}.courseid
AND {$postalias}.publishstate = '" . NOTES_STATE_PUBLIC . "'")
);
// Add report elements from each of the entities we added to the report.
$this->add_all_from_entities();
}
/**
* Return the columns that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'recipient:fullname',
'note:publishstate',
'course:fullname',
'note:content',
'note:timecreated',
];
}
/**
* Return the column sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'recipient:fullname' => SORT_ASC,
'note:timecreated' => SORT_ASC,
];
}
/**
* Return the filters that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'recipient:fullname',
];
}
/**
* Return the conditions that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'note:publishstate',
'course:fullname',
'recipient:fullname',
];
}
}
@@ -0,0 +1,231 @@
<?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\local\entities;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{date, select, text};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
defined('MOODLE_INTERNAL') || die;
global $CFG;
require_once("{$CFG->dirroot}/notes/lib.php");
/**
* Note entity
*
* @package core_notes
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class note extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'post',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('note', 'core_notes');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$postalias = $this->get_table_alias('post');
// Content.
$contentfieldsql = "{$postalias}.content";
if ($DB->get_dbfamily() === 'oracle') {
$contentfieldsql = $DB->sql_order_by_text($contentfieldsql, 1024);
}
$columns[] = (new column(
'content',
new lang_string('content', 'core_notes'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_LONGTEXT)
->add_field($contentfieldsql, 'content')
->add_field("{$postalias}.format")
->add_callback(static function(?string $content, stdClass $note): string {
if ($content === null) {
return '';
}
return format_text($content, $note->format);
});
// Publish state.
$columns[] = (new column(
'publishstate',
new lang_string('publishstate', 'core_notes'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$postalias}.publishstate")
->set_is_sortable(true)
->add_callback(static function(string $publishstate): string {
$states = [
NOTES_STATE_SITE => new lang_string('sitenotes', 'core_notes'),
NOTES_STATE_PUBLIC => new lang_string('coursenotes', 'core_notes'),
NOTES_STATE_DRAFT => new lang_string('personalnotes', 'core_notes'),
];
return (string) ($states[$publishstate] ?? $publishstate);
});
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$postalias}.created")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
// Time modified.
$columns[] = (new column(
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$postalias}.lastmodified")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$postalias = $this->get_table_alias('post');
// Content.
$filters[] = (new filter(
text::class,
'content',
new lang_string('content', 'core_notes'),
$this->get_entity_name(),
$DB->sql_cast_to_char("{$postalias}.content")
))
->add_joins($this->get_joins());
// Publish state.
$filters[] = (new filter(
select::class,
'publishstate',
new lang_string('publishstate', 'core_notes'),
$this->get_entity_name(),
"{$postalias}.publishstate"
))
->add_joins($this->get_joins())
->set_options([
NOTES_STATE_SITE => new lang_string('sitenotes', 'core_notes'),
NOTES_STATE_PUBLIC => new lang_string('coursenotes', 'core_notes'),
NOTES_STATE_DRAFT => new lang_string('personalnotes', 'core_notes'),
]);
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$postalias}.created"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_CURRENT,
date::DATE_LAST,
date::DATE_RANGE,
]);
// Time modified.
$filters[] = (new filter(
date::class,
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name(),
"{$postalias}.lastmodified"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_CURRENT,
date::DATE_LAST,
date::DATE_RANGE,
]);
return $filters;
}
}
+80
View File
@@ -0,0 +1,80 @@
<?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/>.
require_once('../config.php');
require_once('lib.php');
require_once($CFG->dirroot . '/course/lib.php');
$noteid = required_param('id', PARAM_INT);
$PAGE->set_url('/notes/delete.php', array('id' => $noteid));
if (!$note = note_load($noteid)) {
throw new \moodle_exception('invalidid');
}
if (!$course = $DB->get_record('course', array('id' => $note->courseid))) {
throw new \moodle_exception('invalidcourseid');
}
require_login($course);
if (empty($CFG->enablenotes)) {
throw new \moodle_exception('notesdisabled', 'notes');
}
if (!$user = $DB->get_record('user', array('id' => $note->userid))) {
throw new \moodle_exception('invaliduserid');
}
$context = context_course::instance($course->id);
if (!has_capability('moodle/notes:manage', $context)) {
throw new \moodle_exception('nopermissiontodelete', 'notes');
}
if (data_submitted() && confirm_sesskey()) {
// If data was submitted and is valid, then delete note.
$returnurl = $CFG->wwwroot . '/notes/index.php?course=' . $course->id . '&amp;user=' . $note->userid;
note_delete($note);
redirect($returnurl);
} else {
// If data was not submitted yet, then show note data with a delete confirmation form.
$strnotes = get_string('notes', 'notes');
$optionsyes = array('id' => $noteid, 'sesskey' => sesskey());
$optionsno = array('course' => $course->id, 'user' => $note->userid);
// Output HTML.
$link = null;
if (course_can_view_participants($context) || course_can_view_participants(context_system::instance())) {
$link = new moodle_url('/user/index.php', array('id' => $course->id));
}
$PAGE->navbar->add(get_string('participants'), $link);
$PAGE->navbar->add(fullname($user), new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id)));
$PAGE->navbar->add(get_string('notes', 'notes'),
new moodle_url('/notes/index.php', array('user' => $user->id, 'course' => $course->id)));
$PAGE->navbar->add(get_string('delete'));
$PAGE->set_title($course->shortname . ': ' . $strnotes);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->confirm(get_string('deleteconfirm', 'notes'),
new moodle_url('delete.php', $optionsyes),
new moodle_url('index.php', $optionsno));
echo '<br />';
note_print($note, NOTES_SHOW_BODY | NOTES_SHOW_HEAD);
echo $OUTPUT->footer();
}
+114
View File
@@ -0,0 +1,114 @@
<?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/>.
require_once('../config.php');
require_once('lib.php');
require_once('edit_form.php');
require_once($CFG->dirroot . '/course/lib.php');
$noteid = optional_param('id', 0, PARAM_INT);
$url = new moodle_url('/notes/edit.php');
if ($noteid) {
// Existing note.
$url->param('id', $noteid);
if (!$note = note_load($noteid)) {
throw new \moodle_exception('invalidid', 'notes');
}
} else {
// Adding new note.
$courseid = required_param('courseid', PARAM_INT);
$userid = required_param('userid', PARAM_INT);
$state = optional_param('publishstate', NOTES_STATE_PUBLIC, PARAM_ALPHA);
$note = new stdClass();
$note->courseid = $courseid;
$note->userid = $userid;
$note->publishstate = $state;
$url->param('courseid', $courseid);
$url->param('userid', $userid);
if ($state !== NOTES_STATE_PUBLIC) {
$url->param('publishstate', $state);
}
}
$PAGE->set_url($url);
if (!$course = $DB->get_record('course', array('id' => $note->courseid))) {
throw new \moodle_exception('invalidcourseid');
}
require_login($course);
if (empty($CFG->enablenotes)) {
throw new \moodle_exception('notesdisabled', 'notes');
}
$context = context_course::instance($course->id);
require_capability('moodle/notes:manage', $context);
if (!$user = $DB->get_record('user', array('id' => $note->userid))) {
throw new \moodle_exception('invaliduserid');
}
$noteform = new note_edit_form();
$noteform->set_data($note);
// If form was cancelled then return to the notes list of the note.
if ($noteform->is_cancelled()) {
redirect($CFG->wwwroot . '/notes/index.php?course=' . $note->courseid . '&amp;user=' . $note->userid);
}
// If data was submitted and validated, then save it to database.
if ($note = $noteform->get_data()) {
if ($noteid) {
// A noteid has been used, we don't allow editing of course or user so
// lets unset them to be sure we never change that by accident.
unset($note->courseid);
unset($note->userid);
}
note_save($note);
// Redirect to notes list that contains this note.
redirect($CFG->wwwroot . '/notes/index.php?course=' . $note->courseid . '&amp;user=' . $note->userid);
}
if ($noteid) {
$strnotes = get_string('editnote', 'notes');
} else {
$strnotes = get_string('addnewnote', 'notes');
}
// Output HTML.
$link = null;
if (course_can_view_participants($context) || course_can_view_participants(context_system::instance())) {
$link = new moodle_url('/user/index.php', array('id' => $course->id));
}
$PAGE->navbar->add(get_string('participants'), $link);
$PAGE->navbar->add(fullname($user), new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id)));
$PAGE->navbar->add(get_string('notes', 'notes'),
new moodle_url('/notes/index.php', array('user' => $user->id, 'course' => $course->id)));
$PAGE->navbar->add($strnotes);
$PAGE->set_title($course->shortname . ': ' . $strnotes);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading(fullname($user));
$noteform->display();
echo $OUTPUT->footer();
+53
View File
@@ -0,0 +1,53 @@
<?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/>.
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->libdir.'/formslib.php');
class note_edit_form extends moodleform {
/**
* Define the form for editing notes
*/
public function definition() {
$mform =& $this->_form;
$mform->addElement('header', 'general', get_string('note', 'notes'));
$mform->addElement('textarea', 'content', get_string('content', 'notes'), array('rows' => 15, 'cols' => 40));
$mform->setType('content', PARAM_RAW);
$mform->addRule('content', get_string('nocontent', 'notes'), 'required', null, 'client');
$mform->setForceLtr('content', false);
$mform->addElement('select', 'publishstate', get_string('publishstate', 'notes'), note_get_state_names());
$mform->setDefault('publishstate', NOTES_STATE_PUBLIC);
$mform->setType('publishstate', PARAM_ALPHA);
$mform->addHelpButton('publishstate', 'publishstate', 'notes');
$this->add_action_buttons();
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->addElement('hidden', 'userid');
$mform->setType('userid', PARAM_INT);
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
}
}
+733
View File
@@ -0,0 +1,733 @@
<?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/>.
use core_external\external_api;
use core_external\external_format_value;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\util;
/**
* External notes API
*
* @package core_notes
* @category external
* @copyright 2011 Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . "/notes/lib.php");
/**
* Notes external functions
*
* @package core_notes
* @category external
* @copyright 2011 Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.2
*/
class core_notes_external extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 2.2
*/
public static function create_notes_parameters() {
return new external_function_parameters(
array(
'notes' => new external_multiple_structure(
new external_single_structure(
array(
'userid' => new external_value(PARAM_INT, 'id of the user the note is about'),
'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\''),
'courseid' => new external_value(PARAM_INT, 'course id of the note (in Moodle a note can only be created into a course, even for site and personal notes)'),
'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML'),
'format' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE),
'clientnoteid' => new external_value(PARAM_ALPHANUMEXT, 'your own client id for the note. If this id is provided, the fail message id will be returned to you', VALUE_OPTIONAL),
)
)
)
)
);
}
/**
* Create notes about some users
* Note: code should be matching the /notes/edit.php checks
* and the /user/addnote.php checks. (they are similar cheks)
*
* @param array $notes An array of notes to create.
* @return array (success infos and fail infos)
* @since Moodle 2.2
*/
public static function create_notes($notes = array()) {
global $CFG, $DB;
$params = self::validate_parameters(self::create_notes_parameters(), array('notes' => $notes));
// Check if note system is enabled.
if (!$CFG->enablenotes) {
throw new moodle_exception('notesdisabled', 'notes');
}
// Retrieve all courses.
$courseids = array();
foreach ($params['notes'] as $note) {
$courseids[] = $note['courseid'];
}
$courses = $DB->get_records_list("course", "id", $courseids);
// Retrieve all users of the notes.
$userids = array();
foreach ($params['notes'] as $note) {
$userids[] = $note['userid'];
}
list($sqluserids, $sqlparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid_');
$users = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
$resultnotes = array();
foreach ($params['notes'] as $note) {
$success = true;
$resultnote = array(); // The infos about the success of the operation.
// Check the course exists.
if (empty($courses[$note['courseid']])) {
$success = false;
$errormessage = get_string('invalidcourseid', 'error');
} else {
// Ensure the current user is allowed to run this function.
$context = context_course::instance($note['courseid']);
self::validate_context($context);
require_capability('moodle/notes:manage', $context);
}
// Check the user exists.
if (empty($users[$note['userid']])) {
$success = false;
$errormessage = get_string('invaliduserid', 'notes', $note['userid']);
}
// Build the resultnote.
if (isset($note['clientnoteid'])) {
$resultnote['clientnoteid'] = $note['clientnoteid'];
}
if ($success) {
// Now we can create the note.
$dbnote = new stdClass;
$dbnote->courseid = $note['courseid'];
$dbnote->userid = $note['userid'];
// Need to support 'html' and 'text' format values for backward compatibility.
switch (strtolower($note['format'])) {
case 'html':
$textformat = FORMAT_HTML;
break;
case 'text':
$textformat = FORMAT_PLAIN;
break;
default:
$textformat = util::validate_format($note['format']);
break;
}
$dbnote->content = $note['text'];
$dbnote->format = $textformat;
// Get the state ('personal', 'course', 'site').
switch ($note['publishstate']) {
case 'personal':
$dbnote->publishstate = NOTES_STATE_DRAFT;
break;
case 'course':
$dbnote->publishstate = NOTES_STATE_PUBLIC;
break;
case 'site':
$dbnote->publishstate = NOTES_STATE_SITE;
$dbnote->courseid = SITEID;
break;
default:
break;
}
// TODO MDL-31119 performance improvement - if possible create a bulk functions for saving multiple notes at once
if (note_save($dbnote)) { // Note_save attribut an id in case of success.
$success = $dbnote->id;
}
$resultnote['noteid'] = $success;
} else {
// WARNINGS: for backward compatibility we return this errormessage.
// We should have thrown exceptions as these errors prevent results to be returned.
// See http://docs.moodle.org/dev/Errors_handling_in_web_services#When_to_send_a_warning_on_the_server_side .
$resultnote['noteid'] = -1;
$resultnote['errormessage'] = $errormessage;
}
$resultnotes[] = $resultnote;
}
return $resultnotes;
}
/**
* Returns description of method result value
*
* @return \core_external\external_description
* @since Moodle 2.2
*/
public static function create_notes_returns() {
return new external_multiple_structure(
new external_single_structure(
array(
'clientnoteid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the note', VALUE_OPTIONAL),
'noteid' => new external_value(PARAM_INT, 'ID of the created note when successful, -1 when failed'),
'errormessage' => new external_value(PARAM_TEXT, 'error message - if failed', VALUE_OPTIONAL)
)
)
);
}
/**
* Returns description of delete_notes parameters
*
* @return external_function_parameters
* @since Moodle 2.5
*/
public static function delete_notes_parameters() {
return new external_function_parameters(
array(
"notes"=> new external_multiple_structure(
new external_value(PARAM_INT, 'ID of the note to be deleted'), 'Array of Note Ids to be deleted.'
)
)
);
}
/**
* Delete notes about users.
* Note: code should be matching the /notes/delete.php checks.
*
* @param array $notes An array of ids for the notes to delete.
* @return null
* @since Moodle 2.5
*/
public static function delete_notes($notes = array()) {
global $CFG;
$params = self::validate_parameters(self::delete_notes_parameters(), array('notes' => $notes));
// Check if note system is enabled.
if (!$CFG->enablenotes) {
throw new moodle_exception('notesdisabled', 'notes');
}
$warnings = array();
foreach ($params['notes'] as $noteid) {
$note = note_load($noteid);
if (isset($note->id)) {
// Ensure the current user is allowed to run this function.
$context = context_course::instance($note->courseid);
self::validate_context($context);
require_capability('moodle/notes:manage', $context);
note_delete($note);
} else {
$warnings[] = array('item'=>'note', 'itemid'=>$noteid, 'warningcode'=>'badid', 'message'=>'Note does not exist');
}
}
return $warnings;
}
/**
* Returns description of delete_notes result value.
*
* @return \core_external\external_description
* @since Moodle 2.5
*/
public static function delete_notes_returns() {
return new external_warnings('item is always \'note\'',
'When errorcode is savedfailed the note could not be modified.' .
'When errorcode is badparam, an incorrect parameter was provided.' .
'When errorcode is badid, the note does not exist',
'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)');
}
/**
* Returns description of get_notes parameters.
*
* @return external_function_parameters
* @since Moodle 2.5
*/
public static function get_notes_parameters() {
return new external_function_parameters(
array(
"notes"=> new external_multiple_structure(
new external_value(PARAM_INT, 'ID of the note to be retrieved'), 'Array of Note Ids to be retrieved.'
)
)
);
}
/**
* Get notes about users.
*
* @param array $notes An array of ids for the notes to retrieve.
* @return null
* @since Moodle 2.5
*/
public static function get_notes($notes) {
global $CFG;
$params = self::validate_parameters(self::get_notes_parameters(), array('notes' => $notes));
// Check if note system is enabled.
if (!$CFG->enablenotes) {
throw new moodle_exception('notesdisabled', 'notes');
}
$resultnotes = array();
foreach ($params['notes'] as $noteid) {
$resultnote = array();
$note = note_load($noteid);
if (isset($note->id)) {
// Ensure the current user is allowed to run this function.
$context = context_course::instance($note->courseid);
self::validate_context($context);
require_capability('moodle/notes:view', $context);
list($gotnote['text'], $gotnote['format']) = util::format_text($note->content,
$note->format,
$context->id,
'notes',
'',
'');
$gotnote['noteid'] = $note->id;
$gotnote['userid'] = $note->userid;
$gotnote['publishstate'] = $note->publishstate;
$gotnote['courseid'] = $note->courseid;
$resultnotes["notes"][] = $gotnote;
} else {
$resultnotes["warnings"][] = array('item' => 'note',
'itemid' => $noteid,
'warningcode' => 'badid',
'message' => 'Note does not exist');
}
}
return $resultnotes;
}
/**
* Returns description of get_notes result value.
*
* @return \core_external\external_description
* @since Moodle 2.5
*/
public static function get_notes_returns() {
return new external_single_structure(
array(
'notes' => new external_multiple_structure(
new external_single_structure(
array(
'noteid' => new external_value(PARAM_INT, 'id of the note', VALUE_OPTIONAL),
'userid' => new external_value(PARAM_INT, 'id of the user the note is about', VALUE_OPTIONAL),
'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\'', VALUE_OPTIONAL),
'courseid' => new external_value(PARAM_INT, 'course id of the note', VALUE_OPTIONAL),
'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML', VALUE_OPTIONAL),
'format' => new external_format_value('text', VALUE_OPTIONAL),
), 'note'
)
),
'warnings' => new external_warnings('item is always \'note\'',
'When errorcode is savedfailed the note could not be modified.' .
'When errorcode is badparam, an incorrect parameter was provided.' .
'When errorcode is badid, the note does not exist',
'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)')
)
);
}
/**
* Returns description of update_notes parameters.
*
* @return external_function_parameters
* @since Moodle 2.5
*/
public static function update_notes_parameters() {
return new external_function_parameters(
array(
'notes' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'id of the note'),
'publishstate' => new external_value(PARAM_ALPHA, '\'personal\', \'course\' or \'site\''),
'text' => new external_value(PARAM_RAW, 'the text of the message - text or HTML'),
'format' => new external_format_value('text', VALUE_DEFAULT),
)
), "Array of Notes", VALUE_DEFAULT, array()
)
)
);
}
/**
* Update notes about users.
*
* @param array $notes An array of ids for the notes to update.
* @return array fail infos.
* @since Moodle 2.2
*/
public static function update_notes($notes = array()) {
global $CFG, $DB;
$params = self::validate_parameters(self::update_notes_parameters(), array('notes' => $notes));
// Check if note system is enabled.
if (!$CFG->enablenotes) {
throw new moodle_exception('notesdisabled', 'notes');
}
$warnings = array();
foreach ($params['notes'] as $note) {
$notedetails = note_load($note['id']);
if (isset($notedetails->id)) {
// Ensure the current user is allowed to run this function.
$context = context_course::instance($notedetails->courseid);
self::validate_context($context);
require_capability('moodle/notes:manage', $context);
$dbnote = new stdClass;
$dbnote->id = $note['id'];
$dbnote->content = $note['text'];
$dbnote->format = util::validate_format($note['format']);
// Get the state ('personal', 'course', 'site').
switch ($note['publishstate']) {
case 'personal':
$dbnote->publishstate = NOTES_STATE_DRAFT;
break;
case 'course':
$dbnote->publishstate = NOTES_STATE_PUBLIC;
break;
case 'site':
$dbnote->publishstate = NOTES_STATE_SITE;
$dbnote->courseid = SITEID;
break;
default:
$warnings[] = array('item' => 'note',
'itemid' => $note["id"],
'warningcode' => 'badparam',
'message' => 'Provided publishstate incorrect');
break;
}
if (!note_save($dbnote)) {
$warnings[] = array('item' => 'note',
'itemid' => $note["id"],
'warningcode' => 'savedfailed',
'message' => 'Note could not be modified');
}
} else {
$warnings[] = array('item' => 'note',
'itemid' => $note["id"],
'warningcode' => 'badid',
'message' => 'Note does not exist');
}
}
return $warnings;
}
/**
* Returns description of update_notes result value.
*
* @return \core_external\external_description
* @since Moodle 2.5
*/
public static function update_notes_returns() {
return new external_warnings('item is always \'note\'',
'When errorcode is savedfailed the note could not be modified.' .
'When errorcode is badparam, an incorrect parameter was provided.' .
'When errorcode is badid, the note does not exist',
'errorcode can be badparam (incorrect parameter), savedfailed (could not be modified), or badid (note does not exist)');
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 2.9
*/
public static function get_course_notes_parameters() {
return new external_function_parameters(
array(
'courseid' => new external_value(PARAM_INT, 'course id, 0 for SITE'),
'userid' => new external_value(PARAM_INT, 'user id', VALUE_DEFAULT, 0),
)
);
}
/**
* Create a notes list
*
* @param int $courseid ID of the Course
* @param stdClass $context context object
* @param int $userid ID of the User
* @param int $state
* @param int $author
* @return array of notes
* @since Moodle 2.9
*/
protected static function create_note_list($courseid, $context, $userid, $state, $author = 0) {
$results = [];
$notes = note_list($courseid, $userid, $state, $author);
foreach ($notes as $key => $note) {
$note = (array)$note;
[$note['content'], $note['format']] = util::format_text(
$note['content'],
$note['format'],
$context->id,
'',
'',
0
);
$results[$key] = $note;
}
return $results;
}
/**
* Get a list of course notes
*
* @param int $courseid ID of the Course
* @param int $userid ID of the User
* @return array of site, course and personal notes and warnings
* @since Moodle 2.9
* @throws moodle_exception
*/
public static function get_course_notes($courseid, $userid = 0) {
global $CFG, $USER;
if (empty($CFG->enablenotes)) {
throw new moodle_exception('notesdisabled', 'notes');
}
$warnings = array();
$arrayparams = array(
'courseid' => $courseid,
'userid' => $userid,
);
$params = self::validate_parameters(self::get_course_notes_parameters(), $arrayparams);
if (empty($params['courseid'])) {
$params['courseid'] = SITEID;
}
$user = null;
if (!empty($params['userid'])) {
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
core_user::require_active_user($user);
}
$course = get_course($params['courseid']);
$systemcontext = context_system::instance();
$canmanagesystemnotes = has_capability('moodle/notes:manage', $systemcontext);
if ($course->id == SITEID) {
$context = $systemcontext;
$canmanagecoursenotes = $canmanagesystemnotes;
} else {
$context = context_course::instance($course->id);
$canmanagecoursenotes = has_capability('moodle/notes:manage', $context);
}
self::validate_context($context);
$sitenotes = array();
$coursenotes = array();
$personalnotes = array();
if ($course->id != SITEID) {
require_capability('moodle/notes:view', $context);
$sitenotes = self::create_note_list(0, $systemcontext, $params['userid'], NOTES_STATE_SITE);
$coursenotes = self::create_note_list($course->id, $context, $params['userid'], NOTES_STATE_PUBLIC);
$personalnotes = self::create_note_list($course->id, $context, $params['userid'], NOTES_STATE_DRAFT,
$USER->id);
} else {
if (has_capability('moodle/notes:view', $context)) {
$sitenotes = self::create_note_list(0, $context, $params['userid'], NOTES_STATE_SITE);
}
// It returns notes only for a specific user!
if (!empty($user)) {
$usercourses = enrol_get_users_courses($user->id, true);
foreach ($usercourses as $c) {
// All notes at course level, only if we have capability on every course.
if (has_capability('moodle/notes:view', context_course::instance($c->id))) {
$coursenotes += self::create_note_list($c->id, $context, $params['userid'], NOTES_STATE_PUBLIC);
}
}
}
}
$results = array(
'sitenotes' => $sitenotes,
'coursenotes' => $coursenotes,
'personalnotes' => $personalnotes,
'canmanagesystemnotes' => $canmanagesystemnotes,
'canmanagecoursenotes' => $canmanagecoursenotes,
'warnings' => $warnings
);
return $results;
}
/**
* Returns array of note structure
*
* @return \core_external\external_description
* @since Moodle 2.9
*/
protected static function get_note_structure() {
return array(
'id' => new external_value(PARAM_INT, 'id of this note'),
'courseid' => new external_value(PARAM_INT, 'id of the course'),
'userid' => new external_value(PARAM_INT, 'user id'),
'content' => new external_value(PARAM_RAW, 'the content text formated'),
'format' => new external_format_value('content'),
'created' => new external_value(PARAM_INT, 'time created (timestamp)'),
'lastmodified' => new external_value(PARAM_INT, 'time of last modification (timestamp)'),
'usermodified' => new external_value(PARAM_INT, 'user id of the creator of this note'),
'publishstate' => new external_value(PARAM_ALPHA, "state of the note (i.e. draft, public, site) ")
);
}
/**
* Returns description of method result value
*
* @return \core_external\external_description
* @since Moodle 2.9
*/
public static function get_course_notes_returns() {
return new external_single_structure(
array(
'sitenotes' => new external_multiple_structure(
new external_single_structure(self::get_note_structure() , ''), 'site notes', VALUE_OPTIONAL
),
'coursenotes' => new external_multiple_structure(
new external_single_structure(self::get_note_structure() , ''), 'couse notes', VALUE_OPTIONAL
),
'personalnotes' => new external_multiple_structure(
new external_single_structure(self::get_note_structure() , ''), 'personal notes', VALUE_OPTIONAL
),
'canmanagesystemnotes' => new external_value(PARAM_BOOL, 'Whether the user can manage notes at system level.',
VALUE_OPTIONAL),
'canmanagecoursenotes' => new external_value(PARAM_BOOL, 'Whether the user can manage notes at the given course.',
VALUE_OPTIONAL),
'warnings' => new external_warnings()
), 'notes'
);
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 2.9
*/
public static function view_notes_parameters() {
return new external_function_parameters(
array(
'courseid' => new external_value(PARAM_INT, 'course id, 0 for notes at system level'),
'userid' => new external_value(PARAM_INT, 'user id, 0 means view all the user notes', VALUE_DEFAULT, 0)
)
);
}
/**
* Simulates the web interface view of notes/index.php: trigger events
*
* @param int $courseid id of the course
* @param int $userid id of the user
* @return array of warnings and status result
* @since Moodle 2.9
* @throws moodle_exception
*/
public static function view_notes($courseid, $userid = 0) {
global $CFG;
require_once($CFG->dirroot . "/notes/lib.php");
if (empty($CFG->enablenotes)) {
throw new moodle_exception('notesdisabled', 'notes');
}
$warnings = array();
$arrayparams = array(
'courseid' => $courseid,
'userid' => $userid
);
$params = self::validate_parameters(self::view_notes_parameters(), $arrayparams);
if (empty($params['courseid'])) {
$params['courseid'] = SITEID;
}
$course = get_course($params['courseid']);
if ($course->id == SITEID) {
$context = context_system::instance();
} else {
$context = context_course::instance($course->id);
}
// First of all, validate the context before do further permission checks.
self::validate_context($context);
require_capability('moodle/notes:view', $context);
if (!empty($params['userid'])) {
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
core_user::require_active_user($user);
if ($course->id != SITEID and !can_access_course($course, $user, '', true)) {
throw new moodle_exception('notenrolledprofile');
}
}
note_view($context, $params['userid']);
$result = array();
$result['status'] = true;
$result['warnings'] = $warnings;
return $result;
}
/**
* Returns description of method result value
*
* @return \core_external\external_description
* @since Moodle 2.9
*/
public static function view_notes_returns() {
return new external_single_structure(
array(
'status' => new external_value(PARAM_BOOL, 'status: true if success'),
'warnings' => new external_warnings()
)
);
}
}
+203
View File
@@ -0,0 +1,203 @@
<?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/>.
/**
* file index.php
* index page to view notes.
* if a course id is specified then the entries from that course are shown
* if a user id is specified only notes related to that user are shown
*/
require_once('../config.php');
require_once('lib.php');
require_once($CFG->dirroot . '/course/lib.php');
$courseid = optional_param('course', SITEID, PARAM_INT);
$userid = optional_param('user', 0, PARAM_INT);
$filtertype = optional_param('filtertype', '', PARAM_ALPHA);
$filterselect = optional_param('filterselect', 0, PARAM_INT);
if (empty($CFG->enablenotes)) {
throw new \moodle_exception('notesdisabled', 'notes');
}
$url = new moodle_url('/notes/index.php');
if ($courseid != SITEID) {
$url->param('course', $courseid);
}
if ($userid !== 0) {
$url->param('user', $userid);
}
$PAGE->set_url($url);
// Tabs compatibility.
switch($filtertype) {
case 'course':
$courseid = $filterselect;
break;
case 'site':
$courseid = SITEID;
break;
}
if (empty($courseid)) {
$courseid = SITEID;
}
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
if ($userid) {
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
$filtertype = 'user';
$filterselect = $user->id;
if ($user->deleted) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('userdeleted'));
echo $OUTPUT->footer();
die;
}
} else {
$filtertype = 'course';
$filterselect = $course->id;
$user = $USER;
}
require_login($course);
// Output HTML.
if ($course->id == SITEID) {
$coursecontext = context_system::instance();
} else {
$coursecontext = context_course::instance($course->id);
}
require_capability('moodle/notes:view', $coursecontext);
$systemcontext = context_system::instance();
// Trigger event.
note_view($coursecontext, $userid);
$strnotes = get_string('notes', 'notes');
if ($userid && $course->id == SITEID) {
$PAGE->set_context(context_user::instance($user->id));
$PAGE->navigation->extend_for_user($user);
// If we are looking at our own notes, then change focus to 'my notes'.
if ($userid == $USER->id) {
$notenode = $PAGE->navigation->find('notes', null)->make_inactive();
}
$notesurl = new moodle_url('/notes/index.php', array('user' => $userid));
$PAGE->navbar->add(get_string('notes', 'notes'), $notesurl);
} else if ($course->id != SITEID) {
$notenode = $PAGE->navigation->find('currentcoursenotes', null)->make_inactive();
$notesurl = new moodle_url('/notes/index.php', array('user' => $userid, 'course' => $courseid));
$PAGE->navbar->add(get_string('notes', 'notes'), $notesurl);
$PAGE->set_context(context_course::instance($courseid));
} else {
$link = null;
if (course_can_view_participants($coursecontext) || course_can_view_participants($systemcontext)) {
$link = new moodle_url('/user/index.php', array('id' => $course->id));
}
}
$PAGE->set_pagelayout('incourse');
$PAGE->set_title($course->fullname);
if ($course->id == SITEID) {
$PAGE->set_heading(fullname($user));
} else {
$PAGE->set_heading($course->fullname);
}
echo $OUTPUT->header();
if ($course->id != SITEID) {
$backurl = new moodle_url('/user/view.php', ['id' => $userid, 'course' => $courseid]);
echo $OUTPUT->single_button($backurl, get_string('back'), 'get', ['class' => 'mb-3']);
$headerinfo = array('heading' => fullname($user), 'user' => $user);
echo $OUTPUT->context_header($headerinfo, 2);
}
echo $OUTPUT->heading($strnotes);
$strsitenotes = get_string('sitenotes', 'notes');
$strcoursenotes = get_string('coursenotes', 'notes');
$strpersonalnotes = get_string('personalnotes', 'notes');
$straddnewnote = get_string('addnewnote', 'notes');
echo $OUTPUT->box_start();
if ($courseid != SITEID) {
$context = context_course::instance($courseid);
$addid = has_capability('moodle/notes:manage', $context) ? $courseid : 0;
$view = has_capability('moodle/notes:view', $context);
$fullname = format_string($course->fullname, true, array('context' => $context));
note_print_notes(
'<a name="sitenotes"></a>' . $strsitenotes,
$addid,
$view,
0,
$userid,
NOTES_STATE_SITE,
0
);
note_print_notes(
'<a name="coursenotes"></a>' . $strcoursenotes. ' ('.$fullname.')',
$addid,
$view,
$courseid,
$userid,
NOTES_STATE_PUBLIC,
0
);
note_print_notes(
'<a name="personalnotes"></a>' . $strpersonalnotes,
$addid,
$view,
$courseid,
$userid,
NOTES_STATE_DRAFT,
$USER->id
);
} else { // Normal course.
$view = has_capability('moodle/notes:view', context_system::instance());
note_print_notes('<a name="sitenotes"></a>' . $strsitenotes, 0, $view, 0, $userid, NOTES_STATE_SITE, 0);
echo '<a name="coursenotes"></a>';
if (!empty($userid)) {
$courses = enrol_get_users_courses($userid);
foreach ($courses as $c) {
$ccontext = context_course::instance($c->id);
$cfullname = format_string($c->fullname, true, array('context' => $ccontext));
$header = '<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $c->id . '">' . $cfullname . '</a>';
$viewcoursenotes = has_capability('moodle/notes:view', $ccontext);
if (has_capability('moodle/notes:manage', $ccontext)) {
$addid = $c->id;
} else {
$addid = 0;
}
note_print_notes($header, $addid, $viewcoursenotes, $c->id, $userid, NOTES_STATE_PUBLIC, 0);
}
}
}
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
+416
View File
@@ -0,0 +1,416 @@
<?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/>.
/**
* Library of functions and constants for notes
*
* @package core_notes
* @copyright 2007 onwards Yu Zhang
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Constants for states.
*/
define('NOTES_STATE_DRAFT', 'draft');
define('NOTES_STATE_PUBLIC', 'public');
define('NOTES_STATE_SITE', 'site');
/**
* Constants for note parts (flags used by note_print and note_print_list).
*/
define('NOTES_SHOW_FULL', 0x07);
define('NOTES_SHOW_HEAD', 0x02);
define('NOTES_SHOW_BODY', 0x01);
define('NOTES_SHOW_FOOT', 0x04);
/**
* Retrieves a list of note objects with specific atributes.
*
* @param int $courseid id of the course in which the notes were posted (0 means any)
* @param int $userid id of the user to which the notes refer (0 means any)
* @param string $state state of the notes (i.e. draft, public, site) ('' means any)
* @param int $author id of the user who modified the note last time (0 means any)
* @param string $order an order to sort the results in
* @param int $limitfrom number of records to skip (offset)
* @param int $limitnum number of records to fetch
* @return array of note objects
*/
function note_list($courseid=0, $userid=0, $state = '', $author = 0, $order='lastmodified DESC', $limitfrom=0, $limitnum=0) {
global $DB;
// Setup filters.
$selects = array();
$params = array();
if ($courseid) {
$selects[] = 'courseid=?';
$params[] = $courseid;
}
if ($userid) {
$selects[] = 'userid=?';
$params[] = $userid;
}
if ($author) {
$selects[] = 'usermodified=?';
$params[] = $author;
}
if ($state) {
$selects[] = 'publishstate=?';
$params[] = $state;
}
$selects[] = "module=?";
$params[] = 'notes';
$select = implode(' AND ', $selects);
$fields = 'id,courseid,userid,content,format,created,lastmodified,usermodified,publishstate';
return $DB->get_records_select('post', $select, $params, $order, $fields, $limitfrom, $limitnum);
}
/**
* Retrieves a note object based on its id.
*
* @param int $noteid ID of the note to retrieve
* @return stdClass object
*/
function note_load($noteid) {
global $DB;
$fields = 'id,courseid,userid,content,format,created,lastmodified,usermodified,publishstate';
return $DB->get_record('post', array('id' => $noteid, 'module' => 'notes'), $fields);
}
/**
* Saves a note object. The note object is passed by reference and its fields (i.e. id)
* might change during the save.
*
* @param stdClass $note object to save
* @return boolean true if the object was saved; false otherwise
*/
function note_save(&$note) {
global $USER, $DB;
// Setup & clean fields.
$note->module = 'notes';
$note->lastmodified = time();
$note->usermodified = $USER->id;
if (empty($note->format)) {
$note->format = FORMAT_MOODLE;
}
if (empty($note->publishstate)) {
$note->publishstate = NOTES_STATE_PUBLIC;
}
if (empty(trim($note->content))) {
// Don't save empty notes.
return false;
}
// Save data.
if (empty($note->id)) {
// Insert new note.
$note->created = $note->lastmodified;
$id = $DB->insert_record('post', $note);
$note = note_load($id);
// Trigger event.
$event = \core\event\note_created::create(array(
'objectid' => $note->id,
'courseid' => $note->courseid,
'relateduserid' => $note->userid,
'userid' => $note->usermodified,
'context' => context_course::instance($note->courseid),
'other' => array('publishstate' => $note->publishstate)
));
$event->trigger();
} else {
// Update old note.
$DB->update_record('post', $note);
$note = note_load($note->id);
// Trigger event.
$event = \core\event\note_updated::create(array(
'objectid' => $note->id,
'courseid' => $note->courseid,
'relateduserid' => $note->userid,
'userid' => $note->usermodified,
'context' => context_course::instance($note->courseid),
'other' => array('publishstate' => $note->publishstate)
));
$event->trigger();
}
unset($note->module);
return true;
}
/**
* Deletes a note object based on its id.
*
* @param int|object $note id of the note to delete, or a note object which is to be deleted.
* @return boolean true always
*/
function note_delete($note) {
global $DB;
if (is_int($note)) {
$noteid = $note;
} else {
$noteid = $note->id;
}
// Get the full record, note_load doesn't return everything.
$note = $DB->get_record('post', array('id' => $noteid), '*', MUST_EXIST);
$return = $DB->delete_records('post', array('id' => $note->id, 'module' => 'notes'));
// Trigger event.
$event = \core\event\note_deleted::create(array(
'objectid' => $note->id,
'courseid' => $note->courseid,
'relateduserid' => $note->userid,
'userid' => $note->usermodified,
'context' => context_course::instance($note->courseid),
'other' => array('publishstate' => $note->publishstate)
));
$event->add_record_snapshot('post', $note);
$event->trigger();
return $return;
}
/**
* Converts a state value to its corespondent name
*
* @param string $state state value to convert
* @return string corespondent state name
*/
function note_get_state_name($state) {
// Cache state names.
static $states;
if (empty($states)) {
$states = note_get_state_names();
}
if (isset($states[$state])) {
return $states[$state];
} else {
return null;
}
}
/**
* Returns an array of mappings from state values to state names
*
* @return array of mappings
*/
function note_get_state_names() {
return array(
NOTES_STATE_DRAFT => get_string('personal', 'notes'),
NOTES_STATE_PUBLIC => get_string('course', 'notes'),
NOTES_STATE_SITE => get_string('site', 'notes'),
);
}
/**
* Prints a note object
*
* @param note $note the note object to print
* @param int $detail OR-ed NOTES_SHOW_xyz flags that specify which note parts to print
*/
function note_print($note, $detail = NOTES_SHOW_FULL) {
global $CFG, $USER, $DB, $OUTPUT;
if (!$user = $DB->get_record('user', array('id' => $note->userid))) {
debugging("User $note->userid not found");
return;
}
if (!$author = $DB->get_record('user', array('id' => $note->usermodified))) {
debugging("User $note->usermodified not found");
return;
}
$context = context_course::instance($note->courseid);
$systemcontext = context_system::instance();
$authoring = new stdClass();
$authoring->name = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $author->id .
'&amp;course='.$note->courseid . '">' . fullname($author) . '</a>';
$authoring->date = userdate($note->lastmodified);
echo '<div class="notepost '. $note->publishstate . 'notepost' .
($note->usermodified == $USER->id ? ' ownnotepost' : '') .
'" id="note-' . $note->id . '">';
// Print note head (e.g. author, user refering to, etc).
if ($detail & NOTES_SHOW_HEAD) {
echo '<div class="header">';
echo '<div class="user">';
echo $OUTPUT->user_picture($user, array('courseid' => $note->courseid));
echo fullname($user) . '</div>';
echo '<div class="info">' .
get_string('bynameondate', 'notes', $authoring) .
' (' . get_string('created', 'notes') . ': ' . userdate($note->created) . ')</div>';
echo '</div>';
}
// Print note content.
if ($detail & NOTES_SHOW_BODY) {
echo '<div class="content">';
echo format_text($note->content, $note->format, array('overflowdiv' => true));
echo '</div>';
}
// Print note options (e.g. delete, edit).
if ($detail & NOTES_SHOW_FOOT) {
if (has_capability('moodle/notes:manage', $systemcontext) && $note->publishstate == NOTES_STATE_SITE ||
has_capability('moodle/notes:manage', $context) &&
($note->publishstate == NOTES_STATE_PUBLIC || $note->usermodified == $USER->id)) {
echo '<div class="footer"><p>';
echo '<a href="' . $CFG->wwwroot . '/notes/edit.php?id=' . $note->id. '">' . get_string('edit') . '</a> | ';
echo '<a href="' . $CFG->wwwroot . '/notes/delete.php?id=' . $note->id. '">' . get_string('delete') . '</a>';
echo '</p></div>';
}
}
echo '</div>';
}
/**
* Prints a list of note objects
*
* @param array $notes array of note objects to print
* @param int $detail OR-ed NOTES_SHOW_xyz flags that specify which note parts to print
*/
function note_print_list($notes, $detail = NOTES_SHOW_FULL) {
echo '<div class="notelist">';
foreach ($notes as $note) {
note_print($note, $detail);
}
echo '</div>';
}
/**
* Retrieves and prints a list of note objects with specific atributes.
*
* @param string $header HTML to print above the list
* @param int $addcourseid id of the course for the add notes link (0 hide link)
* @param boolean $viewnotes true if the notes should be printed; false otherwise (print notesnotvisible string)
* @param int $courseid id of the course in which the notes were posted (0 means any)
* @param int $userid id of the user to which the notes refer (0 means any)
* @param string $state state of the notes (i.e. draft, public, site) ('' means any)
* @param int $author id of the user who modified the note last time (0 means any)
*/
function note_print_notes($header, $addcourseid = 0, $viewnotes = true, $courseid = 0, $userid = 0, $state = '', $author = 0) {
global $CFG;
if ($header) {
echo '<h3 class="notestitle">' . $header . '</h3>';
echo '<div class="notesgroup">';
}
if ($addcourseid) {
if ($userid) {
echo '<p><a href="' . $CFG->wwwroot . '/notes/edit.php?courseid=' . $addcourseid . '&amp;userid=' . $userid .
'&amp;publishstate=' . $state . '">' . get_string('addnewnote', 'notes') . '</a></p>';
} else {
echo '<p><a href="' . $CFG->wwwroot . '/user/index.php?id=' . $addcourseid. '">' .
get_string('addnewnoteselect', 'notes') . '</a></p>';
}
}
if ($viewnotes) {
$notes = note_list($courseid, $userid, $state, $author);
if ($notes) {
note_print_list($notes);
}
} else {
echo '<p>' . get_string('notesnotvisible', 'notes') . '</p>';
}
if ($header) {
echo '</div>'; // The notesgroup div.
}
}
/**
* Delete all notes about users in course-
* @param int $courseid
* @return bool success
*/
function note_delete_all($courseid) {
global $DB;
return $DB->delete_records('post', array('module' => 'notes', 'courseid' => $courseid));
}
/**
* Return a list of page types
* @param string $pagetype current page type
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
function note_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array('notes-*' => get_string('page-notes-x', 'notes'));
}
/**
* Trigger notes viewed event
*
* @param stdClass $context context object
* @param int $userid user id (the user we are viewing the notes)
* @since Moodle 2.9
*/
function note_view($context, $userid) {
$event = \core\event\notes_viewed::create(array(
'relateduserid' => $userid,
'context' => $context
));
$event->trigger();
}
/**
* Add nodes to myprofile page.
*
* @param \core_user\output\myprofile\tree $tree Tree object
* @param stdClass $user user object
* @param bool $iscurrentuser
* @param stdClass $course Course object
*
* @return bool
*/
function core_notes_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) {
global $CFG;
if (empty($CFG->enablenotes)) {
// Notes are disabled, nothing to do.
return false;
}
if (isguestuser($user)) {
// No notes for guest users.
return false;
}
$url = new moodle_url("/notes/index.php", array('user' => $user->id));
$title = get_string('notes', 'core_notes');
if (empty($course)) {
// Site level profile.
if (!has_capability('moodle/notes:view', context_system::instance())) {
// No cap, nothing to do.
return false;
}
} else {
if (!has_capability('moodle/notes:view', context_course::instance($course->id))) {
// No cap, nothing to do.
return false;
}
$url->param('course', $course->id);
}
$notesnode = new core_user\output\myprofile\node('miscellaneous', 'notes', $title, null, $url);
$tree->add_node($notesnode);
}
@@ -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');
}
}
+8
View File
@@ -0,0 +1,8 @@
This file describes API changes in /notes/*,
information provided here is intended especially for developers.
=== 3.7 ===
* External function core_notes_external::get_course_notes now returns this additional two fields:
- canmanagesystemnotes: Whether the user can manage notes at system level.
- canmanagecoursenotes: Whether the user can manage notes at the given course.