Merge branch 'wip-MDL-61477-master-manager' of git://github.com/marinaglancy/moodle

This commit is contained in:
Jake Dallimore 2018-03-15 11:38:14 +08:00
commit 17b284b937
24 changed files with 938 additions and 88 deletions

View File

@ -123,43 +123,6 @@ if ($hassiteconfig) {
$temp->add($setting);
$ADMIN->add('authsettings', $temp);
$options = array(
0 => get_string('no'),
1 => get_string('yes')
);
$url = new moodle_url('/admin/settings.php?section=supportcontact');
$url = $url->out();
$setting = new admin_setting_configselect('agedigitalconsentverification',
new lang_string('agedigitalconsentverification', 'admin'),
new lang_string('agedigitalconsentverification_desc', 'admin', $url), 0, $options);
$setting->set_force_ltr(true);
$temp->add($setting);
$setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
new lang_string('ageofdigitalconsentmap', 'admin'),
new lang_string('ageofdigitalconsentmap_desc', 'admin'),
// See {@link https://gdpr-info.eu/art-8-gdpr/}.
implode(PHP_EOL, [
'*, 16',
'AT, 14',
'CZ, 13',
'DE, 14',
'DK, 13',
'ES, 13',
'FI, 15',
'GB, 13',
'HU, 14',
'IE, 13',
'LT, 16',
'LU, 16',
'NL, 16',
'PL, 13',
'SE, 13',
]),
PARAM_RAW
);
$temp->add($setting);
$temp = new admin_externalpage('authtestsettings', get_string('testsettings', 'core_auth'), new moodle_url("/auth/test_settings.php"), 'moodle/site:config', true);
$ADMIN->add('authsettings', $temp);

View File

