From 0bdc57856784b5dc46da6d295d15d69008fd703e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Wed, 24 Jan 2018 23:19:51 +0100 Subject: [PATCH] MDL-61477 admin: Allow plugins to act as alternate policy handlers The patch introduces a new site setting 'sitepolicyhandler' that can be either empty or contain a plugin component name. If it is empty, the site policy feature keeps working as before and the core keeps the control over it. If a plugin is specified, it is expected to implement the callback 'site_policy_handler' in its lib.php file. The callback should return a URL to a script where the user can accept the site policies. The plugin itself is responsible for setting the 'policyagreed' flag in the users table. The callback may return an empty value, in which case the user can continue using the site without being redirected. The patch adds support for a new pre-config flag NO_SITEPOLICY_CHECK. This constant should be defined and set to true if we should not check the user's policyagreed status during the require_login(). This is for pages where the user actually accepts the site policies and helps to avoid the redirect loop. --- admin/settings/security.php | 17 +++++++++++++++-- lang/en/admin.php | 4 ++++ lib/moodlelib.php | 28 ++++++++++++++++++++++++++-- lib/upgrade.txt | 6 ++++++ user/policy.php | 3 +++ 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/admin/settings/security.php b/admin/settings/security.php index 56b727acfe7..658a34e9545 100644 --- a/admin/settings/security.php +++ b/admin/settings/security.php @@ -54,8 +54,21 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page 3600 => new lang_string('numminutes', '', 60)))); $temp->add(new admin_setting_configcheckbox('extendedusernamechars', new lang_string('extendedusernamechars', 'admin'), new lang_string('configextendedusernamechars', 'admin'), 0)); - $temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'admin'), new lang_string('sitepolicy_help', 'admin'), '', PARAM_RAW)); - $temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'admin'), new lang_string('sitepolicyguest_help', 'admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW)); + + $sitepolicyhandlers = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')]; + foreach (get_plugins_with_function('site_policy_handler') as $ptype => $pnames) { + foreach ($pnames as $pname => $fname) { + $sitepolicyhandlers[$ptype.'_'.$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin', + ['name' => new lang_string('pluginname', $ptype.'_'.$pname), 'component' => $ptype.'_'.$pname]); + } + } + $temp->add(new admin_setting_configselect('sitepolicyhandler', new lang_string('sitepolicyhandler', 'core_admin'), + new lang_string('sitepolicyhandler_desc', 'core_admin'), '', $sitepolicyhandlers)); + $temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'core_admin'), + new lang_string('sitepolicy_help', 'core_admin'), '', PARAM_RAW)); + $temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'core_admin'), + new lang_string('sitepolicyguest_help', 'core_admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW)); + $temp->add(new admin_setting_configcheckbox('extendedusernamechars', new lang_string('extendedusernamechars', 'admin'), new lang_string('configextendedusernamechars', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('keeptagnamecase', new lang_string('keeptagnamecase','admin'),new lang_string('configkeeptagnamecase', 'admin'),'1')); diff --git a/lang/en/admin.php b/lang/en/admin.php index 5026aa2d107..2fd7068ccec 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -1053,6 +1053,10 @@ $string['sitemaintenancewarning2'] = 'Your site is currently in maintenance mode $string['sitepolicies'] = 'Site policies'; $string['sitepolicy'] = 'Site policy URL'; $string['sitepolicy_help'] = 'If you have a site policy that all registered users must see and agree to before using this site, then specify the URL to it here, otherwise leave this field blank. This setting can contain any public URL.'; +$string['sitepolicyhandler'] = 'Site policy handler'; +$string['sitepolicyhandler_desc'] = 'Select the component to handle user agreements to site policies. The default core handler provides a simple functionality controlled by the two other settings `sitepolicy` and `sitepolicyguest`. Alternative handlers may be provided by additional plugins and offer more advanced control of site policies.'; +$string['sitepolicyhandlercore'] = 'Default (core)'; +$string['sitepolicyhandlerplugin'] = '{$a->name} ({$a->component})'; $string['sitepolicyguest'] = 'Site policy URL for guests'; $string['sitepolicyguest_help'] = 'If you have a site policy that all guests must see and agree to before using this site, then specify the URL to it here, otherwise leave this field blank. This setting can contain any public URL. Note: access of not-logged-in users may be prevented with forcelogin setting.'; $string['sitesectionhelp'] = 'If selected, a topic section will be displayed on the site\'s front page.'; diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 0d7a623224e..28f9d13ce79 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -2715,9 +2715,33 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $ return; } + // Scripts have a chance to declare that $USER->policyagreed should not be checked. + // This is mostly for places where users are actually accepting the policies, to avoid the redirect loop. + if (!defined('NO_SITEPOLICY_CHECK')) { + define('NO_SITEPOLICY_CHECK', false); + } + // Check that the user has agreed to a site policy if there is one - do not test in case of admins. - if (!$USER->policyagreed and !is_siteadmin()) { - if (!empty($CFG->sitepolicy) and !isguestuser()) { + // Do not test if the script explicitly asked for skipping the site policies check. + // Also do not check during AJAX requests as they may be actually performed on pages where policies are accepted. + if (!$USER->policyagreed && !is_siteadmin() && !NO_SITEPOLICY_CHECK && !AJAX_SCRIPT) { + if (!empty($CFG->sitepolicyhandler)) { + try { + $handler = component_callback($CFG->sitepolicyhandler, 'site_policy_handler'); + } catch (Exception $e) { + debugging('Error while trying to execute the site_policy_handler callback!'); + $handler = false; + } + if (!empty($handler) && (empty($PAGE->url) || !$PAGE->url->compare(new moodle_url($handler), URL_MATCH_BASE))) { + if ($preventredirect) { + throw new moodle_exception('sitepolicynotagreed', 'error', '', $handler); + } + if ($setwantsurltome) { + $SESSION->wantsurl = qualified_me(); + } + redirect($handler); + } + } else if (!empty($CFG->sitepolicy) and !isguestuser()) { if ($preventredirect) { throw new moodle_exception('sitepolicynotagreed', 'error', '', $CFG->sitepolicy); } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 8e8e0514e81..648a82009be 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -16,6 +16,12 @@ information provided here is intended especially for developers. * XMLDB now validates the PATH attribute on every install.xml file. Both the XMLDB editor and installation will fail when a problem is detected with it. Please ensure your plugins contain correct directory relative paths. * Add recaptchalib_v2.php for support of reCAPTCHA v2. +* Plugins can provide a new callback 'site_policy_handler' if they implement an alternative mechanisms for site policies + managements and agreements. Administrators can define which component is to be used for handling site policies and + agreements. +* Scripts can define a constant NO_SITEPOLICY_CHECK and set it to true before requiring the main config.php file. It + will make the require_login() skipping the test for the user's policyagreed status. This is useful for plugins that + act as a site policy handler. === 3.4 === diff --git a/user/policy.php b/user/policy.php index 5186868c20d..d3dc4b5b9e2 100644 --- a/user/policy.php +++ b/user/policy.php @@ -22,6 +22,9 @@ * @package core_user */ +// Do not check for the site policies in require_login() to avoid the redirect loop. +define('NO_SITEPOLICY_CHECK', true); + require_once('../config.php'); require_once($CFG->libdir.'/filelib.php'); require_once($CFG->libdir.'/resourcelib.php');