mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 04:22:07 +02:00
MDL-62137 core_completion: Privacy files for core_completion.
This commit is contained in:
parent
446d8483a3
commit
ebab932f71
205
completion/classes/privacy/provider.php
Normal file
205
completion/classes/privacy/provider.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?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 class for requesting user data.
|
||||
*
|
||||
* @package core_completion
|
||||
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_completion\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \core_privacy\local\metadata\collection;
|
||||
use \core_privacy\local\request\transform;
|
||||
use \core_privacy\local\request\contextlist;
|
||||
|
||||
require_once($CFG->dirroot . '/comment/lib.php');
|
||||
|
||||
/**
|
||||
* Privacy class for requesting user data.
|
||||
*
|
||||
* @package core_completion
|
||||
* @copyright 2018 Adrian Greeve <adrian@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\subsystem\plugin_provider {
|
||||
|
||||
/**
|
||||
* Returns meta data about this system.
|
||||
*
|
||||
* @param collection $collection The initialised collection to add items to.
|
||||
* @return collection A listing of user data stored through this system.
|
||||
*/
|
||||
public static function get_metadata(collection $collection) : collection {
|
||||
$collection->add_database_table('course_completions', [
|
||||
'userid' => 'privacy:metadata:userid',
|
||||
'course' => 'privacy:metadata:course',
|
||||
'timeenrolled' => 'privacy:metadata:timeenrolled',
|
||||
'timestarted' => 'privacy:metadata:timestarted',
|
||||
'timecompleted' => 'privacy:metadata:timecompleted',
|
||||
'reaggregate' => 'privacy:metadata:reaggregate'
|
||||
], 'privacy:metadata:coursesummary');
|
||||
$collection->add_database_table('course_modules_completion', [
|
||||
'userid' => 'privacy:metadata:userid',
|
||||
'coursemoduleid' => 'privacy:metadata:coursemoduleid',
|
||||
'completionstate' => 'privacy:metadata:completionstate',
|
||||
'viewed' => 'privacy:metadata:viewed',
|
||||
'overrideby' => 'privacy:metadata:overrideby',
|
||||
'timemodified' => 'privacy:metadata:timemodified'
|
||||
], 'privacy:metadata:coursemodulesummary');
|
||||
$collection->add_database_table('course_completion_crit_compl', [
|
||||
'userid' => 'privacy:metadata:userid',
|
||||
'course' => 'privacy:metadata:course',
|
||||
'gradefinal' => 'privacy:metadata:gradefinal',
|
||||
'unenroled' => 'privacy:metadata:unenroled',
|
||||
'timecompleted' => 'privacy:metadata:timecompleted'
|
||||
], 'privacy:metadata:coursecompletedsummary');
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get join sql to retrieve courses the user is in.
|
||||
*
|
||||
* @param int $userid The user ID
|
||||
* @param string $prefix A unique prefix for these joins.
|
||||
* @param string $joinfield A field to join these tables to. Joins to course ID.
|
||||
* @return array The join, where, and params for this join.
|
||||
*/
|
||||
public static function get_course_completion_join_sql(int $userid, string $prefix, string $joinfield) : array {
|
||||
$cccalias = "{$prefix}_ccc"; // Course completion criteria.
|
||||
$cmcalias = "{$prefix}_cmc"; // Course modules completion.
|
||||
$ccccalias = "{$prefix}_cccc"; // Course completion criteria completion.
|
||||
|
||||
$join = "JOIN {course_completion_criteria} {$cccalias} ON {$joinfield} = {$cccalias}.course
|
||||
LEFT JOIN {course_modules_completion} {$cmcalias} ON {$cccalias}.moduleinstance = {$cmcalias}.coursemoduleid
|
||||
LEFT JOIN {course_completion_crit_compl} {$ccccalias} ON {$ccccalias}.criteriaid = {$cccalias}.id";
|
||||
$where = "{$cmcalias}.userid = :{$prefix}_moduleuserid OR {$ccccalias}.userid = :{$prefix}_courseuserid";
|
||||
$params = ["{$prefix}_moduleuserid" => $userid, "{$prefix}_courseuserid" => $userid];
|
||||
|
||||
return [$join, $where, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns activity completion information about a user.
|
||||
*
|
||||
* @param \stdClass $user The user to return information about.
|
||||
* @param \stdClass $course The course the user is in.
|
||||
* @param \stdClass $cm Course module information.
|
||||
* @return \stdClass Activity completion information.
|
||||
*/
|
||||
public static function get_activity_completion_info(\stdClass $user, \stdClass $course, $cm) : \stdClass {
|
||||
$completioninfo = new \completion_info($course);
|
||||
$completion = $completioninfo->is_enabled($cm);
|
||||
return ($completion != COMPLETION_TRACKING_NONE) ? $completioninfo->get_data($cm, true, $user->id) : new \stdClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns course completion information for a user.
|
||||
*
|
||||
* @param \stdClass $user The user that we are getting completion information for.
|
||||
* @param \stdClass $course The course we are interested in.
|
||||
* @return \stdClass Course completion information.
|
||||
*/
|
||||
public static function get_course_completion_info(\stdClass $user, \stdClass $course) : array {
|
||||
$completioninfo = new \completion_info($course);
|
||||
$completion = $completioninfo->is_enabled();
|
||||
|
||||
if ($completion == COMPLETION_ENABLED) {
|
||||
|
||||
$coursecomplete = $completioninfo->is_course_complete($user->id);
|
||||
$criteriacomplete = $completioninfo->count_course_user_data($user->id);
|
||||
$ccompletion = new \completion_completion(['userid' => $user->id, 'course' => $course->id]);
|
||||
|
||||
$status = ($coursecomplete) ? get_string('complete') : '';
|
||||
$status = (!$criteriacomplete && !$ccompletion->timestarted) ? get_string('notyetstarted', 'completion') :
|
||||
get_string('inprogress', 'completion');
|
||||
|
||||
$completions = $completioninfo->get_completions($user->id);
|
||||
$overall = get_string('nocriteriaset', 'completion');
|
||||
if (!empty($completions)) {
|
||||
if ($completioninfo->get_aggregation_method() == COMPLETION_AGGREGATION_ALL) {
|
||||
$overall = get_string('criteriarequiredall', 'completion');
|
||||
} else {
|
||||
$overall = get_string('criteriarequiredany', 'completion');
|
||||
}
|
||||
}
|
||||
|
||||
$coursecompletiondata = [
|
||||
'status' => $status,
|
||||
'required' => $overall,
|
||||
];
|
||||
|
||||
$coursecompletiondata['criteria'] = array_map(function($completion) use ($completioninfo) {
|
||||
$criteria = $completion->get_criteria();
|
||||
$aggregation = $completioninfo->get_aggregation_method($criteria->criteriatype);
|
||||
$required = ($aggregation == COMPLETION_AGGREGATION_ALL) ? get_string('all', 'completion') :
|
||||
get_string('any', 'completion');
|
||||
$data = [
|
||||
'required' => $required,
|
||||
'completed' => transform::yesno($completion->is_complete()),
|
||||
'timecompleted' => isset($completion->timecompleted) ? transform::datetime($completion->timecompleted) : ''
|
||||
];
|
||||
$details = $criteria->get_details($completion);
|
||||
$data = array_merge($data, $details);
|
||||
return $data;
|
||||
}, $completions);
|
||||
return $coursecompletiondata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete completion information for users.
|
||||
*
|
||||
* @param \stdClass $user The user. If provided will delete completion information for just this user. Else all users.
|
||||
* @param int $courseid The course id. Provide this if you want course completion and activity completion deleted.
|
||||
* @param int $cmid The course module id. Provide this if you only want activity completion deleted.
|
||||
*/
|
||||
public static function delete_completion(\stdClass $user = null, int $courseid = null, int $cmid = null) {
|
||||
global $DB;
|
||||
|
||||
if (isset($cmid)) {
|
||||
$params = (isset($user)) ? ['userid' => $user->id, 'coursemoduleid' => $cmid] : ['coursemoduleid' => $cmid];
|
||||
// Only delete the record for course modules completion.
|
||||
$DB->delete_records('course_modules_completion', $params);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($courseid)) {
|
||||
|
||||
$usersql = isset($user) ? 'AND cmc.userid = :userid' : '';
|
||||
$params = isset($user) ? ['course' => $courseid, 'userid' => $user->id] : ['course' => $courseid];
|
||||
|
||||
// Find records relating to course modules.
|
||||
$sql = "SELECT cmc.id
|
||||
FROM {course_completion_criteria} ccc
|
||||
JOIN {course_modules_completion} cmc ON ccc.moduleinstance = cmc.coursemoduleid
|
||||
WHERE ccc.course = :course $usersql";
|
||||
$recordids = $DB->get_records_sql($sql, $params);
|
||||
$ids = array_keys($recordids);
|
||||
if (!empty($ids)) {
|
||||
list($deletesql, $deleteparams) = $DB->get_in_or_equal($ids);
|
||||
$deletesql = 'id ' . $deletesql;
|
||||
$DB->delete_records_select('course_modules_completion', $deletesql, $deleteparams);
|
||||
}
|
||||
$DB->delete_records('course_completion_crit_compl', $params);
|
||||
$DB->delete_records('course_completions', $params);
|
||||
}
|
||||
}
|
||||
}
|
118
completion/tests/fixtures/completion_creation.php
vendored
Normal file
118
completion/tests/fixtures/completion_creation.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Trait for course completion creation in unit tests
|
||||
*
|
||||
* @package core_completion
|
||||
* @category test
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/completion/criteria/completion_criteria.php');
|
||||
require_once($CFG->dirroot . '/completion/criteria/completion_criteria_activity.php');
|
||||
require_once($CFG->dirroot . '/completion/criteria/completion_criteria_role.php');
|
||||
|
||||
/**
|
||||
* Trait for unit tests and completion.
|
||||
*
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
trait completion_creation {
|
||||
|
||||
/** @var stdClass The course object. */
|
||||
public $course;
|
||||
|
||||
/** @var context The course context object. */
|
||||
public $coursecontext;
|
||||
|
||||
/** @var stdClass The course module object */
|
||||
public $cm;
|
||||
|
||||
/**
|
||||
* Create completion information.
|
||||
*/
|
||||
public function create_course_completion() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
|
||||
$assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'completion' => 1]);
|
||||
$modulecontext = context_module::instance($assign->cmid);
|
||||
$cm = get_coursemodule_from_id('assign', $assign->cmid);
|
||||
|
||||
// Set completion rules.
|
||||
$completion = new \completion_info($course);
|
||||
|
||||
$criteriadata = (object) [
|
||||
'id' => $course->id,
|
||||
'criteria_activity' => [
|
||||
$cm->id => 1
|
||||
]
|
||||
];
|
||||
$criterion = new \completion_criteria_activity();
|
||||
$criterion->update_config($criteriadata);
|
||||
|
||||
$criteriadata = (object) [
|
||||
'id' => $course->id,
|
||||
'criteria_role' => [3 => 3]
|
||||
];
|
||||
$criterion = new \completion_criteria_role();
|
||||
$criterion->update_config($criteriadata);
|
||||
|
||||
// Handle overall aggregation.
|
||||
$aggdata = array(
|
||||
'course' => $course->id,
|
||||
'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY
|
||||
);
|
||||
$aggregation = new \completion_aggregation($aggdata);
|
||||
$aggregation->setMethod(COMPLETION_AGGREGATION_ALL);
|
||||
$aggregation->save();
|
||||
$aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ROLE;
|
||||
$aggregation = new \completion_aggregation($aggdata);
|
||||
$aggregation->setMethod(COMPLETION_AGGREGATION_ANY);
|
||||
$aggregation->save();
|
||||
|
||||
// Set variables for access in tests.
|
||||
$this->course = $course;
|
||||
$this->coursecontext = $coursecontext;
|
||||
$this->cm = $cm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete some of the course completion criteria.
|
||||
*
|
||||
* @param stdClass $user The user object
|
||||
*/
|
||||
public function complete_course($user) {
|
||||
$this->getDataGenerator()->enrol_user($user->id, $this->course->id, 'student');
|
||||
$completion = new \completion_info($this->course);
|
||||
$criteriacompletions = $completion->get_completions($user->id, COMPLETION_CRITERIA_TYPE_ROLE);
|
||||
$criteria = completion_criteria::factory(['id' => 3, 'criteriatype' => COMPLETION_CRITERIA_TYPE_ROLE]);
|
||||
foreach ($criteriacompletions as $ccompletion) {
|
||||
$criteria->complete($ccompletion);
|
||||
}
|
||||
// Set activity as complete.
|
||||
$completion->update_state($this->cm, COMPLETION_COMPLETE, $user->id);
|
||||
}
|
||||
}
|
118
completion/tests/privacy_test.php
Normal file
118
completion/tests/privacy_test.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?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 request helper.
|
||||
*
|
||||
* @package core_completion
|
||||
* @category test
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/completion/tests/fixtures/completion_creation.php');
|
||||
|
||||
/**
|
||||
* Tests for the \core_completion API's provider functionality.
|
||||
*
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_completion_privacy_test extends \core_privacy\tests\provider_testcase {
|
||||
|
||||
use completion_creation;
|
||||
|
||||
/**
|
||||
* Test joining course completion data to an sql statement.
|
||||
*/
|
||||
public function test_get_course_completion_join_sql() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->create_course_completion();
|
||||
$this->complete_course($user);
|
||||
|
||||
list($join, $where, $params) = \core_completion\privacy\provider::get_course_completion_join_sql($user->id, 'comp', 'c.id');
|
||||
$sql = "SELECT DISTINCT c.id
|
||||
FROM {course} c
|
||||
{$join}
|
||||
WHERE {$where}";
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
$data = array_shift($records);
|
||||
$this->assertEquals($this->course->id, $data->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting course completion information.
|
||||
*/
|
||||
public function test_get_course_completion_info() {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->create_course_completion();
|
||||
$this->complete_course($user);
|
||||
$coursecompletion = \core_completion\privacy\provider::get_course_completion_info($user, $this->course);
|
||||
$this->assertEquals('In progress', $coursecompletion['status']);
|
||||
$this->assertCount(2, $coursecompletion['criteria']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting activity completion information.
|
||||
*/
|
||||
public function test_get_activity_completion_info() {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->create_course_completion();
|
||||
$this->complete_course($user);
|
||||
$activitycompletion = \core_completion\privacy\provider::get_activity_completion_info($user, $this->course,
|
||||
$this->cm);
|
||||
$this->assertEquals($user->id, $activitycompletion->userid);
|
||||
$this->assertEquals($this->cm->id, $activitycompletion->coursemoduleid);
|
||||
$this->assertEquals(1, $activitycompletion->completionstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting activity completion information for a user.
|
||||
*/
|
||||
public function test_delete_completion_activity_user() {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->create_course_completion();
|
||||
$this->complete_course($user);
|
||||
\core_completion\privacy\provider::delete_completion($user, null, $this->cm->id);
|
||||
$activitycompletion = \core_completion\privacy\provider::get_activity_completion_info($user, $this->course,
|
||||
$this->cm);
|
||||
$this->assertEquals(0, $activitycompletion->completionstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting course completion information.
|
||||
*/
|
||||
public function test_delete_completion_course() {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->create_course_completion();
|
||||
$this->complete_course($user);
|
||||
\core_completion\privacy\provider::delete_completion(null, $this->course->id);
|
||||
$coursecompletion = \core_completion\privacy\provider::get_course_completion_info($user, $this->course);
|
||||
foreach ($coursecompletion['criteria'] as $criterion) {
|
||||
$this->assertEquals('No', $criterion['completed']);
|
||||
}
|
||||
}
|
||||
}
|
@ -181,6 +181,22 @@ $string['overallaggregation_all'] = 'Course is complete when ALL conditions are
|
||||
$string['overallaggregation_any'] = 'Course is complete when ANY of the conditions are met';
|
||||
$string['pending'] = 'Pending';
|
||||
$string['periodpostenrolment'] = 'Period post enrolment';
|
||||
$string['privacy:metadata:completionstate'] = 'If the course module has been completed.';
|
||||
$string['privacy:metadata:course'] = 'A course identifier.';
|
||||
$string['privacy:metadata:coursecompletedsummary'] = 'Holds information about students that have completed criteria in a course.';
|
||||
$string['privacy:metadata:coursemoduleid'] = 'The identifier to the course module.';
|
||||
$string['privacy:metadata:coursemodulesummary'] = 'Stores the course module completion data for a user.';
|
||||
$string['privacy:metadata:coursesummary'] = 'Stores the course completion data for a user.';
|
||||
$string['privacy:metadata:gradefinal'] = 'Final grade recieved for the course completion.';
|
||||
$string['privacy:metadata:overrideby'] = 'The user ID of the person who overrode the module completion.';
|
||||
$string['privacy:metadata:reaggregate'] = 'If the course completion was reaggregated.';
|
||||
$string['privacy:metadata:timecompleted'] = 'The time that the course was completed.';
|
||||
$string['privacy:metadata:timeenrolled'] = 'The time that the user was enrolled into the course.';
|
||||
$string['privacy:metadata:timemodified'] = 'The time the course module completion was modified.';
|
||||
$string['privacy:metadata:timestarted'] = 'The time the course was started.';
|
||||
$string['privacy:metadata:viewed'] = 'If the course module was viewed.';
|
||||
$string['privacy:metadata:userid'] = 'Course and activity completion is stored against this user.';
|
||||
$string['privacy:metadata:unenroled'] = 'If the user has been unenroled from the course.';
|
||||
$string['progress'] = 'Student progress';
|
||||
$string['progress-title'] = '{$a->user}, {$a->activity}: {$a->state} {$a->date}';
|
||||
$string['progresstotal'] = 'Progress: {$a->complete} / {$a->total}';
|
||||
|
Loading…
x
Reference in New Issue
Block a user