@ -0,0 +1,80 @@
<?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/>.
/**
* Adds privacy and policies links to admin tree.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
// Privacy settings.
$temp = new admin_settingpage('privacysettings', new lang_string('privacysettings', 'admin'));
$options = array(
0 => get_string('no'),
1 => get_string('yes')
);
$url = new moodle_url('/admin/settings.php?section=supportcontact');
$url = $url->out();
$setting = new admin_setting_configselect('agedigitalconsentverification',
new lang_string('agedigitalconsentverification', 'admin'),
new lang_string('agedigitalconsentverification_desc', 'admin', $url), 0, $options);
$setting->set_force_ltr(true);
$temp->add($setting);
$setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
new lang_string('ageofdigitalconsentmap', 'admin'),
new lang_string('ageofdigitalconsentmap_desc', 'admin'),
// See {@link https://gdpr-info.eu/art-8-gdpr/}.
implode(PHP_EOL, [
'*, 16',
'AT, 14',
'CZ, 13',
'DE, 14',
'DK, 13',
'ES, 13',
'FI, 15',
'GB, 13',
'HU, 14',
'IE, 13',
'LT, 16',
'LU, 16',
'NL, 16',
'PL, 13',
'SE, 13',
]),
PARAM_RAW
);
$temp->add($setting);
$ADMIN->add('privacy', $temp);
// Policy settings.
$temp = new admin_settingpage('policysettings', new lang_string('policysettings', 'admin'));
$temp->add(new admin_settings_sitepolicy_handler_select('sitepolicyhandler', new lang_string('sitepolicyhandler', 'core_admin'),
new lang_string('sitepolicyhandler_desc', 'core_admin')));
$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));
$ADMIN->add('privacy', $temp);
}

View File

@ -54,8 +54,7 @@ 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));
$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'));

View File

@ -29,6 +29,7 @@ $ADMIN->add('root', new admin_category('badges', new lang_string('badges'), empt
$ADMIN->add('root', new admin_category('location', new lang_string('location','admin')));
$ADMIN->add('root', new admin_category('language', new lang_string('language')));
$ADMIN->add('root', new admin_category('modules', new lang_string('plugins', 'admin')));
$ADMIN->add('root', new admin_category('privacy', new lang_string('privacyandpolicies', 'admin')));
$ADMIN->add('root', new admin_category('security', new lang_string('security','admin')));
$ADMIN->add('root', new admin_category('appearance', new lang_string('appearance','admin')));
$ADMIN->add('root', new admin_category('frontpage', new lang_string('frontpage','admin')));

View File

@ -231,7 +231,9 @@ class api {
}
if (empty($section) or $section == 'sitepolicies') {
$settings->sitepolicy = $CFG->sitepolicy;
$manager = new \core_privacy\local\sitepolicy\manager();
$settings->sitepolicy = ($sitepolicy = $manager->get_embed_url()) ? $sitepolicy->out(false) : '';
$settings->sitepolicyhandler = $CFG->sitepolicyhandler;
$settings->disableuserimages = $CFG->disableuserimages;
}

View File

@ -156,6 +156,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
array('name' => 'newsitems', 'value' => $SITE->newsitems),
array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage),
array('name' => 'sitepolicy', 'value' => $mysitepolicy),
array('name' => 'sitepolicyhandler', 'value' => ''),
array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages),
array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)),
array('name' => 'tool_mobile_forcelogout', 'value' => 0),

View File

@ -88,8 +88,12 @@ class auth_email_external extends external_api {
if (!empty($CFG->passwordpolicy)) {
$result['passwordpolicy'] = print_password_policy();
}
if (!empty($CFG->sitepolicy)) {
$result['sitepolicy'] = $CFG->sitepolicy;
$manager = new \core_privacy\local\sitepolicy\manager();
if ($sitepolicy = $manager->get_embed_url()) {
$result['sitepolicy'] = $sitepolicy->out(false);
}
if (!empty($CFG->sitepolicyhandler)) {
$result['sitepolicyhandler'] = $CFG->sitepolicyhandler;
}
if (!empty($CFG->defaultcity)) {
$result['defaultcity'] = $CFG->defaultcity;
@ -138,6 +142,7 @@ class auth_email_external extends external_api {
),
'passwordpolicy' => new external_value(PARAM_RAW, 'Password policy', VALUE_OPTIONAL),
'sitepolicy' => new external_value(PARAM_RAW, 'Site policy', VALUE_OPTIONAL),
'sitepolicyhandler' => new external_value(PARAM_PLUGIN, 'Site policy handler', VALUE_OPTIONAL),
'defaultcity' => new external_value(PARAM_NOTAGS, 'Default city', VALUE_OPTIONAL),
'country' => new external_value(PARAM_ALPHA, 'Default country', VALUE_OPTIONAL),
'profilefields' => new external_multiple_structure(
@ -287,7 +292,8 @@ class auth_email_external extends external_api {
$data = $params;
$data['email2'] = $data['email'];
// Force policy agreed if a site policy is set. The client is responsible of implementing the interface check.
if (!empty($CFG->sitepolicy)) {
$manager = new \core_privacy\local\sitepolicy\manager();
if (!$manager->is_defined()) {
$data['policyagreed'] = 1;
}
unset($data['recaptcharesponse']);

View File

@ -6,7 +6,7 @@ Feature: Test validation of 'Age of digital consent' setting.
Background:
Given I log in as "admin"
And I navigate to "Manage authentication" node in "Site administration > Plugins > Authentication"
And I navigate to "Privacy settings" node in "Site administration > Privacy and policies"
Scenario: Admin provides valid value for 'Age of digital consent'.
Given I set the field "s__agedigitalconsentmap" to multiline:

View File

@ -5,6 +5,8 @@ information provided here is intended especially for developers.
* The auth_db and auth_ldap plugins' implementations of update_user_record() have been removed and both now
call the new implementation added in the base class.
* Self registration plugins should use core_privacy\local\sitepolicy\manager instead of directly checking
$CFG->sitepolicy , especially in custom signup forms. See https://docs.moodle.org/dev/Site_policy_handler
=== 3.3 ===

View File

@ -68,7 +68,7 @@ if ($course->id != SITEID) {
require_login($course, true, null, false);
} else if ($CFG->forcelogin) {
if (!empty($CFG->sitepolicy)
if (empty($CFG->sitepolicyhandler) and !empty($CFG->sitepolicy)
and ($CFG->sitepolicy == $CFG->wwwroot.'/file.php/'.$relativepath
or $CFG->sitepolicy == $CFG->wwwroot.'/file.php?file=/'.$relativepath)) {
//do not require login for policy file

View File

@ -860,6 +860,9 @@ $string['pluginscheckfailed'] = 'Dependencies check failed for {$a->pluginslist}
$string['pluginschecktodo'] = 'You must solve all the plugin requirements before proceeding to install this Moodle version!';
$string['pluginsoverview'] = 'Plugins overview';
$string['pluginsoverviewsee'] = 'See <a href="{$a->url}">plugins overview</a> page for more details.';
$string['policysettings'] = 'Policy settings';
$string['privacyandpolicies'] = 'Privacy and policies';
$string['privacysettings'] = 'Privacy settings';
$string['profilecategory'] = 'Category';
$string['profilecategoryname'] = 'Category name (must be unique)';
$string['profilecategorynamenotunique'] = 'This category name is already in use';
@ -1053,6 +1056,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.';

View File

@ -10626,3 +10626,46 @@ class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
return true;
}
}
/**
* Selection of plugins that can work as site policy handlers
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2018 Marina Glancy
*/
class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
/**
* Constructor
* @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
* for ones in config_plugins.
* @param string $visiblename localised
* @param string $description long localised info
* @param string $defaultsetting
*/
public function __construct($name, $visiblename, $description, $defaultsetting = '') {
parent::__construct($name, $visiblename, $description, $defaultsetting, null);
}
/**
* Lazy-load the available choices for the select box
*/
public function load_choices() {
if (during_initial_install()) {
return false;
}
if (is_array($this->choices)) {
return true;
}
$this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
$manager = new \core_privacy\local\sitepolicy\manager();
$plugins = $manager->get_all_handlers();
foreach ($plugins as $pname => $unused) {
$this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
}
return true;
}
}

