From e4b5ce7293ce30436f0ca0eb53fe52c54032728e Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Sun, 12 Nov 2017 12:55:10 +0800 Subject: [PATCH] #142 Add setting to allow site-wide verification of certificates --- db/access.php | 8 +++ lang/en/customcert.php | 6 ++ settings.php | 6 ++ tests/behat/behat_mod_customcert.php | 32 ++++++++++ tests/behat/verify_certificates.feature | 54 +++++++++++++++++ verify_certificate.php | 77 ++++++++++++++++++------- version.php | 4 +- 7 files changed, 165 insertions(+), 22 deletions(-) diff --git a/db/access.php b/db/access.php index 5be7c8e..7848fed 100644 --- a/db/access.php +++ b/db/access.php @@ -88,4 +88,12 @@ $capabilities = array( 'manager' => CAP_ALLOW ) ), + + 'mod/customcert:verifyallcertificates' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_SYSTEM, + 'archetypes' => array( + 'manager' => CAP_ALLOW + ) + ), ); diff --git a/lang/en/customcert.php b/lang/en/customcert.php index fc8e399..054053d 100644 --- a/lang/en/customcert.php +++ b/lang/en/customcert.php @@ -25,6 +25,7 @@ $string['addcertpage'] = 'Add page'; $string['addelement'] = 'Add element'; $string['awardedto'] = 'Awarded to'; +$string['cannotverifyallcertificates'] = 'You do not have the permission to verify all certificates on the site.'; $string['certificate'] = 'Certificate'; $string['code'] = 'Code'; $string['copy'] = 'Copy'; @@ -38,6 +39,7 @@ $string['customcert:manage'] = 'Manage a custom certificate'; $string['customcert:view'] = 'View a custom certificate'; $string['customcert:viewreport'] = 'View course report'; $string['customcert:viewallcertificates'] = 'View all certificates'; +$string['customcert:verifyallcertificates'] = 'Verify all certificates on the site'; $string['customcert:verifycertificate'] = 'Verify a certificate'; $string['deletecertpage'] = 'Delete page'; $string['deleteconfirm'] = 'Delete confirmation'; @@ -159,6 +161,10 @@ $string['uploadimagedesc'] = 'This link will take you to a new screen where you this method will be available throughout your site to all users who are able to create a certificate.'; $string['verified'] = 'Verified'; $string['verify'] = 'Verify'; +$string['verifyallcertificates'] = 'Allow verification of all certificates'; +$string['verifyallcertificates_desc'] = 'When this setting is enabled any person (including users not logged in) can visit the link \'{$a}\' in order to verify any certificate on the site, rather than having to go to the verification link for each certificate. + +Note - this only applies to certificates where \'Allow anyone to verify a certificate\' has been set to \'Yes\' in the certificate settings.'; $string['verifycertificate'] = 'Verify certificate'; $string['verifycertificateanyone'] = 'Allow anyone to verify a certificate'; $string['verifycertificateanyone_help'] = 'This setting enables anyone with the certificate verification link (including users not logged in) to verify a certificate.'; diff --git a/settings.php b/settings.php index 01a998e..c6a7817 100644 --- a/settings.php +++ b/settings.php @@ -24,6 +24,12 @@ defined('MOODLE_INTERNAL') || die; +$url = $CFG->wwwroot . '/mod/customcert/verify_certificate.php'; +$settings->add(new admin_setting_configcheckbox('customcert/verifyallcertificates', + get_string('verifyallcertificates', 'customcert'), + get_string('verifyallcertificates_desc', 'customcert', $url), + 0)); + $settings->add(new admin_setting_configcheckbox('customcert/showposxy', get_string('showposxy', 'customcert'), get_string('showposxy_desc', 'customcert'), diff --git a/tests/behat/behat_mod_customcert.php b/tests/behat/behat_mod_customcert.php index bd7feed..8f2d6a6 100644 --- a/tests/behat/behat_mod_customcert.php +++ b/tests/behat/behat_mod_customcert.php @@ -95,6 +95,28 @@ class behat_mod_customcert extends behat_base { $this->execute('behat_forms::i_set_the_field_to', array(get_string('code', 'customcert'), $issue->code)); $this->execute('behat_forms::press_button', get_string('verify', 'customcert')); $this->execute('behat_general::assert_page_contains_text', get_string('verified', 'customcert')); + $this->execute('behat_general::assert_page_not_contains_text', get_string('notverified', 'customcert')); + } + + /** + * Verifies the certificate code for a user. + * + * @Given /^I can not verify the "(?P(?:[^"]|\\")*)" certificate for the user "(?P(?:[^"]|\\")*)"$/ + * @param string $certificatename + * @param string $username + */ + public function i_can_not_verify_the_custom_certificate_for_user($certificatename, $username) { + global $DB; + + $certificate = $DB->get_record('customcert', array('name' => $certificatename), '*', MUST_EXIST); + $user = $DB->get_record('user', array('username' => $username), '*', MUST_EXIST); + $issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $certificate->id), + '*', MUST_EXIST); + + $this->execute('behat_forms::i_set_the_field_to', array(get_string('code', 'customcert'), $issue->code)); + $this->execute('behat_forms::press_button', get_string('verify', 'customcert')); + $this->execute('behat_general::assert_page_contains_text', get_string('notverified', 'customcert')); + $this->execute('behat_general::assert_page_not_contains_text', get_string('verified', 'customcert')); } /** @@ -115,4 +137,14 @@ class behat_mod_customcert extends behat_base { $url = new moodle_url('/mod/customcert/verify_certificate.php', array('contextid' => $template->contextid)); $this->getSession()->visit($this->locate_path($url->out_as_local_url())); } + + /** + * Directs the user to the URL for verifying all certificates on the site. + * + * @Given /^I visit the verification url for the site$/ + */ + public function i_visit_the_verification_url_for_the_site() { + $url = new moodle_url('/mod/customcert/verify_certificate.php'); + $this->getSession()->visit($this->locate_path($url->out_as_local_url())); + } } diff --git a/tests/behat/verify_certificates.feature b/tests/behat/verify_certificates.feature index 814588a..7864397 100644 --- a/tests/behat/verify_certificates.feature +++ b/tests/behat/verify_certificates.feature @@ -52,3 +52,57 @@ Feature: Being able to verify that a certificate is valid or not And I press "Verify" And I should see "Not verified" And I verify the "Custom certificate 2" certificate for the user "student1" + + Scenario: Attempt to verify a certificate as a non-user using the site-wide URL + And the following config values are set as admin: + | verifyallcertificates | 0 | customcert | + And I visit the verification url for the site + # User should see an error message as we do not allow non-users to verify all certificates on the site. + And I should see "You do not have the permission to verify all certificates on the site" + + Scenario: Attempt to verify a certificate as a teacher using the site-wide URL + And the following config values are set as admin: + | verifyallcertificates | 0 | customcert | + And I log in as "teacher1" + And I visit the verification url for the site + # User should see an error message as we do not allow teachers to verify all certificates on the site. + And I should see "You do not have the permission to verify all certificates on the site" + + Scenario: Verify a certificate as an admin using the site-wide URL + And the following config values are set as admin: + | verifyallcertificates | 0 | customcert | + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Custom certificate 1" + And I press "Download certificate" + And I am on "Course 1" course homepage + And I follow "Custom certificate 2" + And I press "Download certificate" + And I log out + And I log in as "admin" + # The admin (or anyone with the capability 'mod/customcert:verifyallcertificates') can visit the URL regardless of the setting. + And I visit the verification url for the site + And I set the field "Code" to "NOTAVALIDCODE" + And I press "Verify" + And I should see "Not verified" + # The admin (or anyone with the capability 'mod/customcert:verifyallcertificates') can verify any certificate regardless of the 'verifyany' setting. + And I verify the "Custom certificate 1" certificate for the user "student1" + And I verify the "Custom certificate 2" certificate for the user "student1" + + Scenario: Verify a certificate as a non-user using the site-wide URL + And the following config values are set as admin: + | verifyallcertificates | 1 | customcert | + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Custom certificate 1" + And I press "Download certificate" + And I am on "Course 1" course homepage + And I follow "Custom certificate 2" + And I press "Download certificate" + And I log out + And I visit the verification url for the site + And I set the field "Code" to "NOTAVALIDCODE" + And I press "Verify" + And I should see "Not verified" + And I can not verify the "Custom certificate 1" certificate for the user "student1" + And I verify the "Custom certificate 2" certificate for the user "student1" diff --git a/verify_certificate.php b/verify_certificate.php index b96c880..f3a8cc9 100644 --- a/verify_certificate.php +++ b/verify_certificate.php @@ -24,27 +24,52 @@ require_once('../../config.php'); -$contextid = required_param('contextid', PARAM_INT); +$contextid = optional_param('contextid', context_system::instance()->id, PARAM_INT); $code = optional_param('code', '', PARAM_ALPHANUM); // The code for the certificate we are verifying. $context = context::instance_by_id($contextid); -$cm = get_coursemodule_from_id('customcert', $context->instanceid, 0, false, MUST_EXIST); -$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); -$customcert = $DB->get_record('customcert', array('id' => $cm->instance), '*', MUST_EXIST); - -// Check if we are allowing anyone to verify, if so, no need to check login, or permissions. -if (!$customcert->verifyany) { - // Need to be logged in. - require_login($course, false, $cm); - // Ok, now check the user has the ability to verify certificates. - require_capability('mod/customcert:verifycertificate', $context); -} else { - $PAGE->set_cm($cm, $course); -} - // Set up the page. $pageurl = new moodle_url('/mod/customcert/verify_certificate.php', array('contextid' => $contextid)); + +// Ok, a certificate was specified. +if ($context->contextlevel != CONTEXT_SYSTEM) { + $cm = get_coursemodule_from_id('customcert', $context->instanceid, 0, false, MUST_EXIST); + $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); + $customcert = $DB->get_record('customcert', array('id' => $cm->instance), '*', MUST_EXIST); + + // Check if we are allowing anyone to verify, if so, no need to check login, or permissions. + if (!$customcert->verifyany) { + // Need to be logged in. + require_login($course, false, $cm); + // Ok, now check the user has the ability to verify certificates. + require_capability('mod/customcert:verifycertificate', $context); + } else { + $PAGE->set_cm($cm, $course); + } + + $checkallofsite = false; +} else { + // If the 'verifyallcertificates' is not set and the user does not have the capability 'mod/customcert:verifyallcertificates' + // then show them a message letting them know they can not proceed. + $verifyallcertificates = get_config('customcert', 'verifyallcertificates'); + $canverifyallcertificates = has_capability('mod/customcert:verifyallcertificates', $context); + if (!$verifyallcertificates && !$canverifyallcertificates) { + $strheading = get_string('verifycertificate', 'customcert'); + $PAGE->navbar->add($strheading); + $PAGE->set_context(context_system::instance()); + $PAGE->set_title($strheading); + $PAGE->set_url($pageurl); + echo $OUTPUT->header(); + echo $OUTPUT->heading($strheading); + echo $OUTPUT->notification(get_string('cannotverifyallcertificates', 'customcert')); + echo $OUTPUT->footer(); + exit(); + } + + $checkallofsite = true; +} + if ($code) { $pageurl->param('code', $code); } @@ -63,7 +88,7 @@ if ($form->get_data()) { // Ok, now check if the code is valid. $userfields = get_all_user_name_fields(true, 'u'); $sql = "SELECT ci.id, u.id as userid, $userfields, co.id as courseid, - co.fullname as coursefullname, c.name as certificatename + co.fullname as coursefullname, c.name as certificatename, c.verifyany FROM {customcert} c JOIN {customcert_issues} ci ON c.id = ci.customcertid @@ -71,11 +96,23 @@ if ($form->get_data()) { ON c.course = co.id JOIN {user} u ON ci.userid = u.id - WHERE ci.code = :code - AND c.id = :customcertid - AND u.deleted = 0"; + WHERE ci.code = :code"; + + if ($checkallofsite) { + // Only people with the capability to verify all the certificates can verify any. + if (!$canverifyallcertificates) { + $sql .= " AND c.verifyany = 1"; + } + $params = ['code' => $code]; + } else { + $sql .= " AND c.id = :customcertid"; + $params = ['code' => $code, 'customcertid' => $customcert->id]; + } + + $sql .= " AND u.deleted = 0"; + // It is possible (though unlikely) that there is the same code for issued certificates. - if ($issues = $DB->get_records_sql($sql, array('code' => $code, 'customcertid' => $customcert->id))) { + if ($issues = $DB->get_records_sql($sql, $params)) { $result->success = true; $result->issues = $issues; } else { diff --git a/version.php b/version.php index b3535d7..cf94b47 100644 --- a/version.php +++ b/version.php @@ -24,10 +24,10 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2016052316; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2016052317; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2016052300; // Requires this Moodle version (3.1). $plugin->cron = 0; // Period for cron to check this module (secs). $plugin->component = 'mod_customcert'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = "3.1 release (Build: 2016052316)"; // User-friendly version number. +$plugin->release = "3.1 release (Build: 2016052317)"; // User-friendly version number.