}
public function save_changes() {
- global $DB, $CFG;
+ global $DB;
if (!$this->roleid) {
// Creating role.
// the UI. It would be better to do this only when we know that fields affected are
// updated. But thats getting into the weeds of the coursecat cache and role edits
// should not be that frequent, so here is the ugly brutal approach.
- require_once($CFG->libdir . '/coursecatlib.php');
- coursecat::role_assignment_changed($this->role->id, context_system::instance());
+ core_course_category::role_assignment_changed($this->role->id, context_system::instance());
}
// Assignable contexts.
Scenario: Enable registration of multiple accounts with the same email address
Given the following config values are set as admin:
| allowaccountssameemail | 1 |
- When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
+ When I navigate to "Users > Accounts > Add a new user" in site administration
And I set the following fields to these values:
| Username | testmultiemailuser1 |
| Choose an authentication method | Manual accounts |
Scenario: Disable registration of multiple accounts with the same email address
Given the following config values are set as admin:
| allowaccountssameemail | 0 |
- When I navigate to "Add a new user" node in "Site administration>Users>Accounts"
+ When I navigate to "Users > Accounts > Add a new user" in site administration
And I set the following fields to these values:
| Username | testmultiemailuser1 |
| Choose an authentication method | Manual accounts |
And the following config values are set as admin:
| enableavailability | 1 |
And I am on homepage
- And I navigate to "Manage restrictions" node in "Site administration > Plugins > Availability restrictions"
+ And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration
# Having clicked on it, I should also see the list of plugins.
And I should see "Restriction by date"
| Course 1 | C1 | topics |
And I log in as "admin"
And I am on site homepage
- When I navigate to "Manage restrictions" node in "Site administration > Plugins > Availability restrictions"
+ When I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration
# Check the icon is there (it should be a Hide icon, meaning is currently visible).
Then "Hide" "icon" should exist in the "Restriction by date" "table_row"
| student1 | CHSB |
| student1 | CHC |
When I log in as "admin"
- And I navigate to "Cohorts" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > Cohorts" in site administration
Then the following should exist in the "cohorts" table:
| Name | Cohort size |
| System cohort A | 1 |
| wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 |
And I log in as "admin"
And I am on "Course 1" course homepage
- And I navigate to "Reset" node in "Course administration"
+ And I navigate to "Reset" in current page administration
# Select (multi-select) - Checking "the select box should contain".
And I expand all fieldsets
And the "Unenrol users" select box should contain "No roles"
And the field "two" matches value ""
# Check if field xpath set/match works.
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the field with xpath "//input[@id='id_idnumber']" to "Course id number"
And the field with xpath "//input[@name='idnumber']" matches value "Course id number"
And the field with xpath "//input[@name='idnumber']" does not match value ""
And I press "Save and display"
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And the field "Course ID number" matches value "Course id number"
Scenario: with JS disabled all form fields getters and setters works as expected
Background:
Given I am on homepage
And I log in as "admin"
- And I navigate to "Acceptance testing" node in "Site administration > Development"
+ And I navigate to "Development > Acceptance testing" in site administration
@javascript
Scenario: Accessing the list
var stringkeys = [
{
key: 'deletecategory',
- component: 'tool_dataprivacy',
- param: categoryname
+ component: 'tool_dataprivacy'
},
{
key: 'deletecategorytext',
component: 'tool_dataprivacy',
param: categoryname
+ },
+ {
+ key: 'delete'
}
];
Str.get_strings(stringkeys).then(function(langStrings) {
var title = langStrings[0];
var confirmMessage = langStrings[1];
+ var buttonText = langStrings[2];
return ModalFactory.create({
title: title,
body: confirmMessage,
type: ModalFactory.types.SAVE_CANCEL
}).then(function(modal) {
- modal.setSaveButtonText(title);
+ modal.setSaveButtonText(buttonText);
// Handle save event.
modal.getRoot().on(ModalEvents.save, function() {
var stringkeys = [
{
key: 'deletepurpose',
- component: 'tool_dataprivacy',
- param: purposename
+ component: 'tool_dataprivacy'
},
{
key: 'deletepurposetext',
component: 'tool_dataprivacy',
param: purposename
+ },
+ {
+ key: 'delete'
}
];
Str.get_strings(stringkeys).then(function(langStrings) {
var title = langStrings[0];
var confirmMessage = langStrings[1];
+ var buttonText = langStrings[2];
return ModalFactory.create({
title: title,
body: confirmMessage,
type: ModalFactory.types.SAVE_CANCEL
}).then(function(modal) {
- modal.setSaveButtonText(title);
+ modal.setSaveButtonText(buttonText);
// Handle save event.
modal.getRoot().on(ModalEvents.save, function() {
/** The request is now being processed. */
const DATAREQUEST_STATUS_PROCESSING = 4;
- /** Data request completed. */
+ /** Information/other request completed. */
const DATAREQUEST_STATUS_COMPLETE = 5;
/** Data request cancelled by the user. */
/** Data request rejected by the DPO. */
const DATAREQUEST_STATUS_REJECTED = 7;
+ /** Data request download ready. */
+ const DATAREQUEST_STATUS_DOWNLOAD_READY = 8;
+
+ /** Data request expired. */
+ const DATAREQUEST_STATUS_EXPIRED = 9;
+
+ /** Data delete request completed, account is removed. */
+ const DATAREQUEST_STATUS_DELETED = 10;
+
/**
* Determines whether the user can contact the site's Data Protection Officer via Moodle.
*
}
}
+ // If any are due to expire, expire them and re-fetch updated data.
+ if (empty($statuses)
+ || in_array(self::DATAREQUEST_STATUS_DOWNLOAD_READY, $statuses)
+ || in_array(self::DATAREQUEST_STATUS_EXPIRED, $statuses)) {
+ $expiredrequests = data_request::get_expired_requests($userid);
+
+ if (!empty($expiredrequests)) {
+ data_request::expire($expiredrequests);
+ $results = self::get_data_requests($userid, $statuses, $types, $sort, $offset, $limit);
+ }
+ }
+
return $results;
}
self::DATAREQUEST_STATUS_COMPLETE,
self::DATAREQUEST_STATUS_CANCELLED,
self::DATAREQUEST_STATUS_REJECTED,
+ self::DATAREQUEST_STATUS_DOWNLOAD_READY,
+ self::DATAREQUEST_STATUS_EXPIRED,
+ self::DATAREQUEST_STATUS_DELETED,
];
list($insql, $inparams) = $DB->get_in_or_equal($nonpendingstatuses, SQL_PARAMS_NAMED);
$select = 'type = :type AND userid = :userid AND status NOT ' . $insql;
self::DATAREQUEST_STATUS_COMPLETE,
self::DATAREQUEST_STATUS_CANCELLED,
self::DATAREQUEST_STATUS_REJECTED,
+ self::DATAREQUEST_STATUS_DOWNLOAD_READY,
+ self::DATAREQUEST_STATUS_EXPIRED,
+ self::DATAREQUEST_STATUS_DELETED,
];
return !in_array($status, $finalstatuses);
defined('MOODLE_INTERNAL') || die();
-require_once($CFG->libdir . '/coursecatlib.php');
-
/**
* Data registry business logic methods. Mostly internal stuff.
*
/**
* Returns all site categories that are visible to the current user.
*
- * @return \coursecat[]
+ * @return \core_course_category[]
*/
public static function get_site_categories() {
global $DB;
- if (method_exists('\coursecat', 'get_all')) {
- $categories = \coursecat::get_all(['returnhidden' => true]);
+ if (method_exists('\core_course_category', 'get_all')) {
+ $categories = \core_course_category::get_all(['returnhidden' => true]);
} else {
// Fallback (to be removed once this gets integrated into master).
$ids = $DB->get_fieldset_select('course_categories', 'id', '');
- $categories = \coursecat::get_many($ids);
+ $categories = \core_course_category::get_many($ids);
}
foreach ($categories as $key => $category) {
api::DATAREQUEST_STATUS_COMPLETE,
api::DATAREQUEST_STATUS_CANCELLED,
api::DATAREQUEST_STATUS_REJECTED,
+ api::DATAREQUEST_STATUS_DOWNLOAD_READY,
+ api::DATAREQUEST_STATUS_EXPIRED,
+ api::DATAREQUEST_STATUS_DELETED,
],
'type' => PARAM_INT
],
],
];
}
+
+ /**
+ * Determines whether a completed data export request has expired.
+ * The response will be valid regardless of the expiry scheduled task having run.
+ *
+ * @param data_request $request the data request object whose expiry will be checked.
+ * @return bool true if the request has expired.
+ */
+ public static function is_expired(data_request $request) {
+ $result = false;
+
+ // Only export requests expire.
+ if ($request->get('type') == api::DATAREQUEST_TYPE_EXPORT) {
+ switch ($request->get('status')) {
+ // Expired requests are obviously expired.
+ case api::DATAREQUEST_STATUS_EXPIRED:
+ $result = true;
+ break;
+ // Complete requests are expired if the expiry time has elapsed.
+ case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+ $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
+ if ($expiryseconds > 0 && time() >= ($request->get('timemodified') + $expiryseconds)) {
+ $result = true;
+ }
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+
+
+ /**
+ * Fetch completed data requests which are due to expire.
+ *
+ * @param int $userid Optional user ID to filter by.
+ *
+ * @return array Details of completed requests which are due to expire.
+ */
+ public static function get_expired_requests($userid = 0) {
+ global $DB;
+
+ $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
+ $expirytime = strtotime("-{$expiryseconds} second");
+ $table = self::TABLE;
+ $sqlwhere = 'type = :export_type AND status = :completestatus AND timemodified <= :expirytime';
+ $params = array(
+ 'export_type' => api::DATAREQUEST_TYPE_EXPORT,
+ 'completestatus' => api::DATAREQUEST_STATUS_DOWNLOAD_READY,
+ 'expirytime' => $expirytime,
+ );
+ $sort = 'id';
+ $fields = 'id, userid';
+
+ // Filter by user ID if specified.
+ if ($userid > 0) {
+ $sqlwhere .= ' AND (userid = :userid OR requestedby = :requestedby)';
+ $params['userid'] = $userid;
+ $params['requestedby'] = $userid;
+ }
+
+ return $DB->get_records_select_menu($table, $sqlwhere, $params, $sort, $fields, 0, 2000);
+ }
+
+ /**
+ * Expire a given set of data requests.
+ * Update request status and delete the files.
+ *
+ * @param array $expiredrequests [requestid => userid]
+ *
+ * @return void
+ */
+ public static function expire($expiredrequests) {
+ global $DB;
+
+ $ids = array_keys($expiredrequests);
+
+ if (count($ids) > 0) {
+ list($insql, $inparams) = $DB->get_in_or_equal($ids);
+ $initialparams = array(api::DATAREQUEST_STATUS_EXPIRED, time());
+ $params = array_merge($initialparams, $inparams);
+
+ $update = "UPDATE {" . self::TABLE . "}
+ SET status = ?, timemodified = ?
+ WHERE id $insql";
+
+ if ($DB->execute($update, $params)) {
+ $fs = get_file_storage();
+
+ foreach ($expiredrequests as $id => $userid) {
+ $usercontext = \context_user::instance($userid);
+ $fs->delete_area_files($usercontext->id, 'tool_dataprivacy', 'export', $id);
+ }
+ }
+ }
+ }
}
switch ($this->persistent->get('status')) {
case api::DATAREQUEST_STATUS_PENDING:
- $values['statuslabelclass'] = 'label-default';
+ $values['statuslabelclass'] = 'label-info';
// Request can be manually completed for general enquiry requests.
$values['canmarkcomplete'] = $requesttype == api::DATAREQUEST_TYPE_OTHERS;
break;
$values['statuslabelclass'] = 'label-info';
break;
case api::DATAREQUEST_STATUS_COMPLETE:
+ case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+ case api::DATAREQUEST_STATUS_DELETED:
$values['statuslabelclass'] = 'label-success';
break;
case api::DATAREQUEST_STATUS_CANCELLED:
case api::DATAREQUEST_STATUS_REJECTED:
$values['statuslabelclass'] = 'label-important';
break;
+ case api::DATAREQUEST_STATUS_EXPIRED:
+ $values['statuslabelclass'] = 'label-default';
+ break;
}
return $values;
if (!isset($statuses[$status])) {
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
}
+
return $statuses[$status];
}
api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_COMPLETE => get_string('statuscomplete', 'tool_dataprivacy'),
+ api::DATAREQUEST_STATUS_DOWNLOAD_READY => get_string('statusready', 'tool_dataprivacy'),
+ api::DATAREQUEST_STATUS_EXPIRED => get_string('statusexpired', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_CANCELLED => get_string('statuscancelled', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_REJECTED => get_string('statusrejected', 'tool_dataprivacy'),
+ api::DATAREQUEST_STATUS_DELETED => get_string('statusdeleted', 'tool_dataprivacy'),
];
}
use templatable;
use tool_dataprivacy\data_registry;
-require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
require_once($CFG->libdir . '/blocklib.php');
throw new \coding_exception('A course category context should be provided');
}
- $coursecat = \coursecat::get($catcontext->instanceid);
+ $coursecat = \core_course_category::get($catcontext->instanceid);
$courses = $coursecat->get_courses();
$branches = [];
/** @var bool Whether this table is being rendered for managing data requests. */
protected $manage = false;
- /** @var stdClass[] Array of data request persistents. */
+ /** @var \tool_dataprivacy\data_request[] Array of data request persistents. */
protected $datarequests = [];
/**
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
break;
- }
-
- if ($status == api::DATAREQUEST_STATUS_COMPLETE) {
- $userid = $data->foruser->id;
- $usercontext = \context_user::instance($userid, IGNORE_MISSING);
- if ($usercontext && api::can_download_data_request_for_user($userid, $data->requestedbyuser->id)) {
- $actions[] = api::get_download_link($usercontext, $requestid);
- }
+ case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+ $userid = $data->foruser->id;
+ $usercontext = \context_user::instance($userid, IGNORE_MISSING);
+ // If user has permission to view download link, show relevant action item.
+ if ($usercontext && api::can_download_data_request_for_user($userid, $data->requestedbyuser->id)) {
+ $actions[] = api::get_download_link($usercontext, $requestid);
+ }
+ break;
}
$actionsmenu = new action_menu($actions);
public function query_db($pagesize, $useinitialsbar = true) {
global $PAGE;
- // Count data requests from the given conditions.
- $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
- $this->pagesize($pagesize, $total);
+ // Set dummy page total until we fetch full result set.
+ $this->pagesize($pagesize, $pagesize + 1);
$sort = $this->get_sql_sort();
// Get data requests from the given conditions.
$datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types, $sort,
$this->get_page_start(), $this->get_page_size());
+
+ // Count data requests from the given conditions.
+ $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
+ $this->pagesize($pagesize, $total);
+
$this->rawdata = [];
$context = \context_system::instance();
$renderer = $PAGE->get_renderer('tool_dataprivacy');
+
foreach ($datarequests as $persistent) {
+ $this->datarequests[$persistent->get('id')] = $persistent;
$exporter = new data_request_exporter($persistent, ['context' => $context]);
$this->rawdata[] = $exporter->export($renderer);
}
$item->statuslabelclass = 'label-success';
$item->statuslabel = get_string('statuscomplete', 'tool_dataprivacy');
$cancancel = false;
- // Show download links only for export-type data requests.
- $candownload = $type == api::DATAREQUEST_TYPE_EXPORT;
+ break;
+ case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
+ $item->statuslabelclass = 'label-success';
+ $item->statuslabel = get_string('statusready', 'tool_dataprivacy');
+ $cancancel = false;
+ $candownload = true;
+
if ($usercontext) {
$candownload = api::can_download_data_request_for_user(
$request->get('userid'), $request->get('requestedby'));
}
break;
+ case api::DATAREQUEST_STATUS_DELETED:
+ $item->statuslabelclass = 'label-success';
+ $item->statuslabel = get_string('statusdeleted', 'tool_dataprivacy');
+ $cancancel = false;
+ break;
+ case api::DATAREQUEST_STATUS_EXPIRED:
+ $item->statuslabelclass = 'label-default';
+ $item->statuslabel = get_string('statusexpired', 'tool_dataprivacy');
+ $item->statuslabeltitle = get_string('downloadexpireduser', 'tool_dataprivacy');
+ $cancancel = false;
+ break;
case api::DATAREQUEST_STATUS_CANCELLED:
case api::DATAREQUEST_STATUS_REJECTED:
$cancancel = false;
--- /dev/null
+<?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/>.
+
+/**
+ * Scheduled task to delete files and update statuses of expired data requests.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_dataprivacy\task;
+
+use coding_exception;
+use core\task\scheduled_task;
+use tool_dataprivacy\api;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
+
+/**
+ * Scheduled task to delete files and update request statuses once they expire.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class delete_expired_requests extends scheduled_task {
+
+ /**
+ * Returns the task name.
+ *
+ * @return string
+ */
+ public function get_name() {
+ return get_string('deleteexpireddatarequeststask', 'tool_dataprivacy');
+ }
+
+ /**
+ * Run the task to delete expired data request files and update request statuses.
+ *
+ */
+ public function execute() {
+ $expiredrequests = \tool_dataprivacy\data_request::get_expired_requests();
+ $deletecount = count($expiredrequests);
+
+ if ($deletecount > 0) {
+ \tool_dataprivacy\data_request::expire($expiredrequests);
+
+ mtrace($deletecount . ' expired completed data requests have been deleted');
+ }
+ }
+}
// Update the status of this request as pre-processing.
mtrace('Processing request...');
api::update_request_status($requestid, api::DATAREQUEST_STATUS_PROCESSING);
+ $completestatus = api::DATAREQUEST_STATUS_COMPLETE;
if ($request->type == api::DATAREQUEST_TYPE_EXPORT) {
// Get the collection of approved_contextlist objects needed for core_privacy data export.
$filerecord->author = fullname($foruser);
// Save somewhere.
$thing = $fs->create_file_from_pathname($filerecord, $exportedcontent);
-
+ $completestatus = api::DATAREQUEST_STATUS_DOWNLOAD_READY;
} else if ($request->type == api::DATAREQUEST_TYPE_DELETE) {
// Get the collection of approved_contextlist objects needed for core_privacy data deletion.
$approvedclcollection = api::get_approved_contextlist_collection_for_request($requestpersistent);
$manager->set_observer(new \tool_dataprivacy\manager_observer());
$manager->delete_data_for_user($approvedclcollection);
+ $completestatus = api::DATAREQUEST_STATUS_DELETED;
}
// When the preparation of the metadata finishes, update the request status to awaiting approval.
- api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE);
+ api::update_request_status($requestid, $completestatus);
mtrace('The processing of the user data request has been completed...');
// Create message to notify the user regarding the processing results.
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="admin/tool/dataprivacy/db" VERSION="20180313" COMMENT="XMLDB file for Moodle tool/dataprivacy"
+<XMLDB PATH="admin/tool/dataprivacy/db" VERSION="20180821" COMMENT="XMLDB file for Moodle tool/dataprivacy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<FIELD NAME="commentsformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The user ID the request is being made for"/>
<FIELD NAME="requestedby" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The user ID of the one making the request"/>
- <FIELD NAME="status" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The current status of the data request"/>
+ <FIELD NAME="status" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The current status of the data request"/>
<FIELD NAME="dpo" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="The user ID of the Data Protection Officer who is reviewing th request"/>
<FIELD NAME="dpocomment" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="DPO's comments (e.g. reason for rejecting the request, etc.)"/>
<FIELD NAME="dpocommentformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
- <FIELD NAME="status" TYPE="int" LENGTH="2" DEFAULT="0" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="status" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
</KEYS>
</TABLE>
</TABLES>
-</XMLDB>
+</XMLDB>
\ No newline at end of file
'day' => '*',
'dayofweek' => '*',
'month' => '*'
+ ), array(
+ 'classname' => 'tool_dataprivacy\task\delete_expired_requests',
+ 'blocking' => 0,
+ 'minute' => 'R',
+ 'hour' => 'R',
+ 'day' => '*',
+ 'dayofweek' => '*',
+ 'month' => '*'
),
);
upgrade_plugin_savepoint(true, 2018051405, 'tool', 'dataprivacy');
}
+ if ($oldversion < 2018051406) {
+ // Update completed delete requests to new delete status.
+ $query = "UPDATE {tool_dataprivacy_request}
+ SET status = :setstatus
+ WHERE type = :type
+ AND status = :wherestatus";
+ $params = array(
+ 'setstatus' => 10, // Request deleted.
+ 'type' => 2, // Delete type.
+ 'wherestatus' => 5, // Request completed.
+ );
+
+ $DB->execute($query, $params);
+
+ // Update completed data export requests to new download ready status.
+ $params = array(
+ 'setstatus' => 8, // Request download ready.
+ 'type' => 1, // export type.
+ 'wherestatus' => 5, // Request completed.
+ );
+
+ $DB->execute($query, $params);
+
+ upgrade_plugin_savepoint(true, 2018051406, 'tool', 'dataprivacy');
+ }
+
+ if ($oldversion < 2018082100) {
+
+ // Changing precision of field status on table tool_dataprivacy_request to (2).
+ $table = new xmldb_table('tool_dataprivacy_request');
+ $field = new xmldb_field('status', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'requestedby');
+
+ // Launch change of precision for field status.
+ $dbman->change_field_precision($table, $field);
+
+ // Dataprivacy savepoint reached.
+ upgrade_plugin_savepoint(true, 2018082100, 'tool', 'dataprivacy');
+ }
+
return true;
}
$string['daterequested'] = 'Date requested';
$string['daterequesteddetail'] = 'Date requested:';
$string['defaultsinfo'] = 'Default categories and purposes are applied to all newly created instances.';
-$string['deletecategory'] = 'Delete "{$a}" category';
-$string['deletecategorytext'] = 'Are you sure you want to delete "{$a}" category?';
+$string['deletecategory'] = 'Delete category';
+$string['deletecategorytext'] = 'Are you sure you want to delete the category \'{$a}\'?';
$string['deleteexpiredcontextstask'] = 'Delete expired contexts';
-$string['deletepurpose'] = 'Delete "{$a}" purpose';
-$string['deletepurposetext'] = 'Are you sure you want to delete "{$a}" purpose?';
+$string['deleteexpireddatarequeststask'] = 'Delete files from completed data requests that have expired';
+$string['deletepurpose'] = 'Delete purpose';
+$string['deletepurposetext'] = 'Are you sure you want to delete the purpose \'{$a}\'?';
$string['defaultssaved'] = 'Defaults saved';
$string['deny'] = 'Deny';
$string['denyrequest'] = 'Deny request';
$string['deprecated'] = 'Deprecated';
$string['deprecatedexplanation'] = 'This plugin is using an old version of one of the privacy interfaces and should be updated.';
$string['download'] = 'Download';
+$string['downloadexpireduser'] = 'Download has expired. Submit a new request if you wish to export your personal data.';
$string['dporolemapping'] = 'Privacy officer role mapping';
$string['dporolemapping_desc'] = 'The privacy officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a privacy officer role mapping option.';
$string['editcategories'] = 'Edit categories';
$string['privacy:metadata:request:requestedby'] = 'The ID of the user making the request, if made on behalf of another user.';
$string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s privacy officer regarding the request.';
$string['privacy:metadata:request:timecreated'] = 'The timestamp indicating when the request was made by the user.';
+$string['privacyrequestexpiry'] = 'Data request expiry';
+$string['privacyrequestexpiry_desc'] = 'The amount of time that approved data requests will be available for download before expiring. 0 means no time limit.';
$string['protected'] = 'Protected';
$string['protectedlabel'] = 'The retention of this data has a higher legal precedent over a user\'s request to be forgotten. This data will only be deleted after the retention period has expired.';
$string['purpose'] = 'Purpose';
$string['statusawaitingapproval'] = 'Awaiting approval';
$string['statuscancelled'] = 'Cancelled';
$string['statuscomplete'] = 'Complete';
+$string['statusready'] = 'Download ready';
+$string['statusdeleted'] = 'Deleted';
$string['statusdetail'] = 'Status:';
+$string['statusexpired'] = 'Expired';
$string['statuspreprocessing'] = 'Pre-processing';
$string['statusprocessing'] = 'Processing';
$string['statuspending'] = 'Pending';
return false;
}
+ // Make the file unavailable if it has expired.
+ if (\tool_dataprivacy\data_request::is_expired($datarequest)) {
+ send_file_not_found();
+ }
+
// All good. Serve the exported data.
$fs = get_file_storage();
$relativepath = implode('/', $args);
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
-$requests = tool_dataprivacy\api::get_data_requests($USER->id);
+$requests = tool_dataprivacy\api::get_data_requests($USER->id, [], [], 'timecreated DESC');
$requestlist = new tool_dataprivacy\output\my_data_requests_page($requests);
$requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
echo $requestlistoutput->render($requestlist);
new lang_string('contactdataprotectionofficer_desc', 'tool_dataprivacy'), 0)
);
+ // Set days approved data requests will be accessible. 1 week default.
+ $privacysettings->add(new admin_setting_configduration('tool_dataprivacy/privacyrequestexpiry',
+ new lang_string('privacyrequestexpiry', 'tool_dataprivacy'),
+ new lang_string('privacyrequestexpiry_desc', 'tool_dataprivacy'),
+ WEEKSECS, 1));
+
// Fetch roles that are assignable.
$assignableroles = get_assignable_roles(context_system::instance());
<div data-region="categories" class="m-t-3 m-b-1">
<h3>{{#str}}categories, tool_dataprivacy{{/str}}</h3>
<div class="m-y-1">
- <button class="btn btn-secondary" data-add-element="category">
+ <button class="btn btn-secondary" data-add-element="category" title="{{#str}}addcategory, tool_dataprivacy{{/str}}">
{{#pix}}t/add, moodle, {{#str}}addcategory, tool_dataprivacy{{/str}}{{/pix}}
</button>
</div>
"typename" : "Data deletion",
"comments": "Please delete all of my son's personal data.",
"statuslabelclass": "label-success",
- "statuslabel": "Complete",
+ "statuslabel": "Deleted",
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"fullname": "Martha Smith",
"profileurl": "#"
}
+ },
+ {
+ "id": 6,
+ "typename" : "Data export",
+ "comments": "Please let me download my data",
+ "statuslabelclass": "label",
+ "statuslabel": "Expired",
+ "statuslabeltitle": "Download has expired. Submit a new request if you wish to export your personal data.",
+ "timecreated" : 1517902087,
+ "requestedbyuser" : {
+ "fullname": "Martha Smith",
+ "profileurl": "#"
+ }
}
]
}
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
<td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
<td>
- <span class="label {{statuslabelclass}}">{{statuslabel}}</span>
+ <span class="label {{statuslabelclass}}" title="{{statuslabeltitle}}">{{statuslabel}}</span>
</td>
<td>{{comments}}</td>
<td>
<div data-region="purposes" class="m-t-3 m-b-1">
<h3>{{#str}}purposes, tool_dataprivacy{{/str}}</h3>
<div class="m-y-1">
- <button class="btn btn-secondary" data-add-element="purpose">
+ <button class="btn btn-secondary" data-add-element="purpose" title="{{#str}}addpurpose, tool_dataprivacy{{/str}}">
{{#pix}}t/add, moodle, {{#str}}addpurpose, tool_dataprivacy{{/str}}{{/pix}}
</button>
</div>
$requestid = $datarequest->get('id');
// Update with a valid status.
- $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE);
+ $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_DOWNLOAD_READY);
$this->assertTrue($result);
// Fetch the request record again.
$datarequest = new data_request($requestid);
- $this->assertEquals(api::DATAREQUEST_STATUS_COMPLETE, $datarequest->get('status'));
+ $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $datarequest->get('status'));
// Update with an invalid status.
$this->expectException(invalid_persistent_exception::class);
* @return array
*/
public function get_data_requests_provider() {
- $completeonly = [api::DATAREQUEST_STATUS_COMPLETE];
- $completeandcancelled = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_CANCELLED];
+ $completeonly = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_DOWNLOAD_READY, api::DATAREQUEST_STATUS_DELETED];
+ $completeandcancelled = array_merge($completeonly, [api::DATAREQUEST_STATUS_CANCELLED]);
return [
// Own data requests.
[api::DATAREQUEST_STATUS_COMPLETE, false],
[api::DATAREQUEST_STATUS_CANCELLED, false],
[api::DATAREQUEST_STATUS_REJECTED, false],
+ [api::DATAREQUEST_STATUS_DOWNLOAD_READY, false],
+ [api::DATAREQUEST_STATUS_EXPIRED, false],
+ [api::DATAREQUEST_STATUS_DELETED, false],
];
}
--- /dev/null
+@tool @tool_dataprivacy
+Feature: Data delete from the privacy API
+ In order to delete data for users and meet legal requirements
+ As an admin, user, or parent
+ I need to be able to request a user and their data data be deleted
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname |
+ | victim | Victim User | 1 |
+ | parent | Long-suffering | Parent |
+ And the following "roles" exist:
+ | shortname | name | archetype |
+ | tired | Tired | |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | tool/dataprivacy:makedatarequestsforchildren | Allow | tired | System | |
+ And the following "role assigns" exist:
+ | user | role | contextlevel | reference |
+ | parent | tired | User | victim |
+ And the following config values are set as admin:
+ | contactdataprotectionofficer | 1 | tool_dataprivacy |
+
+ @javascript
+ Scenario: As admin, delete a user and their data
+ Given I log in as "victim"
+ And I should see "Victim User 1"
+ And I log out
+
+ And I log in as "admin"
+ And I navigate to "Users > Privacy and policies > Data requests" in site administration
+ And I follow "New request"
+ And I set the field "Requesting for" to "Victim User 1"
+ And I set the field "Type" to "Delete all of my personal data"
+ And I press "Save changes"
+ Then I should see "Victim User 1"
+ And I should see "Pending" in the "Victim User 1" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+ And I follow "Actions"
+ And I follow "Approve request"
+ And I press "Approve request"
+ And I should see "Approved" in the "Victim User 1" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "Deleted" in the "Victim User 1" "table_row"
+
+ And I log out
+ And I log in as "victim"
+ And I should see "Invalid login"
+
+ @javascript
+ Scenario: As a student, request deletion of account and data
+ Given I log in as "victim"
+ And I follow "Profile" in the user menu
+ And I follow "Data requests"
+ And I follow "New request"
+ And I set the field "Type" to "Delete all of my personal data"
+ And I press "Save changes"
+ Then I should see "Delete all of my personal data"
+ And I should see "Pending" in the "Delete all of my personal data" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "Awaiting approval" in the "Delete all of my personal data" "table_row"
+
+ And I log out
+ And I log in as "admin"
+ And I navigate to "Users > Privacy and policies > Data requests" in site administration
+ And I follow "Actions"
+ And I follow "Approve request"
+ And I press "Approve request"
+
+ And I log out
+ And I log in as "victim"
+ And I follow "Profile" in the user menu
+ And I follow "Data requests"
+ And I should see "Approved" in the "Delete all of my personal data" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "Your session has timed out"
+ And I log in as "victim"
+ And I should see "Invalid login"
+
+ And I log in as "admin"
+ And I am on site homepage
+ And I navigate to "Users > Privacy and policies > Data requests" in site administration
+ And I should see "Deleted"
+
+ @javascript
+ Scenario: As a parent, request account and data deletion for my child
+ Given I log in as "parent"
+ And I follow "Profile" in the user menu
+ And I follow "Data requests"
+ And I follow "New request"
+ And I set the field "Requesting for" to "Victim User 1"
+ And I set the field "Type" to "Delete all of my personal data"
+ And I press "Save changes"
+ Then I should see "Victim User 1"
+ And I should see "Pending" in the "Victim User 1" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "Awaiting approval" in the "Victim User 1" "table_row"
+
+ And I log out
+ And I log in as "admin"
+ And I navigate to "Users > Privacy and policies > Data requests" in site administration
+ And I follow "Actions"
+ And I follow "Approve request"
+ And I press "Approve request"
+
+ And I log out
+ And I log in as "parent"
+ And I follow "Profile" in the user menu
+ And I follow "Data requests"
+ And I should see "Approved" in the "Victim User 1" "table_row"
+ And I run all adhoc tasks
+ And I reload the page
+ And I should see "You don't have any personal data requests"
| user | role | contextlevel | reference |
| parent | tired | User | victim |
And the following config values are set as admin:
- | contactdataprotectionofficer | 1 | tool_dataprivacy |
+ | contactdataprotectionofficer | 1 | tool_dataprivacy |
+ | privacyrequestexpiry | 55 | tool_dataprivacy |
@javascript
- Scenario: As admin, export data for a user and download it
+ Scenario: As admin, export data for a user and download it, unless it has expired
Given I log in as "admin"
And I navigate to "Users > Privacy and policies > Data requests" in site administration
And I follow "New request"
And I should see "Approved" in the "Victim User 1" "table_row"
And I run all adhoc tasks
And I reload the page
- And I should see "Complete" in the "Victim User 1" "table_row"
+ And I should see "Download ready" in the "Victim User 1" "table_row"
And I follow "Actions"
And following "Download" should download between "1" and "100000" bytes
+ And the following config values are set as admin:
+ | privacyrequestexpiry | 1 | tool_dataprivacy |
+ And I wait "1" seconds
+ And I navigate to "Users > Privacy and policies > Data requests" in site administration
+ And I should see "Expired" in the "Victim User 1" "table_row"
+ And I follow "Actions"
+ And I should not see "Download"
@javascript
- Scenario: As a student, request data export and then download it when approved
+ Scenario: As a student, request data export and then download it when approved, unless it has expired
Given I log in as "victim"
And I follow "Profile" in the user menu
And I follow "Data requests"
And I should see "Approved" in the "Export all of my personal data" "table_row"
And I run all adhoc tasks
And I reload the page
- And I should see "Complete" in the "Export all of my personal data" "table_row"
+ And I should see "Download ready" in the "Export all of my personal data" "table_row"
And I follow "Actions"
And following "Download" should download between "1" and "100000" bytes
+ And the following config values are set as admin:
+ | privacyrequestexpiry | 1 | tool_dataprivacy |
+ And I wait "1" seconds
+ And I reload the page
+
+ And I should see "Expired" in the "Export all of my personal data" "table_row"
+ And I should not see "Actions"
+
@javascript
Scenario: As a parent, request data export for my child because I don't trust the little blighter
Given I log in as "parent"
And I should see "Approved" in the "Victim User 1" "table_row"
And I run all adhoc tasks
And I reload the page
- And I should see "Complete" in the "Victim User 1" "table_row"
+ And I should see "Download ready" in the "Victim User 1" "table_row"
And I follow "Actions"
And following "Download" should download between "1" and "100000" bytes
+
+ And the following config values are set as admin:
+ | privacyrequestexpiry | 1 | tool_dataprivacy |
+ And I wait "1" seconds
+ And I reload the page
+
+ And I should see "Expired" in the "Victim User 1" "table_row"
+ And I should not see "Actions"
--- /dev/null
+@tool @tool_dataprivacy @javascript
+Feature: Manage data categories
+ As the privacy officer
+ In order to manage the data registry
+ I need to be able to manage the data categories for the data registry
+
+ Background:
+ Given I log in as "admin"
+ And I navigate to "Users > Privacy and policies > Data registry" in site administration
+ And I click on "Edit" "link"
+ And I choose "Categories" in the open action menu
+ And I press "Add category"
+ And I set the field "Name" to "Category 1"
+ And I set the field "Description" to "Category 1 description"
+ When I press "Save"
+ Then I should see "Category 1" in the "List of data categories" "table"
+ And I should see "Category 1 description" in the "Category 1" "table_row"
+
+ Scenario: Update a data category
+ Given I click on "Actions" "link" in the "Category 1" "table_row"
+ And I choose "Edit" in the open action menu
+ And I set the field "Name" to "Category 1 edited"
+ And I set the field "Description" to "Category 1 description edited"
+ When I press "Save changes"
+ Then I should see "Category 1 edited" in the "List of data categories" "table"
+ And I should see "Category 1 description edited" in the "List of data categories" "table"
+
+ Scenario: Delete a data category
+ Given I click on "Actions" "link" in the "Category 1" "table_row"
+ And I choose "Delete" in the open action menu
+ And I should see "Delete category"
+ And I should see "Are you sure you want to delete the category 'Category 1'?"
+ When I press "Delete"
+ Then I should not see "Category 1" in the "List of data categories" "table"
--- /dev/null
+@tool @tool_dataprivacy @javascript
+Feature: Manage data storage purposes
+ As the privacy officer
+ In order to manage the data registry
+ I need to be able to manage the data storage purposes for the data registry
+
+ Background:
+ Given I log in as "admin"
+ And I navigate to "Users > Privacy and policies > Data registry" in site administration
+ And I click on "Edit" "link"
+ And I choose "Purposes" in the open action menu
+ And I press "Add purpose"
+ And I set the field "Name" to "Purpose 1"
+ And I set the field "Description" to "Purpose 1 description"
+ And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
+ And I click on "Contract (GDPR Art. 6.1(b))" "list_item"
+ And I click on "Legal obligation (GDPR Art 6.1(c))" "list_item"
+ And I press key "27" in the field "Lawful bases"
+ And I click on ".form-autocomplete-downarrow" "css_element" in the "Sensitive personal data processing reasons" "form_row"
+ And I click on "Explicit consent (GDPR Art. 9.2(a))" "list_item"
+ And I press key "27" in the field "Sensitive personal data processing reasons"
+ And I set the field "retentionperiodnumber" to "2"
+ When I press "Save"
+ Then I should see "Purpose 1" in the "List of data purposes" "table"
+ And I should see "Contract (GDPR Art. 6.1(b))" in the "Purpose 1" "table_row"
+ And I should see "Legal obligation (GDPR Art 6.1(c))" in the "Purpose 1" "table_row"
+ And I should see "Explicit consent (GDPR Art. 9.2(a))" in the "Purpose 1" "table_row"
+ And I should see "2 years" in the "Purpose 1" "table_row"
+ And I should see "No" in the "Purpose 1" "table_row"
+
+ Scenario: Update a data storage purpose
+ Given I click on "Actions" "link" in the "Purpose 1" "table_row"
+ And I choose "Edit" in the open action menu
+ And I set the field "Name" to "Purpose 1 edited"
+ And I set the field "Description" to "Purpose 1 description edited"
+ And I click on "Legal obligation (GDPR Art 6.1(c))" "text" in the ".form-autocomplete-selection" "css_element"
+ And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
+ And I click on "Vital interests (GDPR Art. 6.1(d))" "list_item"
+ And I press key "27" in the field "Lawful bases"
+ And I set the field "retentionperiodnumber" to "3"
+ And I click on "protected" "checkbox"
+ When I press "Save changes"
+ Then I should see "Purpose 1 edited" in the "List of data purposes" "table"
+ And I should see "Purpose 1 description edited" in the "Purpose 1 edited" "table_row"
+ And I should see "Vital interests (GDPR Art. 6.1(d))" in the "Purpose 1 edited" "table_row"
+ And I should see "3 years" in the "Purpose 1 edited" "table_row"
+ But I should not see "Legal obligation (GDPR Art 6.1(c))" in the "Purpose 1 edited" "table_row"
+ And I should not see "No" in the "Purpose 1 edited" "table_row"
+
+ Scenario: Delete a data storage purpose
+ Given I click on "Actions" "link" in the "Purpose 1" "table_row"
+ And I choose "Delete" in the open action menu
+ And I should see "Delete purpose"
+ And I should see "Are you sure you want to delete the purpose 'Purpose 1'?"
+ When I press "Delete"
+ Then I should not see "Purpose 1" in the "List of data purposes" "table"
--- /dev/null
+<?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/>.
+
+/**
+ * Parent class for tests which need data privacy functionality.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Parent class for tests which need data privacy functionality.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class data_privacy_testcase extends advanced_testcase {
+
+ /**
+ * Assign one or more user IDs as site DPO
+ *
+ * @param stdClass|array $users User ID or array of user IDs to be assigned as site DPO
+ * @return void
+ */
+ protected function assign_site_dpo($users) {
+ global $DB;
+ $this->resetAfterTest();
+
+ if (!is_array($users)) {
+ $users = array($users);
+ }
+
+ $context = context_system::instance();
+
+ // Give the manager role with the capability to manage data requests.
+ $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
+ assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
+
+ // Assign user(s) as manager.
+ foreach ($users as $user) {
+ role_assign($managerroleid, $user->id, $context->id);
+ }
+
+ // Only map the manager role to the DPO role.
+ set_config('dporoles', $managerroleid, 'tool_dataprivacy');
+ }
+}
--- /dev/null
+<?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/>.
+
+/**
+ * Expired data requests tests.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_dataprivacy\api;
+use tool_dataprivacy\data_request;
+
+defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
+require_once('data_privacy_testcase.php');
+
+/**
+ * Expired data requests tests.
+ *
+ * @package tool_dataprivacy
+ * @copyright 2018 Michael Hawkins
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_dataprivacy_expired_data_requests_testcase extends data_privacy_testcase {
+
+ /**
+ * Test tearDown.
+ */
+ public function tearDown() {
+ \core_privacy\local\request\writer::reset();
+ }
+
+ /**
+ * Test finding and deleting expired data requests
+ */
+ public function test_data_request_expiry() {
+ global $DB;
+ $this->resetAfterTest();
+ \core_privacy\local\request\writer::setup_real_writer_instance();
+
+ // Set up test users.
+ $this->setAdminUser();
+ $studentuser = $this->getDataGenerator()->create_user();
+ $studentusercontext = context_user::instance($studentuser->id);
+
+ $dpouser = $this->getDataGenerator()->create_user();
+ $this->assign_site_dpo($dpouser);
+
+ // Set request expiry to 5 minutes.
+ set_config('privacyrequestexpiry', 300, 'tool_dataprivacy');
+
+ // Create and approve data request.
+ $this->setUser($studentuser->id);
+ $datarequest = api::create_data_request($studentuser->id, api::DATAREQUEST_TYPE_EXPORT);
+ $this->setAdminUser();
+ ob_start();
+ $this->runAdhocTasks('\tool_dataprivacy\task\initiate_data_request_task');
+ $requestid = $datarequest->get('id');
+ $this->setUser($dpouser->id);
+ api::approve_data_request($requestid);
+ $this->setAdminUser();
+ $this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task');
+ ob_end_clean();
+
+ // Confirm approved and exported.
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+ $fileconditions = array(
+ 'userid' => $studentuser->id,
+ 'component' => 'tool_dataprivacy',
+ 'filearea' => 'export',
+ 'itemid' => $requestid,
+ 'contextid' => $studentusercontext->id,
+ );
+ $this->assertEquals(2, $DB->count_records('files', $fileconditions));
+
+ // Run expiry deletion - should not affect test export.
+ $expiredrequests = data_request::get_expired_requests();
+ $this->assertEquals(0, count($expiredrequests));
+ data_request::expire($expiredrequests);
+
+ // Confirm test export was not deleted.
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+ $this->assertEquals(2, $DB->count_records('files', $fileconditions));
+
+ // Change request expiry to 1 second and allow it to elapse.
+ set_config('privacyrequestexpiry', 1, 'tool_dataprivacy');
+ $this->waitForSecond();
+
+ // Re-run expiry deletion, confirm the request expires and export is deleted.
+ $expiredrequests = data_request::get_expired_requests();
+ $this->assertEquals(1, count($expiredrequests));
+ data_request::expire($expiredrequests);
+
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_EXPIRED, $request->get('status'));
+ $this->assertEquals(0, $DB->count_records('files', $fileconditions));
+ }
+
+
+ /**
+ * Test for \tool_dataprivacy\data_request::is_expired()
+ * Tests for the expected request status to protect from false positive/negative,
+ * then tests is_expired() is returning the expected response.
+ */
+ public function test_is_expired() {
+ $this->resetAfterTest();
+ \core_privacy\local\request\writer::setup_real_writer_instance();
+
+ // Set request expiry beyond this test.
+ set_config('privacyrequestexpiry', 20, 'tool_dataprivacy');
+
+ $admin = get_admin();
+ $this->setAdminUser();
+
+ // Create export request.
+ $datarequest = api::create_data_request($admin->id, api::DATAREQUEST_TYPE_EXPORT);
+ $requestid = $datarequest->get('id');
+
+ // Approve the request.
+ ob_start();
+ $this->runAdhocTasks('\tool_dataprivacy\task\initiate_data_request_task');
+ $this->setAdminUser();
+ api::approve_data_request($requestid);
+ $this->runAdhocTasks('\tool_dataprivacy\task\process_data_request_task');
+ ob_end_clean();
+
+ // Test Download ready (not expired) response.
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+ $result = data_request::is_expired($request);
+ $this->assertFalse($result);
+
+ // Let request expiry time lapse.
+ set_config('privacyrequestexpiry', 1, 'tool_dataprivacy');
+ $this->waitForSecond();
+
+ // Test Download ready (time expired) response.
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_DOWNLOAD_READY, $request->get('status'));
+ $result = data_request::is_expired($request);
+ $this->assertTrue($result);
+
+ // Run the expiry task to properly expire the request.
+ ob_start();
+ $task = \core\task\manager::get_scheduled_task('\tool_dataprivacy\task\delete_expired_requests');
+ $task->execute();
+ ob_end_clean();
+
+ // Test Expired response status response.
+ $request = new data_request($requestid);
+ $this->assertEquals(api::DATAREQUEST_STATUS_EXPIRED, $request->get('status'));
+ $result = data_request::is_expired($request);
+ $this->assertTrue($result);
+ }
+}
*/
defined('MOODLE_INTERNAL') || die();
+require_once('data_privacy_testcase.php');
/**
* API tests.
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class tool_dataprivacy_manager_observer_testcase extends advanced_testcase {
-
- /**
- * Helper to set andn return two users who are DPOs.
- */
- protected function setup_site_dpos() {
- global $DB;
- $this->resetAfterTest();
-
- $generator = new testing_data_generator();
- $u1 = $this->getDataGenerator()->create_user();
- $u2 = $this->getDataGenerator()->create_user();
-
- $context = context_system::instance();
-
- // Give the manager role with the capability to manage data requests.
- $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
- assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
-
- // Assign both users as manager.
- role_assign($managerroleid, $u1->id, $context->id);
- role_assign($managerroleid, $u2->id, $context->id);
-
- // Only map the manager role to the DPO role.
- set_config('dporoles', $managerroleid, 'tool_dataprivacy');
-
- return \tool_dataprivacy\api::get_site_dpos();
- }
-
+class tool_dataprivacy_manager_observer_testcase extends data_privacy_testcase {
/**
* Ensure that when users are configured as DPO, they are sent an message upon failure.
*/
// Create another user who is not a DPO.
$this->getDataGenerator()->create_user();
- // Create the DPOs.
- $dpos = $this->setup_site_dpos();
+ // Create two DPOs.
+ $dpo1 = $this->getDataGenerator()->create_user();
+ $dpo2 = $this->getDataGenerator()->create_user();
+ $this->assign_site_dpo(array($dpo1, $dpo2));
+ $dpos = \tool_dataprivacy\api::get_site_dpos();
$observer = new \tool_dataprivacy\manager_observer();
defined('MOODLE_INTERNAL') || die;
-$plugin->version = 2018051405;
+$plugin->version = 2018082100;
$plugin->requires = 2018050800; // Moodle 3.5dev (Build 2018031600) and upwards.
$plugin->component = 'tool_dataprivacy';
Scenario: Add a new file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
And I press "Add"
# Try setting all the form fields, not just the optional ones.
And I set the following fields to these values:
Scenario: Update an existing file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
When I click on "Edit 7z" "link"
And I set the following fields to these values:
| Extension | doc |
Scenario: Change the text option (was buggy)
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
When I click on "Edit 7z" "link"
And I set the following fields to these values:
| Description type | Custom description specified in this form |
Scenario: Try to select a text option without entering a value.
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
When I click on "Edit dmg" "link"
And I set the field "Description type" to "Custom description"
And I press "Save changes"
Scenario: Delete an existing file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
When I click on "Delete 7z" "link"
Then I should see "Are you absolutely sure you want to remove .7z?"
And I press "Yes"
Scenario: Delete a custom file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
And I press "Add"
And I set the following fields to these values:
| Extension | frog |
Scenario: Revert changes to deleted file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
When I click on "Delete 7z" "link"
And I press "Yes"
And I follow "Restore 7z to Moodle defaults"
Scenario: Revert changes to updated file type
Given I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
And I click on "Edit 7z" "link"
And I set the following fields to these values:
| Type groups | document |
| fullname | shortname |
| Course 1 | C1 |
And I log in as "admin"
- And I navigate to "File types" node in "Site administration > Server"
+ And I navigate to "Server > File types" in site administration
And I press "Add"
And I set the following fields to these values:
| Extension | frog |
@javascript
Scenario: Go to the HTTPS replace report screen. Make sure broken domains are reported.
- When I navigate to "HTTP security" node in "Site administration > Security"
+ When I navigate to "Security > HTTP security" in site administration
And I follow "HTTPS conversion tool"
And I press "Continue"
Then I should see "intentionally.unavailable"
@javascript
Scenario: Use the find and replace tool.
- When I navigate to "HTTP security" node in "Site administration > Security"
+ When I navigate to "Security > HTTP security" in site administration
And I follow "HTTPS conversion tool"
And I press "Continue"
And I set the field "I understand the risks of this operation" to "1"
Scenario: Install language pack
Given I log in as "admin"
- And I navigate to "Language packs" node in "Site administration > Language"
+ And I navigate to "Language > Language packs" in site administration
When I set the field "Available language packs" to "en_ar"
And I press "Install selected language pack(s)"
Then I should see "Language pack 'en_ar' was successfully installed"
And the "Installed language packs" select box should contain "en_ar"
- And I navigate to "Live logs" node in "Site administration > Reports"
+ And I navigate to "Reports > Live logs" in site administration
And I should see "The language pack 'en_ar' was installed."
And I log out
Scenario: Update language pack
Given outdated langpack 'en_ar' is installed
And I log in as "admin"
- And I navigate to "Language packs" node in "Site administration > Language"
+ And I navigate to "Language > Language packs" in site administration
When I press "Update all installed language packs"
Then I should see "Language pack 'en_ar' was successfully updated"
And I should see "Language pack update completed"
- And I navigate to "Live logs" node in "Site administration > Reports"
+ And I navigate to "Reports > Live logs" in site administration
And I should see "The language pack 'en_ar' was updated."
And I log out
Scenario: Try to uninstall language pack
Given I log in as "admin"
- And I navigate to "Language packs" node in "Site administration > Language"
+ And I navigate to "Language > Language packs" in site administration
And I set the field "Available language packs" to "en_ar"
And I press "Install selected language pack(s)"
When I set the field "Installed language packs" to "en_ar"
Then I should see "Language pack 'en_ar' was uninstalled"
And the "Installed language packs" select box should not contain "en_ar"
And the "Available language packs" select box should contain "en_ar"
- And I navigate to "Live logs" node in "Site administration > Reports"
+ And I navigate to "Reports > Live logs" in site administration
And I should see "The language pack 'en_ar' was removed."
And I should see "Language pack uninstalled"
And I log out
Scenario: Try to uninstall English language pack
Given I log in as "admin"
- And I navigate to "Language packs" node in "Site administration > Language"
+ And I navigate to "Language > Language packs" in site administration
When I set the field "Installed language packs" to "en"
And I press "Uninstall selected language pack(s)"
Then I should see "The English language pack cannot be uninstalled."
- And I navigate to "Live logs" node in "Site administration > Reports"
+ And I navigate to "Reports > Live logs" in site administration
And I should not see "Language pack uninstalled"
And I log out
Scenario: Tool is disabled by default.
Given I log in as "admin"
- When I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ When I navigate to "Reports > Event monitoring rules" in site administration
Then I should see "Event monitoring is currently disabled"
And I should see "Enable"
And I should not see "Add a new rule"
| user | course | role |
| teacher1 | C1 | editingteacher |
And I log in as "admin"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
And I click on "Enable" "link"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
And I press "Add a new rule"
And I set the following fields to these values:
| name | New rule course level |
| minutes | 1 |
| Notification message | The forum post was created. {modulelink} |
And I press "Save changes"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
And I press "Add a new rule"
And I set the following fields to these values:
| name | New rule site level |
Scenario: Add a rule on course level
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
When I press "Add a new rule"
And I set the following fields to these values:
| name | New rule |
Scenario: Delete a rule on course level
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
When I click on "Delete rule" "link"
Then I should see "Are you sure you want to delete the rule \"New rule course level\"?"
And I press "Continue"
Scenario: Edit a rule on course level
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
When I click on "Edit rule" "link"
And I set the following fields to these values:
| name | New rule quiz |
Scenario: Duplicate a rule on course level
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
When I click on "Duplicate rule" "link" in the "New rule course level" "table_row"
Then I should see "Rule successfully duplicated"
And "#toolmonitorrules_r1" "css_element" should appear before "#toolmonitorrules_r2" "css_element"
Scenario: Add a rule on site level
Given I log in as "admin"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
When I press "Add a new rule"
And I set the following fields to these values:
| name | New rule |
Scenario: Delete a rule on site level
Given I log in as "admin"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
When I click on "Delete rule" "link"
Then I should see "Are you sure you want to delete the rule \"New rule site level\"?"
And I press "Continue"
Scenario: Edit a rule on site level
Given I log in as "admin"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
When I click on "Edit rule" "link"
And I set the following fields to these values:
| name | New Rule Quiz |
Scenario: Duplicate a rule on site level
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
When I click on "Duplicate rule" "link" in the "New rule site level" "table_row"
Then I should see "Rule successfully duplicated"
And "#toolmonitorrules_r2" "css_element" should appear after "#toolmonitorrules_r1" "css_element"
| teacher2 | C1 | teacher |
| teacher2 | C2 | editingteacher |
And I log in as "admin"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
And I click on "Enable" "link"
And I am on "Course 1" course homepage
- And I navigate to "Event monitoring rules" node in "Course administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in current page administration
And I press "Add a new rule"
And I set the following fields to these values:
| name | New rule course level |
| minutes | 1 |
| Notification message | The course was viewed. {modulelink} |
And I press "Save changes"
- And I navigate to "Event monitoring rules" node in "Site administration > Reports"
+ And I navigate to "Reports > Event monitoring rules" in site administration
And I press "Add a new rule"
And I set the following fields to these values:
| name | New rule site level |
| minutes | 1 |
| Notification message | The course was viewed. {modulelink} |
And I press "Save changes"
- And I navigate to "Define roles" node in "Site administration > Users > Permissions"
+ And I navigate to "Users > Permissions > Define roles" in site administration
And I follow "Non-editing teacher"
And I press "Edit"
And I click on "tool/monitor:managerules" "checkbox"
args: params
};
+ var modalTitle = $.Deferred();
+ var modalBody = $.Deferred();
+
+ var modal = ModalFactory.create({
+ title: modalTitle,
+ body: modalBody,
+ large: true
+ })
+ .then(function(modal) {
+ // Handle hidden event.
+ modal.getRoot().on(ModalEvents.hidden, function() {
+ // Destroy when hidden.
+ modal.destroy();
+ });
+
+ return modal;
+ })
+ .then(function(modal) {
+ modal.show();
+
+ return modal;
+ })
+ .catch(Notification.exception);
+
+ // Make the request now that the modal is configured.
var promises = Ajax.call([request]);
- var modalTitle = '';
- var modalType = ModalFactory.types.DEFAULT;
$.when(promises[0]).then(function(data) {
if (data.result.policy) {
- modalTitle = data.result.policy.name;
- return data.result.policy.content;
+ modalTitle.resolve(data.result.policy.name);
+ modalBody.resolve(data.result.policy.content);
+
+ return data;
+ } else {
+ throw new Error(data.warnings[0].message);
}
- // Fail.
- Notification.addNotification({
- message: data.warnings[0].message,
+ }).catch(function(message) {
+ modal.then(function(modal) {
+ modal.hide();
+ modal.destroy();
+
+ return modal;
+ })
+ .catch(Notification.exception);
+
+ return Notification.addNotification({
+ message: message,
type: 'error'
});
- return false;
-
- }).then(function(html) {
- if (html != false) {
- return ModalFactory.create({
- title: modalTitle,
- body: html,
- type: modalType,
- large: true
- }).then(function(modal) {
- // Handle hidden event.
- modal.getRoot().on(ModalEvents.hidden, function() {
- // Destroy when hidden.
- modal.destroy();
- });
-
- return modal;
- });
- }
- return false;
- }).done(function(modal) {
- // Show the modal.
- modal.show();
- }).fail(Notification.exception);
+ });
});
};
z-index: 9999999;
}
+.behat-site .eupopup-container-bottom {
+ position: relative;
+}
+
.eupopup-container-bottom {
position: fixed;
bottom: 0;
And I log out
# Create new policy document.
And I log in as "admin"
- And I navigate to "Manage policies" node in "Site administration > Users > Privacy and policies"
+ And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I should see "Policies and agreements"
And I should see "New policy"
And I follow "New policy"
And I log out
# Create new version of the policy document.
And I log in as "admin"
- And I navigate to "Manage policies" node in "Site administration > Users > Privacy and policies"
+ And I navigate to "Users > Privacy and policies > Manage policies" in site administration
When I follow "Actions"
Then I should see "View"
And I should see "Edit"
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
| This guests policy | 0 | | full text4 | short text4 | active | guest |
And I am on site homepage
- And I change window size to "large"
And I follow "Log in"
When I press "Log in as a guest"
Then I should see "If you continue browsing this website, you agree to our policies"
And I am on "Course 1" course homepage with editing mode on
And I delete "Quiz 1" activity
And I run all adhoc tasks
- And I navigate to "Recycle bin" node in "Course administration"
+ And I navigate to "Recycle bin" in current page administration
And I should see "Quiz 1"
And I click on "Restore" "link" in the "region-main" "region"
And I log out
| Assignment name | Test assign |
| Description | Test |
And I delete "Test assign" activity
- When I navigate to "Recycle bin" node in "Course administration"
+ When I navigate to "Recycle bin" in current page administration
Then I should see "Test assign"
And I should see "Contents will be permanently deleted after 7 days"
And I click on "Restore" "link" in the "region-main" "region"
And I go to the courses management page
And I should see "Course 2" in the "#course-listing" "css_element"
And I am on "Course 2" course homepage
- And I navigate to "Groups" node in "Course administration > Users"
+ And I navigate to "Users > Groups" in current page administration
And I follow "Overview"
And "Student 1" "text" should exist in the "Group A" "table_row"
And "Student 2" "text" should exist in the "Group A" "table_row"
| Description | Test |
And I delete "Test assign" activity
And I run all adhoc tasks
- And I navigate to "Recycle bin" node in "Course administration"
+ And I navigate to "Recycle bin" in current page administration
When I click on "Delete" "link"
Then I should see "Are you sure you want to delete the selected item from the recycle bin?"
And I press "Cancel"
And I delete "Test assign 1" activity
And I delete "Test assign 2" activity
And I run all adhoc tasks
- And I navigate to "Recycle bin" node in "Course administration"
+ And I navigate to "Recycle bin" in current page administration
And I should see "Test assign 1"
And I should see "Test assign 2"
When I click on "Delete all" "link"
$this->assertEquals(1, $DB->count_records('tool_recyclebin_category'));
// Now let's delete the course category.
- $category = coursecat::get($this->course->category);
+ $category = core_course_category::get($this->course->category);
$category->delete_full(false);
// Check that the course was deleted from the category recycle bin.
Background:
Given the scheduled task "\core\task\send_new_user_passwords_task" has a fail delay of "60" seconds
And I log in as "admin"
- And I navigate to "Scheduled tasks" node in "Site administration > Server"
+ And I navigate to "Server > Scheduled tasks" in site administration
Scenario: Clear fail delay
When I click on "Clear" "text" in the "Send new user passwords" "table_row"
Background:
Given I log in as "admin"
- And I navigate to "Scheduled tasks" node in "Site administration > Server"
+ And I navigate to "Server > Scheduled tasks" in site administration
Scenario: Disable scheduled task
When I click on "Edit task schedule: Log table cleanup" "link" in the "Log table cleanup" "table_row"
Scenario: Run a task
Given I log in as "admin"
- When I navigate to "Scheduled tasks" node in "Site administration > Server"
+ When I navigate to "Server > Scheduled tasks" in site administration
Then I should see "Never" in the "Log table cleanup" "table_row"
And I click on "Run now" "text" in the "Log table cleanup" "table_row"
Scenario: Cancel running a task
Given I log in as "admin"
- When I navigate to "Scheduled tasks" node in "Site administration > Server"
+ When I navigate to "Server > Scheduled tasks" in site administration
And I click on "Run now" "text" in the "Log table cleanup" "table_row"
And I press "Cancel"
# Confirm we're back on the scheduled tasks page by looking for the table.
Given the following config values are set as admin:
| enablerunnow | 0 | tool_task |
When I log in as "admin"
- And I navigate to "Scheduled tasks" node in "Site administration > Server"
+ And I navigate to "Server > Scheduled tasks" in site administration
Then I should not see "Run now"
*/
defined('MOODLE_INTERNAL') || die();
-require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->dirroot . '/cache/lib.php');
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
$catid = null;
if (!empty($data['category'])) {
- $category = coursecat::get((int) $data['category'], IGNORE_MISSING);
+ $category = core_course_category::get((int) $data['category'], IGNORE_MISSING);
if (!empty($category) && !empty($category->id)) {
$catid = $category->id;
} else {
$mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
$mform->setExpanded('defaultheader', true);
- $displaylist = coursecat::make_categories_list('moodle/course:create');
+ $displaylist = core_course_category::make_categories_list('moodle/course:create');
$mform->addElement('select', 'defaults[category]', get_string('coursecategory'), $displaylist);
$mform->addHelpButton('defaults[category]', 'coursecategory');
require(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir . '/clilib.php');
-require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->libdir . '/csvlib.class.php');
$courseconfig = get_config('moodlecourse');
'allowrenames' => false,
'allowresets' => false,
'reset' => false,
- 'category' => coursecat::get_default()->id,
+ 'category' => core_course_category::get_default()->id,
),
array(
'h' => 'help',
require(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
-require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->libdir . '/csvlib.class.php');
admin_externalpage_setup('tooluploadcourse');
| fullname | shortname | category |
| First course | C1 | 0 |
And I log in as "admin"
- And I navigate to "Upload courses" node in "Site administration > Courses"
+ And I navigate to "Courses > Upload courses" in site administration
@javascript
Scenario: Creation of unexisting courses
| fullname | shortname | category |
| Some random name | C1 | 0 |
And I log in as "admin"
- And I navigate to "Upload courses" node in "Site administration > Courses"
+ And I navigate to "Courses > Upload courses" in site administration
@javascript
Scenario: Updating a course fullname
| Section 1 | math102 | S1 |
| Section 3 | math102 | S3 |
And I log in as "admin"
- And I navigate to "Upload users" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts >Upload users" in site administration
When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filemanager
And I press "Upload users"
Then I should see "Upload users preview"
| Section 1 | math102 | S1 |
| Section 3 | math102 | S3 |
And I log in as "admin"
- And I navigate to "Upload users" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > Upload users" in site administration
When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filemanager
And I press "Upload users"
And I set the following fields to these values:
Scenario: Upload users with custom profile fields
# Create user profile field.
Given I log in as "admin"
- And I navigate to "User profile fields" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Text area"
And I set the following fields to these values:
| Short name | superfield |
| Name | Super field |
And I click on "Save changes" "button"
# Upload users.
- When I navigate to "Upload users" node in "Site administration > Users > Accounts"
+ When I navigate to "Users > Accounts > Upload users" in site administration
And I upload "lib/tests/fixtures/upload_users_profile.csv" file to "File" filemanager
And I press "Upload users"
And I press "Upload users"
# Check that users were created and the superfield is filled.
- And I navigate to "Browse list of users" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > Browse list of users" in site administration
And I follow "Tom Jones"
And I should see "Super field"
And I should see "The big guy"
* And whose values are the values to display
*/
public static function get_filter_options() {
- $options = \coursecat::make_categories_list();
+ $options = \core_course_category::make_categories_list();
return $options;
}
* @Given /^I open the User tour settings page$/
*/
public function i_open_the_user_tour_settings_page() {
- $this->execute('behat_navigation::i_navigate_to_node_in', [
- get_string('usertours', 'tool_usertours'),
- implode(' > ', [
- get_string('administrationsite', 'moodle'),
- get_string('appearance', 'admin'),
- ])
- ]);
+ $this->execute('behat_navigation::i_navigate_to_in_site_administration',
+ get_string('appearance', 'admin') . ' > ' .
+ get_string('usertours', 'tool_usertours')
+ );
}
}
Background:
Given I log in as "admin"
- And I navigate to "Privacy settings" node in "Site administration > Users > Privacy and policies"
+ And I navigate to "Users > Privacy and policies > Privacy settings" in site administration
Scenario: Admin provides valid value for 'Age of digital consent'.
Given I set the field "s__agedigitalconsentmap" to multiline:
# Set up course.
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
And I set the field "Enable completion tracking" to "Yes"
And I press "Save and display"
Scenario: Test with custom user profile field
# Add custom field.
Given I log in as "admin"
- And I navigate to "User profile fields" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > User profile fields" in site administration
And I set the field "datatype" to "Text input"
And I set the following fields to these values:
| Short name | superfield |
And I click on "Save changes" "button"
# Set field value for user.
- And I navigate to "Browse list of users" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > Browse list of users" in site administration
And I click on ".icon[title=Edit]" "css_element" in the "s@example.com" "table_row"
And I expand all fieldsets
And I set the field "Super field" to "Bananaman"
Scenario: Include groups and groupings when importing a course to another course
Given I import "Course 1" course into "Course 2" course using this options:
| Initial | Include groups and groupings | 1 |
- When I navigate to "Groups" node in "Course administration > Users"
+ When I navigate to "Users > Groups" in current page administration
Then I should see "Group 1"
And I should see "Group 2"
And I follow "Groupings"
Scenario: Do not include groups and groupings when importing a course to another course
Given I import "Course 1" course into "Course 2" course using this options:
| Initial | Include groups and groupings | 0 |
- When I navigate to "Groups" node in "Course administration > Users"
+ When I navigate to "Users > Groups" in current page administration
Then I should not see "Group 1"
And I should not see "Group 2"
And I follow "Groupings"
And I should see "Test forum name"
And I should see "Topic 15"
And I should not see "Topic 16"
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
And the field "id_format" matches value "Topics format"
And I press "Cancel"
And I add a "Forum" to section "1" and I fill the form with:
| Forum name | Test forum post backup name |
| Description | Test forum post backup description |
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
| Schema | Section 3 | 0 |
Then I should see "Course 1"
When I restore "test_backup.mbz" backup into a new course using this options:
Then I should see "Topic 1"
And I should see "Test forum name"
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
And the field "id_format" matches value "Topics format"
And I set the following fields to these values:
And I press "Save and display"
And I should see "1 January - 7 January"
And I should see "Test forum name"
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
And the field "id_format" matches value "Weekly format"
And I set the following fields to these values:
| id_format | Social format |
And I press "Save and display"
And I should see "An open forum for chatting about anything you want to"
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
And the field "id_format" matches value "Social format"
And I press "Cancel"
| Confirmation | Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into "Course 2" course using this options:
| Schema | Overwrite course configuration | Yes |
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
Then the field "id_format" matches value "Topics format"
And the field "Course layout" matches value "Show one section per page"
| Confirmation | Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into "Course 2" course using this options:
| Schema | Overwrite course configuration | No |
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
Then the field "id_format" matches value "Topics format"
And the field "Course short name" matches value "C2"
| Initial | Include enrolled users | 0 |
| Confirmation | Filename | test_backup.mbz |
And I am on "Course 2" course homepage
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
| Schema | Overwrite course configuration | Yes |
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
Then the field "id_format" matches value "Topics format"
And the field "Course layout" matches value "Show one section per page"
| Initial | Include enrolled users | 0 |
| Confirmation | Filename | test_backup.mbz |
And I am on "Course 2" course homepage
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
| Schema | Overwrite course configuration | No |
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
Then the field "id_format" matches value "Topics format"
And the field "Course short name" matches value "C2"
| Initial | Include enrolled users | 0 |
| Confirmation | Filename | test_backup.mbz |
And I am on "Course 4" course homepage
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
| Schema | Overwrite course configuration | No |
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I expand all fieldsets
Then the field "id_format" matches value "Topics format"
And the field "Course short name" matches value "C4"
@javascript
Scenario: Restore a backup with user data with site config for including users set to 0
- Given I navigate to "General restore defaults" node in "Site administration > Courses > Backups"
+ Given I navigate to "Courses > Backups > General restore defaults" in site administration
And I set the field "s_restore_restore_general_users" to ""
And I press "Save changes"
And I am on "Course 1" course homepage
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
# "User data" marks the user data field for the section
# "-" marks the user data field for the data activity
And I restore "test_backup.mbz" backup into a new course using this options:
@javascript
Scenario: Restore a backup with user data with local and site config config for including users set to 0
- Given I navigate to "General restore defaults" node in "Site administration > Courses > Backups"
+ Given I navigate to "Courses > Backups > General restore defaults" in site administration
And I set the field "s_restore_restore_general_users" to ""
And I press "Save changes"
And I am on "Course 1" course homepage
- And I navigate to "Restore" node in "Course administration"
+ And I navigate to "Restore" in current page administration
When I restore "test_backup.mbz" backup into a new course using this options:
| Settings | Include enrolled users | 0 |
Then I should see "Test database name"
// Get courses with enabled completion.
$courses = $DB->get_records('course', array('enablecompletion' => COMPLETION_ENABLED));
if (!empty($courses)) {
- require_once($CFG->libdir . '/coursecatlib.php');
- $list = coursecat::make_categories_list();
+ $list = core_course_category::make_categories_list();
$select = array();
$selected = array();
@javascript
Scenario: Setting badges settings
- Given I navigate to "Badges settings" node in "Site administration > Badges"
+ Given I navigate to "Badges > Badges settings" in site administration
And I set the field "Default badge issuer name" to "Test Badge Site"
And I set the field "Default badge issuer contact details" to "testuser@example.com"
And I press "Save changes"
And I press "Customise this page"
# TODO MDL-57120 site "Badges" link not accessible without navigation block.
And I add the "Navigation" block if not present
- Given I navigate to "Site badges" node in "Site pages"
+ And I click on "Site pages" "list_item" in the "Navigation" "block"
+ Given I click on "Site badges" "link" in the "Navigation" "block"
Then I should see "There are no badges available."
@javascript @_file_upload
Scenario: Add a badge
- Given I navigate to "Add a new badge" node in "Site administration > Badges"
+ Given I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Test badge with 'apostrophe' and other friends (<>&@#) |
| Description | Test badge description |
And I log in as "teacher1"
And I am on "Course 1" course homepage
# Create course badge 1.
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge 1 |
And I press "Enable access"
And I press "Continue"
# Badge #2
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I am on "Course 1" course homepage
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge 2 |
@javascript
Scenario: Award profile badge
Given I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Profile Badge |
| Description | Test badge description |
| teacher | teacher | 1 | teacher1@example.com |
| student | student | 1 | student1@example.com |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| student2 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| Enable completion tracking | Yes |
And I press "Save and display"
| Description | Submit your online text |
| id_completion | 1 |
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| Enable completion tracking | Yes |
And I press "Save and display"
| Description | Submit your online text |
| assignsubmission_onlinetext_enabled | 1 |
| id_completion | 1 |
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I set the field "id_overall_aggregation" to "2"
And I click on "Condition: Activity completion" "link"
And I set the field "Assignment - Test assignment name" to "1"
And I press "Save changes"
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
And I log in as "teacher1"
And I am on "Course 1" course homepage
# Create course badge 1.
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge 1 |
And I follow "Recipients (1)"
Then I should see "Recipients (1)"
# Add course badge 2.
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I am on "Course 1" course homepage
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge 2 |
| student2 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
| user1 | CH1 |
| user2 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user2 | CH1 |
| user2 | CH3 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user3 | CH2 |
| user3 | CH3 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user1 | CH1 |
| user2 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user2 | CH2 |
| user3 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user2 | CH2 |
| user2 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user2 | CH1 |
| user3 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
| user1 | CH2 |
| user2 | CH2 |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge 1 |
| Description | Site badge description |
And I press "Enable access"
When I press "Continue"
And I should see "Recipients (1)"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge 2 |
| Description | Site badge description |
| user2 | Second | User | second@example.com |
| user3 | Third | User | third@example.com |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge 1 |
| Description | Site badge description |
And I press "Enable access"
When I press "Continue"
And I should see "Recipients (0)"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge 2 |
| Description | Site badge description |
And I press "Save"
And I press "Enable access"
And I press "Continue"
- Then I navigate to "Cohorts" node in "Site administration > Users > Accounts"
+ Then I navigate to "Users > Accounts >Cohorts" in site administration
And I add "First User (first@example.com)" user to "CH1" cohort members
And I add "First User (first@example.com)" user to "CH2" cohort members
And I add "Second User (second@example.com)" user to "CH2" cohort members
| username | firstname | lastname | email |
| user1 | First | User | first@example.com |
And I log in as "admin"
- And I navigate to "Add a new badge" node in "Site administration > Badges"
+ And I navigate to "Badges > Add a new badge" in site administration
And I set the following fields to these values:
| Name | Site Badge |
| Description | Site badge description |
Scenario: Check the default roles are visible
Given I log in as "manager1"
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
Scenario: Check hidden roles are not visible
Given I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
Background:
Given I log in as "admin"
- And I navigate to "Scheduled tasks" node in "Site administration > Server"
+ And I navigate to "Server > Scheduled tasks" in site administration
And I click on "Bookmark this page" "link" in the "Admin bookmarks" "block"
And I log out
# Test bookmark functionality using the "User profile fields" page as our bookmark.
Scenario: Admin page can be bookmarked
Given I log in as "admin"
- And I navigate to "User profile fields" node in "Site administration > Users > Accounts"
+ And I navigate to "Users > Accounts > User profile fields" in site administration
When I click on "Bookmark this page" "link" in the "Admin bookmarks" "block"
Then I should see "User profile fields" in the "Admin bookmarks" "block"
# See the existing bookmark is there too.
Scenario: Admin page can be accessed through bookmarks block
Given I log in as "admin"
- And I navigate to "Notifications" node in "Site administration"
+ And I navigate to "Notifications" in site administration
And I click on "Scheduled tasks" "link" in the "Admin bookmarks" "block"
# Verify that we are on the right page.
Then I should see "Scheduled tasks" in the "h1" "css_element"
Scenario: Admin page can be removed from bookmarks
Given I log in as "admin"
- And I navigate to "Notifications" node in "Site administration"
+ And I navigate to "Notifications" in site administration
And I click on "Scheduled tasks" "link" in the "Admin bookmarks" "block"
When I click on "Unbookmark this page" "link" in the "Admin bookmarks" "block"
Then I should see "Bookmark deleted"
And I log in as "teacher1"
And I am on "Course 1" course homepage
# Issue badge 1 of 2
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I set the following fields to these values:
| id_name | Badge 1 |
| id_description | Badge 1 |
And I set the field "potentialrecipients[]" to "Teacher 1 (teacher1@example.com)"
And I press "Award badge"
# Issue Badge 2 of 2
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I am on "Course 1" course homepage
+ And I navigate to "Badges > Add a new badge" in current page administration
And I set the following fields to these values:
| id_name | Badge 2 |
| id_description | Badge 2 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
# Issue badge 1 of 2
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I set the following fields to these values:
| id_name | Badge 1 |
| id_description | Badge 1 |
| teacher1 | C1 | editingteacher |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Latest badges" block
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
# Issue badge 1 of 2
- And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I navigate to "Badges > Add a new badge" in current page administration
And I set the following fields to these values:
| id_name | Badge 1 |
| id_description | Badge 1 |
| student1 | Student | 1 | student1@example.com | S1 |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Blog menu" block
And I log out
| student1 | Student | 1 | student1@example.com | S1 |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Recent blog entries" block
# TODO MDL-57120 site "Blogs" link not accessible without navigation block.
And I add the "Navigation" block if not present
Scenario: Students use the recent blog entries block to view blogs
Given I log in as "student1"
And I am on site homepage
- And I navigate to "Site blogs" node in "Site pages"
+ And I click on "Site blogs" "link" in the "Navigation" "block"
And I follow "Add a new entry"
When I set the following fields to these values:
| Entry title | S1 First Blog |
Scenario: Students only see a few entries in the recent blog entries block
Given I log in as "student1"
And I am on site homepage
- And I navigate to "Site blogs" node in "Site pages"
+ And I click on "Site blogs" "link" in the "Navigation" "block"
And I follow "Add a new entry"
# Blog 1 of 5
And I set the following fields to these values:
Then I log out
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I configure the "Recent blog entries" block
And I set the following fields to these values:
| id_config_numberofrecentblogentries | 2 |
| student2 | G2 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| id_groupmode | Separate groups |
| id_groupmodeforce | Yes |
| student2 | G2 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| id_groupmode | Separate groups |
| id_groupmodeforce | Yes |
| id_eventtype | Site |
| id_name | My Site Event |
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Upcoming events" block
And I log out
When I log in as "teacher1"
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Comments" block
And I follow "Show comments"
And I add "I'm a comment from admin" comment to comments block
Scenario: Add the block to a the course where completion is disabled
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
- And I navigate to "Edit settings" node in "Course administration"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| Enable completion tracking | No |
And I press "Save and display"
| Require view | 1 |
And I press "Save and return to course"
When I add the "Course completion status" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| Test page name | 1 |
| Require view | 1 |
And I press "Save and return to course"
And I add the "Course completion status" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| Test page name | 1 |
| Require view | 1 |
And I press "Save and return to course"
And I add the "Course completion status" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| Test page name | 1 |
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add the "Course completion status" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| Teacher | 1 |
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Course completion" node in "Course administration > Reports"
+ And I navigate to "Reports > Course completion" in current page administration
And I follow "Click to mark user complete"
# Running completion task just after clicking sometimes fail, as record
# should be created before the task runs.
Given I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add the "Course completion status" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| Teacher | 1 |
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
- And I navigate to "Course completion" node in "Course administration > Reports"
+ And I navigate to "Reports > Course completion" in current page administration
And I follow "Click to mark user complete"
And I log out
And I log in as "student1"
And I log out
And I log in as "teacher2"
And I am on "Course 1" course homepage
- And I navigate to "Course completion" node in "Course administration > Reports"
+ And I navigate to "Reports > Course completion" in current page administration
And I follow "Click to mark user complete"
# Running completion task just after clicking sometimes fail, as record
# should be created before the task runs.
And I am on "Course 1" course homepage with editing mode on
And I add the "Course completion status" block
And I add the "Self completion" block
- And I navigate to "Course completion" node in "Course administration"
+ And I navigate to "Course completion" in current page administration
And I expand all fieldsets
And I set the following fields to these values:
| id_criteria_self | 1 |
*/
include_once($CFG->dirroot . '/course/lib.php');
-include_once($CFG->libdir . '/coursecatlib.php');
class block_course_list extends block_list {
function init() {
}
}
- $categories = coursecat::get(0)->get_children(); // Parent = 0 ie top-level categories only
+ $categories = core_course_category::get(0)->get_children(); // Parent = 0 ie top-level categories only
if ($categories) { //Check we have categories
if (count($categories) > 1 || (count($categories) == 1 && $DB->count_records('course') > 200)) { // Just print top level category links
foreach ($categories as $category) {
Scenario: Add the course list block on the frontpage and navigate to the course listing
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Courses" block
And I log out
When I log in as "teacher1"
Scenario: Add the course list block on the frontpage page and navigate to another course
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Courses" block
And I log out
When I log in as "teacher1"
Scenario: Add the course list block on the frontpage page and view as an admin
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
When I add the "Courses" block
Then I should see "Miscellaneous" in the "Course categories" "block"
And I should see "Category 1" in the "Course categories" "block"
Scenario: Add the course list block on the frontpage page and view as a guest
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Courses" block
And I log out
When I log in as "guest"
And I am on site homepage
And I turn editing mode on
And I add the "Course/site summary" block
- And I navigate to "Edit settings" node in "Front page settings"
+ And I navigate to "Edit settings" in current page administration
And I set the following fields to these values:
| summary | Proved the summary block works! |
And I press "Save changes"
| testuser | testpass | Test | User | student1@example.com |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Login" block
Scenario: Login block visible to non-logged in users
public function export_for_template(renderer_base $output) {
global $CFG;
require_once($CFG->dirroot.'/course/lib.php');
- require_once($CFG->dirroot.'/lib/coursecatlib.php');
// Build courses view data structure.
$coursesview = [
// Convert summary to plain text.
$exportedcourse->summary = content_to_text($exportedcourse->summary, $exportedcourse->summaryformat);
- $course = new \course_in_list($course);
+ $course = new \core_course_list_element($course);
foreach ($course->get_course_overviewfiles() as $file) {
$isimage = $file->is_valid_image();
if ($isimage) {
| teacher1 | Teacher | One | teacher1@example.com | T1 |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Logged in user" block
And I log out
And I press "Save changes"
And I turn editing mode off
And I am on "Course 2" course homepage
- And I navigate to "Enrolment methods" node in "Course administration > Users"
+ And I navigate to "Users > Enrolment methods" in current page administration
And I click on "Edit" "link" in the "Guest access" "table_row"
And I set the following fields to these values:
| Allow guest access | Yes |
Scenario: View the online users block on the front page and see myself
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
When I add the "Online users" block
Then I should see "Admin User" in the "Online users" "block"
And I should see "1 online user" in the "Online users" "block"
Scenario: View the online users block on the front page as a logged in user
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Online users" block
And I log out
And I log in as "student2"
Scenario: View the online users block on the front page as a guest
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Online users" block
And I log out
And I log in as "student2"
Scenario: Hide/show user's online status from/to other users in the online users block on front page
Given I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Online users" block
And I log out
When I log in as "student1"
| student1 | Sam | Student | student1@example.com |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "People" block
And I log out
| teacher1 | Teacher | 1 | teacher1@example.com |
And I log in as "admin"
And I am on site homepage
- And I navigate to "Turn editing on" node in "Front page settings"
+ And I navigate to "Turn editing on" in current page administration
And I add the "Private files" block
And I log out