View File

@ -2715,24 +2715,24 @@ 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.
if (!$USER->policyagreed && !is_siteadmin() && !NO_SITEPOLICY_CHECK) {
$manager = new \core_privacy\local\sitepolicy\manager();
if ($policyurl = $manager->get_redirect_url(isguestuser())) {
if ($preventredirect) {
throw new moodle_exception('sitepolicynotagreed', 'error', '', $CFG->sitepolicy);
throw new moodle_exception('sitepolicynotagreed', 'error', '', $policyurl->out());
}
if ($setwantsurltome) {
$SESSION->wantsurl = qualified_me();
}
redirect($CFG->wwwroot .'/user/policy.php');
} else if (!empty($CFG->sitepolicyguest) and isguestuser()) {
if ($preventredirect) {
throw new moodle_exception('sitepolicynotagreed', 'error', '', $CFG->sitepolicyguest);
}
if ($setwantsurltome) {
$SESSION->wantsurl = qualified_me();
}
redirect($CFG->wwwroot .'/user/policy.php');
redirect($policyurl);
}
}

View File

@ -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 define class 'PLUGINNAME\privacy\local\sitepolicy\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. See https://docs.moodle.org/dev/Site_policy_handler
* 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 ===

View File

@ -97,13 +97,10 @@ class login_signup_form extends moodleform implements renderable, templatable {
$mform->closeHeaderBefore('recaptcha_element');
}
if (!empty($CFG->sitepolicy)) {
$mform->addElement('header', 'policyagreement', get_string('policyagreement'), '');
$mform->setExpanded('policyagreement');
$mform->addElement('static', 'policylink', '', '<a href="'.$CFG->sitepolicy.'" onclick="this.target=\'_blank\'">'.get_String('policyagreementclick').'</a>');
$mform->addElement('checkbox', 'policyagreed', get_string('policyaccept'));
$mform->addRule('policyagreed', get_string('policyagree'), 'required', null, 'client');
}
// Add "Agree to sitepolicy" controls. By default it is a link to the policy text and a checkbox but
// it can be implemented differently in custom sitepolicy handlers.
$manager = new \core_privacy\local\sitepolicy\manager();
$manager->signup_form($mform);
// buttons
$this->add_action_buttons(true, get_string('createaccount'));

