diff --git a/.travis.yml b/.travis.yml index 35aa3c88e..1086084d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,8 @@ cache: - $HOME/.npm php: - - 5.6 - - 7.1 + - 7.0 + - 7.2 addons: firefox: 47.0.1 @@ -22,7 +22,7 @@ addons: env: global: - - MOODLE_BRANCH=MOODLE_33_STABLE + - MOODLE_BRANCH=MOODLE_34_STABLE - IGNORE_NAMES=mobile_*.mustache # Mobile mustache has specific syntax, ignore their templates matrix: - DB=pgsql diff --git a/CHANGES.md b/CHANGES.md index 79d2f5fe6..26e1171b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,13 +4,7 @@ All notable changes to this project will be documented in this file. Note - All hash comments refer to the issue number. Eg. #169 refers to https://github.com/markn86/moodle-mod_customcert/issues/169. -## [3.3.17] - 2019-02-04 - -### Fixed - -- Fixed invalid syntax for PHP 5.6 as it is still supported in 3.3. - -## [3.3.16] - 2019-02-04 +## [3.4.7] - 2018-12-31 ### Changed @@ -18,12 +12,14 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g ### Fixed +- Missing implementation for privacy provider (#260). - Use course module context when calling format_string/text (#200). - Exception being thrown when adding the 'teachername' element to site template (#261). -## [3.3.15] - 2018-12-20 +## [3.4.6] - 2018-12-20 ### Added +- GDPR: Add support for removal of users from a context (see MDL-62560) (#252). - Images can be made transparent (#186). - Set default values of activity instance settings (#180). - Allow element plugins to control if they can be added to a certificate (#225). @@ -46,7 +42,7 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g - Removed incorrect course reset logic (#223). - Description strings referring to the wrong setting (#254). -## [3.3.14] - 2018-07-13 +## [3.4.5] - 2018-07-13 ### Fixed - Use custom fonts if present (#211). @@ -61,7 +57,7 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g - One big change here is combining the report and activity view page into one. - Allow short dates with leading zeros (#210). -## [3.3.13] - 2018-06-26 +## [3.4.4] - 2018-06-26 ### Fixed - Respect filters in the 'My certificates' and 'Verify certificate' pages (#197). @@ -72,12 +68,12 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g - Multiple UX improvements to both the browser and mobile views (#203). -## [3.3.12] - 2018-06-07 +## [3.4.3] - 2018-06-07 ### Fixed - Hotfix to prevent misalignment of 'text' elements after last release (#196). -## [3.3.11] - 2018-06-06 +## [3.4.2] - 2018-06-06 ### Added - Mobile app support (#70). ``` @@ -109,7 +105,7 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g - No longer display the 'action' column and user picture URL when downloading the user report (#192). - Elements no longer ignore filters (#170). -## [3.3.10] - 2018-05-17 +## [3.4.1] - 2018-05-17 ### Added - GDPR Compliance (#189). diff --git a/classes/element_helper.php b/classes/element_helper.php index 2a1cffa56..153dae0c2 100644 --- a/classes/element_helper.php +++ b/classes/element_helper.php @@ -389,7 +389,7 @@ public static function get_courseid($elementid) { * @param int $elementid The element id * @return \context The context */ - public static function get_context($elementid) { + public static function get_context(int $elementid) : \context { global $DB; $sql = "SELECT ct.contextid diff --git a/classes/output/mobile.php b/classes/output/mobile.php index 44264ec4d..5e480c731 100644 --- a/classes/output/mobile.php +++ b/classes/output/mobile.php @@ -202,7 +202,7 @@ private static function update_active_group($groupmode, $groupid, $allowedgroups * @param \context $context * @param string $cap */ - protected static function require_capability(\stdClass $cm, \context $context, $cap) { + protected static function require_capability(\stdClass $cm, \context $context, string $cap) { require_login($cm->course, false, $cm, true, true); require_capability($cap, $context); } diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 406447425..29bebc63c 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -25,9 +25,11 @@ use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; use core_privacy\local\request\contextlist; use core_privacy\local\request\helper; use core_privacy\local\request\transform; +use core_privacy\local\request\userlist; use core_privacy\local\request\writer; defined('MOODLE_INTERNAL') || die(); @@ -40,7 +42,8 @@ */ class provider implements \core_privacy\local\metadata\provider, - \core_privacy\local\request\plugin\provider { + \core_privacy\local\request\plugin\provider, + \core_privacy\local\request\core_userlist_provider { /** * Return the fields which contain personal data. @@ -48,7 +51,7 @@ class provider implements * @param collection $items a reference to the collection to use to store the metadata. * @return collection the updated collection of metadata items. */ - public static function get_metadata(collection $items) { + public static function get_metadata(collection $items) : collection { $items->add_database_table( 'customcert_issues', [ @@ -70,7 +73,7 @@ public static function get_metadata(collection $items) { * @param int $userid the userid. * @return contextlist the list of contexts containing user info for the user. */ - public static function get_contexts_for_userid($userid) { + public static function get_contexts_for_userid(int $userid) : contextlist { $sql = "SELECT c.id FROM {context} c INNER JOIN {course_modules} cm @@ -96,6 +99,37 @@ public static function get_contexts_for_userid($userid) { return $contextlist; } + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + $context = $userlist->get_context(); + + if (!$context instanceof \context_module) { + return; + } + + // Fetch all users who have a custom certificate. + $sql = "SELECT customcertissues.userid + FROM {course_modules} cm + JOIN {modules} m + ON m.id = cm.module AND m.name = :modname + JOIN {customcert} customcert + ON customcert.id = cm.instance + JOIN {customcert_issues} customcertissues + ON customcertissues.customcertid = customcert.id + WHERE cm.id = :cmid"; + + $params = [ + 'cmid' => $context->instanceid, + 'modname' => 'customcert', + ]; + + $userlist->add_from_sql('userid', $sql, $params); + } + /** * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist. * @@ -182,6 +216,33 @@ public static function delete_data_for_user(approved_contextlist $contextlist) { } } + /** + * Delete multiple users within a single context. + * + * @param approved_userlist $userlist The approved context and user information to delete information for. + */ + public static function delete_data_for_users(approved_userlist $userlist) { + global $DB; + + $context = $userlist->get_context(); + if (!$context instanceof \context_module) { + return; + } + + $cm = get_coursemodule_from_id('customcert', $context->instanceid); + if (!$cm) { + // Only customcert module will be handled. + return; + } + + $userids = $userlist->get_userids(); + list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + + $select = "customcertid = :customcertid AND userid $usersql"; + $params = ['customcertid' => $cm->instance] + $userparams; + $DB->delete_records_select('customcert_issues', $select, $params); + } + /** * Return a list of Customcert IDs mapped to their course module ID. * diff --git a/db/install.xml b/db/install.xml index d776f54ee..8db82e772 100644 --- a/db/install.xml +++ b/db/install.xml @@ -10,7 +10,7 @@ - + @@ -76,7 +76,7 @@ - + diff --git a/db/upgrade.php b/db/upgrade.php index 849028c2c..d0f5ef4b5 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -121,6 +121,26 @@ function xmldb_customcert_upgrade($oldversion) { upgrade_mod_savepoint(true, 2017050502, 'customcert'); } + if ($oldversion < 2017050518.01) { + $table = new xmldb_table('customcert'); + $index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']); + if ($dbman->index_exists($table, $index)) { + $dbman->drop_index($table, $index); + } + $key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, array('templateid'), 'customcert_templates', array('id')); + $dbman->add_key($table, $key); + + $table = new xmldb_table('customcert_pages'); + $index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']); + if ($dbman->index_exists($table, $index)) { + $dbman->drop_index($table, $index); + } + $key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, array('templateid'), 'customcert_templates', array('id')); + $dbman->add_key($table, $key); + + upgrade_mod_savepoint(true, 2017050518.01, 'customcert'); + } + if ($oldversion < 2017050506) { $table = new xmldb_table('customcert_elements'); $field = new xmldb_field('size'); @@ -135,7 +155,7 @@ function xmldb_customcert_upgrade($oldversion) { upgrade_mod_savepoint(true, 2017050506, 'customcert'); } - if ($oldversion < 2017050515) { + if ($oldversion < 2017111306) { $table = new xmldb_table('customcert_elements'); $field = new xmldb_field('element', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'name'); @@ -143,27 +163,7 @@ function xmldb_customcert_upgrade($oldversion) { $dbman->change_field_type($table, $field); // Savepoint reached. - upgrade_mod_savepoint(true, 2017050515, 'customcert'); - } - - if ($oldversion < 2017050518.01) { - $table = new xmldb_table('customcert'); - $index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']); - if ($dbman->index_exists($table, $index)) { - $dbman->drop_index($table, $index); - } - $key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, array('templateid'), 'customcert_templates', array('id')); - $dbman->add_key($table, $key); - - $table = new xmldb_table('customcert_pages'); - $index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']); - if ($dbman->index_exists($table, $index)) { - $dbman->drop_index($table, $index); - } - $key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, array('templateid'), 'customcert_templates', array('id')); - $dbman->add_key($table, $key); - - upgrade_mod_savepoint(true, 2017050518.01, 'customcert'); + upgrade_mod_savepoint(true, 2017111306, 'customcert'); } return true; diff --git a/element/bgimage/classes/privacy/provider.php b/element/bgimage/classes/privacy/provider.php index e29b69841..61a208273 100644 --- a/element/bgimage/classes/privacy/provider.php +++ b/element/bgimage/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/bgimage/version.php b/element/bgimage/version.php index 7ded31f64..847512d54 100644 --- a/element/bgimage/version.php +++ b/element/bgimage/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_bgimage'; diff --git a/element/border/classes/privacy/provider.php b/element/border/classes/privacy/provider.php index 1639e5367..0aa2b0a61 100644 --- a/element/border/classes/privacy/provider.php +++ b/element/border/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/border/version.php b/element/border/version.php index aec8ecacb..2aa335097 100644 --- a/element/border/version.php +++ b/element/border/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_border'; diff --git a/element/categoryname/classes/element.php b/element/categoryname/classes/element.php index e358589c4..48d157820 100644 --- a/element/categoryname/classes/element.php +++ b/element/categoryname/classes/element.php @@ -63,7 +63,7 @@ public function render_html() { * * @return string */ - protected function get_category_name() { + protected function get_category_name() : string { global $DB, $SITE; $courseid = \mod_customcert\element_helper::get_courseid($this->get_id()); diff --git a/element/categoryname/classes/privacy/provider.php b/element/categoryname/classes/privacy/provider.php index 30ae33a92..bcdb8a759 100644 --- a/element/categoryname/classes/privacy/provider.php +++ b/element/categoryname/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/categoryname/version.php b/element/categoryname/version.php index ec4c99a38..6f9b86535 100644 --- a/element/categoryname/version.php +++ b/element/categoryname/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_categoryname'; diff --git a/element/code/classes/privacy/provider.php b/element/code/classes/privacy/provider.php index a834da45e..cbdbb5019 100644 --- a/element/code/classes/privacy/provider.php +++ b/element/code/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/code/version.php b/element/code/version.php index 80ac61995..b95025a73 100644 --- a/element/code/version.php +++ b/element/code/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_code'; diff --git a/element/coursename/classes/element.php b/element/coursename/classes/element.php index 1012fa43b..87a1764f5 100644 --- a/element/coursename/classes/element.php +++ b/element/coursename/classes/element.php @@ -63,7 +63,7 @@ public function render_html() { * * @return string */ - protected function get_course_name() { + protected function get_course_name() : string { $courseid = \mod_customcert\element_helper::get_courseid($this->get_id()); $course = get_course($courseid); $context = \mod_customcert\element_helper::get_context($this->get_id()); diff --git a/element/coursename/classes/privacy/provider.php b/element/coursename/classes/privacy/provider.php index 12b436cde..1c2d02a3b 100644 --- a/element/coursename/classes/privacy/provider.php +++ b/element/coursename/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/coursename/version.php b/element/coursename/version.php index 2ba2993c7..0bf953167 100644 --- a/element/coursename/version.php +++ b/element/coursename/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_coursename'; diff --git a/element/date/classes/privacy/provider.php b/element/date/classes/privacy/provider.php index cda042db2..1ce5f23d3 100644 --- a/element/date/classes/privacy/provider.php +++ b/element/date/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/date/version.php b/element/date/version.php index a81b9d0ce..d98a3164c 100644 --- a/element/date/version.php +++ b/element/date/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_date'; diff --git a/element/digitalsignature/classes/privacy/provider.php b/element/digitalsignature/classes/privacy/provider.php index d92747065..6e46add57 100644 --- a/element/digitalsignature/classes/privacy/provider.php +++ b/element/digitalsignature/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/digitalsignature/version.php b/element/digitalsignature/version.php index e35d28051..01892ac0c 100644 --- a/element/digitalsignature/version.php +++ b/element/digitalsignature/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_digitalsignature'; diff --git a/element/grade/classes/privacy/provider.php b/element/grade/classes/privacy/provider.php index 052efc22b..2f6b29599 100644 --- a/element/grade/classes/privacy/provider.php +++ b/element/grade/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/grade/version.php b/element/grade/version.php index cd59a8796..8f24b547f 100644 --- a/element/grade/version.php +++ b/element/grade/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_grade'; diff --git a/element/gradeitemname/classes/element.php b/element/gradeitemname/classes/element.php index 4d287dc3d..401de904a 100644 --- a/element/gradeitemname/classes/element.php +++ b/element/gradeitemname/classes/element.php @@ -114,7 +114,7 @@ public function definition_after_data($mform) { * * @return string */ - protected function get_grade_item_name() { + protected function get_grade_item_name() : string { global $DB; // Get the course module information. diff --git a/element/gradeitemname/classes/privacy/provider.php b/element/gradeitemname/classes/privacy/provider.php index d30709f9e..8cf3b952e 100644 --- a/element/gradeitemname/classes/privacy/provider.php +++ b/element/gradeitemname/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/gradeitemname/version.php b/element/gradeitemname/version.php index b6265f4f1..9aff1dc4e 100644 --- a/element/gradeitemname/version.php +++ b/element/gradeitemname/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_gradeitemname'; diff --git a/element/image/classes/privacy/provider.php b/element/image/classes/privacy/provider.php index e6db81a8a..f800d6c4e 100644 --- a/element/image/classes/privacy/provider.php +++ b/element/image/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/image/version.php b/element/image/version.php index 3aaf16c9d..55e6c8161 100644 --- a/element/image/version.php +++ b/element/image/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_image'; diff --git a/element/studentname/classes/privacy/provider.php b/element/studentname/classes/privacy/provider.php index 0d6d1ad23..a7ae40215 100644 --- a/element/studentname/classes/privacy/provider.php +++ b/element/studentname/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/studentname/version.php b/element/studentname/version.php index dd4ccef68..cb37cfa68 100644 --- a/element/studentname/version.php +++ b/element/studentname/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_studentname'; diff --git a/element/teachername/classes/privacy/provider.php b/element/teachername/classes/privacy/provider.php index 9d2ae952e..800c91f2f 100644 --- a/element/teachername/classes/privacy/provider.php +++ b/element/teachername/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/teachername/version.php b/element/teachername/version.php index 8b41b889a..f11f0ceb1 100644 --- a/element/teachername/version.php +++ b/element/teachername/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_teachername'; diff --git a/element/text/classes/element.php b/element/text/classes/element.php index ccfdba6b2..c2a4f1360 100644 --- a/element/text/classes/element.php +++ b/element/text/classes/element.php @@ -100,7 +100,7 @@ public function definition_after_data($mform) { * * @return string */ - protected function get_text() { + protected function get_text() : string { $context = \mod_customcert\element_helper::get_context($this->get_id()); return format_text($this->get_data(), FORMAT_HTML, ['context' => $context]); } diff --git a/element/text/classes/privacy/provider.php b/element/text/classes/privacy/provider.php index 74ae4d384..19ca3e3a9 100644 --- a/element/text/classes/privacy/provider.php +++ b/element/text/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/text/version.php b/element/text/version.php index 705f10af8..0084c3ad6 100644 --- a/element/text/version.php +++ b/element/text/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_text'; diff --git a/element/userfield/classes/element.php b/element/userfield/classes/element.php index 1148d6d46..84db94ac2 100644 --- a/element/userfield/classes/element.php +++ b/element/userfield/classes/element.php @@ -133,7 +133,7 @@ public function definition_after_data($mform) { * @param bool $preview Is this a preview? * @return string */ - protected function get_user_field_value(\stdClass $user, $preview) { + protected function get_user_field_value(\stdClass $user, bool $preview) : string { global $CFG, $DB; // The user field to display. diff --git a/element/userfield/classes/privacy/provider.php b/element/userfield/classes/privacy/provider.php index 58a385cdd..4669a8616 100644 --- a/element/userfield/classes/privacy/provider.php +++ b/element/userfield/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/userfield/version.php b/element/userfield/version.php index cb0207ccc..b908ffbd5 100644 --- a/element/userfield/version.php +++ b/element/userfield/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_userfield'; diff --git a/element/userpicture/classes/privacy/provider.php b/element/userpicture/classes/privacy/provider.php index 4d0234f65..8b0b3f0f8 100644 --- a/element/userpicture/classes/privacy/provider.php +++ b/element/userpicture/classes/privacy/provider.php @@ -40,7 +40,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * * @return string */ - public static function get_reason() { + public static function get_reason() : string { return 'privacy:metadata'; } } diff --git a/element/userpicture/version.php b/element/userpicture/version.php index 1df9c7d56..f6a3173d2 100644 --- a/element/userpicture/version.php +++ b/element/userpicture/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050500; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->component = 'customcertelement_userpicture'; diff --git a/tests/external_test.php b/tests/external_test.php index 50615f27f..6540e644f 100644 --- a/tests/external_test.php +++ b/tests/external_test.php @@ -27,8 +27,6 @@ global $CFG; -require_once($CFG->dirroot . '/webservice/tests/helpers.php'); - /** * Unit tests for the webservices. * @@ -37,12 +35,12 @@ * @copyright 2018 Mark Nelson * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_customcert_external_test extends externallib_advanced_testcase { +class mod_customcert_external_test_testcase extends advanced_testcase { /** * Test set up. */ - public function setUp(): void { + public function setUp() { $this->resetAfterTest(); } diff --git a/tests/privacy_provider_test.php b/tests/privacy_provider_test.php new file mode 100644 index 000000000..bd9fe1aa1 --- /dev/null +++ b/tests/privacy_provider_test.php @@ -0,0 +1,304 @@ +. + +/** + * Privacy provider tests. + * + * @package mod_customcert + * @copyright 2018 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use mod_customcert\privacy\provider; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Privacy provider tests class. + * + * @package mod_customcert + * @copyright 2018 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_customcert_privacy_provider_testcase extends \core_privacy\tests\provider_testcase { + + /** + * Test for provider::get_contexts_for_userid(). + */ + public function test_get_contexts_for_userid() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + + // The customcert activity the user will have an issue from. + $customcert = $this->getDataGenerator()->create_module('customcert', ['course' => $course->id]); + + // Another customcert activity that has no issued certificates. + $this->getDataGenerator()->create_module('customcert', ['course' => $course->id]); + + // Create a user who will be issued a certificate. + $user = $this->getDataGenerator()->create_user(); + + // Issue the certificate. + $this->create_certificate_issue($customcert->id, $user->id); + + // Check the context supplied is correct. + $contextlist = provider::get_contexts_for_userid($user->id); + $this->assertCount(1, $contextlist); + + $contextformodule = $contextlist->current(); + $cmcontext = context_module::instance($customcert->cmid); + $this->assertEquals($cmcontext->id, $contextformodule->id); + } + + /** + * Test for provider::get_users_in_context(). + */ + public function test_get_users_in_context() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + + // The customcert activity the user will have an issue from. + $customcert1 = $this->getDataGenerator()->create_module('customcert', ['course' => $course->id]); + $customcert2 = $this->getDataGenerator()->create_module('customcert', ['course' => $course->id]); + + // Call get_users_in_context() when the customcert hasn't any user. + $cm = get_coursemodule_from_instance('customcert', $customcert1->id); + $cmcontext = context_module::instance($cm->id); + $userlist = new \core_privacy\local\request\userlist($cmcontext, 'mod_customcert'); + provider::get_users_in_context($userlist); + + // Check no user has been returned. + $this->assertCount(0, $userlist->get_userids()); + + // Create some users who will be issued a certificate. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + $this->create_certificate_issue($customcert1->id, $user1->id); + $this->create_certificate_issue($customcert1->id, $user2->id); + $this->create_certificate_issue($customcert2->id, $user3->id); + + // Call get_users_in_context() again. + provider::get_users_in_context($userlist); + + // Check this time there are 2 users. + $this->assertCount(2, $userlist->get_userids()); + $this->assertContains($user1->id, $userlist->get_userids()); + $this->assertContains($user2->id, $userlist->get_userids()); + $this->assertNotContains($user3->id, $userlist->get_userids()); + } + + /** + * Test for provider::get_users_in_context() with invalid context type. + */ + public function test_get_users_in_context_invalid_context_type() { + $systemcontext = context_system::instance(); + + $userlist = new \core_privacy\local\request\userlist($systemcontext, 'mod_customcert'); + \mod_customcert\privacy\provider::get_users_in_context($userlist); + + $this->assertCount(0, $userlist->get_userids()); + } + + /** + * Test for provider::export_user_data(). + */ + public function test_export_for_context() { + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + + $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + + // Create users who will be issued a certificate. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->create_certificate_issue($customcert->id, $user1->id); + $this->create_certificate_issue($customcert->id, $user1->id); + $this->create_certificate_issue($customcert->id, $user2->id); + + // Export all of the data for the context for user 1. + $cmcontext = context_module::instance($customcert->cmid); + $this->export_context_data_for_user($user1->id, $cmcontext, 'mod_customcert'); + $writer = \core_privacy\local\request\writer::with_context($cmcontext); + + $this->assertTrue($writer->has_any_data()); + + $data = $writer->get_data(); + $this->assertCount(2, $data->issues); + + $issues = $data->issues; + foreach ($issues as $issue) { + $this->assertArrayHasKey('code', $issue); + $this->assertArrayHasKey('emailed', $issue); + $this->assertArrayHasKey('timecreated', $issue); + } + } + + /** + * Test for provider::delete_data_for_all_users_in_context(). + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + + $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + $customcert2 = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + + // Create users who will be issued a certificate. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->create_certificate_issue($customcert->id, $user1->id); + $this->create_certificate_issue($customcert->id, $user2->id); + + $this->create_certificate_issue($customcert2->id, $user1->id); + $this->create_certificate_issue($customcert2->id, $user2->id); + + // Before deletion, we should have 2 issued certificates for the first certificate. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert->id]); + $this->assertEquals(2, $count); + + // Delete data based on context. + $cmcontext = context_module::instance($customcert->cmid); + provider::delete_data_for_all_users_in_context($cmcontext); + + // After deletion, the issued certificates for the activity should have been deleted. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert->id]); + $this->assertEquals(0, $count); + + // We should still have the issues for the second certificate. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert2->id]); + $this->assertEquals(2, $count); + } + + /** + * Test for provider::delete_data_for_user(). + */ + public function test_delete_data_for_user() { + global $DB; + + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + + $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + + // Create users who will be issued a certificate. + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + + $this->create_certificate_issue($customcert->id, $user1->id); + $this->create_certificate_issue($customcert->id, $user2->id); + + // Before deletion we should have 2 issued certificates. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert->id]); + $this->assertEquals(2, $count); + + $context = \context_module::instance($customcert->cmid); + $contextlist = new \core_privacy\local\request\approved_contextlist($user1, 'customcert', + [$context->id]); + provider::delete_data_for_user($contextlist); + + // After deletion, the issued certificates for the first user should have been deleted. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert->id, 'userid' => $user1->id]); + $this->assertEquals(0, $count); + + // Check the issue for the other user is still there. + $customcertissue = $DB->get_records('customcert_issues'); + $this->assertCount(1, $customcertissue); + $lastissue = reset($customcertissue); + $this->assertEquals($user2->id, $lastissue->userid); + } + + /** + * Test for provider::delete_data_for_users(). + */ + public function test_delete_data_for_users() { + global $DB; + + $this->resetAfterTest(); + + // Create course, customcert and users who will be issued a certificate. + $course = $this->getDataGenerator()->create_course(); + $customcert1 = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + $customcert2 = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id)); + $cm1 = get_coursemodule_from_instance('customcert', $customcert1->id); + $cm2 = get_coursemodule_from_instance('customcert', $customcert2->id); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + $this->create_certificate_issue($customcert1->id, $user1->id); + $this->create_certificate_issue($customcert1->id, $user2->id); + $this->create_certificate_issue($customcert1->id, $user3->id); + $this->create_certificate_issue($customcert2->id, $user1->id); + $this->create_certificate_issue($customcert2->id, $user2->id); + + // Before deletion we should have 3 + 2 issued certificates. + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert1->id]); + $this->assertEquals(3, $count); + $count = $DB->count_records('customcert_issues', ['customcertid' => $customcert2->id]); + $this->assertEquals(2, $count); + + $context1 = context_module::instance($cm1->id); + $approveduserlist = new \core_privacy\local\request\approved_userlist($context1, 'customcert', + [$user1->id, $user2->id]); + provider::delete_data_for_users($approveduserlist); + + // After deletion, the customcert of the 2 students provided above should have been deleted + // from the activity. So there should only remain 1 certificate which is for $user3. + $customcertissues1 = $DB->get_records('customcert_issues', ['customcertid' => $customcert1->id]); + $this->assertCount(1, $customcertissues1); + $lastissue = reset($customcertissues1); + $this->assertEquals($user3->id, $lastissue->userid); + + // Confirm that the certificates issues in the other activity are intact. + $customcertissues1 = $DB->get_records('customcert_issues', ['customcertid' => $customcert2->id]); + $this->assertCount(2, $customcertissues1); + } + + /** + * Mimicks the creation of a customcert issue. + * + * There is no API we can use to insert an customcert issue, so we + * will simply insert directly into the database. + * + * @param int $customcertid + * @param int $userid + */ + protected function create_certificate_issue(int $customcertid, int $userid) { + global $DB; + + static $i = 1; + + $customcertissue = new stdClass(); + $customcertissue->customcertid = $customcertid; + $customcertissue->userid = $userid; + $customcertissue->code = \mod_customcert\certificate::generate_code(); + $customcertissue->timecreated = time() + $i; + + // Insert the record into the database. + $DB->insert_record('customcert_issues', $customcertissue); + + $i++; + } +} diff --git a/version.php b/version.php index 915be45d1..96099f8a8 100644 --- a/version.php +++ b/version.php @@ -24,10 +24,10 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2017050518.01; // The current module version (Date: YYYYMMDDXX). -$plugin->requires = 2017050500; // Requires this Moodle version (3.3). +$plugin->version = 2017111308; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->cron = 0; // Period for cron to check this module (secs). $plugin->component = 'mod_customcert'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = "3.3.17"; // User-friendly version number. +$plugin->release = "3.4.7"; // User-friendly version number.