View File

@ -36,7 +36,8 @@ function message_popup_render_navbar_output(\renderer_base $renderer) {
// Early bail out conditions.
if (!isloggedin() || isguestuser() || user_not_fully_set_up($USER) ||
get_user_preferences('auth_forcepasswordchange') ||
($CFG->sitepolicy && !$USER->policyagreed && !is_siteadmin())) {
(!$USER->policyagreed && !is_siteadmin() &&
($manager = new \core_privacy\local\sitepolicy\manager()) && $manager->is_defined())) {
return '';
}

View File

@ -0,0 +1,86 @@
<?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/>.
/**
* Default (core) handler for site policies.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_privacy\local\sitepolicy;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Default (core) handler for site policies.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class default_handler extends handler {
/**
* Checks if the site has site policy defined
*
* @param bool $forguests
* @return bool
*/
public static function is_defined($forguests = false) {
global $CFG;
if (!$forguests) {
return !empty($CFG->sitepolicy);
} else {
return !empty($CFG->sitepolicyguest);
}
}
/**
* Returns URL to redirect user to when user needs to agree to site policy
*
* This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
* allow user to view policies and accept them.
*
* @param bool $forguests
* @return moodle_url|null (returns null if site policy is not defined)
*/
public static function get_redirect_url($forguests = false) {
return static::is_defined($forguests) ? new moodle_url('/user/policy.php') : null;
}
/**
* Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
*
* This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
* the "Accept" button and call {@link self::accept()} on completion.
*
* @param bool $forguests
* @return moodle_url|null
*/
public static function get_embed_url($forguests = false) {
global $CFG;
if ($forguests && !empty($CFG->sitepolicyguest)) {
return new moodle_url($CFG->sitepolicyguest);
} else if (!$forguests && !empty($CFG->sitepolicy)) {
return new moodle_url($CFG->sitepolicy);
}
return null;
}
}

View File

@ -0,0 +1,113 @@
<?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/>.
/**
* Base class for site policy handlers.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_privacy\local\sitepolicy;
defined('MOODLE_INTERNAL') || die();
/**
* Base class for site policy handlers.
*
* If a plugin wants to act as a site policy handler it has to define class
* PLUGINNAME\privacy\sitepolicy\handler that extends \core_privacy\sitepolicy\handler
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class handler {
/**
* Checks if the site has site policy defined
*
* @param bool $forguests
* @return bool
*/
public static function is_defined($forguests = false) {
$url = static::get_redirect_url($forguests);
return !empty($url);
}
/**
* Returns URL to redirect user to when user needs to agree to site policy
*
* This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
* allow user to view policies and accept them.
*
* @param bool $forguests
* @return moodle_url|null (returns null if site policy is not defined)
*/
abstract public static function get_redirect_url($forguests = false);
/**
* Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
*
* This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
* the "Accept" button and call {@link self::accept()} on completion.
*
* @param bool $forguests
* @return moodle_url|null
*/
abstract public static function get_embed_url($forguests = false);
/**
* Accept site policy for the current user
*
* @return bool - false if sitepolicy not defined, user is not logged in or user has already agreed to site policy;
* true - if we have successfully marked the user as agreed to the site policy
*/
public static function accept() {
global $USER, $DB;
if (!isloggedin()) {
return false;
}
if ($USER->policyagreed || !static::is_defined(isguestuser())) {
return false;
}
if (!isguestuser()) {
// For the guests agreement in stored in session only, for other users - in DB.
$DB->set_field('user', 'policyagreed', 1, array('id' => $USER->id));
}
$USER->policyagreed = 1;
return true;
}
/**
* Adds "Agree to site policy" checkbox to the signup form.
*
* Sitepolicy handlers can override the simple checkbox with their own controls.
*
* @param \MoodleQuickForm $mform
*/
public static function signup_form($mform) {
if ($url = static::get_embed_url()) {
$mform->addElement('header', 'policyagreement', get_string('policyagreement'), '');
$mform->setExpanded('policyagreement');
$mform->addElement('static', 'policylink', '', '<a href="' . $url .
'" onclick="this.target=\'_blank\'">' . get_string('policyagreementclick') . '</a>');
$mform->addElement('checkbox', 'policyagreed', get_string('policyaccept'));
$mform->addRule('policyagreed', get_string('policyagree'), 'required', null, 'client');
}
}
}

View File

@ -0,0 +1,137 @@
<?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/>.
/**
* Site policy management class.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_privacy\local\sitepolicy;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Site policy management class.
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/**
* Returns the list of plugins that can work as sitepolicy handlers (have class PLUGINNAME\privacy\sitepolicy\handler)
* @return array
*/
public function get_all_handlers() {
$sitepolicyhandlers = [];
foreach (\core_component::get_plugin_types() as $ptype => $unused) {
$plugins = \core_component::get_plugin_list_with_class($ptype, 'privacy\local\sitepolicy\handler') +
\core_component::get_plugin_list_with_class($ptype, 'privacy_local_sitepolicy_handler');
// Allow plugins to have the class either with namespace or without (useful for unittest).
foreach ($plugins as $pname => $class) {
$sitepolicyhandlers[$pname] = $class;
}
}
return $sitepolicyhandlers;
}
/**
* Returns the current site policy handler
*
* @return handler
*/
public function get_handler_classname() {
global $CFG;
if (!empty($CFG->sitepolicyhandler)) {
$sitepolicyhandlers = $this->get_all_handlers();
$classname = $sitepolicyhandlers[$CFG->sitepolicyhandler];
return $classname;
} else {
return default_handler::class;
}
}
/**
* Checks if the site has site policy defined
*
* @param bool $forguests
* @return bool
*/
public function is_defined($forguests = false) {
return component_class_callback($this->get_handler_classname(), 'is_defined', [$forguests]);
}
/**
* Returns URL to redirect user to when user needs to agree to site policy
*
* This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
* allow user to view policies and accept them.
*
* @param bool $forguests
* @return moodle_url|null (returns null if site policy is not defined)
*/
public function get_redirect_url($forguests = false) {
$url = component_class_callback($this->get_handler_classname(), 'get_redirect_url', [$forguests]);
if ($url && !($url instanceof moodle_url)) {
$url = new moodle_url($url);
}
return $url;
}
/**
* Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
*
* This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
* the "Accept" button and call {@link self::accept()} on completion.
*
* @param bool $forguests
* @return moodle_url|null
*/
public function get_embed_url($forguests = false) {
$url = component_class_callback($this->get_handler_classname(), 'get_embed_url', [$forguests]);
if ($url && !($url instanceof moodle_url)) {
$url = new moodle_url($url);
}
return $url;
}
/**
* Accept site policy for the current user
*
* @return bool - false if sitepolicy not defined, user is not logged in or user has already agreed to site policy;
* true - if we have successfully marked the user as agreed to the site policy
*/
public function accept() {
return component_class_callback($this->get_handler_classname(), 'accept', []);
}
/**
* Adds "Agree to site policy" checkbox to the signup form.
*
* Sitepolicy handlers can override the simple checkbox with their own controls.
*
* @param \MoodleQuickForm $mform
*/
public function signup_form($mform) {
component_class_callback($this->get_handler_classname(), 'signup_form', [$mform]);
}
}

View File

@ -0,0 +1,77 @@
<?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/>.
/**
* Mock handler for site policies
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Mock handler for site policies
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mock_sitepolicy_handler extends \core_privacy\local\sitepolicy\handler {
/**
* Returns URL to redirect user to when user needs to agree to site policy
*
* This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
* allow user to view policies and accept them.
*
* @param bool $forguests
* @return moodle_url|null (returns null if site policy is not defined)
*/
public static function get_redirect_url($forguests = false) {
return 'http://example.com/policy.php';
}
/**
* Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
*
* This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
* the "Accept" button and call {@link self::accept()} on completion.
*
* @param bool $forguests
* @return moodle_url|null
*/
public static function get_embed_url($forguests = false) {
return 'http://example.com/view.htm';
}
/**
* Accept site policy for the current user
*
* @return bool - false if sitepolicy not defined, user is not logged in or user has already agreed to site policy;
* true - if we have successfully marked the user as agreed to the site policy
*/
public static function accept() {
global $USER, $DB;
// Accepts policy on behalf of the current user. We set it to 2 here to check that this callback was called.
$USER->policyagreed = 2;
if (!isguestuser()) {
$DB->update_record('user', ['policyagreed' => 2, 'id' => $USER->id]);
}
return true;
}
}

View File

@ -0,0 +1,324 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit Tests for sitepolicy manager
*
* @package core_privacy
* @category test
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
/**
* Unit Tests for sitepolicy manager
*
* @package core_privacy
* @category test
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sitepolicy_test extends advanced_testcase {
/**
* Tests for \core_privacy\local\sitepolicy\manager::is_defined()
*/
public function test_is_defined() {
global $CFG;
$this->resetAfterTest(true);
$manager = new \core_privacy\local\sitepolicy\manager();
$this->assertFalse($manager->is_defined(true));
$this->assertFalse($manager->is_defined(false));
$CFG->sitepolicy = 'http://example.com/sitepolicy.html';
$this->assertFalse($manager->is_defined(true));
$this->assertTrue($manager->is_defined(false));
$CFG->sitepolicyguest = 'http://example.com/sitepolicyguest.html';
$this->assertTrue($manager->is_defined(true));
$this->assertTrue($manager->is_defined(false));
$CFG->sitepolicy = null;
$this->assertTrue($manager->is_defined(true));
$this->assertFalse($manager->is_defined(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url()
*/
public function test_get_redirect_url() {
global $CFG;
$this->resetAfterTest(true);
$manager = new \core_privacy\local\sitepolicy\manager();
$this->assertEquals(null, $manager->get_redirect_url(true));
$this->assertEquals(null, $manager->get_redirect_url(false));
$CFG->sitepolicy = 'http://example.com/sitepolicy.html';
$this->assertEquals(null, $manager->get_redirect_url(true));
$this->assertEquals($CFG->wwwroot.'/user/policy.php', $manager->get_redirect_url(false)->out(false));
$CFG->sitepolicyguest = 'http://example.com/sitepolicyguest.html';
$this->assertEquals($CFG->wwwroot.'/user/policy.php', $manager->get_redirect_url(true)->out(false));
$this->assertEquals($CFG->wwwroot.'/user/policy.php', $manager->get_redirect_url(false)->out(false));
$CFG->sitepolicy = null;
$this->assertEquals($CFG->wwwroot.'/user/policy.php', $manager->get_redirect_url(true)->out(false));
$this->assertEquals(null, $manager->get_redirect_url(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url()
*/
public function test_get_embed_url() {
global $CFG;
$this->resetAfterTest(true);
$manager = new \core_privacy\local\sitepolicy\manager();
$this->assertEquals(null, $manager->get_embed_url(true));
$this->assertEquals(null, $manager->get_embed_url(false));
$CFG->sitepolicy = 'http://example.com/sitepolicy.html';
$this->assertEquals(null, $manager->get_embed_url(true));
$this->assertEquals($CFG->sitepolicy, $manager->get_embed_url(false)->out(false));
$CFG->sitepolicyguest = 'http://example.com/sitepolicyguest.html';
$this->assertEquals($CFG->sitepolicyguest, $manager->get_embed_url(true)->out(false));
$this->assertEquals($CFG->sitepolicy, $manager->get_embed_url(false)->out(false));
$CFG->sitepolicy = null;
$this->assertEquals($CFG->sitepolicyguest, $manager->get_embed_url(true)->out(false));
$this->assertEquals(null, $manager->get_embed_url(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url()
*/
public function test_accept() {
global $CFG, $USER, $DB;
$this->resetAfterTest(true);
$manager = new \core_privacy\local\sitepolicy\manager();
// No site policy.
$user1 = $this->getDataGenerator()->create_user();
$this->setUser($user1);
$this->assertFalse($manager->accept());
$this->assertEquals(0, $USER->policyagreed);
// With site policy.
$CFG->sitepolicy = 'http://example.com/sitepolicy.html';
$user2 = $this->getDataGenerator()->create_user();
$this->setUser($user2);
$this->assertEquals(0, $USER->policyagreed);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
$this->assertTrue($manager->accept());
$this->assertEquals(1, $USER->policyagreed);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url() for guests
*/
public function test_accept_guests() {
global $CFG, $USER, $DB;
$this->resetAfterTest(true);
$manager = new \core_privacy\local\sitepolicy\manager();
$this->setGuestUser();
// No site policy.
$this->assertFalse($manager->accept());
$this->assertEquals(0, $USER->policyagreed);
// With site policy.
$CFG->sitepolicyguest = 'http://example.com/sitepolicy.html';
$this->assertEquals(0, $USER->policyagreed);
$this->assertTrue($manager->accept());
$this->assertEquals(1, $USER->policyagreed);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
}
/**
* Helper to spoof the results of the internal function get_all_handlers, allowing mock handler to be tested.
*
* @return PHPUnit_Framework_MockObject_MockObject
*/
protected function get_mock_manager_with_handler() {
global $CFG;
require_once($CFG->dirroot.'/privacy/tests/fixtures/mock_sitepolicy_handler.php');
$mock = $this->getMockBuilder(\core_privacy\local\sitepolicy\manager::class)
->setMethods(['get_all_handlers'])
->getMock();
$mock->expects($this->any())
->method('get_all_handlers')
->will($this->returnValue(['testtool_testhandler' => 'mock_sitepolicy_handler']));
return $mock;
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::is_defined() with a handler
*/
public function test_is_defined_with_handler() {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'testtool_testhandler';
$manager = $this->get_mock_manager_with_handler();
$this->assertTrue($manager->is_defined(true));
$this->assertTrue($manager->is_defined(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url() with a handler
*/
public function test_get_redirect_url_with_handler() {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'testtool_testhandler';
$manager = $this->get_mock_manager_with_handler();
$this->assertEquals('http://example.com/policy.php', $manager->get_redirect_url(true)->out(false));
$this->assertEquals('http://example.com/policy.php', $manager->get_redirect_url(false)->out(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url() with a handler
*/
public function test_get_embed_url_with_handler() {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'testtool_testhandler';
$manager = $this->get_mock_manager_with_handler();
$this->assertEquals('http://example.com/view.htm', $manager->get_embed_url(true)->out(false));
$this->assertEquals('http://example.com/view.htm', $manager->get_embed_url(false)->out(false));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url() with a handler
*/
public function test_accept_with_handler() {
global $CFG, $USER, $DB;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'testtool_testhandler';
$manager = $this->get_mock_manager_with_handler();
$user2 = $this->getDataGenerator()->create_user();
$this->setUser($user2);
$this->assertEquals(0, $USER->policyagreed);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
$this->assertTrue($manager->accept());
$this->assertEquals(2, $USER->policyagreed);
$this->assertEquals(2, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
}
/**
* Tests for \core_privacy\local\sitepolicy\manager::get_redirect_url() for guests with a handler
*/
public function test_accept_guests_with_handler() {
global $CFG, $USER, $DB;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'testtool_testhandler';
$manager = $this->get_mock_manager_with_handler();
$this->setGuestUser();
$this->assertEquals(0, $USER->policyagreed);
$this->assertTrue($manager->accept());
$this->assertEquals(2, $USER->policyagreed);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $USER->id]));
}
}
/**
* Mock handler for site policies
*
* @package core_privacy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class handler extends \core_privacy\local\sitepolicy\handler {
/**
* Checks if the site has site policy defined
*
* @param bool $forguests
* @return bool
*/
public static function is_defined($forguests = false) {
return true;
}
/**
* Returns URL to redirect user to when user needs to agree to site policy
*
* This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
* allow user to view policies and accept them.
*
* @param bool $forguests
* @return moodle_url|null (returns null if site policy is not defined)
*/
public static function get_redirect_url($forguests = false) {
return 'http://example.com/policy.php';
}
/**
* Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
*
* This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
* the "Accept" button and call {@link self::accept()} on completion.
*
* @param bool $forguests
* @return moodle_url|null
*/
public static function get_embed_url($forguests = false) {
return 'http://example.com/view.htm';
}
/**
* Accept site policy for the current user
*
* @return bool - false if sitepolicy not defined, user is not logged in or user has already agreed to site policy;
* true - if we have successfully marked the user as agreed to the site policy
*/
public static function accept() {
global $USER, $DB;
// Accepts policy on behalf of the current user. We set it to 2 here to check that this callback was called.
$USER->policyagreed = 2;
if (!isguestuser()) {
$DB->update_record('user', ['policyagreed' => 2, 'id' => $USER->id]);
}
return true;
}
}

View File

@ -72,7 +72,8 @@ class user_profile_set extends \core_analytics\local\indicator\linear {
// Nothing set results in -1.
$calculatedvalue = self::MIN_VALUE;
if (!empty($CFG->sitepolicy) && !$user->policyagreed) {
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
if ($sitepolicymanager->is_defined() && !$user->policyagreed) {
return self::MIN_VALUE;
}

View File

@ -1828,15 +1828,8 @@ class core_user_external extends external_api {
}
}
if (empty($CFG->sitepolicy)) {
$status = false;
$warnings[] = array(
'item' => 'user',
'itemid' => $USER->id,
'warningcode' => 'nositepolicy',
'message' => 'The site does not have a site policy configured.'
);
} else if (!empty($USER->policyagreed)) {
$manager = new \core_privacy\local\sitepolicy\manager();
if (!empty($USER->policyagreed)) {
$status = false;
$warnings[] = array(
'item' => 'user',
@ -1844,10 +1837,16 @@ class core_user_external extends external_api {
'warningcode' => 'alreadyagreed',
'message' => 'The user already agreed the site policy.'
);
} else if (!$manager->is_defined()) {
$status = false;
$warnings[] = array(
'item' => 'user',
'itemid' => $USER->id,
'warningcode' => 'nositepolicy',
'message' => 'The site does not have a site policy configured.'
);
} else {
$DB->set_field('user', 'policyagreed', 1, array('id' => $USER->id));
$USER->policyagreed = 1;
$status = true;
$status = $manager->accept();
}
$result = array();

View File

@ -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');
@ -35,28 +38,30 @@ if (!isloggedin()) {
require_login();
}
if (isguestuser()) {
$sitepolicy = $CFG->sitepolicyguest;
} else {
$sitepolicy = $CFG->sitepolicy;
}
if (!empty($SESSION->wantsurl)) {
$return = $SESSION->wantsurl;
} else {
$return = $CFG->wwwroot.'/';
}
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
if (!empty($CFG->sitepolicyhandler)) {
// We are on the wrong page, site policies are managed by somebody else.
if ($sitepolicyurl = $sitepolicymanager->get_redirect_url(isguestuser())) {
redirect($sitepolicyurl);
} else {
redirect($return);
}
}
$sitepolicy = $sitepolicymanager->get_embed_url(isguestuser());
if (empty($sitepolicy)) {
// Nothing to agree to, sorry, hopefully we will not get to infinite loop.
redirect($return);
}
if ($agree and confirm_sesskey()) { // User has agreed.
if (!isguestuser()) { // Don't remember guests.
$DB->set_field('user', 'policyagreed', 1, array('id' => $USER->id));
}
$USER->policyagreed = 1;
$sitepolicymanager->accept();
unset($SESSION->wantsurl);
redirect($return);
}