first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
<?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/>.
/**
* Accept or revoke policies on behalf of users (non-JS version)
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__.'/../../../config.php');
require_once($CFG->dirroot.'/user/editlib.php');
$userids = optional_param_array('userids', null, PARAM_INT);
$versionids = optional_param_array('versionids', null, PARAM_INT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
$action = optional_param('action', null, PARAM_ALPHA);
require_login();
if (isguestuser()) {
throw new \moodle_exception('noguest');
}
$context = context_system::instance();
$PAGE->set_context($context);
$PAGE->set_url(new moodle_url('/admin/tool/policy/accept.php'));
if (!in_array($action, ['accept', 'decline', 'revoke'])) {
throw new moodle_exception('invalidaccessparameter');
}
if ($returnurl) {
$returnurl = new moodle_url($returnurl);
} else if (count($userids) == 1) {
$userid = reset($userids);
$returnurl = new moodle_url('/admin/tool/policy/user.php', ['userid' => $userid]);
} else {
$returnurl = new moodle_url('/admin/tool/policy/acceptances.php');
}
// Initialise the form, this will also validate users, versions and check permission to accept policies.
$form = new \tool_policy\form\accept_policy(null,
['versionids' => $versionids, 'userids' => $userids, 'showbuttons' => true, 'action' => $action]);
$form->set_data(['returnurl' => $returnurl]);
if ($form->is_cancelled()) {
redirect($returnurl);
} else if ($form->get_data()) {
$form->process();
redirect($returnurl);
}
$output = $PAGE->get_renderer('tool_policy');
echo $output->header();
echo $output->heading(get_string('statusformtitle'.$action, 'tool_policy'));
$form->display();
echo $output->footer();
+65
View File
@@ -0,0 +1,65 @@
<?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/>.
/**
* View user acceptances to the policies
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
use core\output\notification;
$policyid = optional_param('policyid', null, PARAM_INT);
$versionid = optional_param('versionid', null, PARAM_INT);
$versionid = optional_param('versionid', null, PARAM_INT);
$filtersapplied = optional_param_array('unified-filters', [], PARAM_NOTAGS);
$acceptancesfilter = new \tool_policy\output\acceptances_filter($policyid, $versionid, $filtersapplied);
$policyid = $acceptancesfilter->get_policy_id_filter();
$versionid = $acceptancesfilter->get_version_id_filter();
// Set up the page as an admin page 'tool_policy_managedocs'.
$urlparams = ($policyid ? ['policyid' => $policyid] : []) + ($versionid ? ['versionid' => $versionid] : []);
admin_externalpage_setup('tool_policy_acceptances', '', $urlparams,
new moodle_url('/admin/tool/policy/acceptances.php'));
$acceptancesfilter->validate_ids();
$output = $PAGE->get_renderer('tool_policy');
if ($acceptancesfilter->get_versions()) {
$acceptances = new \tool_policy\acceptances_table('tool_policy_user_acceptances', $acceptancesfilter, $output);
if ($acceptances->is_downloading()) {
$acceptances->download();
}
}
echo $output->header();
echo $output->heading(get_string('useracceptances', 'tool_policy'));
echo $output->render($acceptancesfilter);
if (!empty($acceptances)) {
$acceptances->display();
} else if ($acceptancesfilter->get_avaliable_policies()) {
// There are no non-guest policies.
echo $output->notification(get_string('selectpolicyandversion', 'tool_policy'), notification::NOTIFY_INFO);
} else {
// There are no non-guest policies.
echo $output->notification(get_string('nopolicies', 'tool_policy'), notification::NOTIFY_INFO);
}
echo $output->footer();
+10
View File
@@ -0,0 +1,10 @@
/**
* Unified filter page JS module for the course participants page.
*
* @module tool_policy/acceptances_filter
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_policy/acceptances_filter",["jquery","core/form-autocomplete","core/str","core/notification"],(function($,Autocomplete,Str,Notification){var SELECTORS_UNIFIED_FILTERS="#unified-filters";return{init:function(){!function(){M.util.js_pending("acceptances_filter_datasource"),Str.get_strings([{key:"filterplaceholder",component:"tool_policy"},{key:"nofiltersapplied",component:"tool_policy"}]).done((function(langstrings){var placeholder=langstrings[0],noSelectionString=langstrings[1];Autocomplete.enhance(SELECTORS_UNIFIED_FILTERS,!0,"tool_policy/acceptances_filter_datasource",placeholder,!1,!0,noSelectionString,!0).then((function(){M.util.js_complete("acceptances_filter_datasource")})).fail(Notification.exception)})).fail(Notification.exception);var last=$(SELECTORS_UNIFIED_FILTERS).val();$(SELECTORS_UNIFIED_FILTERS).on("change",(function(){var current=$(this).val(),listoffilters=[],textfilters=[],updatedselectedfilters=!1;if($.each(current,(function(index,catoption){var catandoption=catoption.split(":",2);if(2!==catandoption.length)return textfilters.push(catoption),!0;var category=catandoption[0],option=catandoption[1];return void 0!==listoffilters[category]&&(updatedselectedfilters=!0),listoffilters[category]=option,!0})),updatedselectedfilters){var updatefilters=[];for(var category in listoffilters)updatefilters.push(category+":"+listoffilters[category]);updatefilters=updatefilters.concat(textfilters),$(this).val(updatefilters)}last.join(",")!=current.join(",")&&this.form.submit()}))}()},getForm:function(){return $(SELECTORS_UNIFIED_FILTERS).closest("form")}}}));
//# sourceMappingURL=acceptances_filter.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
/**
* Datasource for the tool_policy/acceptances_filter.
*
* This module is compatible with core/form-autocomplete.
*
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_policy/acceptances_filter_datasource",["jquery","core/ajax","core/notification"],(function($,Ajax,Notification){return{list:function(selector,query){var filteredOptions=[],el=$(selector),originalOptions=$(selector).data("originaloptionsjson"),selectedFilters=el.val();$.each(originalOptions,(function(index,option){return""!==query.trim()&&-1===option.label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase())||$.inArray(option.value,selectedFilters)>-1||filteredOptions.push(option),!0}));var deferred=new $.Deferred;return deferred.resolve(filteredOptions),deferred.promise()},processResults:function(selector,results){var options=[];return $.each(results,(function(index,data){options.push({value:data.value,label:data.label})})),options},transport:function(selector,query,callback){this.list(selector,query).then(callback).catch(Notification.exception)}}}));
//# sourceMappingURL=acceptances_filter_datasource.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"acceptances_filter_datasource.min.js","sources":["../src/acceptances_filter_datasource.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Datasource for the tool_policy/acceptances_filter.\n *\n * This module is compatible with core/form-autocomplete.\n *\n * @copyright 2017 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n return /** @alias module:tool_policy/acceptances_filter_datasource */ {\n /**\n * List filter options.\n *\n * @param {String} selector The select element selector.\n * @param {String} query The query string.\n * @return {Promise}\n */\n list: function(selector, query) {\n var filteredOptions = [];\n\n var el = $(selector);\n var originalOptions = $(selector).data('originaloptionsjson');\n var selectedFilters = el.val();\n $.each(originalOptions, function(index, option) {\n // Skip option if it does not contain the query string.\n if (query.trim() !== '' && option.label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) === -1) {\n return true;\n }\n // Skip filters that have already been selected.\n if ($.inArray(option.value, selectedFilters) > -1) {\n return true;\n }\n\n filteredOptions.push(option);\n return true;\n });\n\n var deferred = new $.Deferred();\n deferred.resolve(filteredOptions);\n\n return deferred.promise();\n },\n\n /**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results.\n * @return {Array} New array of results.\n */\n processResults: function(selector, results) {\n var options = [];\n $.each(results, function(index, data) {\n options.push({\n value: data.value,\n label: data.label\n });\n });\n return options;\n },\n\n /**\n * Source of data for Ajax element.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n */\n /* eslint-disable promise/no-callback-in-promise */\n transport: function(selector, query, callback) {\n this.list(selector, query).then(callback).catch(Notification.exception);\n }\n };\n\n});\n"],"names":["define","$","Ajax","Notification","list","selector","query","filteredOptions","el","originalOptions","data","selectedFilters","val","each","index","option","trim","label","toLocaleLowerCase","indexOf","inArray","value","push","deferred","Deferred","resolve","promise","processResults","results","options","transport","callback","then","catch","exception"],"mappings":";;;;;;;;AAwBAA,mDAAO,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,oBAEG,CAQlEC,KAAM,SAASC,SAAUC,WACjBC,gBAAkB,GAElBC,GAAKP,EAAEI,UACPI,gBAAkBR,EAAEI,UAAUK,KAAK,uBACnCC,gBAAkBH,GAAGI,MACzBX,EAAEY,KAAKJ,iBAAiB,SAASK,MAAOC,cAEf,KAAjBT,MAAMU,SAA0F,IAAzED,OAAOE,MAAMC,oBAAoBC,QAAQb,MAAMY,sBAItEjB,EAAEmB,QAAQL,OAAOM,MAAOV,kBAAoB,GAIhDJ,gBAAgBe,KAAKP,SAPV,SAWXQ,SAAW,IAAItB,EAAEuB,gBACrBD,SAASE,QAAQlB,iBAEVgB,SAASG,WAUpBC,eAAgB,SAAStB,SAAUuB,aAC3BC,QAAU,UACd5B,EAAEY,KAAKe,SAAS,SAASd,MAAOJ,MAC5BmB,QAAQP,KAAK,CACTD,MAAOX,KAAKW,MACZJ,MAAOP,KAAKO,WAGbY,SAWXC,UAAW,SAASzB,SAAUC,MAAOyB,eAC5B3B,KAAKC,SAAUC,OAAO0B,KAAKD,UAAUE,MAAM9B,aAAa+B"}
+10
View File
@@ -0,0 +1,10 @@
/**
* Add policy consent modal to the page
*
* @module tool_policy/acceptmodal
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_policy/acceptmodal",["jquery","core/str","core/modal_save_cancel","core/modal_events","core/notification","core/fragment","core/ajax","core_form/changechecker"],(function($,Str,ModalSaveCancel,ModalEvents,Notification,Fragment,Ajax,FormChangeChecker){var AcceptOnBehalf=function(contextid){this.contextid=contextid,this.init()};return AcceptOnBehalf.prototype.modal=null,AcceptOnBehalf.prototype.contextid=-1,AcceptOnBehalf.prototype.currentTrigger=null,AcceptOnBehalf.prototype.triggers={SINGLE:"a[data-action=acceptmodal]",BULK:"input[data-action=acceptmodal]"},AcceptOnBehalf.prototype.init=function(){$(this.triggers.SINGLE).on("click",function(e){e.preventDefault(),this.currentTrigger=$(e.currentTarget);var href=$(e.currentTarget).attr("href"),formData=href.slice(href.indexOf("?")+1);this.showFormModal(formData)}.bind(this)),$(this.triggers.BULK).on("click",function(e){e.preventDefault(),this.currentTrigger=$(e.currentTarget);var form=$(e.currentTarget).closest("form");if(form.find('input[type=checkbox][name="userids[]"]:checked').length){var formData=form.serialize();this.showFormModal(formData)}else Str.get_strings([{key:"notice"},{key:"selectusersforconsent",component:"tool_policy"},{key:"ok"}]).then((function(strings){Notification.alert(strings[0],strings[1],strings[2])})).fail(Notification.exception)}.bind(this))},AcceptOnBehalf.prototype.showFormModal=function(formData){for(var action,params=formData.split("&"),i=0;i<params.length;i++){var pair=params[i].split("=");"action"==pair[0]&&(action=pair[1])}Str.get_strings([{key:"statusformtitleaccept",component:"tool_policy"},{key:"iagreetothepolicy",component:"tool_policy"},{key:"statusformtitlerevoke",component:"tool_policy"},{key:"irevokethepolicy",component:"tool_policy"},{key:"statusformtitledecline",component:"tool_policy"},{key:"declinethepolicy",component:"tool_policy"}]).then(function(strings){var title,saveText;return"accept"==action?(title=strings[0],saveText=strings[1]):"revoke"==action?(title=strings[2],saveText=strings[3]):"decline"==action&&(title=strings[4],saveText=strings[5]),ModalSaveCancel.create({title:title,body:""}).then(function(modal){this.modal=modal,this.setupFormModal(formData,saveText)}.bind(this))}.bind(this)).catch(Notification.exception)},AcceptOnBehalf.prototype.setupFormModal=function(formData,saveText){var modal=this.modal;modal.setLarge(),modal.setSaveButtonText(saveText),modal.getRoot().on(ModalEvents.hidden,this.destroy.bind(this)),modal.setBody(this.getBody(formData)),modal.getRoot().on(ModalEvents.save,this.submitForm.bind(this)),modal.getRoot().on("submit","form",this.submitFormAjax.bind(this)),modal.show()},AcceptOnBehalf.prototype.getBody=function(formData){void 0===formData&&(formData={});var params={jsonformdata:JSON.stringify(formData)};return Fragment.loadFragment("tool_policy","accept_on_behalf",this.contextid,params)},AcceptOnBehalf.prototype.submitFormAjax=function(e){e.preventDefault();var formData=this.modal.getRoot().find("form").serialize();Ajax.call([{methodname:"tool_policy_submit_accept_on_behalf",args:{jsonformdata:JSON.stringify(formData)}}])[0].done(function(data){data.validationerrors?this.modal.setBody(this.getBody(formData)):this.close()}.bind(this)).fail(Notification.exception)},AcceptOnBehalf.prototype.submitForm=function(e){e.preventDefault(),this.modal.getRoot().find("form").submit()},AcceptOnBehalf.prototype.close=function(){this.destroy(),document.location.reload()},AcceptOnBehalf.prototype.destroy=function(){FormChangeChecker.resetAllFormDirtyStates(),this.modal.destroy(),this.currentTrigger.focus()},{getInstance:function(contextid){return new AcceptOnBehalf(contextid)}}}));
//# sourceMappingURL=acceptmodal.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
define("tool_policy/jquery-eu-cookie-law-popup",["jquery"],(function($){window.console||(window.console={}),window.console.log||(window.console.log=function(){}),$.fn.euCookieLawPopup=function(){var _self=this;_self.params={cookiePolicyUrl:"/?cookie-policy",popupPosition:"top",colorStyle:"default",compactStyle:!1,popupTitle:"This website is using cookies",popupText:"We use cookies to ensure that we give you the best experience on our website. If you continue without changing your settings, we'll assume that you are happy to receive all cookies on this website.",buttonContinueTitle:"Continue",buttonLearnmoreTitle:"Learn&nbsp;more",buttonLearnmoreOpenInNewWindow:!0,agreementExpiresInDays:30,autoAcceptCookiePolicy:!1,htmlMarkup:null},_self.vars={INITIALISED:!1,HTML_MARKUP:null,COOKIE_NAME:"EU_COOKIE_LAW_CONSENT"};var setUserAcceptsCookies=function(consent){var d=new Date,expiresInDays=24*_self.params.agreementExpiresInDays*60*60*1e3;d.setTime(d.getTime()+expiresInDays);var expires="expires="+d.toGMTString();document.cookie=_self.vars.COOKIE_NAME+"="+consent+"; "+expires+";path=/",$(document).trigger("user_cookie_consent_changed",{consent:consent})},hideContainer=function(){$(".eupopup-container").animate({opacity:0,height:0},200,(function(){$(".eupopup-container").hide(0)}))};return{init:function(settings){!function(object,markup,settings){if(object){var className=$(object).attr("class")?$(object).attr("class"):"";className.indexOf("eupopup-top")>-1?_self.params.popupPosition="top":className.indexOf("eupopup-fixedtop")>-1?_self.params.popupPosition="fixedtop":className.indexOf("eupopup-bottomright")>-1?_self.params.popupPosition="bottomright":className.indexOf("eupopup-bottomleft")>-1?_self.params.popupPosition="bottomleft":className.indexOf("eupopup-bottom")>-1?_self.params.popupPosition="bottom":className.indexOf("eupopup-block")>-1&&(_self.params.popupPosition="block"),className.indexOf("eupopup-color-default")>-1?_self.params.colorStyle="default":className.indexOf("eupopup-color-inverse")>-1&&(_self.params.colorStyle="inverse"),className.indexOf("eupopup-style-compact")>-1&&(_self.params.compactStyle=!0)}markup&&(_self.params.htmlMarkup=markup),settings&&(void 0!==settings.cookiePolicyUrl&&(_self.params.cookiePolicyUrl=settings.cookiePolicyUrl),void 0!==settings.popupPosition&&(_self.params.popupPosition=settings.popupPosition),void 0!==settings.colorStyle&&(_self.params.colorStyle=settings.colorStyle),void 0!==settings.popupTitle&&(_self.params.popupTitle=settings.popupTitle),void 0!==settings.popupText&&(_self.params.popupText=settings.popupText),void 0!==settings.buttonContinueTitle&&(_self.params.buttonContinueTitle=settings.buttonContinueTitle),void 0!==settings.buttonLearnmoreTitle&&(_self.params.buttonLearnmoreTitle=settings.buttonLearnmoreTitle),void 0!==settings.buttonLearnmoreOpenInNewWindow&&(_self.params.buttonLearnmoreOpenInNewWindow=settings.buttonLearnmoreOpenInNewWindow),void 0!==settings.agreementExpiresInDays&&(_self.params.agreementExpiresInDays=settings.agreementExpiresInDays),void 0!==settings.autoAcceptCookiePolicy&&(_self.params.autoAcceptCookiePolicy=settings.autoAcceptCookiePolicy),void 0!==settings.htmlMarkup&&(_self.params.htmlMarkup=settings.htmlMarkup))}($(".eupopup").first(),$(".eupopup-markup").html(),settings),function(){for(var userAcceptedCookies=!1,cookies=document.cookie.split(";"),i=0;i<cookies.length;i++){var c=cookies[i].trim();-1!==c.indexOf(_self.vars.COOKIE_NAME)&&(userAcceptedCookies=c.substring(_self.vars.COOKIE_NAME.length+1,c.length))}return userAcceptedCookies}()?$(document).trigger("user_cookie_already_accepted",{consent:!0}):_self.vars.INITIALISED||(_self.vars.INITIALISED=!0,_self.vars.HTML_MARKUP=_self.params.htmlMarkup?_self.params.htmlMarkup:'<div class="eupopup-container eupopup-container-'+_self.params.popupPosition+(_self.params.compactStyle?" eupopup-style-compact":"")+" eupopup-color-"+_self.params.colorStyle+'"><div class="eupopup-head">'+_self.params.popupTitle+'</div><div class="eupopup-body">'+_self.params.popupText+'</div><div class="eupopup-buttons"><a href="#" class="eupopup-button eupopup-button_1">'+_self.params.buttonContinueTitle+'</a><a href="'+_self.params.cookiePolicyUrl+'"'+(_self.params.buttonLearnmoreOpenInNewWindow?" target=_blank ":"")+' class="eupopup-button eupopup-button_2">'+_self.params.buttonLearnmoreTitle+'</a><div class="clearfix"></div></div><a href="#" class="eupopup-closebutton">x</a></div>',$(".eupopup-block").length>0?$(".eupopup-block").append(_self.vars.HTML_MARKUP):$("BODY").append(_self.vars.HTML_MARKUP),$(".eupopup-button_1").click((function(){return setUserAcceptsCookies(!0),hideContainer(),!1})),$(".eupopup-closebutton").click((function(){return setUserAcceptsCookies(!0),hideContainer(),!1})),$(".eupopup-container").show(),_self.params.autoAcceptCookiePolicy&&setUserAcceptsCookies(!0))}}}}));
//# sourceMappingURL=jquery-eu-cookie-law-popup.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
/**
* Adds support for confirmation via JS modal for some management actions at the Manage policies page.
*
* @module tool_policy/managedocsactions
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_policy/managedocsactions",["jquery","core/log","core/config","core/str","core/modal_save_cancel","core/modal_events"],(function($,Log,Config,Str,ModalSaveCancel,ModalEvents){var ACTION_LINKS="[data-action]",ACTION_MAKE_CURRENT='[data-action="makecurrent"]',ACTION_INACTIVATE='[data-action="inactivate"]',ACTION_DELETE='[data-action="delete"]';function ManageDocsActions(base){this.base=base,this.initEvents()}return ManageDocsActions.prototype.initEvents=function(){this.base.on("click",ACTION_LINKS,(function(e){e.stopPropagation();var promise,strings,link=$(e.currentTarget);if(link.is(ACTION_MAKE_CURRENT))promise=Str.get_strings([{key:"activating",component:"tool_policy"},{key:"activateconfirm",component:"tool_policy",param:{name:link.closest("[data-policy-name]").attr("data-policy-name"),revision:link.closest("[data-policy-revision]").attr("data-policy-revision")}},{key:"activateconfirmyes",component:"tool_policy"}]);else if(link.is(ACTION_INACTIVATE))promise=Str.get_strings([{key:"inactivating",component:"tool_policy"},{key:"inactivatingconfirm",component:"tool_policy",param:{name:link.closest("[data-policy-name]").attr("data-policy-name"),revision:link.closest("[data-policy-revision]").attr("data-policy-revision")}},{key:"inactivatingconfirmyes",component:"tool_policy"}]);else{if(!link.is(ACTION_DELETE))return void Log.error("unknown action type detected","tool_policy/managedocsactions");promise=Str.get_strings([{key:"deleting",component:"tool_policy"},{key:"deleteconfirm",component:"tool_policy",param:{name:link.closest("[data-policy-name]").attr("data-policy-name"),revision:link.closest("[data-policy-revision]").attr("data-policy-revision")}},{key:"delete",component:"core"}])}e.preventDefault(),promise.then((function(strs){return strings=strs,ModalSaveCancel.create({title:strings[0],body:strings[1]})})).then((function(modal){return modal.setSaveButtonText(strings[2]),modal.getRoot().on(ModalEvents.save,(function(){window.location.href=link.attr("href")+"&sesskey="+Config.sesskey+"&confirm=1"})),modal.getRoot().on(ModalEvents.hidden,(function(){modal.destroy()})),modal.show(),!0})).catch((function(e){return Log.error(e),!1}))}))},{init:function(baseid){var base=$(document.getElementById(baseid));if(base.length)return new ManageDocsActions(base);throw new Error("managedocsactions: Invalid base element identifier")}}}));
//# sourceMappingURL=managedocsactions.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
/**
* Policy actions.
*
* @module tool_policy/policyactions
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_policy/policyactions",["jquery","core/ajax","core/notification","core/modal"],(function($,Ajax,Notification,Modal){var PolicyActions=function(root){this.registerEvents(root)};return PolicyActions.prototype.registerEvents=function(root){root.on("click",(function(e){e.preventDefault();var request={methodname:"tool_policy_get_policy_version",args:{versionid:$(this).data("versionid"),behalfid:$(this).data("behalfid")}},modalTitle=$.Deferred(),modalBody=$.Deferred(),modal=Modal.create({title:modalTitle,body:modalBody,large:!0,removeOnClose:!0,show:!0}).catch(Notification.exception),promises=Ajax.call([request]);$.when(promises[0]).then((function(data){if(data.result.policy)return modalTitle.resolve(data.result.policy.name),modalBody.resolve(data.result.policy.content),data;throw new Error(data.warnings[0].message)})).catch((function(message){return modal.then((function(modal){return modal.hide(),modal})).catch(Notification.exception),Notification.addNotification({message:message,type:"error"})}))}))},{init:function(root){return root=$(root),new PolicyActions(root)}}}));
//# sourceMappingURL=policyactions.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"policyactions.min.js","sources":["../src/policyactions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Policy actions.\n *\n * @module tool_policy/policyactions\n * @copyright 2018 Sara Arjona (sara@moodle.com)\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/modal',\n], function($, Ajax, Notification, Modal) {\n\n /**\n * PolicyActions class.\n *\n * @param {jQuery} root\n */\n var PolicyActions = function(root) {\n this.registerEvents(root);\n };\n\n /**\n * Register event listeners.\n *\n * @param {jQuery} root\n */\n PolicyActions.prototype.registerEvents = function(root) {\n root.on(\"click\", function(e) {\n e.preventDefault();\n\n var versionid = $(this).data('versionid');\n var behalfid = $(this).data('behalfid');\n\n var params = {\n 'versionid': versionid,\n 'behalfid': behalfid\n };\n\n var request = {\n methodname: 'tool_policy_get_policy_version',\n args: params\n };\n\n var modalTitle = $.Deferred();\n var modalBody = $.Deferred();\n\n var modal = Modal.create({\n title: modalTitle,\n body: modalBody,\n large: true,\n removeOnClose: true,\n show: true,\n })\n .catch(Notification.exception);\n\n // Make the request now that the modal is configured.\n var promises = Ajax.call([request]);\n $.when(promises[0]).then(function(data) {\n if (data.result.policy) {\n modalTitle.resolve(data.result.policy.name);\n modalBody.resolve(data.result.policy.content);\n\n return data;\n } else {\n throw new Error(data.warnings[0].message);\n }\n }).catch(function(message) {\n modal.then(function(modal) {\n modal.hide();\n\n return modal;\n })\n .catch(Notification.exception);\n\n return Notification.addNotification({\n message: message,\n type: 'error'\n });\n });\n });\n\n };\n\n return /** @alias module:tool_policy/policyactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the actions helper.\n *\n * @method init\n * @param {object} root\n * @return {PolicyActions}\n */\n 'init': function(root) {\n root = $(root);\n return new PolicyActions(root);\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Modal","PolicyActions","root","registerEvents","prototype","on","e","preventDefault","request","methodname","args","this","data","modalTitle","Deferred","modalBody","modal","create","title","body","large","removeOnClose","show","catch","exception","promises","call","when","then","result","policy","resolve","name","content","Error","warnings","message","hide","addNotification","type"],"mappings":";;;;;;;AAsBAA,mCAAO,CACH,SACA,YACA,oBACA,eACD,SAASC,EAAGC,KAAMC,aAAcC,WAO3BC,cAAgB,SAASC,WACpBC,eAAeD,cAQxBD,cAAcG,UAAUD,eAAiB,SAASD,MAC9CA,KAAKG,GAAG,SAAS,SAASC,GACtBA,EAAEC,qBAUEC,QAAU,CACVC,WAAY,iCACZC,KAPS,WAHGb,EAAEc,MAAMC,KAAK,sBACdf,EAAEc,MAAMC,KAAK,cAYxBC,WAAahB,EAAEiB,WACfC,UAAYlB,EAAEiB,WAEdE,MAAQhB,MAAMiB,OAAO,CACrBC,MAAOL,WACPM,KAAMJ,UACNK,OAAO,EACPC,eAAe,EACfC,MAAM,IAETC,MAAMxB,aAAayB,WAGhBC,SAAW3B,KAAK4B,KAAK,CAAClB,UAC1BX,EAAE8B,KAAKF,SAAS,IAAIG,MAAK,SAAShB,SAC1BA,KAAKiB,OAAOC,cACZjB,WAAWkB,QAAQnB,KAAKiB,OAAOC,OAAOE,MACtCjB,UAAUgB,QAAQnB,KAAKiB,OAAOC,OAAOG,SAE9BrB,WAED,IAAIsB,MAAMtB,KAAKuB,SAAS,GAAGC,YAEtCb,OAAM,SAASa,gBACdpB,MAAMY,MAAK,SAASZ,cAChBA,MAAMqB,OAECrB,SAEVO,MAAMxB,aAAayB,WAEbzB,aAAauC,gBAAgB,CAChCF,QAASA,QACTG,KAAM,iBAOgC,MAU1C,SAASrC,aACbA,KAAOL,EAAEK,MACF,IAAID,cAAcC"}
@@ -0,0 +1,143 @@
// 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/>.
/**
* Unified filter page JS module for the course participants page.
*
* @module tool_policy/acceptances_filter
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'],
function($, Autocomplete, Str, Notification) {
/**
* Selectors.
*
* @access private
* @type {{UNIFIED_FILTERS: string}}
*/
var SELECTORS = {
UNIFIED_FILTERS: '#unified-filters'
};
/**
* Init function.
*
* @method init
* @private
*/
var init = function() {
var stringkeys = [{
key: 'filterplaceholder',
component: 'tool_policy'
}, {
key: 'nofiltersapplied',
component: 'tool_policy'
}];
M.util.js_pending('acceptances_filter_datasource');
Str.get_strings(stringkeys).done(function(langstrings) {
var placeholder = langstrings[0];
var noSelectionString = langstrings[1];
Autocomplete.enhance(SELECTORS.UNIFIED_FILTERS, true, 'tool_policy/acceptances_filter_datasource', placeholder,
false, true, noSelectionString, true)
.then(function() {
M.util.js_complete('acceptances_filter_datasource');
return;
})
.fail(Notification.exception);
}).fail(Notification.exception);
var last = $(SELECTORS.UNIFIED_FILTERS).val();
$(SELECTORS.UNIFIED_FILTERS).on('change', function() {
var current = $(this).val();
var listoffilters = [];
var textfilters = [];
var updatedselectedfilters = false;
$.each(current, function(index, catoption) {
var catandoption = catoption.split(':', 2);
if (catandoption.length !== 2) {
textfilters.push(catoption);
return true; // Text search filter.
}
var category = catandoption[0];
var option = catandoption[1];
// The last option (eg. 'Teacher') out of a category (eg. 'Role') in this loop is the one that was last
// selected, so we want to use that if there are multiple options from the same category. Eg. The user
// may have chosen to filter by the 'Student' role, then wanted to filter by the 'Teacher' role - the
// last option in the category to be selected (in this case 'Teacher') will come last, so will overwrite
// 'Student' (after this if). We want to let the JS know that the filters have been updated.
if (typeof listoffilters[category] !== 'undefined') {
updatedselectedfilters = true;
}
listoffilters[category] = option;
return true;
});
// Check if we have something to remove from the list of filters.
if (updatedselectedfilters) {
// Go through and put the list into something we can use to update the list of filters.
var updatefilters = [];
for (var category in listoffilters) {
updatefilters.push(category + ":" + listoffilters[category]);
}
updatefilters = updatefilters.concat(textfilters);
$(this).val(updatefilters);
}
// Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.
if (last.join(',') != current.join(',')) {
this.form.submit();
}
});
};
/**
* Return the unified user filter form.
*
* @method getForm
* @return {DOMElement}
*/
var getForm = function() {
return $(SELECTORS.UNIFIED_FILTERS).closest('form');
};
return /** @alias module:core/form-autocomplete */ {
/**
* Initialise the unified user filter.
*
* @method init
*/
init: function() {
init();
},
/**
* Return the unified user filter form.
*
* @method getForm
* @return {DOMElement}
*/
getForm: function() {
return getForm();
}
};
});
@@ -0,0 +1,92 @@
// 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/>.
/**
* Datasource for the tool_policy/acceptances_filter.
*
* This module is compatible with core/form-autocomplete.
*
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {
return /** @alias module:tool_policy/acceptances_filter_datasource */ {
/**
* List filter options.
*
* @param {String} selector The select element selector.
* @param {String} query The query string.
* @return {Promise}
*/
list: function(selector, query) {
var filteredOptions = [];
var el = $(selector);
var originalOptions = $(selector).data('originaloptionsjson');
var selectedFilters = el.val();
$.each(originalOptions, function(index, option) {
// Skip option if it does not contain the query string.
if (query.trim() !== '' && option.label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) === -1) {
return true;
}
// Skip filters that have already been selected.
if ($.inArray(option.value, selectedFilters) > -1) {
return true;
}
filteredOptions.push(option);
return true;
});
var deferred = new $.Deferred();
deferred.resolve(filteredOptions);
return deferred.promise();
},
/**
* Process the results for auto complete elements.
*
* @param {String} selector The selector of the auto complete element.
* @param {Array} results An array or results.
* @return {Array} New array of results.
*/
processResults: function(selector, results) {
var options = [];
$.each(results, function(index, data) {
options.push({
value: data.value,
label: data.label
});
});
return options;
},
/**
* Source of data for Ajax element.
*
* @param {String} selector The selector of the auto complete element.
* @param {String} query The query string.
* @param {Function} callback A callback function receiving an array of results.
*/
/* eslint-disable promise/no-callback-in-promise */
transport: function(selector, query, callback) {
this.list(selector, query).then(callback).catch(Notification.exception);
}
};
});
+280
View File
@@ -0,0 +1,280 @@
// 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/>.
/**
* Add policy consent modal to the page
*
* @module tool_policy/acceptmodal
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery',
'core/str',
'core/modal_save_cancel',
'core/modal_events',
'core/notification',
'core/fragment',
'core/ajax',
'core_form/changechecker',
], function(
$,
Str,
ModalSaveCancel,
ModalEvents,
Notification,
Fragment,
Ajax,
FormChangeChecker
) {
"use strict";
/**
* Constructor
*
* @param {int} contextid
*
* Each call to init gets it's own instance of this class.
*/
var AcceptOnBehalf = function(contextid) {
this.contextid = contextid;
this.init();
};
/**
* @var {Modal} modal
* @private
*/
AcceptOnBehalf.prototype.modal = null;
/**
* @var {int} contextid
* @private
*/
AcceptOnBehalf.prototype.contextid = -1;
/**
* @var {object} currentTrigger The triggered HTML jQuery object
* @private
*/
AcceptOnBehalf.prototype.currentTrigger = null;
/**
* @var {object} triggers The trigger selectors
* @private
*/
AcceptOnBehalf.prototype.triggers = {
SINGLE: 'a[data-action=acceptmodal]',
BULK: 'input[data-action=acceptmodal]'
};
/**
* Initialise the class.
*
* @private
*/
AcceptOnBehalf.prototype.init = function() {
// Initialise for links accepting policies for individual users.
$(this.triggers.SINGLE).on('click', function(e) {
e.preventDefault();
this.currentTrigger = $(e.currentTarget);
var href = $(e.currentTarget).attr('href'),
formData = href.slice(href.indexOf('?') + 1);
this.showFormModal(formData);
}.bind(this));
// Initialise for multiple users acceptance form.
$(this.triggers.BULK).on('click', function(e) {
e.preventDefault();
this.currentTrigger = $(e.currentTarget);
var form = $(e.currentTarget).closest('form');
if (form.find('input[type=checkbox][name="userids[]"]:checked').length) {
var formData = form.serialize();
this.showFormModal(formData);
} else {
Str.get_strings([
{key: 'notice'},
{key: 'selectusersforconsent', component: 'tool_policy'},
{key: 'ok'}
]).then(function(strings) {
Notification.alert(strings[0], strings[1], strings[2]);
return;
}).fail(Notification.exception);
}
}.bind(this));
};
/**
* Show modal with a form
*
* @param {String} formData
*/
AcceptOnBehalf.prototype.showFormModal = function(formData) {
var action;
var params = formData.split('&');
for (var i = 0; i < params.length; i++) {
var pair = params[i].split('=');
if (pair[0] == 'action') {
action = pair[1];
}
}
// Fetch the title string.
Str.get_strings([
{key: 'statusformtitleaccept', component: 'tool_policy'},
{key: 'iagreetothepolicy', component: 'tool_policy'},
{key: 'statusformtitlerevoke', component: 'tool_policy'},
{key: 'irevokethepolicy', component: 'tool_policy'},
{key: 'statusformtitledecline', component: 'tool_policy'},
{key: 'declinethepolicy', component: 'tool_policy'}
]).then(function(strings) {
var title;
var saveText;
if (action == 'accept') {
title = strings[0];
saveText = strings[1];
} else if (action == 'revoke') {
title = strings[2];
saveText = strings[3];
} else if (action == 'decline') {
title = strings[4];
saveText = strings[5];
}
// Create the modal.
return ModalSaveCancel.create({
title: title,
body: ''
}).then(function(modal) {
this.modal = modal;
this.setupFormModal(formData, saveText);
}.bind(this));
}.bind(this))
.catch(Notification.exception);
};
/**
* Setup form inside a modal
*
* @param {String} formData
* @param {String} saveText
*/
AcceptOnBehalf.prototype.setupFormModal = function(formData, saveText) {
var modal = this.modal;
modal.setLarge();
modal.setSaveButtonText(saveText);
// We want to reset the form every time it is opened.
modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
modal.setBody(this.getBody(formData));
// We catch the modal save event, and use it to submit the form inside the modal.
// Triggering a form submission will give JS validation scripts a chance to check for errors.
modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
// We also catch the form submit event and use it to submit the form with ajax.
modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
modal.show();
};
/**
* Load the body of the modal (contains the form)
*
* @method getBody
* @private
* @param {String} formData
* @return {Promise}
*/
AcceptOnBehalf.prototype.getBody = function(formData) {
if (typeof formData === "undefined") {
formData = {};
}
// Get the content of the modal.
var params = {jsonformdata: JSON.stringify(formData)};
return Fragment.loadFragment('tool_policy', 'accept_on_behalf', this.contextid, params);
};
/**
* Submit the form inside the modal via AJAX request
*
* @method submitFormAjax
* @private
* @param {Event} e Form submission event.
*/
AcceptOnBehalf.prototype.submitFormAjax = function(e) {
// We don't want to do a real form submission.
e.preventDefault();
// Convert all the form elements values to a serialised string.
var formData = this.modal.getRoot().find('form').serialize();
var requests = Ajax.call([{
methodname: 'tool_policy_submit_accept_on_behalf',
args: {jsonformdata: JSON.stringify(formData)}
}]);
requests[0].done(function(data) {
if (data.validationerrors) {
this.modal.setBody(this.getBody(formData));
} else {
this.close();
}
}.bind(this)).fail(Notification.exception);
};
/**
* This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
*
* @method submitForm
* @param {Event} e Form submission event.
* @private
*/
AcceptOnBehalf.prototype.submitForm = function(e) {
e.preventDefault();
this.modal.getRoot().find('form').submit();
};
/**
* Close the modal
*/
AcceptOnBehalf.prototype.close = function() {
this.destroy();
document.location.reload();
};
/**
* Destroy the modal
*/
AcceptOnBehalf.prototype.destroy = function() {
FormChangeChecker.resetAllFormDirtyStates();
this.modal.destroy();
this.currentTrigger.focus();
};
return /** @alias module:tool_policy/acceptmodal */ {
// Public variables and functions.
/**
* Attach event listeners to initialise this module.
*
* @method init
* @param {int} contextid The contextid for the course.
* @return {AcceptOnBehalf}
*/
getInstance: function(contextid) {
return new AcceptOnBehalf(contextid);
}
};
});
+252
View File
@@ -0,0 +1,252 @@
/**
*
* JQUERY EU COOKIE LAW POPUPS
* version 1.1.1
*
* Code on Github:
* https://github.com/wimagguc/jquery-eu-cookie-law-popup
*
* To see a live demo, go to:
* http://www.wimagguc.com/2018/05/gdpr-compliance-with-the-jquery-eu-cookie-law-plugin/
*
* by Richard Dancsi
* http://www.wimagguc.com/
*
*/
define(['jquery'], function($) {
// for ie9 doesn't support debug console >>>
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function () { };
// ^^^
$.fn.euCookieLawPopup = (function() {
var _self = this;
///////////////////////////////////////////////////////////////////////////////////////////////
// PARAMETERS (MODIFY THIS PART) //////////////////////////////////////////////////////////////
_self.params = {
cookiePolicyUrl : '/?cookie-policy',
popupPosition : 'top',
colorStyle : 'default',
compactStyle : false,
popupTitle : 'This website is using cookies',
popupText : 'We use cookies to ensure that we give you the best experience on our website. If you continue without changing your settings, we\'ll assume that you are happy to receive all cookies on this website.',
buttonContinueTitle : 'Continue',
buttonLearnmoreTitle : 'Learn&nbsp;more',
buttonLearnmoreOpenInNewWindow : true,
agreementExpiresInDays : 30,
autoAcceptCookiePolicy : false,
htmlMarkup : null
};
///////////////////////////////////////////////////////////////////////////////////////////////
// VARIABLES USED BY THE FUNCTION (DON'T MODIFY THIS PART) ////////////////////////////////////
_self.vars = {
INITIALISED : false,
HTML_MARKUP : null,
COOKIE_NAME : 'EU_COOKIE_LAW_CONSENT'
};
///////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS FOR MANIPULATING DATA ////////////////////////////////////////////////////
// Overwrite default parameters if any of those is present
var parseParameters = function(object, markup, settings) {
if (object) {
var className = $(object).attr('class') ? $(object).attr('class') : '';
if (className.indexOf('eupopup-top') > -1) {
_self.params.popupPosition = 'top';
}
else if (className.indexOf('eupopup-fixedtop') > -1) {
_self.params.popupPosition = 'fixedtop';
}
else if (className.indexOf('eupopup-bottomright') > -1) {
_self.params.popupPosition = 'bottomright';
}
else if (className.indexOf('eupopup-bottomleft') > -1) {
_self.params.popupPosition = 'bottomleft';
}
else if (className.indexOf('eupopup-bottom') > -1) {
_self.params.popupPosition = 'bottom';
}
else if (className.indexOf('eupopup-block') > -1) {
_self.params.popupPosition = 'block';
}
if (className.indexOf('eupopup-color-default') > -1) {
_self.params.colorStyle = 'default';
}
else if (className.indexOf('eupopup-color-inverse') > -1) {
_self.params.colorStyle = 'inverse';
}
if (className.indexOf('eupopup-style-compact') > -1) {
_self.params.compactStyle = true;
}
}
if (markup) {
_self.params.htmlMarkup = markup;
}
if (settings) {
if (typeof settings.cookiePolicyUrl !== 'undefined') {
_self.params.cookiePolicyUrl = settings.cookiePolicyUrl;
}
if (typeof settings.popupPosition !== 'undefined') {
_self.params.popupPosition = settings.popupPosition;
}
if (typeof settings.colorStyle !== 'undefined') {
_self.params.colorStyle = settings.colorStyle;
}
if (typeof settings.popupTitle !== 'undefined') {
_self.params.popupTitle = settings.popupTitle;
}
if (typeof settings.popupText !== 'undefined') {
_self.params.popupText = settings.popupText;
}
if (typeof settings.buttonContinueTitle !== 'undefined') {
_self.params.buttonContinueTitle = settings.buttonContinueTitle;
}
if (typeof settings.buttonLearnmoreTitle !== 'undefined') {
_self.params.buttonLearnmoreTitle = settings.buttonLearnmoreTitle;
}
if (typeof settings.buttonLearnmoreOpenInNewWindow !== 'undefined') {
_self.params.buttonLearnmoreOpenInNewWindow = settings.buttonLearnmoreOpenInNewWindow;
}
if (typeof settings.agreementExpiresInDays !== 'undefined') {
_self.params.agreementExpiresInDays = settings.agreementExpiresInDays;
}
if (typeof settings.autoAcceptCookiePolicy !== 'undefined') {
_self.params.autoAcceptCookiePolicy = settings.autoAcceptCookiePolicy;
}
if (typeof settings.htmlMarkup !== 'undefined') {
_self.params.htmlMarkup = settings.htmlMarkup;
}
}
};
var createHtmlMarkup = function() {
if (_self.params.htmlMarkup) {
return _self.params.htmlMarkup;
}
var html =
'<div class="eupopup-container' +
' eupopup-container-' + _self.params.popupPosition +
(_self.params.compactStyle ? ' eupopup-style-compact' : '') +
' eupopup-color-' + _self.params.colorStyle + '">' +
'<div class="eupopup-head">' + _self.params.popupTitle + '</div>' +
'<div class="eupopup-body">' + _self.params.popupText + '</div>' +
'<div class="eupopup-buttons">' +
'<a href="#" class="eupopup-button eupopup-button_1">' + _self.params.buttonContinueTitle + '</a>' +
'<a href="' + _self.params.cookiePolicyUrl + '"' +
(_self.params.buttonLearnmoreOpenInNewWindow ? ' target=_blank ' : '') +
' class="eupopup-button eupopup-button_2">' + _self.params.buttonLearnmoreTitle + '</a>' +
'<div class="clearfix"></div>' +
'</div>' +
'<a href="#" class="eupopup-closebutton">x</a>' +
'</div>';
return html;
};
// Storing the consent in a cookie
var setUserAcceptsCookies = function(consent) {
var d = new Date();
var expiresInDays = _self.params.agreementExpiresInDays * 24 * 60 * 60 * 1000;
d.setTime( d.getTime() + expiresInDays );
var expires = "expires=" + d.toGMTString();
document.cookie = _self.vars.COOKIE_NAME + '=' + consent + "; " + expires + ";path=/";
$(document).trigger("user_cookie_consent_changed", {'consent' : consent});
};
// Let's see if we have a consent cookie already
var userAlreadyAcceptedCookies = function() {
var userAcceptedCookies = false;
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var c = cookies[i].trim();
if (c.indexOf(_self.vars.COOKIE_NAME) !== -1) {
userAcceptedCookies = c.substring(_self.vars.COOKIE_NAME.length + 1, c.length);
}
}
return userAcceptedCookies;
};
var hideContainer = function() {
// $('.eupopup-container').slideUp(200);
$('.eupopup-container').animate({
opacity: 0,
height: 0
}, 200, function() {
$('.eupopup-container').hide(0);
});
};
///////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////
var publicfunc = {
// INITIALIZE EU COOKIE LAW POPUP /////////////////////////////////////////////////////////
init : function(settings) {
parseParameters(
$(".eupopup").first(),
$(".eupopup-markup").html(),
settings);
// No need to display this if user already accepted the policy
if (userAlreadyAcceptedCookies()) {
$(document).trigger("user_cookie_already_accepted", {'consent': true});
return;
}
// We should initialise only once
if (_self.vars.INITIALISED) {
return;
}
_self.vars.INITIALISED = true;
// Markup and event listeners >>>
_self.vars.HTML_MARKUP = createHtmlMarkup();
if ($('.eupopup-block').length > 0) {
$('.eupopup-block').append(_self.vars.HTML_MARKUP);
} else {
$('BODY').append(_self.vars.HTML_MARKUP);
}
$('.eupopup-button_1').click(function() {
setUserAcceptsCookies(true);
hideContainer();
return false;
});
$('.eupopup-closebutton').click(function() {
setUserAcceptsCookies(true);
hideContainer();
return false;
});
// ^^^ Markup and event listeners
// Ready to start!
$('.eupopup-container').show();
// In case it's alright to just display the message once
if (_self.params.autoAcceptCookiePolicy) {
setUserAcceptsCookies(true);
}
}
};
return publicfunc;
});
});
@@ -0,0 +1,152 @@
// 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 support for confirmation via JS modal for some management actions at the Manage policies page.
*
* @module tool_policy/managedocsactions
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery',
'core/log',
'core/config',
'core/str',
'core/modal_save_cancel',
'core/modal_events'
], function($, Log, Config, Str, ModalSaveCancel, ModalEvents) {
"use strict";
/**
* List of action selectors.
*
* @property {string} LINKS - Selector for all action links
* @property {string} MAKE_CURRENT
*/
var ACTION = {
LINKS: '[data-action]',
MAKE_CURRENT: '[data-action="makecurrent"]',
INACTIVATE: '[data-action="inactivate"]',
DELETE: '[data-action="delete"]'
};
/**
* @constructor
* @param {Element} base - Management area wrapping element
*/
function ManageDocsActions(base) {
this.base = base;
this.initEvents();
}
/**
* Register event listeners.
*/
ManageDocsActions.prototype.initEvents = function() {
var self = this;
self.base.on('click', ACTION.LINKS, function(e) {
e.stopPropagation();
var link = $(e.currentTarget);
var promise;
var strings;
if (link.is(ACTION.MAKE_CURRENT)) {
promise = Str.get_strings([
{key: 'activating', component: 'tool_policy'},
{key: 'activateconfirm', component: 'tool_policy', param: {
name: link.closest('[data-policy-name]').attr('data-policy-name'),
revision: link.closest('[data-policy-revision]').attr('data-policy-revision')
}},
{key: 'activateconfirmyes', component: 'tool_policy'}
]);
} else if (link.is(ACTION.INACTIVATE)) {
promise = Str.get_strings([
{key: 'inactivating', component: 'tool_policy'},
{key: 'inactivatingconfirm', component: 'tool_policy', param: {
name: link.closest('[data-policy-name]').attr('data-policy-name'),
revision: link.closest('[data-policy-revision]').attr('data-policy-revision')
}},
{key: 'inactivatingconfirmyes', component: 'tool_policy'}
]);
} else if (link.is(ACTION.DELETE)) {
promise = Str.get_strings([
{key: 'deleting', component: 'tool_policy'},
{key: 'deleteconfirm', component: 'tool_policy', param: {
name: link.closest('[data-policy-name]').attr('data-policy-name'),
revision: link.closest('[data-policy-revision]').attr('data-policy-revision')
}},
{key: 'delete', component: 'core'}
]);
} else {
Log.error('unknown action type detected', 'tool_policy/managedocsactions');
return;
}
e.preventDefault();
promise.then(function(strs) {
strings = strs;
return ModalSaveCancel.create({
title: strings[0],
body: strings[1],
});
}).then(function(modal) {
modal.setSaveButtonText(strings[2]);
modal.getRoot().on(ModalEvents.save, function() {
window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';
});
modal.getRoot().on(ModalEvents.hidden, function() {
modal.destroy();
});
modal.show();
return true;
}).catch(function(e) {
Log.error(e);
return false;
});
});
};
return {
/**
* Factory method returning instance of the ManageDocsActions
*
* @param {String} baseid - ID of the management area wrapping element
* @return {ManageDocsActions}
*/
init: function(baseid) {
var base = $(document.getElementById(baseid));
if (base.length) {
return new ManageDocsActions(base);
} else {
throw new Error("managedocsactions: Invalid base element identifier");
}
}
};
});
+116
View File
@@ -0,0 +1,116 @@
// 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/>.
/**
* Policy actions.
*
* @module tool_policy/policyactions
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery',
'core/ajax',
'core/notification',
'core/modal',
], function($, Ajax, Notification, Modal) {
/**
* PolicyActions class.
*
* @param {jQuery} root
*/
var PolicyActions = function(root) {
this.registerEvents(root);
};
/**
* Register event listeners.
*
* @param {jQuery} root
*/
PolicyActions.prototype.registerEvents = function(root) {
root.on("click", function(e) {
e.preventDefault();
var versionid = $(this).data('versionid');
var behalfid = $(this).data('behalfid');
var params = {
'versionid': versionid,
'behalfid': behalfid
};
var request = {
methodname: 'tool_policy_get_policy_version',
args: params
};
var modalTitle = $.Deferred();
var modalBody = $.Deferred();
var modal = Modal.create({
title: modalTitle,
body: modalBody,
large: true,
removeOnClose: true,
show: true,
})
.catch(Notification.exception);
// Make the request now that the modal is configured.
var promises = Ajax.call([request]);
$.when(promises[0]).then(function(data) {
if (data.result.policy) {
modalTitle.resolve(data.result.policy.name);
modalBody.resolve(data.result.policy.content);
return data;
} else {
throw new Error(data.warnings[0].message);
}
}).catch(function(message) {
modal.then(function(modal) {
modal.hide();
return modal;
})
.catch(Notification.exception);
return Notification.addNotification({
message: message,
type: 'error'
});
});
});
};
return /** @alias module:tool_policy/policyactions */ {
// Public variables and functions.
/**
* Initialise the actions helper.
*
* @method init
* @param {object} root
* @return {PolicyActions}
*/
'init': function(root) {
root = $(root);
return new PolicyActions(root);
}
};
});
@@ -0,0 +1,650 @@
<?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/>.
/**
* View user acceptances to the policies
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy;
use tool_policy\output\acceptances_filter;
use tool_policy\output\renderer;
use tool_policy\output\user_agreement;
use core_user;
use stdClass;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/lib/tablelib.php');
/**
* Class acceptances_table
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class acceptances_table extends \table_sql {
/** @var array */
protected $versionids;
/** @var acceptances_filter */
protected $acceptancesfilter;
/** @var renderer */
protected $output;
/**
* @var string[] The list of countries.
*/
protected $countries;
/** @var bool are there any users that this user can agree on behalf of */
protected $canagreeany = false;
/**
* Constructor.
*
* @param string $uniqueid Table identifier.
* @param acceptances_filter $acceptancesfilter
* @param renderer $output
*/
public function __construct($uniqueid, acceptances_filter $acceptancesfilter, renderer $output) {
global $CFG;
parent::__construct($uniqueid);
$this->set_attribute('id', 'acceptancetable');
$this->acceptancesfilter = $acceptancesfilter;
$this->is_downloading(optional_param('download', 0, PARAM_ALPHA), 'user_acceptances');
$this->baseurl = $acceptancesfilter->get_url();
$this->output = $output;
$this->versionids = [];
$versions = $acceptancesfilter->get_versions();
if (count($versions) > 1) {
foreach ($versions as $version) {
$this->versionids[$version->id] = $version->name;
}
} else {
$version = reset($versions);
$this->versionids[$version->id] = $version->name;
if ($version->status != policy_version::STATUS_ACTIVE) {
$this->versionids[$version->id] .= '<br>' . $version->revision;
}
}
// TODO Does not support custom user profile fields (MDL-70456).
$userfieldsapi = \core_user\fields::for_identity(\context_system::instance(), false)->with_userpic();
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
$extrafields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
$this->set_sql("$userfields",
"{user} u",
'u.id <> :siteguestid AND u.deleted = 0',
['siteguestid' => $CFG->siteguest]);
if (!$this->is_downloading()) {
$this->add_column_header('select', get_string('select'), false, 'colselect');
}
$this->add_column_header('fullname', get_string('fullnameuser', 'core'));
foreach ($extrafields as $field) {
$this->add_column_header($field, \core_user\fields::get_display_name($field));
}
if (!$this->is_downloading() && !has_capability('tool/policy:acceptbehalf', \context_system::instance())) {
// We will need to check capability to accept on behalf in each user's context, preload users contexts.
$this->sql->fields .= ',' . \context_helper::get_preload_record_columns_sql('ctx');
$this->sql->from .= ' JOIN {context} ctx ON ctx.contextlevel = :usercontextlevel AND ctx.instanceid = u.id';
$this->sql->params['usercontextlevel'] = CONTEXT_USER;
}
if ($this->acceptancesfilter->get_single_version()) {
$this->configure_for_single_version();
} else {
$this->configure_for_multiple_versions();
}
$this->build_sql_for_search_string($extrafields);
$this->build_sql_for_capability_filter();
$this->build_sql_for_roles_filter();
$this->sortable(true, 'firstname');
}
/**
* Remove randomness from the list by always sorting by user id in the end
*
* @return array
*/
public function get_sort_columns() {
$c = parent::get_sort_columns();
$c['u.id'] = SORT_ASC;
return $c;
}
/**
* Allows to add only one column name and header to the table (parent class methods only allow to set all).
*
* @param string $key
* @param string $label
* @param bool $sortable
* @param string $columnclass
*/
protected function add_column_header($key, $label, $sortable = true, $columnclass = '') {
if (empty($this->columns)) {
$this->define_columns([$key]);
$this->define_headers([$label]);
} else {
$this->columns[$key] = count($this->columns);
$this->column_style[$key] = array();
$this->column_class[$key] = $columnclass;
$this->columnsticky[$key] = '';
$this->column_suppress[$key] = false;
$this->headers[] = $label;
}
if ($columnclass !== null) {
$this->column_class($key, $columnclass);
}
if (!$sortable) {
$this->no_sorting($key);
}
}
/**
* Helper configuration method.
*/
protected function configure_for_single_version() {
$userfieldsapi = \core_user\fields::for_name();
$userfieldsmod = $userfieldsapi->get_sql('m', false, 'mod', '', false)->selects;
$v = key($this->versionids);
$this->sql->fields .= ", $userfieldsmod, a{$v}.status AS status{$v}, a{$v}.note, ".
"a{$v}.timemodified, a{$v}.usermodified AS usermodified{$v}";
$join = "JOIN {tool_policy_acceptances} a{$v} ON a{$v}.userid = u.id AND a{$v}.policyversionid=:versionid{$v}";
$filterstatus = $this->acceptancesfilter->get_status_filter();
if ($filterstatus == 1) {
$this->sql->from .= " $join AND a{$v}.status=1";
} else if ($filterstatus == 2) {
$this->sql->from .= " $join AND a{$v}.status=0";
} else {
$this->sql->from .= " LEFT $join";
}
$this->sql->from .= " LEFT JOIN {user} m ON m.id = a{$v}.usermodified AND m.id <> u.id AND a{$v}.status IS NOT NULL";
$this->sql->params['versionid' . $v] = $v;
if ($filterstatus === 0) {
$this->sql->where .= " AND a{$v}.status IS NULL";
}
$this->add_column_header('status' . $v, get_string('response', 'tool_policy'));
$this->add_column_header('timemodified', get_string('responseon', 'tool_policy'));
$this->add_column_header('usermodified' . $v, get_string('responseby', 'tool_policy'));
$this->add_column_header('note', get_string('acceptancenote', 'tool_policy'), false);
}
/**
* Helper configuration method.
*/
protected function configure_for_multiple_versions() {
$this->add_column_header('statusall', get_string('acceptancestatusoverall', 'tool_policy'));
$filterstatus = $this->acceptancesfilter->get_status_filter();
$statusall = [];
foreach ($this->versionids as $v => $versionname) {
$this->sql->fields .= ", a{$v}.status AS status{$v}, a{$v}.usermodified AS usermodified{$v}";
$join = "JOIN {tool_policy_acceptances} a{$v} ON a{$v}.userid = u.id AND a{$v}.policyversionid=:versionid{$v}";
if ($filterstatus == 1) {
$this->sql->from .= " {$join} AND a{$v}.status=1";
} else if ($filterstatus == 2) {
$this->sql->from .= " {$join} AND a{$v}.status=0";
} else {
$this->sql->from .= " LEFT {$join}";
}
$this->sql->params['versionid' . $v] = $v;
$this->add_column_header('status' . $v, $versionname);
$statusall[] = "COALESCE(a{$v}.status, 0)";
}
$this->sql->fields .= ",".join('+', $statusall)." AS statusall";
if ($filterstatus === 0) {
$statussql = [];
foreach ($this->versionids as $v => $versionname) {
$statussql[] = "a{$v}.status IS NULL";
}
$this->sql->where .= " AND (u.policyagreed = 0 OR ".join(" OR ", $statussql).")";
}
}
/**
* Download the data.
*/
public function download() {
\core\session\manager::write_close();
$this->out(0, false);
exit;
}
/**
* Get sql to add to where statement.
*
* @return string
*/
public function get_sql_where() {
list($where, $params) = parent::get_sql_where();
$where = preg_replace('/firstname/', 'u.firstname', $where);
$where = preg_replace('/lastname/', 'u.lastname', $where);
return [$where, $params];
}
/**
* Helper SQL query builder.
*
* @param array $userfields
*/
protected function build_sql_for_search_string($userfields) {
global $DB, $USER;
$search = $this->acceptancesfilter->get_search_strings();
if (empty($search)) {
return;
}
$wheres = [];
$params = [];
foreach ($search as $index => $keyword) {
$searchkey1 = 'search' . $index . '1';
$searchkey2 = 'search' . $index . '2';
$searchkey3 = 'search' . $index . '3';
$searchkey4 = 'search' . $index . '4';
$searchkey5 = 'search' . $index . '5';
$searchkey6 = 'search' . $index . '6';
$searchkey7 = 'search' . $index . '7';
$conditions = array();
// Search by fullname.
$fullname = $DB->sql_fullname('u.firstname', 'u.lastname');
$conditions[] = $DB->sql_like($fullname, ':' . $searchkey1, false, false);
// Search by email.
$email = $DB->sql_like('u.email', ':' . $searchkey2, false, false);
if (!in_array('email', $userfields)) {
$maildisplay = 'maildisplay' . $index;
$userid1 = 'userid' . $index . '1';
// Prevent users who hide their email address from being found by others
// who aren't allowed to see hidden email addresses.
$email = "(". $email ." AND (" .
"u.maildisplay <> :$maildisplay " .
"OR u.id = :$userid1". // User can always find himself.
"))";
$params[$maildisplay] = core_user::MAILDISPLAY_HIDE;
$params[$userid1] = $USER->id;
}
$conditions[] = $email;
// Search by idnumber.
$idnumber = $DB->sql_like('u.idnumber', ':' . $searchkey3, false, false);
if (!in_array('idnumber', $userfields)) {
$userid2 = 'userid' . $index . '2';
// Users who aren't allowed to see idnumbers should at most find themselves
// when searching for an idnumber.
$idnumber = "(". $idnumber . " AND u.id = :$userid2)";
$params[$userid2] = $USER->id;
}
$conditions[] = $idnumber;
// Search by middlename.
$middlename = $DB->sql_like('u.middlename', ':' . $searchkey4, false, false);
$conditions[] = $middlename;
// Search by alternatename.
$alternatename = $DB->sql_like('u.alternatename', ':' . $searchkey5, false, false);
$conditions[] = $alternatename;
// Search by firstnamephonetic.
$firstnamephonetic = $DB->sql_like('u.firstnamephonetic', ':' . $searchkey6, false, false);
$conditions[] = $firstnamephonetic;
// Search by lastnamephonetic.
$lastnamephonetic = $DB->sql_like('u.lastnamephonetic', ':' . $searchkey7, false, false);
$conditions[] = $lastnamephonetic;
$wheres[] = "(". implode(" OR ", $conditions) .") ";
$params[$searchkey1] = "%$keyword%";
$params[$searchkey2] = "%$keyword%";
$params[$searchkey3] = "%$keyword%";
$params[$searchkey4] = "%$keyword%";
$params[$searchkey5] = "%$keyword%";
$params[$searchkey6] = "%$keyword%";
$params[$searchkey7] = "%$keyword%";
}
$this->sql->where .= ' AND '.join(' AND ', $wheres);
$this->sql->params += $params;
}
/**
* If there is a filter to find users who can/cannot accept on their own behalf add it to the SQL query
*/
protected function build_sql_for_capability_filter() {
global $CFG;
$hascapability = $this->acceptancesfilter->get_capability_accept_filter();
if ($hascapability === null) {
return;
}
list($neededroles, $forbiddenroles) = get_roles_with_cap_in_context(\context_system::instance(), 'tool/policy:accept');
if (empty($neededroles)) {
// There are no roles that allow to accept agreement on one own's behalf.
$this->sql->where .= $hascapability ? ' AND 1=0' : '';
return;
}
if (empty($forbiddenroles)) {
// There are no roles that prohibit to accept agreement on one own's behalf.
$this->sql->where .= ' AND ' . $this->sql_has_role($neededroles, $hascapability);
return;
}
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
if (!empty($neededroles[$defaultuserroleid])) {
// Default role allows to accept agreement. Make sure user has/does not have one of the roles prohibiting it.
$this->sql->where .= ' AND ' . $this->sql_has_role($forbiddenroles, !$hascapability);
return;
}
if ($hascapability) {
// User has at least one role allowing to accept and no roles prohibiting.
$this->sql->where .= ' AND ' . $this->sql_has_role($neededroles);
$this->sql->where .= ' AND ' . $this->sql_has_role($forbiddenroles, false);
} else {
// Option 1: User has one of the roles prohibiting to accept.
$this->sql->where .= ' AND (' . $this->sql_has_role($forbiddenroles);
// Option 2: User has none of the roles allowing to accept.
$this->sql->where .= ' OR ' . $this->sql_has_role($neededroles, false) . ")";
}
}
/**
* Returns SQL snippet for users that have (do not have) one of the given roles in the system context
*
* @param array $roles list of roles indexed by role id
* @param bool $positive true: return users who HAVE roles; false: return users who DO NOT HAVE roles
* @return string
*/
protected function sql_has_role($roles, $positive = true) {
global $CFG;
if (empty($roles)) {
return $positive ? '1=0' : '1=1';
}
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
if (!empty($roles[$defaultuserroleid])) {
// No need to query, everybody has the default role.
return $positive ? '1=1' : '1=0';
}
return "u.id " . ($positive ? "" : "NOT") . " IN (
SELECT userid
FROM {role_assignments}
WHERE contextid = " . SYSCONTEXTID . " AND roleid IN (" . implode(',', array_keys($roles)) . ")
)";
}
/**
* If there is a filter by user roles add it to the SQL query.
*/
protected function build_sql_for_roles_filter() {
foreach ($this->acceptancesfilter->get_role_filters() as $roleid) {
$this->sql->where .= ' AND ' . $this->sql_has_role([$roleid => $roleid]);
}
}
/**
* Hook that can be overridden in child classes to wrap a table in a form
* for example. Called only when there is data to display and not
* downloading.
*/
public function wrap_html_start() {
echo \html_writer::start_tag('form',
['action' => new \moodle_url('/admin/tool/policy/accept.php')]);
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]);
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'returnurl',
'value' => $this->get_return_url()]);
foreach (array_keys($this->versionids) as $versionid) {
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'versionids[]',
'value' => $versionid]);
}
}
/**
* Hook that can be overridden in child classes to wrap a table in a form
* for example. Called only when there is data to display and not
* downloading.
*/
public function wrap_html_finish() {
global $PAGE;
if ($this->canagreeany) {
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'accept']);
echo \html_writer::empty_tag('input', ['type' => 'submit', 'data-action' => 'acceptmodal',
'value' => get_string('consentbulk', 'tool_policy'), 'class' => 'btn btn-primary mt-1']);
$PAGE->requires->js_call_amd('tool_policy/acceptmodal', 'getInstance', [\context_system::instance()->id]);
}
echo "</form>\n";
}
/**
* Render the table.
*/
public function display() {
$this->out(100, true);
}
/**
* Call appropriate methods on this table class to perform any processing on values before displaying in table.
* Takes raw data from the database and process it into human readable format, perhaps also adding html linking when
* displaying table as html, adding a div wrap, etc.
*
* See for example col_fullname below which will be called for a column whose name is 'fullname'.
*
* @param array|object $row row of data from db used to make one row of the table.
* @return array one row for the table, added using add_data_keyed method.
*/
public function format_row($row) {
\context_helper::preload_from_record($row);
$row->canaccept = false;
$row->user = \user_picture::unalias($row, [], $this->useridfield);
$row->select = null;
if (!$this->is_downloading()) {
if (has_capability('tool/policy:acceptbehalf', \context_system::instance()) ||
has_capability('tool/policy:acceptbehalf', \context_user::instance($row->id))) {
$row->canaccept = true;
$row->select = \html_writer::empty_tag('input',
['type' => 'checkbox', 'name' => 'userids[]', 'value' => $row->id, 'class' => 'usercheckbox',
'id' => 'selectuser' . $row->id]) .
\html_writer::tag('label', get_string('selectuser', 'tool_policy', $this->username($row->user, false)),
['for' => 'selectuser' . $row->id, 'class' => 'accesshide']);
$this->canagreeany = true;
}
}
return parent::format_row($row);
}
/**
* Get the column fullname value.
*
* @param stdClass $row
* @return string
*/
public function col_fullname($row) {
global $OUTPUT;
$userpic = $this->is_downloading() ? '' : $OUTPUT->user_picture($row->user);
return $userpic . $this->username($row->user, true);
}
/**
* User name with a link to profile
*
* @param stdClass $user
* @param bool $profilelink show link to profile (when we are downloading never show links)
* @return string
*/
protected function username($user, $profilelink = true) {
$canviewfullnames = has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
has_capability('moodle/site:viewfullnames', \context_user::instance($user->id));
$name = fullname($user, $canviewfullnames);
if (!$this->is_downloading() && $profilelink) {
$profileurl = new \moodle_url('/user/profile.php', array('id' => $user->id));
return \html_writer::link($profileurl, $name);
}
return $name;
}
/**
* Helper.
*/
protected function get_return_url() {
$pageurl = $this->baseurl;
if ($this->currpage) {
$pageurl = new \moodle_url($pageurl, [$this->request[TABLE_VAR_PAGE] => $this->currpage]);
}
return $pageurl;
}
/**
* Return agreement status
*
* @param int $versionid either id of an individual version or empty for overall status
* @param stdClass $row
* @return string
*/
protected function status($versionid, $row) {
$onbehalf = false;
$versions = $versionid ? [$versionid => $this->versionids[$versionid]] : $this->versionids; // List of versions.
$accepted = []; // List of versionids that user has accepted.
$declined = [];
foreach ($versions as $v => $name) {
if ($row->{'status' . $v} !== null) {
if (empty($row->{'status' . $v})) {
$declined[] = $v;
} else {
$accepted[] = $v;
}
$agreedby = $row->{'usermodified' . $v};
if ($agreedby && $agreedby != $row->id) {
$onbehalf = true;
}
}
}
$ua = new user_agreement($row->id, $accepted, $declined, $this->get_return_url(), $versions, $onbehalf, $row->canaccept);
if ($this->is_downloading()) {
return $ua->export_for_download();
} else {
return $this->output->render($ua);
}
}
/**
* Get the column timemodified value.
*
* @param stdClass $row
* @return string
*/
public function col_timemodified($row) {
if ($row->timemodified) {
if ($this->is_downloading()) {
// Use timestamp format readable for both machines and humans.
return date_format_string((int) $row->timemodified, '%Y-%m-%d %H:%M:%S %Z');
} else {
// Use localised calendar format.
return userdate($row->timemodified, get_string('strftimedatetime'));
}
} else {
return null;
}
}
/**
* Get the column note value.
*
* @param stdClass $row
* @return string
*/
public function col_note($row) {
if ($this->is_downloading()) {
return $row->note;
} else {
return format_text($row->note, FORMAT_MOODLE);
}
}
/**
* Get the column statusall value.
*
* @param stdClass $row
* @return string
*/
public function col_statusall($row) {
return $this->status(0, $row);
}
/**
* Generate the country column.
*
* @param \stdClass $data
* @return string
*/
public function col_country($data) {
if ($data->country && $this->countries === null) {
$this->countries = get_string_manager()->get_list_of_countries();
}
if (!empty($this->countries[$data->country])) {
return $this->countries[$data->country];
}
return '';
}
/**
* You can override this method in a child class. See the description of
* build_table which calls this method.
*
* @param string $column
* @param stdClass $row
* @return string
*/
public function other_cols($column, $row) {
if (preg_match('/^status([\d]+)$/', $column, $matches)) {
$versionid = $matches[1];
return $this->status($versionid, $row);
}
if (preg_match('/^usermodified([\d]+)$/', $column, $matches)) {
if ($row->$column && $row->$column != $row->id) {
$user = (object)['id' => $row->$column];
username_load_fields_from_object($user, $row, 'mod');
return $this->username($user, true);
}
return ''; // User agreed by themselves.
}
return parent::other_cols($column, $row);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
<?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/>.
/**
* Provides {@link tool_policy\event\acceptance_base} class.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\event;
use core\event\base;
defined('MOODLE_INTERNAL') || die();
/**
* Base class for acceptance_created and acceptance_updated events.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class acceptance_base extends base {
/**
* Initialise the event.
*/
protected function init() {
$this->data['objecttable'] = 'tool_policy_acceptances';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Create event from record.
*
* @param stdClass $record
* @return acceptance_created
*/
public static function create_from_record($record) {
$event = static::create([
'objectid' => $record->id,
'relateduserid' => $record->userid,
'context' => \context_user::instance($record->userid),
'other' => [
'policyversionid' => $record->policyversionid,
'note' => $record->note,
'status' => $record->status,
],
]);
$event->add_record_snapshot($event->objecttable, $record);
return $event;
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/admin/tool/policy/acceptance.php', array('userid' => $this->relateduserid,
'versionid' => $this->other['policyversionid']));
}
/**
* Get the object ID mapping.
*
* @return array
*/
public static function get_objectid_mapping() {
return array('db' => 'tool_policy', 'restore' => \core\event\base::NOT_MAPPED);
}
/**
* Custom validation.
*
* @throws \coding_exception
*/
protected function validate_data() {
parent::validate_data();
if (empty($this->other['policyversionid'])) {
throw new \coding_exception('The \'policyversionid\' value must be set');
}
if (!isset($this->other['status'])) {
throw new \coding_exception('The \'status\' value must be set');
}
if (empty($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
}
/**
* No mapping required for this event because this event is not backed up.
*
* @return bool
*/
public static function get_other_mapping() {
return false;
}
}
@@ -0,0 +1,73 @@
<?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/>.
/**
* Provides {@link tool_policy\event\acceptance_created} class.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\event;
use core\event\base;
defined('MOODLE_INTERNAL') || die();
/**
* Event acceptance_created
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class acceptance_created extends acceptance_base {
/**
* Initialise the event.
*/
protected function init() {
parent::init();
$this->data['crud'] = 'c';
}
/**
* Returns event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_acceptance_created', 'tool_policy');
}
/**
* Get the event description.
*
* @return string
*/
public function get_description() {
if ($this->other['status'] == 1) {
$action = 'added consent to';
} else if ($this->other['status'] == -1) {
$action = 'revoked consent to';
} else {
$action = 'created an empty consent record for';
}
return "The user with id '{$this->userid}' $action the policy with revision {$this->other['policyversionid']} ".
"for the user with id '{$this->relateduserid}'";
}
}
@@ -0,0 +1,73 @@
<?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/>.
/**
* Provides {@link tool_policy\event\acceptance_updated} class.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\event;
use core\event\base;
defined('MOODLE_INTERNAL') || die();
/**
* Event acceptance_updated
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class acceptance_updated extends acceptance_base {
/**
* Initialise the event.
*/
protected function init() {
parent::init();
$this->data['crud'] = 'u';
}
/**
* Returns event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_acceptance_updated', 'tool_policy');
}
/**
* Get the event description.
*
* @return string
*/
public function get_description() {
if ($this->other['status'] == 1) {
$action = 'added consent to';
} else if ($this->other['status'] == -1) {
$action = 'revoked consent to';
} else {
$action = 'updated consent to';
}
return "The user with id '{$this->userid}' $action the policy with revision {$this->other['policyversionid']} ".
"for the user with id '{$this->relateduserid}'";
}
}
+189
View File
@@ -0,0 +1,189 @@
<?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/>.
/**
* Class containing the external API functions functions for the Policy tool.
*
* @package tool_policy
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy;
use context_system;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use moodle_exception;
use tool_policy\form\accept_policy;
/**
* Class external.
*
* The external API for the Policy tool.
*
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external extends external_api {
/**
* Parameter description for get_policy_version_parameters().
*
* @return external_function_parameters
*/
public static function get_policy_version_parameters() {
return new external_function_parameters([
'versionid' => new external_value(PARAM_INT, 'The policy version ID', VALUE_REQUIRED),
'behalfid' => new external_value(PARAM_INT, 'The id of user on whose behalf the user is viewing the policy',
VALUE_DEFAULT, 0)
]);
}
/**
* Fetch the details of a policy version.
*
* @param int $versionid The policy version ID.
* @param int $behalfid The id of user on whose behalf the user is viewing the policy.
* @return array
* @throws coding_exception
* @throws dml_exception
* @throws invalid_parameter_exception
* @throws restricted_context_exception
* @throws moodle_exception
*/
public static function get_policy_version($versionid, $behalfid = null) {
global $PAGE;
$result = [];
$warnings = [];
$params = external_api::validate_parameters(self::get_policy_version_parameters(), [
'versionid' => $versionid,
'behalfid' => $behalfid
]);
$versionid = $params['versionid'];
$behalfid = $params['behalfid'];
$context = context_system::instance();
$PAGE->set_context($context);
try {
// Validate if the user has access to the policy version.
$version = api::get_policy_version($versionid);
if (!api::can_user_view_policy_version($version, $behalfid)) {
$warnings[] = [
'item' => $versionid,
'warningcode' => 'errorusercantviewpolicyversion',
'message' => get_string('errorusercantviewpolicyversion', 'tool_policy')
];
} else if (!empty($version)) {
$version = api::get_policy_version($versionid);
$policy['name'] = $version->name;
$policy['versionid'] = $versionid;
list($policy['content'], $notusedformat) = \core_external\util::format_text(
$version->content,
$version->contentformat,
\context_system::instance(),
'tool_policy',
'policydocumentcontent',
$version->id
);
$result['policy'] = $policy;
}
} catch (moodle_exception $e) {
$warnings[] = [
'item' => $versionid,
'warningcode' => 'errorpolicyversionnotfound',
'message' => get_string('errorpolicyversionnotfound', 'tool_policy')
];
}
return [
'result' => $result,
'warnings' => $warnings
];
}
/**
* Parameter description for get_policy_version().
*
* @return \core_external\external_description
*/
public static function get_policy_version_returns() {
return new external_single_structure([
'result' => new external_single_structure([
'policy' => new external_single_structure([
'name' => new external_value(PARAM_RAW, 'The policy version name', VALUE_OPTIONAL),
'versionid' => new external_value(PARAM_INT, 'The policy version id', VALUE_OPTIONAL),
'content' => new external_value(PARAM_RAW, 'The policy version content', VALUE_OPTIONAL)
], 'Policy information', VALUE_OPTIONAL)
]),
'warnings' => new external_warnings()
]);
}
/**
* Describes the parameters for submit_create_group_form webservice.
* @return external_function_parameters
*/
public static function submit_accept_on_behalf_parameters() {
return new external_function_parameters(
array(
'jsonformdata' => new external_value(PARAM_RAW, 'The data from the create group form, encoded as a json array')
)
);
}
/**
* Submit the create group form.
*
* @param string $jsonformdata The data from the form, encoded as a json array.
* @return int new group id.
*/
public static function submit_accept_on_behalf($jsonformdata) {
// We always must pass webservice params through validate_parameters.
$params = self::validate_parameters(self::submit_accept_on_behalf_parameters(),
['jsonformdata' => $jsonformdata]);
self::validate_context(context_system::instance());
$serialiseddata = json_decode($params['jsonformdata']);
$data = array();
parse_str($serialiseddata, $data);
// The last param is the ajax submitted data.
$mform = new accept_policy(null, $data, 'post', '', null, true, $data);
// Do the action.
$mform->process();
return true;
}
/**
* Returns description of method result value.
*
* @return \core_external\external_description
* @since Moodle 3.0
*/
public static function submit_accept_on_behalf_returns() {
return new external_value(PARAM_BOOL, 'success');
}
}
@@ -0,0 +1,173 @@
<?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/>.
namespace tool_policy\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_multiple_structure;
use core_external\external_format_value;
use core_external\external_value;
use core_external\external_warnings;
use tool_policy\api;
use context_user;
use core_user;
use core_external\util;
/**
* External function for retrieving user policies acceptances.
*
* @package tool_policy
* @copyright 2023 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.4
*/
class get_user_acceptances extends external_api {
/**
* Webservice parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters(
[
'userid' => new external_value(PARAM_INT, 'The user id we want to retrieve the acceptances.',
VALUE_DEFAULT, 0),
]
);
}
/**
* Returns the acceptance status for all the policies the given user can see.
*
* @param int $userid the user id we want to retrieve the acceptances
* @throws \required_capability_exception
* @return array policies and acceptance status
*/
public static function execute(int $userid = 0): array {
global $USER;
$params = self::validate_parameters(self::execute_parameters(),
[
'userid' => $userid,
]
);
// Do not check for the site policies in validate_context() to avoid the redirect loop.
if (!defined('NO_SITEPOLICY_CHECK')) {
define('NO_SITEPOLICY_CHECK', true);
}
$systemcontext = \context_system::instance();
external_api::validate_context($systemcontext);
if (empty($params['userid']) || $params['userid'] == $USER->id) {
$user = $USER;
} else {
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
core_user::require_active_user($user);
$usercontext = context_user::instance($user->id);
// Check capability to view acceptances. No capability is needed to view your own acceptances.
if (!has_capability('tool/policy:acceptbehalf', $usercontext)) {
require_capability('tool/policy:viewacceptances', $usercontext);
}
}
$canviewfullnames = has_capability('moodle/site:viewfullnames', $systemcontext);
$userpolicies = api::get_policies_with_acceptances($user->id);
$policies = [];
foreach ($userpolicies as $userpolicy) {
foreach ($userpolicy->versions as $version) {
$policy = (array) clone $version;
unset($policy['acceptance']); // This might return NULL and break the WS response.
$policy['versionid'] = $version->id;
$policy['name'] = util::format_string($version->name, $systemcontext);
$policy['revision'] = util::format_string($version->revision, $systemcontext);
[$policy['summary'], $policy['summaryformat']] = util::format_text($version->summary,
$version->summaryformat, $systemcontext);
[$policy['content'], $policy['contentformat']] = util::format_text($version->content,
$version->contentformat, $systemcontext);
if (!empty($version->acceptance)) {
$policy['acceptance'] = (array) $version->acceptance;
if ($version->acceptance->usermodified && $version->acceptance->usermodified != $user->id) {
// Get the full name of who accepted on behalf.
$usermodified = (object)['id' => $version->acceptance->usermodified];
username_load_fields_from_object($usermodified, $version->acceptance, 'mod');
$override = $canviewfullnames || has_capability('moodle/site:viewfullnames', context_user::instance($version->acceptance->usermodified));
$policy['acceptance']['modfullname'] = fullname($usermodified, $override);
}
if (!empty($version->acceptance->note)) {
[$policy['acceptance']['note']] = util::format_text($version->acceptance->note, FORMAT_MOODLE, $systemcontext);
}
}
// Return permission for actions for the current policy and user.
$policy['canaccept'] = api::can_accept_policies([$version->id], $user->id);
$policy['candecline'] = api::can_decline_policies([$version->id], $user->id);
$policy['canrevoke'] = api::can_revoke_policies([$version->id], $user->id);
$policies[] = $policy;
}
}
$return = [
'policies' => $policies,
'warnings' => [],
];
return $return;
}
/**
* Webservice returns.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'policies' => new external_multiple_structure(
new external_single_structure([
'policyid' => new external_value(PARAM_INT, 'The policy id.'),
'versionid' => new external_value(PARAM_INT, 'The policy version id.'),
'agreementstyle' => new external_value(PARAM_INT, 'The policy agreement style. 0: consent page, 1: own page.'),
'optional' => new external_value(PARAM_INT, 'Whether the policy is optional. 0: compulsory, 1: optional'),
'revision' => new external_value(PARAM_TEXT, 'The policy revision.'),
'status' => new external_value(PARAM_INT, 'The policy status. 0: draft, 1: active, 2: archived.'),
'name' => new external_value(PARAM_TEXT, 'The policy name'),
'summary' => new external_value(PARAM_RAW, 'The policy summary.', VALUE_OPTIONAL),
'summaryformat' => new external_format_value('summary'),
'content' => new external_value(PARAM_RAW, 'The policy content.', VALUE_OPTIONAL),
'contentformat' => new external_format_value('content'),
'acceptance' => new external_single_structure([
'status' => new external_value(PARAM_INT, 'The acceptance status. 0: declined, 1: accepted.'),
'lang' => new external_value(PARAM_LANG, 'The policy lang.'),
'timemodified' => new external_value(PARAM_INT, 'The time the acceptance was set.'),
'usermodified' => new external_value(PARAM_INT, 'The user who accepted.'),
'note' => new external_value(PARAM_TEXT, 'The policy note/remarks.', VALUE_OPTIONAL),
'modfullname' => new external_value(PARAM_NOTAGS, 'The fullname who accepted on behalf.', VALUE_OPTIONAL),
], 'Acceptance status for the given user.', VALUE_OPTIONAL),
'canaccept' => new external_value(PARAM_BOOL, 'Whether the policy can be accepted.'),
'candecline' => new external_value(PARAM_BOOL, 'Whether the policy can be declined.'),
'canrevoke' => new external_value(PARAM_BOOL, 'Whether the policy can be revoked.'),
]), 'Policies and acceptance status for the given user.', VALUE_OPTIONAL
),
'warnings' => new external_warnings(),
]);
}
}
@@ -0,0 +1,151 @@
<?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/>.
namespace tool_policy\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_multiple_structure;
use core_external\external_value;
use core_external\external_warnings;
use tool_policy\api;
use tool_policy\policy_version;
use core_user;
/**
* External function for setting user policies acceptances.
*
* @package tool_policy
* @copyright 2023 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.4
*/
class set_acceptances_status extends external_api {
/**
* Webservice parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters(
[
'policies' => new external_multiple_structure(
new external_single_structure([
'versionid' => new external_value(PARAM_INT, 'The policy version id.'),
'status' => new external_value(PARAM_INT, 'The policy acceptance status. 0: decline, 1: accept.'),
'note' => new external_value(PARAM_NOTAGS,
'Any comments added by a user when giving consent on behalf of another user.', VALUE_OPTIONAL, null),
]), 'Policies acceptances for the given user.'
),
'userid' => new external_value(PARAM_INT,
'The user id we want to set the acceptances. Default is the current user.', VALUE_DEFAULT, 0
),
]
);
}
/**
* Set the acceptance status (accept or decline only) for the indicated policies for the given user.
*
* @param array $policies the policies to set the acceptance status
* @param int $userid the user id we want to retrieve the acceptances
* @throws \moodle_exception
* @return array policies and acceptance status
*/
public static function execute(array $policies, int $userid = 0): array {
global $USER;
$params = self::validate_parameters(self::execute_parameters(),
[
'policies' => $policies,
'userid' => $userid,
]
);
// Do not check for the site policies in validate_context() to avoid the redirect loop.
if (!defined('NO_SITEPOLICY_CHECK')) {
define('NO_SITEPOLICY_CHECK', true);
}
$systemcontext = \context_system::instance();
external_api::validate_context($systemcontext);
if (empty($params['userid']) || $params['userid'] == $USER->id) {
$user = $USER;
} else {
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
core_user::require_active_user($user);
}
// Split acceptances.
$requestedpolicies = $agreepolicies = $declinepolicies = $notes = [];
foreach ($params['policies'] as $policy) {
$requestedpolicies[$policy['versionid']] = $policy['status'];
if ($USER->id != $user->id) {
// Notes are only allowed when setting acceptances on behalf of another user.
$notes[$policy['versionid']] = $policy['note'] ?? null;
}
}
// Retrieve all policies and their acceptances.
$allpolicies = api::get_policies_with_acceptances($user->id);
foreach ($allpolicies as $policy) {
foreach ($policy->versions as $version) {
if (isset($requestedpolicies[$version->id])) {
if ($requestedpolicies[$version->id] === 1) {
$agreepolicies[] = $version->id;
} else if ($requestedpolicies[$version->id] === 0) {
$declinepolicies[] = $version->id;
}
}
}
}
// Permissions check.
api::can_accept_policies($agreepolicies, $user->id, true);
api::can_decline_policies($declinepolicies, $user->id, true);
// Good to go.
foreach ($agreepolicies as $policyversionid) {
api::accept_policies($policyversionid, $user->id, $notes[$policyversionid] ?? null);
}
foreach ($declinepolicies as $policyversionid) {
api::decline_policies($policyversionid, $user->id, $notes[$policyversionid] ?? null);
}
$return = [
'policyagreed' => (int) $user->policyagreed, // Final policy agreement status for $user.
'warnings' => [],
];
return $return;
}
/**
* Webservice returns.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'policyagreed' => new external_value(PARAM_INT,
'Whether the user has provided acceptance to all current site policies. 1 if yes, 0 if not'),
'warnings' => new external_warnings(),
]);
}
}
@@ -0,0 +1,201 @@
<?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/>.
/**
* Provides {@link tool_policy\form\accept_policy} class.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\form;
use tool_policy\api;
use tool_policy\policy_version;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Represents the form for accepting or revoking a policy.
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class accept_policy extends \moodleform {
/**
* Defines the form fields.
*/
public function definition() {
global $PAGE, $USER;
$mform = $this->_form;
if (empty($this->_customdata['userids']) || !is_array($this->_customdata['userids'])) {
throw new \moodle_exception('missingparam', 'error', '', 'userids');
}
if (empty($this->_customdata['versionids']) || !is_array($this->_customdata['versionids'])) {
throw new \moodle_exception('missingparam', '', '', 'versionids');
}
$action = $this->_customdata['action'];
$userids = clean_param_array($this->_customdata['userids'], PARAM_INT);
$versionids = clean_param_array($this->_customdata['versionids'], PARAM_INT);
$usernames = $this->validate_and_get_users($versionids, $userids, $action);
$versionnames = $this->validate_and_get_versions($versionids);
foreach ($usernames as $userid => $name) {
$mform->addElement('hidden', 'userids['.$userid.']', $userid);
$mform->setType('userids['.$userid.']', PARAM_INT);
}
foreach ($versionnames as $versionid => $name) {
$mform->addElement('hidden', 'versionids['.$versionid.']', $versionid);
$mform->setType('versionids['.$versionid.']', PARAM_INT);
}
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
$useracceptancelabel = (count($usernames) > 1) ? get_string('acceptanceusers', 'tool_policy') :
get_string('user');
$mform->addElement('static', 'user', $useracceptancelabel, join(', ', $usernames));
$policyacceptancelabel = (count($versionnames) > 1) ? get_string('acceptancepolicies', 'tool_policy') :
get_string('policydochdrpolicy', 'tool_policy');
$mform->addElement('static', 'policy', $policyacceptancelabel, join(', ', $versionnames));
if ($action === 'revoke') {
$mform->addElement('static', 'ack', '', get_string('revokeacknowledgement', 'tool_policy'));
$mform->addElement('hidden', 'action', 'revoke');
} else if ($action === 'accept') {
$mform->addElement('static', 'ack', '', get_string('acceptanceacknowledgement', 'tool_policy'));
$mform->addElement('hidden', 'action', 'accept');
} else if ($action === 'decline') {
$mform->addElement('static', 'ack', '', get_string('declineacknowledgement', 'tool_policy'));
$mform->addElement('hidden', 'action', 'decline');
} else {
throw new \moodle_exception('invalidaccessparameter');
}
$mform->setType('action', PARAM_ALPHA);
if (count($usernames) == 1 && isset($usernames[$USER->id])) {
// No need to display the acknowledgement if the users are giving/revoking acceptance on their own.
$mform->removeElement('ack');
}
$mform->addElement('textarea', 'note', get_string('acceptancenote', 'tool_policy'));
$mform->setType('note', PARAM_NOTAGS);
if (!empty($this->_customdata['showbuttons'])) {
if ($action === 'revoke') {
$this->add_action_buttons(true, get_string('irevokethepolicy', 'tool_policy'));
} else if ($action === 'accept') {
$this->add_action_buttons(true, get_string('iagreetothepolicy', 'tool_policy'));
} else if ($action === 'decline') {
$this->add_action_buttons(true, get_string('declinethepolicy', 'tool_policy'));
}
}
$PAGE->requires->js_call_amd('tool_policy/policyactions', 'init', ['[data-action="view"]']);
}
/**
* Validate userids and return usernames
*
* @param array $versionids int[] List of policy version ids to process.
* @param array $userids
* @param string $action accept|decline|revoke
* @return array (userid=>username)
*/
protected function validate_and_get_users($versionids, $userids, $action) {
global $DB;
$usernames = [];
list($sql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$params['usercontextlevel'] = CONTEXT_USER;
$userfieldsapi = \core_user\fields::for_name();
$users = $DB->get_records_sql("SELECT u.id" . $userfieldsapi->get_sql('u')->selects . ", " .
\context_helper::get_preload_record_columns_sql('ctx') .
" FROM {user} u JOIN {context} ctx ON ctx.contextlevel=:usercontextlevel AND ctx.instanceid = u.id
WHERE u.id " . $sql, $params);
foreach ($userids as $userid) {
if (!isset($users[$userid])) {
throw new \dml_missing_record_exception('user', 'id=?', [$userid]);
}
$user = $users[$userid];
if (isguestuser($user)) {
throw new \moodle_exception('noguest');
}
\context_helper::preload_from_record($user);
if ($action === 'revoke') {
api::can_revoke_policies($versionids, $userid, true);
} else if ($action === 'accept') {
api::can_accept_policies($versionids, $userid, true);
} else if ($action === 'decline') {
api::can_decline_policies($versionids, $userid, true);
}
$usernames[$userid] = fullname($user);
}
return $usernames;
}
/**
* Validate versionids and return their names
*
* @param array $versionids
* @return array (versionid=>name)
*/
protected function validate_and_get_versions($versionids) {
$versionnames = [];
$policies = api::list_policies();
foreach ($versionids as $versionid) {
$version = api::get_policy_version($versionid, $policies);
if ($version->audience == policy_version::AUDIENCE_GUESTS) {
throw new \moodle_exception('errorpolicyversionnotfound', 'tool_policy');
}
$url = new \moodle_url('/admin/tool/policy/view.php', ['versionid' => $version->id]);
$policyname = $version->name;
if ($version->status != policy_version::STATUS_ACTIVE) {
$policyname .= ' ' . $version->revision;
}
$versionnames[$version->id] = \html_writer::link($url, $policyname,
['data-action' => 'view', 'data-versionid' => $version->id]);
}
return $versionnames;
}
/**
* Process form submission
*/
public function process() {
if ($data = $this->get_data()) {
foreach ($data->userids as $userid) {
if ($data->action === 'revoke') {
foreach ($data->versionids as $versionid) {
\tool_policy\api::revoke_acceptance($versionid, $userid, $data->note);
}
} else if ($data->action === 'accept') {
\tool_policy\api::accept_policies($data->versionids, $userid, $data->note);
} else if ($data->action === 'decline') {
\tool_policy\api::decline_policies($data->versionids, $userid, $data->note);
}
}
}
}
}
@@ -0,0 +1,168 @@
<?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/>.
/**
* Provides {@link tool_policy\form\policydoc} class.
*
* @package tool_policy
* @category output
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\form;
use context_system;
use html_writer;
use moodleform;
use tool_policy\api;
use tool_policy\policy_version;
defined('MOODLE_INTERNAL') || die();
/**
* Defines the form for editing a policy document version.
*
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class policydoc extends moodleform {
/**
* Defines the form fields.
*/
public function definition() {
$mform = $this->_form;
$formdata = $this->_customdata['formdata'];
$mform->addElement('text', 'name', get_string('policydocname', 'tool_policy'), ['maxlength' => 1333]);
$mform->settype('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 1333), 'maxlength', 1333, 'client');
$options = [];
foreach ([policy_version::TYPE_SITE,
policy_version::TYPE_PRIVACY,
policy_version::TYPE_THIRD_PARTY,
policy_version::TYPE_OTHER] as $type) {
$options[$type] = get_string('policydoctype'.$type, 'tool_policy');
}
$mform->addElement('select', 'type', get_string('policydoctype', 'tool_policy'), $options);
$options = [];
foreach ([policy_version::AUDIENCE_ALL,
policy_version::AUDIENCE_LOGGEDIN,
policy_version::AUDIENCE_GUESTS] as $audience) {
$options[$audience] = get_string('policydocaudience'.$audience, 'tool_policy');
}
$mform->addElement('select', 'audience', get_string('policydocaudience', 'tool_policy'), $options);
if (empty($formdata->id)) {
$default = userdate(time(), get_string('strftimedate', 'core_langconfig'));
} else {
$default = userdate($formdata->timecreated, get_string('strftimedate', 'core_langconfig'));
}
$mform->addElement('text', 'revision', get_string('policydocrevision', 'tool_policy'),
['maxlength' => 1333, 'placeholder' => $default]);
$mform->settype('revision', PARAM_TEXT);
$mform->addRule('revision', get_string('maximumchars', '', 1333), 'maxlength', 1333, 'client');
$mform->addElement('editor', 'summary_editor', get_string('policydocsummary', 'tool_policy'), ['rows' => 7],
api::policy_summary_field_options());
$mform->addRule('summary_editor', null, 'required', null, 'client');
$mform->addElement('editor', 'content_editor', get_string('policydoccontent', 'tool_policy'), null,
api::policy_content_field_options());
$mform->addRule('content_editor', null, 'required', null, 'client');
$mform->addElement('selectyesno', 'agreementstyle', get_string('policypriorityagreement', 'tool_policy'));
$mform->addElement('selectyesno', 'optional', get_string('policydocoptional', 'tool_policy'));
if (!$formdata->id || $formdata->status == policy_version::STATUS_DRAFT) {
// Creating a new version or editing a draft/archived version.
$mform->addElement('hidden', 'minorchange');
$mform->setType('minorchange', PARAM_INT);
$statusgrp = [
$mform->createElement('radio', 'status', '', get_string('status'.policy_version::STATUS_ACTIVE, 'tool_policy'),
policy_version::STATUS_ACTIVE),
$mform->createElement('radio', 'status', '', get_string('status'.policy_version::STATUS_DRAFT, 'tool_policy'),
policy_version::STATUS_DRAFT),
$mform->createElement('static', 'statusinfo', '', html_writer::div(get_string('statusinfo', 'tool_policy'),
'muted text-muted')),
];
$mform->addGroup($statusgrp, null, get_string('status', 'tool_policy'), ['<br>'], false);
} else {
// Editing an active version.
$mform->addElement('hidden', 'status', policy_version::STATUS_ACTIVE);
$mform->setType('status', PARAM_INT);
$statusgrp = [
$mform->createElement('checkbox', 'minorchange', '', get_string('minorchange', 'tool_policy')),
$mform->createElement('static', 'minorchangeinfo', '',
html_writer::div(get_string('minorchangeinfo', 'tool_policy'), 'muted text-muted')),
];
$mform->addGroup($statusgrp, null, get_string('status', 'tool_policy'), ['<br>'], false);
}
// Add "Save" button and, optionally, "Save as draft".
$buttonarray = [];
$buttonarray[] = $mform->createElement('submit', 'save', get_string('save', 'tool_policy'));
if ($formdata->id && $formdata->status == policy_version::STATUS_ACTIVE) {
$buttonarray[] = $mform->createElement('submit', 'saveasdraft', get_string('saveasdraft', 'tool_policy'));
}
$buttonarray[] = $mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
$this->set_data($formdata);
}
/**
* Form validation
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
if (!empty($data['minorchange']) && !empty($data['saveasdraft'])) {
// If minorchange is checked and "save as draft" is pressed - return error.
$errors['minorchange'] = get_string('errorsaveasdraft', 'tool_policy');
}
return $errors;
}
/**
* Return submitted data if properly submitted or returns NULL if validation fails or
* if there is no submitted data.
*
* @return object submitted data; NULL if not valid or not submitted or cancelled
*/
public function get_data() {
if ($data = parent::get_data()) {
if (!empty($data->saveasdraft)) {
$data->status = policy_version::STATUS_DRAFT;
}
}
return $data;
}
}
@@ -0,0 +1,89 @@
<?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/>.
namespace tool_policy;
use core\hook\output\before_standard_footer_html_generation;
use core\hook\output\before_standard_top_of_body_html_generation;
use html_writer;
use moodle_url;
/**
* Allows the plugin to add any elements to the footer.
*
* @package tool_policy
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class hook_callbacks {
/**
* Add the guest consent form to the top of the body.
*
* @param before_standard_top_of_body_html_generation $hook
*/
public static function before_standard_top_of_body_html_generation(before_standard_top_of_body_html_generation $hook): void {
global $CFG, $PAGE, $USER;
if (empty($CFG->sitepolicyhandler)) {
return;
}
if ($CFG->sitepolicyhandler !== 'tool_policy') {
return;
}
if (!empty($USER->policyagreed)) {
return;
}
if (!isguestuser() && isloggedin()) {
return;
}
$output = $PAGE->get_renderer('tool_policy');
try {
$page = new \tool_policy\output\guestconsent();
$hook->add_html($output->render($page));
} catch (\dml_read_exception $e) {
// During upgrades, the new plugin code with new SQL could be in place but the DB not upgraded yet.
return;
}
}
/**
* Add the user policy settings link to the footer.
*
* @param before_standard_footer_html_generation $hook
*/
public static function before_standard_footer_html_generation(before_standard_footer_html_generation $hook): void {
global $CFG, $PAGE;
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
return;
}
$policies = api::get_current_versions_ids();
if (!empty($policies)) {
$url = new moodle_url('/admin/tool/policy/viewall.php', ['returnurl' => $PAGE->url]);
$hook->add_html(
html_writer::div(
html_writer::link($url, get_string('userpolicysettings', 'tool_policy')),
'policiesfooter',
),
);
}
}
}
@@ -0,0 +1,142 @@
<?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/>.
/**
* Provides {@link tool_policy\output\acceptances} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use tool_policy\api;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use renderable;
use renderer_base;
use single_button;
use templatable;
use tool_policy\policy_version;
/**
* List of users and their acceptances
*
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class acceptances implements renderable, templatable {
/** @var id */
protected $userid;
/** @var moodle_url */
protected $returnurl;
/**
* Contructor.
*
* @param int $userid
* @param string|moodle_url $returnurl
*/
public function __construct($userid, $returnurl = null) {
$this->userid = $userid;
$this->returnurl = $returnurl ? (new moodle_url($returnurl))->out(false) : null;
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = (object)[];
$data->hasonbehalfagreements = false;
$data->pluginbaseurl = (new moodle_url('/admin/tool/policy'))->out(false);
$data->returnurl = $this->returnurl;
// Get the list of policies and versions that current user is able to see
// and the respective acceptance records for the selected user.
$policies = api::get_policies_with_acceptances($this->userid);
$versionids = [];
$canviewfullnames = has_capability('moodle/site:viewfullnames', \context_system::instance());
foreach ($policies as $policy) {
foreach ($policy->versions as $version) {
$versionids[$version->id] = $version->id;
unset($version->summary);
unset($version->content);
$version->iscurrent = ($version->status == policy_version::STATUS_ACTIVE);
$version->isoptional = ($version->optional == policy_version::AGREEMENT_OPTIONAL);
$version->name = $version->name;
$version->revision = $version->revision;
$returnurl = new moodle_url('/admin/tool/policy/user.php', ['userid' => $this->userid]);
$version->viewurl = (new moodle_url('/admin/tool/policy/view.php', [
'policyid' => $policy->id,
'versionid' => $version->id,
'returnurl' => $returnurl->out(false),
]))->out(false);
if ($version->acceptance !== null) {
$acceptance = $version->acceptance;
$version->timeaccepted = userdate($acceptance->timemodified, get_string('strftimedatetime'));
$onbehalf = $acceptance->usermodified && $acceptance->usermodified != $this->userid;
if ($version->acceptance->status == 1) {
$version->agreement = new user_agreement($this->userid, [$version->id], [], $returnurl,
[$version->id => $version->name], $onbehalf);
} else {
$version->agreement = new user_agreement($this->userid, [], [$version->id], $returnurl,
[$version->id => $version->name], $onbehalf);
}
if ($onbehalf) {
$usermodified = (object)['id' => $acceptance->usermodified];
username_load_fields_from_object($usermodified, $acceptance, 'mod');
$profileurl = new \moodle_url('/user/profile.php', array('id' => $usermodified->id));
$version->acceptedby = \html_writer::link($profileurl, fullname($usermodified, $canviewfullnames ||
has_capability('moodle/site:viewfullnames', \context_user::instance($acceptance->usermodified))));
$data->hasonbehalfagreements = true;
}
$version->note = format_text($acceptance->note);
} else if ($version->iscurrent) {
$version->agreement = new user_agreement($this->userid, [], [], $returnurl, [$version->id => $version->name]);
}
if (isset($version->agreement)) {
$version->agreement = $version->agreement->export_for_template($output);
}
}
if ($policy->versions[0]->status != policy_version::STATUS_ACTIVE) {
// Add an empty "currentversion" on top.
$policy->versions = [0 => (object)[]] + $policy->versions;
}
$policy->versioncount = count($policy->versions);
$policy->versions = array_values($policy->versions);
$policy->versions[0]->isfirst = 1;
$policy->versions[0]->hasarchived = (count($policy->versions) > 1);
}
$data->policies = array_values($policies);
$data->canrevoke = \tool_policy\api::can_revoke_policies(array_keys($versionids), $this->userid);
return $data;
}
}
@@ -0,0 +1,464 @@
<?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/>.
/**
* Provides {@link tool_policy\output\acceptances_filter} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use tool_policy\api;
use tool_policy\policy_version;
defined('MOODLE_INTERNAL') || die();
/**
* Implements the widget allowing to filter the acceptance records.
*
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class acceptances_filter implements \templatable, \renderable {
/** @var array $filtersapplied The list of selected filter options. */
protected $filtersapplied;
/** @var string $searchstring */
protected $searchstrings;
/** @var array list of available versions */
protected $versions = null;
/** @var array list of available roles for the filter */
protected $roles;
/** @var array cached list of all available policies, to retrieve use {@link self::get_avaliable_policies()} */
protected $policies;
/** @var int */
const FILTER_SEARCH_STRING = 0;
/** @var int */
const FILTER_POLICYID = 1;
/** @var int */
const FILTER_VERSIONID = 2;
/** @var int */
const FILTER_CAPABILITY_ACCEPT = 3;
/** @var int */
const FILTER_STATUS = 4;
/** @var int */
const FILTER_ROLE = 5;
/**
* Constructor.
*
* @param array $policyid Specified policy id
* @param array $versionid Specified version id
* @param array $filtersapplied The list of selected filter option values.
*/
public function __construct($policyid, $versionid, $filtersapplied) {
$this->filtersapplied = [];
$this->roles = get_assignable_roles(\context_system::instance());
if ($policyid) {
$this->add_filter(self::FILTER_POLICYID, $policyid);
}
if ($versionid) {
$this->add_filter(self::FILTER_VERSIONID, $versionid);
}
foreach ($filtersapplied as $filter) {
if (preg_match('/^([1-9]\d*):(\d+)$/', $filter, $parts)) {
// This is a pre-set filter (policy, version, status, etc.).
$allowmultiple = false;
switch ((int)$parts[1]) {
case self::FILTER_POLICYID:
case self::FILTER_VERSIONID:
case self::FILTER_CAPABILITY_ACCEPT:
case self::FILTER_STATUS:
$value = (int)$parts[2];
break;
case self::FILTER_ROLE:
$value = (int)$parts[2];
if (!array_key_exists($value, $this->roles)) {
continue 2;
}
$allowmultiple = true;
break;
default:
// Unrecognised filter.
continue 2;
}
$this->add_filter((int)$parts[1], $value, $allowmultiple);
} else if (trim($filter) !== '') {
// This is a search string.
$this->add_filter(self::FILTER_SEARCH_STRING, trim($filter), true);
}
}
}
/**
* Adds an applied filter
*
* @param mixed $key
* @param mixed $value
* @param bool $allowmultiple
*/
protected function add_filter($key, $value, $allowmultiple = false) {
if ($allowmultiple || empty($this->get_filter_values($key))) {
$this->filtersapplied[] = [$key, $value];
}
}
/**
* Is there a filter by policy
*
* @return null|int null if there is no filter, otherwise the policy id
*/
public function get_policy_id_filter() {
return $this->get_filter_value(self::FILTER_POLICYID);
}
/**
* Is there a filter by version
*
* @return null|int null if there is no filter, otherwise the version id
*/
public function get_version_id_filter() {
return $this->get_filter_value(self::FILTER_VERSIONID);
}
/**
* Are there filters by search strings
*
* @return string[] array of string filters
*/
public function get_search_strings() {
return $this->get_filter_values(self::FILTER_SEARCH_STRING);
}
/**
* Is there a filter by status (agreed/not agreed).
*
* @return null|0|1 null if there is no filter, 0/1 if there is a filter by status
*/
public function get_status_filter() {
return $this->get_filter_value(self::FILTER_STATUS);
}
/**
* Are there filters by role
*
* @return array list of role ids
*/
public function get_role_filters() {
return $this->get_filter_values(self::FILTER_ROLE);
}
/**
* Is there a filter by capability (can accept/cannot accept).
*
* @return null|0|1 null if there is no filter, 0/1 if there is a filter by capability
*/
public function get_capability_accept_filter() {
return $this->get_filter_value(self::FILTER_CAPABILITY_ACCEPT);
}
/**
* Get all values of the applied filter
*
* @param string $filtername
* @return array
*/
protected function get_filter_values($filtername) {
$values = [];
foreach ($this->filtersapplied as $filter) {
if ($filter[0] == $filtername) {
$values[] = $filter[1];
}
}
return $values;
}
/**
* Get one value of the applied filter
*
* @param string $filtername
* @param string $default
* @return mixed
*/
protected function get_filter_value($filtername, $default = null) {
if ($values = $this->get_filter_values($filtername)) {
$value = reset($values);
return $value;
}
return $default;
}
/**
* Returns all policies that have versions with possible acceptances (excl. drafts and guest-only versions)
*
* @return array|null
*/
public function get_avaliable_policies() {
if ($this->policies === null) {
$this->policies = [];
foreach (\tool_policy\api::list_policies() as $policy) {
// Make a list of all versions that are not draft and are not guest-only.
$policy->versions = [];
if ($policy->currentversion && $policy->currentversion->audience != policy_version::AUDIENCE_GUESTS) {
$policy->versions[$policy->currentversion->id] = $policy->currentversion;
} else {
$policy->currentversion = null;
}
foreach ($policy->archivedversions as $version) {
if ($version->audience != policy_version::AUDIENCE_GUESTS) {
$policy->versions[$version->id] = $version;
}
}
if ($policy->versions) {
$this->policies[$policy->id] = $policy;
}
}
}
return $this->policies;
}
/**
* List of policies that match current filters
*
* @return array of versions to display indexed by versionid
*/
public function get_versions() {
if ($this->versions === null) {
$policyid = $this->get_policy_id_filter();
$versionid = $this->get_version_id_filter();
$this->versions = [];
foreach ($this->get_avaliable_policies() as $policy) {
if ($policyid && $policy->id != $policyid) {
continue;
}
if ($versionid) {
if (array_key_exists($versionid, $policy->versions)) {
$this->versions[$versionid] = $policy->versions[$versionid];
break; // No need to keep searching.
}
} else if ($policy->currentversion) {
$this->versions[$policy->currentversion->id] = $policy->currentversion;
}
}
}
return $this->versions;
}
/**
* Validates if policyid and versionid are valid (if specified)
*/
public function validate_ids() {
$policyid = $this->get_policy_id_filter();
$versionid = $this->get_version_id_filter();
if ($policyid || $versionid) {
$found = array_filter($this->get_avaliable_policies(), function($policy) use ($policyid, $versionid) {
return (!$policyid || $policy->id == $policyid) &&
(!$versionid || array_key_exists($versionid, $policy->versions));
});
if (!$found) {
// Throw exception that policy/version is not found.
throw new \moodle_exception('errorpolicyversionnotfound', 'tool_policy');
}
}
}
/**
* If policyid or versionid is specified return one single policy that needs to be shown
*
* If neither policyid nor versionid is specified this method returns null.
*
* When versionid is specified this method will always return an object (this is validated in {@link self::validate_ids()}
* When only policyid is specified this method either returns the current version of the policy or null if there is
* no current version (for example, it is an old policy).
*
* @return mixed|null
*/
public function get_single_version() {
if ($this->get_version_id_filter() || $this->get_policy_id_filter()) {
$versions = $this->get_versions();
return reset($versions);
}
return null;
}
/**
* Returns URL of the acceptances page with all current filters applied
*
* @return \moodle_url
*/
public function get_url() {
$urlparams = [];
if ($policyid = $this->get_policy_id_filter()) {
$urlparams['policyid'] = $policyid;
}
if ($versionid = $this->get_version_id_filter()) {
$urlparams['versionid'] = $versionid;
}
$i = 0;
foreach ($this->filtersapplied as $filter) {
if ($filter[0] != self::FILTER_POLICYID && $filter[0] != self::FILTER_VERSIONID) {
if ($filter[0] == self::FILTER_SEARCH_STRING) {
$urlparams['unified-filters['.($i++).']'] = $filter[1];
} else {
$urlparams['unified-filters['.($i++).']'] = join(':', $filter);
}
}
}
return new \moodle_url('/admin/tool/policy/acceptances.php', $urlparams);
}
/**
* Creates an option name for the smart select for the version
*
* @param \stdClass $version
* @return string
*/
protected function get_version_option_for_filter($version) {
if ($version->status == policy_version::STATUS_ACTIVE) {
$a = (object)[
'name' => format_string($version->revision),
'status' => get_string('status'.policy_version::STATUS_ACTIVE, 'tool_policy'),
];
return get_string('filterrevisionstatus', 'tool_policy', $a);
} else {
return get_string('filterrevision', 'tool_policy', $version->revision);
}
}
/**
* Build list of filters available for this page
*
* @return array [$availablefilters, $selectedoptions]
*/
protected function build_available_filters() {
$selectedoptions = [];
$availablefilters = [];
$versionid = $this->get_version_id_filter();
$policyid = $versionid ? $this->get_single_version()->policyid : $this->get_policy_id_filter();
// Policies.
$policies = $this->get_avaliable_policies();
if ($policyid) {
// If policy is selected, display only the current policy in the selector.
$selectedoptions[] = $key = self::FILTER_POLICYID . ':' . $policyid;
$version = $versionid ? $policies[$policyid]->versions[$versionid] : reset($policies[$policyid]->versions);
$availablefilters[$key] = get_string('filterpolicy', 'tool_policy', $version->name);
} else {
// If no policy/version is selected display the list of all policies.
foreach ($policies as $policy) {
$firstversion = reset($policy->versions);
$key = self::FILTER_POLICYID . ':' . $policy->id;
$availablefilters[$key] = get_string('filterpolicy', 'tool_policy', $firstversion->name);
}
}
// Versions.
if ($versionid) {
$singleversion = $this->get_single_version();
$selectedoptions[] = $key = self::FILTER_VERSIONID . ':' . $singleversion->id;
$availablefilters[$key] = $this->get_version_option_for_filter($singleversion);
} else if ($policyid) {
foreach ($policies[$policyid]->versions as $version) {
$key = self::FILTER_VERSIONID . ':' . $version->id;
$availablefilters[$key] = $this->get_version_option_for_filter($version);
}
}
// Permissions.
$permissions = [
self::FILTER_CAPABILITY_ACCEPT . ':1' => get_string('filtercapabilityyes', 'tool_policy'),
self::FILTER_CAPABILITY_ACCEPT . ':0' => get_string('filtercapabilityno', 'tool_policy'),
];
if (($currentpermission = $this->get_capability_accept_filter()) !== null) {
$selectedoptions[] = $key = self::FILTER_CAPABILITY_ACCEPT . ':' . $currentpermission;
$permissions = array_intersect_key($permissions, [$key => true]);
}
$availablefilters += $permissions;
// Status.
$statuses = [
self::FILTER_STATUS.':2' => get_string('filterstatusdeclined', 'tool_policy'),
self::FILTER_STATUS.':1' => get_string('filterstatusyes', 'tool_policy'),
self::FILTER_STATUS.':0' => get_string('filterstatuspending', 'tool_policy'),
];
if (($currentstatus = $this->get_status_filter()) !== null) {
$selectedoptions[] = $key = self::FILTER_STATUS . ':' . $currentstatus;
$statuses = array_intersect_key($statuses, [$key => true]);
}
$availablefilters += $statuses;
// Roles.
$currentroles = $this->get_role_filters();
foreach ($this->roles as $roleid => $rolename) {
$key = self::FILTER_ROLE . ':' . $roleid;
$availablefilters[$key] = get_string('filterrole', 'tool_policy', $rolename);
if (in_array($roleid, $currentroles)) {
$selectedoptions[] = $key;
}
}
// Search string.
foreach ($this->get_search_strings() as $str) {
$selectedoptions[] = $str;
$availablefilters[$str] = $str;
}
return [$availablefilters, $selectedoptions];
}
/**
* Function to export the renderer data in a format that is suitable for a mustache template.
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return \stdClass|array
*/
public function export_for_template(\renderer_base $output) {
$data = new \stdClass();
$data->action = (new \moodle_url('/admin/tool/policy/acceptances.php'))->out(false);
$data->filteroptions = [];
$originalfilteroptions = [];
list($avilablefilters, $selectedoptions) = $this->build_available_filters();
foreach ($avilablefilters as $value => $label) {
$selected = in_array($value, $selectedoptions);
$filteroption = (object)[
'value' => $value,
'label' => $label
];
$originalfilteroptions[] = $filteroption;
$filteroption->selected = $selected;
$data->filteroptions[] = $filteroption;
}
$data->originaloptionsjson = json_encode($originalfilteroptions);
return $data;
}
}
@@ -0,0 +1,74 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use renderable;
use renderer_base;
use templatable;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Renderer for the policies plugin.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class guestconsent implements renderable, templatable {
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
global $PAGE;
$data = (object) [];
$data->pluginbaseurl = (new moodle_url('/admin/tool/policy'))->out(true);
if (strpos(qualified_me(), '/tool/policy/view.php') === false) {
// Current page is not a policy doc, so returnurl parameter will be it.
$data->returnurl = qualified_me();
} else {
// If current page is also a policy doc to view, get previous returnurl parameter to avoid error.
$returnurl = $PAGE->url->get_param('returnurl');
if (isset($returnurl)) {
$data->returnurl = $returnurl;
}
}
$data->returnurl = urlencode($data->returnurl);
$policies = api::list_current_versions(policy_version::AUDIENCE_GUESTS);
$data->policies = array_values($policies);
$data->haspolicies = !empty($policies);
return $data;
}
}
@@ -0,0 +1,502 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
defined('MOODLE_INTERNAL') || die();
use context_system;
use core\output\notification;
use core\session\manager;
use core_user;
use html_writer;
use moodle_url;
use renderable;
use renderer_base;
use single_button;
use templatable;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Represents a page for showing all the policy documents which a user has to agree to.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_agreedocs implements renderable, templatable {
/** @var array $policies List of public policies objects with information about the user acceptance. */
protected $policies = null;
/** @var array List of policy version ids that were displayed to the user to agree with. */
protected $listdocs = null;
/** @var array $agreedocs List of policy identifiers which the user has agreed using the form. */
protected $agreedocs = null;
/** @var array $declinedocs List of policy identifiers that the user declined. */
protected $declinedocs = null;
/** @var string $action Form action to identify when user agreeds policies. */
protected $action = null;
/** @var int User id who wants to accept this page. */
protected $behalfid = null;
/** @var object User who wants to accept this page. */
protected $behalfuser = null;
/** @var boolean True if signup user has agreed to all the policies; false otherwise. */
protected $signupuserpolicyagreed = false;
/** @var array Info or error messages to show. */
protected $messages = [];
/** @var bool This is an existing user (rather than non-loggedin/guest). */
protected $isexistinguser;
/**
* Prepare the page for rendering.
*
* @param array $listdocs List of policy version ids that were displayed to the user to agree with.
* @param array $agreedocs List of policy version ids that the user actually agreed with.
* @param array $declinedocs List of policy version ids that the user declined.
* @param int $behalfid The userid to accept the policy versions as (such as child's id).
* @param string $action Form action to identify when user agreeds policies.
*/
public function __construct(array $listdocs, array $agreedocs = [], array $declinedocs = [], $behalfid = 0, $action = null) {
global $USER;
$realuser = manager::get_realuser();
$this->listdocs = $listdocs;
$this->agreedocs = $agreedocs;
$this->declinedocs = $declinedocs;
$this->action = $action;
$this->isexistinguser = isloggedin() && !isguestuser();
$behalfid = $behalfid ?: $USER->id;
if ($realuser->id != $behalfid) {
$this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
$this->behalfid = $this->behalfuser->id;
}
$this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
if (!$this->isexistinguser) {
// During the signup, show compulsory policies only.
foreach ($this->policies as $ix => $policyversion) {
if ($policyversion->optional == policy_version::AGREEMENT_OPTIONAL) {
unset($this->policies[$ix]);
}
}
$this->policies = array_values($this->policies);
}
if (empty($this->behalfid)) {
$userid = $USER->id;
} else {
$userid = $this->behalfid;
}
$this->accept_and_revoke_policies();
$this->prepare_global_page_access($userid);
$this->prepare_user_acceptances($userid);
}
/**
* Accept and revoke the policy versions.
* The capabilities for accepting/revoking policies are checked into the api functions.
*
*/
protected function accept_and_revoke_policies() {
global $USER;
if ($this->isexistinguser) {
// Existing user.
if (!empty($this->action) && confirm_sesskey()) {
// The form has been sent, update policies acceptances.
$lang = current_language();
// Accept / revoke policies.
$acceptversionids = [];
$declineversionids = [];
foreach ($this->policies as $policy) {
if (in_array($policy->id, $this->listdocs)) {
if (in_array($policy->id, $this->agreedocs)) {
$acceptversionids[] = $policy->id;
} else if (in_array($policy->id, $this->declinedocs)) {
$declineversionids[] = $policy->id;
} else {
// If the policy was displayed but not answered, revoke the eventually given acceptance.
api::revoke_acceptance($policy->id, $this->behalfid);
}
}
}
api::accept_policies($acceptversionids, $this->behalfid, null, $lang);
api::decline_policies($declineversionids, $this->behalfid, null, $lang);
// Show a message to let know the user he/she must agree all the policies.
if ((count($acceptversionids) + count($declineversionids)) != count($this->policies)) {
$message = (object) [
'type' => 'error',
'text' => get_string('mustagreetocontinue', 'tool_policy')
];
} else {
$message = (object) [
'type' => 'success',
'text' => get_string('acceptancessavedsucessfully', 'tool_policy')
];
}
$this->messages[] = $message;
} else if (!empty($this->policies) && empty($USER->policyagreed)) {
// Inform users they must agree to all policies before continuing.
$message = (object) [
'type' => 'error',
'text' => get_string('mustagreetocontinue', 'tool_policy')
];
$this->messages[] = $message;
}
} else {
// New user.
if (!empty($this->action) && confirm_sesskey()) {
$currentpolicyversionids = [];
$presignupcache = \cache::make('core', 'presignup');
$acceptances = $presignupcache->get('tool_policy_policyversionidsagreed');
if (!$acceptances) {
$acceptances = [];
}
foreach ($this->policies as $policy) {
$currentpolicyversionids[] = $policy->id;
if (in_array($policy->id, $this->listdocs)) {
if (in_array($policy->id, $this->agreedocs)) {
$acceptances[] = $policy->id;
} else {
$acceptances = array_values(array_diff($acceptances, [$policy->id]));
}
}
}
// If the user has accepted all the policies, add it to the session to let continue with the signup process.
$this->signupuserpolicyagreed = empty(array_diff($currentpolicyversionids, $acceptances));
$presignupcache->set('tool_policy_userpolicyagreed', $this->signupuserpolicyagreed);
$presignupcache->set('tool_policy_policyversionidsagreed', $acceptances);
} else if (empty($this->policies)) {
// There are no policies to agree to. Update the policyagreed value to avoid show empty consent page.
\cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed', 1);
}
if (!empty($this->policies) && !$this->signupuserpolicyagreed) {
// During the signup process, inform users they must agree to all policies before continuing.
$message = (object) [
'type' => 'error',
'text' => get_string('mustagreetocontinue', 'tool_policy')
];
$this->messages[] = $message;
}
}
}
/**
* Before display the consent page, the user has to view all the still-non-accepted policy docs.
* This function checks if the non-accepted policy docs have been shown and redirect to them.
*
* @param int $userid User identifier who wants to access to the consent page.
* @param moodle_url $returnurl URL to return after shown the policy docs.
*/
protected function redirect_to_policies($userid, $returnurl = null) {
// Make a list of all policies that the user has not answered yet.
$allpolicies = $this->policies;
if ($this->isexistinguser) {
$acceptances = api::get_user_acceptances($userid);
foreach ($allpolicies as $ix => $policy) {
$isaccepted = api::is_user_version_accepted($userid, $policy->id, $acceptances);
if ($isaccepted) {
// The user has accepted this policy, do not show it again.
unset($allpolicies[$ix]);
} else if ($isaccepted === false && $policy->optional == policy_version::AGREEMENT_OPTIONAL) {
// The user declined this policy but the agreement was optional, do not show it.
unset($allpolicies[$ix]);
} else {
// The user has not answered the policy yet, or the agreement is compulsory. Show it.
continue;
}
}
} else {
$presignupcache = \cache::make('core', 'presignup');
$acceptances = $presignupcache->get('tool_policy_policyversionidsagreed');
if ($acceptances) {
foreach ($allpolicies as $ix => $policy) {
if (in_array($policy->id, $acceptances)) {
unset($allpolicies[$ix]);
}
}
}
}
if (!empty($allpolicies)) {
// Check if some of the to-be-accepted policies should be agreed on their own page.
foreach ($allpolicies as $policy) {
if ($policy->agreementstyle == policy_version::AGREEMENTSTYLE_OWNPAGE) {
if (empty($returnurl)) {
$returnurl = (new moodle_url('/admin/tool/policy/index.php'))->out_as_local_url(false);
}
$urlparams = ['versionid' => $policy->id, 'returnurl' => $returnurl];
redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
}
}
$currentpolicyversionids = [];
foreach ($allpolicies as $policy) {
$currentpolicyversionids[] = $policy->id;
}
$cache = \cache::make('core', 'presignup');
$cachekey = 'tool_policy_viewedpolicies';
$viewedpolicies = $cache->get($cachekey) ?: [];
if (!empty($viewedpolicies)) {
// Get the list of the policies docs which the user haven't viewed during this session.
$pendingpolicies = array_diff($currentpolicyversionids, $viewedpolicies);
} else {
$pendingpolicies = $currentpolicyversionids;
}
if (count($pendingpolicies) > 0) {
// Still is needed to show some policies docs. Save in the session and redirect.
$policyversionid = array_shift($pendingpolicies);
$viewedpolicies[] = $policyversionid;
$cache->set($cachekey, $viewedpolicies);
if (empty($returnurl)) {
$returnurl = new moodle_url('/admin/tool/policy/index.php');
}
$urlparams = ['versionid' => $policyversionid,
'returnurl' => $returnurl,
'numpolicy' => count($currentpolicyversionids) - count($pendingpolicies),
'totalpolicies' => count($currentpolicyversionids),
];
redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
}
} else {
// Update the policyagreed for the user to avoid infinite loop because there are no policies to-be-accepted.
api::update_policyagreed($userid);
$this->redirect_to_previous_url();
}
}
/**
* Redirect to signup page if defined or to $CFG->wwwroot if not.
*/
protected function redirect_to_previous_url() {
global $SESSION;
if ($this->isexistinguser) {
// Existing user.
if (!empty($SESSION->wantsurl)) {
$returnurl = $SESSION->wantsurl;
unset($SESSION->wantsurl);
} else {
$returnurl = new moodle_url('/admin/tool/policy/user.php');
}
} else {
// Non-authenticated user.
$issignup = \cache::make('core', 'presignup')->get('tool_policy_issignup');
if ($issignup) {
// User came here from signup page - redirect back there.
$returnurl = new moodle_url('/login/signup.php');
\cache::make('core', 'presignup')->set('tool_policy_issignup', false);
} else {
// Guests should not be on this page unless it's part of signup - redirect home.
$returnurl = new moodle_url('/');
}
}
redirect($returnurl);
}
/**
* Sets up the global $PAGE and performs the access checks.
*
* @param int $userid
*/
protected function prepare_global_page_access($userid) {
global $PAGE, $SITE, $USER;
// Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
$newsignupuser = \cache::make('core', 'presignup')->get('tool_policy_issignup');
if (!$this->isexistinguser && !$newsignupuser) {
$this->redirect_to_previous_url();
}
// Check for correct user capabilities.
if ($this->isexistinguser) {
// For existing users, it's needed to check if they have the capability for accepting policies.
api::can_accept_policies($this->listdocs, $this->behalfid, true);
} else {
// For new users, the behalfid parameter is ignored.
if ($this->behalfid) {
redirect(new moodle_url('/admin/tool/policy/index.php'));
}
}
// If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
// redirect to the return page.
$hasagreedsignupuser = !$this->isexistinguser && $this->signupuserpolicyagreed;
$hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
$this->redirect_to_previous_url();
}
$myparams = [];
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
$myparams['userid'] = $this->behalfid;
}
$myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
// Redirect to policy docs before the consent page.
$this->redirect_to_policies($userid, $myurl);
// Page setup.
$PAGE->set_context(context_system::instance());
$PAGE->set_url($myurl);
$PAGE->set_heading($SITE->fullname);
$PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
$PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
}
/**
* Prepare user acceptances.
*
* @param int $userid
*/
protected function prepare_user_acceptances($userid) {
global $USER;
// Get all the policy version acceptances for this user.
$lang = current_language();
foreach ($this->policies as $policy) {
// Get a link to display the full policy document.
$policy->url = new moodle_url('/admin/tool/policy/view.php',
array('policyid' => $policy->policyid, 'returnurl' => qualified_me()));
$policyattributes = array('data-action' => 'view',
'data-versionid' => $policy->id,
'data-behalfid' => $this->behalfid);
$policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
// Check if this policy version has been agreed or not.
if ($this->isexistinguser) {
// Existing user.
$versionagreed = false;
$versiondeclined = false;
$acceptances = api::get_user_acceptances($userid);
$policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
if (!empty($policy->versionacceptance)) {
// The policy version has ever been replied to before. Check if status = 1 to know if still is accepted.
if ($policy->versionacceptance->status) {
$versionagreed = true;
} else {
$versiondeclined = true;
}
if ($versionagreed) {
if ($policy->versionacceptance->lang != $lang) {
// Add a message because this version has been accepted in a different language than the current one.
$policy->versionlangsagreed = get_string('policyversionacceptedinotherlang', 'tool_policy');
}
$usermodified = $policy->versionacceptance->usermodified;
if ($usermodified && $usermodified != $userid && $USER->id == $userid) {
// Add a message because this version has been accepted on behalf of current user.
$policy->versionbehalfsagreed = get_string('policyversionacceptedinbehalf', 'tool_policy');
}
}
}
} else {
// New user.
$versionagreed = in_array($policy->id, $this->agreedocs);
$versiondeclined = false;
}
$policy->versionagreed = $versionagreed;
$policy->versiondeclined = $versiondeclined;
$policy->policylink = html_writer::link($policy->url, $policy->name);
$policy->policymodal = $policymodal;
}
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return \stdClass
*/
public function export_for_template(renderer_base $output) {
global $USER;
$myparams = [];
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
$myparams['userid'] = $this->behalfid;
}
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
'myurl' => (new moodle_url('/admin/tool/policy/index.php', $myparams))->out(false),
'sesskey' => sesskey(),
];
if (!empty($this->messages)) {
foreach ($this->messages as $message) {
switch ($message->type) {
case 'error':
$data->messages[] = $output->notification($message->text, notification::NOTIFY_ERROR);
break;
case 'success':
$data->messages[] = $output->notification($message->text, notification::NOTIFY_SUCCESS);
break;
default:
$data->messages[] = $output->notification($message->text, notification::NOTIFY_INFO);
break;
}
}
}
// Filter out policies already shown on their own page, keep just policies to be shown here on the consent page.
$data->policies = array_values(array_filter($this->policies, function ($policy) {
return $policy->agreementstyle == policy_version::AGREEMENTSTYLE_CONSENTPAGE;
}));
// If viewing docs in behalf of other user, get his/her full name and profile link.
if (!empty($this->behalfuser)) {
$userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
$data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
}
// User can cancel accepting policies only if it is a part of signup.
$data->cancancel = !isloggedin() || isguestuser();
return $data;
}
}
@@ -0,0 +1,264 @@
<?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/>.
/**
* Provides {@link tool_policy\output\page_managedocs_list} class.
*
* @package tool_policy
* @category output
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use html_writer;
use tool_policy\api;
defined('MOODLE_INTERNAL') || die();
use action_menu;
use action_menu_link;
use moodle_url;
use pix_icon;
use renderable;
use renderer_base;
use single_button;
use templatable;
use tool_policy\policy_version;
/**
* Represents a management page with the list of policy documents.
*
* The page displays all policy documents in their sort order, together with draft future versions.
*
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_managedocs_list implements renderable, templatable {
/** @var int */
protected $policyid = null;
/** @var moodle_url */
protected $returnurl = null;
/**
* page_managedocs_list constructor.
* @param int $policyid when specified only archived versions of this policy will be displayed.
*/
public function __construct($policyid = null) {
$this->policyid = $policyid;
$this->returnurl = new moodle_url('/admin/tool/policy/managedocs.php');
if ($this->policyid) {
$this->returnurl->param('archived', $this->policyid);
}
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = (object) [];
$data->pluginbaseurl = (new moodle_url('/admin/tool/policy'))->out(false);
$data->canmanage = has_capability('tool/policy:managedocs', \context_system::instance());
$data->canaddnew = $data->canmanage && !$this->policyid;
$data->canviewacceptances = has_capability('tool/policy:viewacceptances', \context_system::instance());
$data->title = get_string('policiesagreements', 'tool_policy');
$data->policies = [];
if ($this->policyid) {
// We are only interested in the archived versions of the given policy.
$data->backurl = (new moodle_url('/admin/tool/policy/managedocs.php'))->out(false);
$policy = api::list_policies([$this->policyid], true)[0];
if ($firstversion = $policy->currentversion ?: (reset($policy->draftversions) ?: reset($policy->archivedversions))) {
$data->title = get_string('previousversions', 'tool_policy', format_string($firstversion->name));
}
foreach ($policy->archivedversions as $i => $version) {
$data->versions[] = $this->export_version_for_template($output, $policy, $version,
false, false, false);
}
return $data;
}
// List all policies. Display current and all draft versions of each policy in this list.
// If none found, then show only one archived version.
$policies = api::list_policies(null, true);
foreach ($policies as $i => $policy) {
if (empty($policy->currentversion) && empty($policy->draftversions)) {
// There is no current and no draft versions, display the first archived version.
$firstpolicy = array_shift($policy->archivedversions);
$data->versions[] = $this->export_version_for_template($output, $policy, $firstpolicy,
false, $i > 0, $i < count($policies) - 1);
}
if (!empty($policy->currentversion)) {
// Current version of the policy.
$data->versions[] = $this->export_version_for_template($output, $policy, $policy->currentversion,
false, $i > 0, $i < count($policies) - 1);
} else if ($policy->draftversions) {
// There is no current version, display the first draft version as the current.
$firstpolicy = array_shift($policy->draftversions);
$data->versions[] = $this->export_version_for_template($output, $policy, $firstpolicy,
false, $i > 0, $i < count($policies) - 1);
}
foreach ($policy->draftversions as $draft) {
// Show all [other] draft policies indented.
$data->versions[] = $this->export_version_for_template($output, $policy, $draft,
true, false, false);
}
}
return $data;
}
/**
* Exports one version for the list of policies
*
* @param \renderer_base $output
* @param \stdClass $policy
* @param \stdClass $version
* @param bool $isindented display indented (normally drafts of the current version)
* @param bool $moveup can move up
* @param bool $movedown can move down
* @return \stdClass
*/
protected function export_version_for_template($output, $policy, $version, $isindented, $moveup, $movedown) {
$status = $version->status;
$version->statustext = get_string('status' . $status, 'tool_policy');
if ($status == policy_version::STATUS_ACTIVE) {
$version->statustext = html_writer::span($version->statustext, 'badge bg-success text-white');
} else if ($status == policy_version::STATUS_DRAFT) {
$version->statustext = html_writer::span($version->statustext, 'badge bg-warning text-dark');
} else {
$version->statustext = html_writer::span($version->statustext, 'label');
}
if ($version->optional == policy_version::AGREEMENT_OPTIONAL) {
$version->optionaltext = get_string('policydocoptionalyes', 'tool_policy');
} else {
$version->optionaltext = get_string('policydocoptionalno', 'tool_policy');
}
$version->indented = $isindented;
$editbaseurl = new moodle_url('/admin/tool/policy/editpolicydoc.php', [
'sesskey' => sesskey(),
'policyid' => $policy->id,
'returnurl' => $this->returnurl->out_as_local_url(false),
]);
$viewurl = new moodle_url('/admin/tool/policy/view.php', [
'policyid' => $policy->id,
'versionid' => $version->id,
'manage' => 1,
'returnurl' => $this->returnurl->out_as_local_url(false),
]);
$actionmenu = new action_menu();
$actionmenu->set_menu_trigger(get_string('actions', 'tool_policy'));
$actionmenu->prioritise = true;
if ($moveup) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['moveup' => $policy->id]),
new pix_icon('t/up', get_string('moveup', 'tool_policy')),
get_string('moveup', 'tool_policy'),
true
));
}
if ($movedown) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['movedown' => $policy->id]),
new pix_icon('t/down', get_string('movedown', 'tool_policy')),
get_string('movedown', 'tool_policy'),
true
));
}
$actionmenu->add(new action_menu_link(
$viewurl,
null,
get_string('view'),
false
));
if ($status != policy_version::STATUS_ARCHIVED) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['versionid' => $version->id]),
null,
get_string('edit'),
false
));
}
if ($status == policy_version::STATUS_ACTIVE) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['inactivate' => $policy->id]),
null,
get_string('inactivate', 'tool_policy'),
false,
['data-action' => 'inactivate']
));
}
if ($status == policy_version::STATUS_DRAFT) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['makecurrent' => $version->id]),
null,
get_string('activate', 'tool_policy'),
false,
['data-action' => 'makecurrent']
));
}
if (api::can_delete_version($version)) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['delete' => $version->id]),
null,
get_string('delete'),
false,
['data-action' => 'delete']
));
}
if ($status == policy_version::STATUS_ARCHIVED) {
$actionmenu->add(new action_menu_link(
new moodle_url($editbaseurl, ['versionid' => $version->id]),
null,
get_string('settodraft', 'tool_policy'),
false
));
}
if (!$this->policyid && !$isindented && $policy->archivedversions &&
($status != policy_version::STATUS_ARCHIVED || count($policy->archivedversions) > 1)) {
$actionmenu->add(new action_menu_link(
new moodle_url('/admin/tool/policy/managedocs.php', ['archived' => $policy->id]),
null,
get_string('viewarchived', 'tool_policy'),
false
));
}
$version->actionmenu = $actionmenu->export_for_template($output);
return $version;
}
}
@@ -0,0 +1,172 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use core\session\manager;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
use context_system;
use core_user;
use html_writer;
use moodle_url;
use renderable;
use renderer_base;
use templatable;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Represents a page for showing the error messages.
*
* This is used when a user has no permission to agree to policies or accept policies on behalf of defined behalfid.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_nopermission implements renderable, templatable {
/** @var int User id who wants to view this page. */
protected $behalfid = null;
/** @var object User who wants to accept this page. */
protected $behalfuser = null;
/** @var bool True if user has permission to accept policy documents; false otherwise. */
protected $haspermissionagreedocs = true;
/** @var array $policies List of public policies objects. */
protected $policies = null;
/**
* Prepare the page for rendering.
*
* @param array $versionids int[] List of policy version ids that were checked.
* @param int $behalfid The userid to consent policies as (such as child's id).
*/
public function __construct(array $versionids, $behalfid) {
global $USER;
$behalfid = $behalfid ?: $USER->id;
$realuser = manager::get_realuser();
if ($realuser->id != $behalfid) {
$this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
$this->behalfid = $this->behalfuser->id;
}
if (!empty($USER->id)) {
// For existing users, it's needed to check if they have the capability for accepting policies.
$this->haspermissionagreedocs = api::can_accept_policies($versionids, $this->behalfid);
}
$this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
if (empty($this->policies) && !empty($USER->id)) {
// Existing user without policies to agree to.
$currentuser = (!empty($this->behalfuser)) ? $this->behalfuser : $USER;
if (!$currentuser->policyagreed) {
// If there are no policies to agreed, change $user->policyagreed to true.
api::update_policyagreed($currentuser);
}
}
$this->prepare_global_page_access();
}
/**
* Sets up the global $PAGE and performs the access checks.
*/
protected function prepare_global_page_access() {
global $PAGE, $SITE, $USER;
$myurl = new moodle_url('/admin/tool/policy/index.php', [
'behalfid' => $this->behalfid,
]);
if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
// Disable notifications for new users, guests or users who haven't agreed to the policies.
$PAGE->set_popup_notification_allowed(false);
}
$PAGE->set_context(context_system::instance());
$PAGE->set_pagelayout('standard');
$PAGE->set_url($myurl);
$PAGE->set_heading($SITE->fullname);
$PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
$PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return \stdClass
*/
public function export_for_template(renderer_base $output) {
global $OUTPUT;
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
'haspermissionagreedocs' => $this->haspermissionagreedocs,
'supportemail' => $OUTPUT->supportemail(['class' => 'font-weight-bold'])
];
// Get the messages to display.
$messagetitle = null;
$messagedesc = null;
if (!$this->haspermissionagreedocs) {
if (!empty($this->behalfuser)) {
// If viewing docs in behalf of other user, get his/her full name and profile link.
$userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance())
|| has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
$data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
$messagetitle = get_string('nopermissiontoagreedocsbehalf', 'tool_policy');
$messagedesc = get_string('nopermissiontoagreedocsbehalf_desc', 'tool_policy', $data->behalfuser);
} else {
$messagetitle = get_string('nopermissiontoagreedocs', 'tool_policy');
$messagedesc = get_string('nopermissiontoagreedocs_desc', 'tool_policy');
}
}
$data->messagetitle = $messagetitle;
$data->messagedesc = $messagedesc;
// Add policies list.
$policieslinks = array();
foreach ($this->policies as $policyversion) {
// Get a link to display the full policy document.
$policyurl = new moodle_url('/admin/tool/policy/view.php',
array('policyid' => $policyversion->policyid, 'returnurl' => qualified_me()));
$policyattributes = array('data-action' => 'view',
'data-versionid' => $policyversion->id,
'data-behalfid' => $this->behalfid);
$policieslinks[] = html_writer::link($policyurl, $policyversion->name, $policyattributes);
}
$data->policies = $policieslinks;
return $data;
}
}
@@ -0,0 +1,122 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/filelib.php");
use context_system;
use moodle_url;
use renderable;
use renderer_base;
use single_button;
use templatable;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Represents a page for showing all the policy documents with a current version.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_viewalldoc implements renderable, templatable {
/** @var ?moodle_url Return url */
private $returnurl = null;
/** @var array List current (active) policy versions. */
private array $policies = [];
/**
* Prepare the page for rendering.
*
*/
public function __construct($returnurl) {
if (!empty($returnurl)) {
$this->returnurl = new moodle_url($returnurl);
}
$this->prepare_global_page_access();
$this->prepare_policies();
}
/**
* Loads the policy versions to display on the page.
*
*/
protected function prepare_policies() {
$this->policies = api::list_current_versions();
}
/**
* Sets up the global $PAGE and performs the access checks.
*/
protected function prepare_global_page_access() {
global $PAGE, $SITE, $USER;
$myurl = new moodle_url('/admin/tool/policy/viewall.php', []);
// Disable notifications for new users, guests or users who haven't agreed to the policies.
if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
$PAGE->set_popup_notification_allowed(false);
}
$PAGE->set_context(context_system::instance());
$PAGE->set_pagelayout('popup');
$PAGE->set_url($myurl);
$PAGE->set_heading($SITE->fullname);
$PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
];
$data->policies = array_values($this->policies);
if (!empty($this->returnurl)) {
$data->returnurl = $this->returnurl;
}
array_walk($data->policies, function($item, $key) {
$item->policytypestr = get_string('policydoctype'.$item->type, 'tool_policy');
$item->policyaudiencestr = get_string('policydocaudience'.$item->audience, 'tool_policy');
});
return $data;
}
}
@@ -0,0 +1,200 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
use context_system;
use moodle_url;
use renderable;
use renderer_base;
use single_button;
use templatable;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Represents a page for showing the given policy document version.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_viewdoc implements renderable, templatable {
/** @var stdClass Exported {@link \tool_policy\policy_version_exporter} to display on this page. */
protected $policy;
/** @var string Return URL. */
protected $returnurl = null;
/** @var int User id who wants to view this page. */
protected $behalfid = null;
/** @var bool View the policy as a part of the management UI. */
protected $manage;
/** @var int Position of the current policy with respect to the total of policy docs to display. */
protected $numpolicy = 0;
/** @var int Total number of policy documents which the user has to agree to. */
protected $totalpolicies = 0;
/**
* Prepare the page for rendering.
*
* @param int $policyid The policy id for this page.
* @param int $versionid The version id to show. Empty tries to load the current one.
* @param string $returnurl URL of a page to continue after reading the policy text.
* @param int $behalfid The userid to view this policy version as (such as child's id).
* @param bool $manage View the policy as a part of the management UI.
* @param int $numpolicy Position of the current policy with respect to the total of policy docs to display.
* @param int $totalpolicies Total number of policy documents which the user has to agree to.
*/
public function __construct($policyid, $versionid, $returnurl, $behalfid, $manage, $numpolicy = 0, $totalpolicies = 0) {
$this->returnurl = $returnurl;
$this->behalfid = $behalfid;
$this->manage = $manage;
$this->numpolicy = $numpolicy;
$this->totalpolicies = $totalpolicies;
$this->prepare_policy($policyid, $versionid);
$this->prepare_global_page_access();
}
/**
* Loads the policy version to display on the page.
*
* @param int $policyid The policy id for this page.
* @param int $versionid The version id to show. Empty tries to load the current one.
*/
protected function prepare_policy($policyid, $versionid) {
if ($versionid) {
$this->policy = api::get_policy_version($versionid);
} else {
$this->policy = array_reduce(api::list_current_versions(), function ($carry, $current) use ($policyid) {
if ($current->policyid == $policyid) {
return $current;
}
return $carry;
});
}
if (empty($this->policy)) {
throw new \moodle_exception('errorpolicyversionnotfound', 'tool_policy');
}
}
/**
* Sets up the global $PAGE and performs the access checks.
*/
protected function prepare_global_page_access() {
global $CFG, $PAGE, $SITE, $USER;
$myurl = new moodle_url('/admin/tool/policy/view.php', [
'policyid' => $this->policy->policyid,
'versionid' => $this->policy->id,
'returnurl' => $this->returnurl,
'behalfid' => $this->behalfid,
'manage' => $this->manage,
'numpolicy' => $this->numpolicy,
'totalpolicies' => $this->totalpolicies,
]);
if ($this->manage) {
require_once($CFG->libdir.'/adminlib.php');
admin_externalpage_setup('tool_policy_managedocs', '', null, $myurl);
require_capability('tool/policy:managedocs', context_system::instance());
$PAGE->navbar->add(format_string($this->policy->name),
new moodle_url('/admin/tool/policy/managedocs.php', ['id' => $this->policy->policyid]));
} else {
if ($this->policy->status != policy_version::STATUS_ACTIVE) {
require_login();
} else if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
// Disable notifications for new users, guests or users who haven't agreed to the policies.
$PAGE->set_popup_notification_allowed(false);
}
$PAGE->set_url($myurl);
$PAGE->set_heading($SITE->fullname);
$PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
$PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
$PAGE->navbar->add(format_string($this->policy->name));
}
if (!api::can_user_view_policy_version($this->policy, $this->behalfid)) {
throw new moodle_exception('accessdenied', 'tool_policy');
}
}
/**
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
global $USER;
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
'returnurl' => $this->returnurl ? (new moodle_url($this->returnurl))->out(false) : null,
'numpolicy' => $this->numpolicy ? : null,
'totalpolicies' => $this->totalpolicies ? : null,
];
if ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) {
$paramsurl = ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id];
$data->editurl = (new moodle_url('/admin/tool/policy/editpolicydoc.php', $paramsurl))->out(false);
}
if ($this->policy->agreementstyle == policy_version::AGREEMENTSTYLE_OWNPAGE) {
if (!api::is_user_version_accepted($USER->id, $this->policy->id)) {
unset($data->returnurl);
$data->accepturl = (new moodle_url('/admin/tool/policy/index.php', [
'listdoc[]' => $this->policy->id,
'status'.$this->policy->id => 1,
'submit' => 'accept',
'sesskey' => sesskey(),
]))->out(false);
if ($this->policy->optional == policy_version::AGREEMENT_OPTIONAL) {
$data->declineurl = (new moodle_url('/admin/tool/policy/index.php', [
'listdoc[]' => $this->policy->id,
'status'.$this->policy->id => 0,
'submit' => 'decline',
'sesskey' => sesskey(),
]))->out(false);
}
}
}
$data->policy = clone($this->policy);
return $data;
}
}
@@ -0,0 +1,71 @@
<?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/>.
/**
* Provides {@link tool_policy\output\renderer} class.
*
* @package tool_policy
* @category output
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
defined('MOODLE_INTERNAL') || die();
use core\output\mustache_template_finder;
use plugin_renderer_base;
use renderable;
use Exception;
/**
* Renderer for the policies plugin.
*
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Overrides the parent so that templatable widgets are handled even without their explicit render method.
*
* @param renderable $widget
* @return string
*/
public function render(renderable $widget) {
$namespacedclassname = get_class($widget);
$plainclassname = preg_replace('/^.*\\\/', '', $namespacedclassname);
$rendermethod = 'render_'.$plainclassname;
if (method_exists($this, $rendermethod)) {
// Explicit rendering method exists, fall back to the default behaviour.
return parent::render($widget);
}
$interfaces = class_implements($namespacedclassname);
if (isset($interfaces['templatable'])) {
// Default implementation of template-based rendering.
$data = $widget->export_for_template($this);
return parent::render_from_template('tool_policy/'.$plainclassname, $data);
} else {
return parent::render($widget);
}
}
}
@@ -0,0 +1,345 @@
<?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/>.
/**
* Provides {@link tool_policy\output\user_agreement} class.
*
* @package tool_policy
* @category output
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\output;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use renderable;
use renderer_base;
use single_button;
use templatable;
/**
* List of users and their acceptances
*
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_agreement implements \templatable, \renderable {
/** @var int */
protected $userid;
/** @var bool */
protected $onbehalf;
/** @var moodle_url */
protected $pageurl;
/** @var array */
protected $versions;
/** @var array */
protected $accepted;
/** @var array */
protected $declined;
/** @var bool */
protected $canaccept;
/** @var bool */
protected $canrevoke;
/**
* user_agreement constructor
*
* @param int $userid
* @param array $accepted list of ids of accepted versions
* @param array $declined list of ids of declined versions
* @param moodle_url $pageurl
* @param array $versions list of versions (id=>name)
* @param bool $onbehalf whether at least one version was accepted by somebody else on behalf of the user
* @param bool $canaccept does the current user have permission to accept/decline the policy on behalf of user $userid
* @param bool $canrevoke does the current user have permission to revoke the policy on behalf of user $userid
*/
public function __construct($userid, array $accepted, array $declined, moodle_url $pageurl, $versions, $onbehalf = false,
$canaccept = null, $canrevoke = null) {
// Make sure that all ids in $accepted and $declined are present in $versions.
if (array_diff(array_merge($accepted, $declined), array_keys($versions))) {
throw new \coding_exception('Policy version ids mismatch');
}
$this->userid = $userid;
$this->onbehalf = $onbehalf;
$this->pageurl = $pageurl;
$this->versions = $versions;
$this->accepted = $accepted;
$this->declined = $declined;
$this->canaccept = $canaccept;
if (count($this->accepted) < count($this->versions) && $canaccept === null) {
$this->canaccept = \tool_policy\api::can_accept_policies(array_keys($this->versions), $this->userid);
}
if (count($this->accepted) > 0 && $canrevoke === null) {
$this->canrevoke = \tool_policy\api::can_revoke_policies(array_keys($this->versions), $this->userid);
}
}
/**
* Export data to be rendered.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(\renderer_base $output) {
$data = (object)[
'statusicon' => '',
'statustext' => '',
'statuslink' => '',
'actions' => [],
];
if (count($this->versions) == 1) {
// We represent one particular policy's agreement status.
$versionname = reset($this->versions);
$versionid = key($this->versions);
$actionaccept = (object)[
'text' => get_string('useracceptanceactionaccept', 'tool_policy'),
'title' => get_string('useracceptanceactionacceptone', 'tool_policy', $versionname),
'data' => 'acceptmodal',
'url' => (new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'versionids[]' => $versionid,
'action' => 'accept',
'returnurl' => $this->pageurl->out_as_local_url(false),
]))->out(false),
];
$actionrevoke = (object)[
'text' => get_string('useracceptanceactionrevoke', 'tool_policy'),
'title' => get_string('useracceptanceactionrevokeone', 'tool_policy', $versionname),
'data' => 'acceptmodal',
'url' => (new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'versionids[]' => $versionid,
'action' => 'revoke',
'returnurl' => $this->pageurl->out_as_local_url(false),
]))->out(false),
];
$actiondecline = (object)[
'text' => get_string('useracceptanceactiondecline', 'tool_policy'),
'title' => get_string('useracceptanceactiondeclineone', 'tool_policy', $versionname),
'data' => 'acceptmodal',
'url' => (new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'versionids[]' => $versionid,
'action' => 'decline',
'returnurl' => $this->pageurl->out_as_local_url(false),
]))->out(false),
];
if ($this->accepted) {
$data->statusicon = 'agreed';
if ($this->onbehalf) {
$data->statustext = get_string('acceptancestatusacceptedbehalf', 'tool_policy');
} else {
$data->statustext = get_string('acceptancestatusaccepted', 'tool_policy');
}
if ($this->canrevoke) {
$data->actions[] = $actionrevoke;
}
} else if ($this->declined) {
$data->statusicon = 'declined';
if ($this->onbehalf) {
$data->statustext = get_string('acceptancestatusdeclinedbehalf', 'tool_policy');
} else {
$data->statustext = get_string('acceptancestatusdeclined', 'tool_policy');
}
if ($this->canaccept) {
$data->actions[] = $actionaccept;
}
} else {
$data->statusicon = 'pending';
$data->statustext = get_string('acceptancestatuspending', 'tool_policy');
if ($this->canaccept) {
$data->actions[] = $actionaccept;
$data->actions[] = $actiondecline;
}
}
} else if (count($this->versions) > 1) {
// We represent the summary status for multiple policies.
$data->actions[] = (object)[
'text' => get_string('useracceptanceactiondetails', 'tool_policy'),
'url' => (new \moodle_url('/admin/tool/policy/user.php', [
'userid' => $this->userid,
'returnurl' => $this->pageurl->out_as_local_url(false),
]))->out(false),
];
// Prepare the action link to accept all pending policies.
$accepturl = new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'action' => 'accept',
'returnurl' => $this->pageurl->out_as_local_url(false),
]);
foreach (array_diff(array_keys($this->versions), $this->accepted, $this->declined) as $ix => $versionid) {
$accepturl->param('versionids['.$ix.']', $versionid);
}
$actionaccept = (object)[
'text' => get_string('useracceptanceactionaccept', 'tool_policy'),
'title' => get_string('useracceptanceactionacceptpending', 'tool_policy'),
'data' => 'acceptmodal',
'url' => $accepturl->out(false),
];
// Prepare the action link to revoke all agreed policies.
$revokeurl = new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'action' => 'revoke',
'returnurl' => $this->pageurl->out_as_local_url(false),
]);
foreach ($this->accepted as $ix => $versionid) {
$revokeurl->param('versionids['.$ix.']', $versionid);
}
$actionrevoke = (object)[
'text' => get_string('useracceptanceactionrevoke', 'tool_policy'),
'title' => get_string('useracceptanceactionrevokeall', 'tool_policy'),
'data' => 'acceptmodal',
'url' => $revokeurl->out(false),
];
// Prepare the action link to decline all pending policies.
$declineurl = new \moodle_url('/admin/tool/policy/accept.php', [
'userids[]' => $this->userid,
'action' => 'decline',
'returnurl' => $this->pageurl->out_as_local_url(false),
]);
foreach (array_diff(array_keys($this->versions), $this->accepted, $this->declined) as $ix => $versionid) {
$declineurl->param('versionids['.$ix.']', $versionid);
}
$actiondecline = (object)[
'text' => get_string('useracceptanceactiondecline', 'tool_policy'),
'title' => get_string('useracceptanceactiondeclinepending', 'tool_policy'),
'data' => 'acceptmodal',
'url' => $declineurl->out(false),
];
$countversions = count($this->versions);
$countaccepted = count($this->accepted);
$countdeclined = count($this->declined);
if ($countaccepted == $countversions) {
// All policies accepted.
$data->statusicon = 'agreed';
$data->statustext = get_string('acceptancestatusaccepted', 'tool_policy');
if ($this->canrevoke) {
$data->actions[] = $actionrevoke;
}
} else if ($countdeclined == $countversions) {
// All policies declined.
$data->statusicon = 'declined';
$data->statustext = get_string('acceptancestatusdeclined', 'tool_policy');
} else if ($countaccepted + $countdeclined == $countversions) {
// All policies responded, only some of them accepted.
$data->statusicon = 'partial';
$data->statustext = get_string('acceptancestatuspartial', 'tool_policy');
if ($this->accepted && $this->canrevoke) {
$data->actions[] = $actionrevoke;
}
} else {
// Some policies are pending.
$data->statusicon = 'pending';
$data->statustext = get_string('acceptancestatuspending', 'tool_policy');
if ($this->canaccept) {
$data->actions[] = $actionaccept;
$data->actions[] = $actiondecline;
}
}
}
return $data;
}
/**
* Describe the status with a plain text for downloading purposes.
*
* @return string
*/
public function export_for_download() {
if (count($this->versions) == 1) {
if ($this->accepted) {
if ($this->onbehalf) {
return get_string('acceptancestatusacceptedbehalf', 'tool_policy');
} else {
return get_string('acceptancestatusaccepted', 'tool_policy');
}
} else if ($this->declined) {
if ($this->onbehalf) {
return get_string('acceptancestatusdeclinedbehalf', 'tool_policy');
} else {
return get_string('acceptancestatusdeclined', 'tool_policy');
}
} else {
return get_string('acceptancestatuspending', 'tool_policy');
}
} else if (count($this->versions) > 1) {
if (count($this->accepted) == count($this->versions)) {
return get_string('acceptancestatusaccepted', 'tool_policy');
} else if (count($this->declined) == count($this->versions)) {
return get_string('acceptancestatusdeclined', 'tool_policy');
} else if (count($this->accepted) > 0 || count($this->declined) > 0) {
return get_string('acceptancestatuspartial', 'tool_policy');
} else {
return get_string('acceptancestatuspending', 'tool_policy');
}
}
}
}
@@ -0,0 +1,124 @@
<?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/>.
/**
* Provides the {@link tool_policy\policy_exporter} class.
*
* @package tool_policy
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
/**
* Exporter of a policy document model.
*
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class policy_exporter extends exporter {
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'id' => [
'type' => PARAM_INT,
],
'sortorder' => [
'type' => PARAM_INT,
'default' => 999,
],
'currentversionid' => [
'type' => PARAM_INT,
'null' => NULL_ALLOWED,
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'versions' => 'tool_policy\policy_version_exporter[]',
];
}
/**
* Return the list of additional, generated dynamically from the given properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'currentversion' => [
'type' => policy_version_exporter::read_properties_definition(),
'null' => NULL_ALLOWED,
],
'draftversions' => [
'type' => policy_version_exporter::read_properties_definition(),
'multiple' => true,
],
'archivedversions' => [
'type' => policy_version_exporter::read_properties_definition(),
'multiple' => true,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$othervalues = [
'currentversion' => null,
'draftversions' => [],
'archivedversions' => [],
];
foreach ($this->related['versions'] as $exporter) {
$data = $exporter->export($output);
if ($data->id == $this->data->currentversionid) {
$othervalues['currentversion'] = $data;
} else if ($data->archived) {
$othervalues['archivedversions'][] = $data;
} else {
$othervalues['draftversions'][] = $data;
}
}
return $othervalues;
}
}
@@ -0,0 +1,193 @@
<?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/>.
/**
* Provides the {@link tool_policy\policy_version} persistent.
*
* @package tool_policy
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy;
defined('MOODLE_INTERNAL') || die();
use core\persistent;
/**
* Persistent model representing a single policy document version.
*
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class policy_version extends persistent {
/** @var string Table name this persistent is mapped to. */
const TABLE = 'tool_policy_versions';
/** @var int Site policy document. */
const TYPE_SITE = 0;
/** @var int Privacy policy document. */
const TYPE_PRIVACY = 1;
/** @var int Third party policy document. */
const TYPE_THIRD_PARTY = 2;
/** @var int Other policy document. */
const TYPE_OTHER = 99;
/** @var int Policy applies to all users. */
const AUDIENCE_ALL = 0;
/** @var int Policy applies to logged in users only. */
const AUDIENCE_LOGGEDIN = 1;
/** @var int Policy applies to guests only. */
const AUDIENCE_GUESTS = 2;
/** @var int Policy version is a draft. */
const STATUS_DRAFT = 0;
/** @var int Policy version is the active one. */
const STATUS_ACTIVE = 1;
/** @var int Policy version has been archived. */
const STATUS_ARCHIVED = 2;
/** @var int Policy to be accepted together with others on the consent page. */
const AGREEMENTSTYLE_CONSENTPAGE = 0;
/** @var int Policy to be accepted on its own page before reaching the consent page. */
const AGREEMENTSTYLE_OWNPAGE = 1;
/** @var int Users must agree to the policy in order to use the site. */
const AGREEMENT_COMPULSORY = 0;
/** @var int Users may or may not agree to the policy. */
const AGREEMENT_OPTIONAL = 1;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return [
'name' => [
'type' => PARAM_TEXT,
'default' => '',
],
'type' => [
'type' => PARAM_INT,
'choices' => [
self::TYPE_SITE,
self::TYPE_PRIVACY,
self::TYPE_THIRD_PARTY,
self::TYPE_OTHER,
],
'default' => self::TYPE_SITE,
],
'audience' => [
'type' => PARAM_INT,
'choices' => [
self::AUDIENCE_ALL,
self::AUDIENCE_LOGGEDIN,
self::AUDIENCE_GUESTS,
],
'default' => self::AUDIENCE_ALL,
],
'archived' => [
'type' => PARAM_BOOL,
'default' => false,
],
'policyid' => [
'type' => PARAM_INT,
],
'agreementstyle' => [
'type' => PARAM_INT,
'choices' => [
self::AGREEMENTSTYLE_CONSENTPAGE,
self::AGREEMENTSTYLE_OWNPAGE,
],
'default' => self::AGREEMENTSTYLE_CONSENTPAGE,
],
'optional' => [
'type' => PARAM_INT,
'choices' => [
self::AGREEMENT_OPTIONAL,
self::AGREEMENT_COMPULSORY,
],
'default' => self::AGREEMENT_COMPULSORY,
],
'revision' => [
'type' => PARAM_TEXT,
'default' => '',
],
'summary' => [
'type' => PARAM_RAW,
'default' => '',
],
'summaryformat' => [
'type' => PARAM_INT,
'default' => FORMAT_HTML,
'choices' => [
FORMAT_PLAIN,
FORMAT_HTML,
FORMAT_MOODLE,
FORMAT_MARKDOWN,
],
],
'content' => [
'type' => PARAM_RAW,
'default' => '',
],
'contentformat' => [
'type' => PARAM_INT,
'default' => FORMAT_HTML,
'choices' => [
FORMAT_PLAIN,
FORMAT_HTML,
FORMAT_MOODLE,
FORMAT_MARKDOWN,
],
],
];
}
/**
* Hook to execute after an update.
*
* @param bool $result Whether or not the update was successful (but it always is)
*/
protected function after_update($result) {
$optcache = \cache::make('tool_policy', 'policy_optional');
$optcache->delete($this->raw_get('id'));
}
/**
* Hook to execute after an update.
*
* @param bool $result Whether or not the update was successful (but it always is)
*/
protected function after_delete($result) {
$optcache = \cache::make('tool_policy', 'policy_optional');
$optcache->delete($this->raw_get('id'));
}
}
@@ -0,0 +1,163 @@
<?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/>.
/**
* Provides the {@link tool_policy\policy_version_exporter} class.
*
* @package tool_policy
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use tool_policy\api;
/**
* Exporter of a single policy document version.
*
* Note we cannot use the persistent_exporter as our super class because we want to add some properties not present in
* the persistent (e.g. acceptancescount).
*
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class policy_version_exporter extends exporter {
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return policy_version::properties_definition() + [
'acceptancescount' => [
'type' => PARAM_INT,
'default' => 0,
],
'status' => [
'type' => PARAM_INT,
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'context' => 'context',
];
}
/**
* Return the list of additional (calculated and readonly) properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
// Human readable type of the policy document version.
'typetext' => [
'type' => PARAM_TEXT,
],
// Human readable audience of the policy document audience.
'audiencetext' => [
'type' => PARAM_TEXT,
],
// Detailed information about the number of policy acceptances.
'acceptancescounttext' => [
'type' => PARAM_TEXT,
],
// Link to view acceptances.
'acceptancescounturl' => [
'type' => PARAM_LOCALURL,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$othervalues = [
'typetext' => get_string('policydoctype'.$this->data->type, 'tool_policy'),
'audiencetext' => get_string('policydocaudience'.$this->data->audience, 'tool_policy'),
];
if (!isset($this->data->acceptancescount) || $this->data->status == policy_version::STATUS_DRAFT) {
// Return "N/A" for acceptances count.
$othervalues['acceptancescounttext'] = get_string('useracceptancecountna', 'tool_policy');
$othervalues['acceptancescounturl'] = null;
return $othervalues;
}
$acceptancescount = empty($this->data->acceptancescount) ? 0 : $this->data->acceptancescount;
$acceptancesexpected = api::count_total_users();
$a = [
'agreedcount' => $acceptancescount,
'userscount' => $acceptancesexpected,
'percent' => min(100, round($acceptancescount * 100 / max($acceptancesexpected, 1))),
];
$othervalues['acceptancescounttext'] = get_string('useracceptancecount', 'tool_policy', $a);
$acceptancesurl = new \moodle_url('/admin/tool/policy/acceptances.php', ['policyid' => $this->data->policyid]);
if ($this->data->status != policy_version::STATUS_ACTIVE) {
$acceptancesurl->param('versionid', $this->data->id);
}
$othervalues['acceptancescounturl'] = $acceptancesurl->out(false);
return $othervalues;
}
/**
* Get the formatting parameters for the summary field.
*
* @return array
*/
protected function get_format_parameters_for_summary() {
return [
'component' => 'tool_policy',
'filearea' => 'policydocumentsummary',
'itemid' => $this->data->id
];
}
/**
* Get the formatting parameters for the content field.
*
* @return array
*/
protected function get_format_parameters_for_content() {
return [
'component' => 'tool_policy',
'filearea' => 'policydocumentcontent',
'itemid' => $this->data->id
];
}
}
@@ -0,0 +1,129 @@
<?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 handler class.
*
* @package tool_policy
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\privacy\local\sitepolicy;
defined('MOODLE_INTERNAL') || die();
use tool_policy\api;
use tool_policy\policy_version;
/**
* Class implementation for a site policy handler.
*
* @package tool_policy
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class 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) {
// There is no redirect for guests, policies are shown in the popup, only return redirect url for the logged in users.
if (!$forguests && api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN)) {
return new \moodle_url('/admin/tool/policy/index.php');
}
return 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) {
if (api::get_current_versions_ids($forguests ? policy_version::AUDIENCE_GUESTS : policy_version::AUDIENCE_LOGGEDIN)) {
return new \moodle_url('/admin/tool/policy/viewall.php');
}
return null;
}
/**
* 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) {
return false;
}
if (isguestuser()) {
// For guests, agreement is stored in the session only.
$USER->policyagreed = 1;
return true;
}
// Find all compulsory policies and mark them as accepted.
$compulsory = [];
foreach (api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN) as $policyversion) {
if ($policyversion->optional == policy_version::AGREEMENT_COMPULSORY) {
$compulsory[] = $policyversion->id;
}
}
if ($compulsory) {
api::accept_policies($compulsory);
}
// Mark policies as agreed.
$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.
*
* @param \MoodleQuickForm $mform
*/
public static function signup_form($mform) {
if (static::is_defined()) {
// This plugin displays policies to the user who is signing up before the signup form is shown.
// By the time user has access to signup form they have already agreed to all compulsory policies.
$mform->addElement('hidden', 'policyagreed', 1);
$mform->setType('policyagreed', PARAM_INT);
}
}
}
@@ -0,0 +1,356 @@
<?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/>.
/**
* Privacy Subsystem implementation for tool_policy.
*
* @package tool_policy
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\privacy;
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\moodle_content_writer;
use core_privacy\local\request\userlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Implementation of the privacy subsystem plugin provider for the policy tool.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This tool stores user data.
\core_privacy\local\metadata\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider,
// This tool may provide access to and deletion of user data.
\core_privacy\local\request\plugin\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table(
'tool_policy_acceptances',
[
'policyversionid' => 'privacy:metadata:acceptances:policyversionid',
'userid' => 'privacy:metadata:acceptances:userid',
'status' => 'privacy:metadata:acceptances:status',
'lang' => 'privacy:metadata:acceptances:lang',
'usermodified' => 'privacy:metadata:acceptances:usermodified',
'timecreated' => 'privacy:metadata:acceptances:timecreated',
'timemodified' => 'privacy:metadata:acceptances:timemodified',
'note' => 'privacy:metadata:acceptances:note',
],
'privacy:metadata:acceptances'
);
$collection->add_database_table(
'tool_policy_versions',
[
'name' => 'privacy:metadata:versions:name',
'type' => 'privacy:metadata:versions:type',
'audience' => 'privacy:metadata:versions:audience',
'archived' => 'privacy:metadata:versions:archived',
'usermodified' => 'privacy:metadata:versions:usermodified',
'timecreated' => 'privacy:metadata:versions:timecreated',
'timemodified' => 'privacy:metadata:versions:timemodified',
'policyid' => 'privacy:metadata:versions:policyid',
'revision' => 'privacy:metadata:versions:revision',
'summary' => 'privacy:metadata:versions:summary',
'summaryformat' => 'privacy:metadata:versions:summaryformat',
'content' => 'privacy:metadata:versions:content',
'contentformat' => 'privacy:metadata:versions:contentformat',
],
'privacy:metadata:versions'
);
$collection->add_subsystem_link('core_files', [], 'privacy:metadata:subsystem:corefiles');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The userid.
* @return contextlist The list of contexts containing user info for the user.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$contextlist = new contextlist();
// Policies a user has modified.
$sql = "SELECT c.id
FROM {context} c
JOIN {tool_policy_versions} v ON v.usermodified = :userid
WHERE c.contextlevel = :contextlevel";
$params = [
'contextlevel' => CONTEXT_SYSTEM,
'userid' => $userid,
];
$contextlist->add_from_sql($sql, $params);
// Policies a user has accepted.
$sql = "SELECT c.id
FROM {context} c
JOIN {tool_policy_acceptances} a ON c.instanceid = a.userid
WHERE
c.contextlevel = :contextlevel
AND (
a.userid = :userid OR a.usermodified = :usermodified
)";
$params = [
'contextlevel' => CONTEXT_USER,
'userid' => $userid,
'usermodified' => $userid,
];
$contextlist->add_from_sql($sql, $params);
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();
// Users that have modified any policies, if fetching for system context.
if (is_a($context, \context_system::class)) {
$sql = "SELECT v.usermodified AS userid
FROM {tool_policy_versions} v";
$userlist->add_from_sql('userid', $sql, []);
}
// Users that have accepted any policies, if fetching for user context.
if (is_a($context, \context_user::class)) {
$sql = "SELECT a.userid, a.usermodified
FROM {tool_policy_acceptances} a
WHERE a.userid = :instanceid";
$params = ['instanceid' => $context->instanceid];
$userlist->add_from_sql('userid', $sql, $params);
$userlist->add_from_sql('usermodified', $sql, $params);
}
}
/**
* Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
*
* @param approved_contextlist $contextlist A list of contexts approved for export.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
// Export user agreements.
foreach ($contextlist->get_contexts() as $context) {
if ($context->contextlevel == CONTEXT_USER) {
static::export_policy_agreements_for_context($context);
} else if ($context->contextlevel == CONTEXT_SYSTEM) {
static::export_authored_policies($contextlist->get_user());
}
}
}
/**
* Delete all data for all users in the specified context.
*
* We never delete user agreements to the policies because they are part of privacy data.
* We never delete policy versions because they are part of privacy data.
*
* @param \context $context The context to delete in.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* We never delete user agreements to the policies because they are part of privacy data.
* We never delete policy versions because they are part of privacy data.
*
* @param approved_contextlist $contextlist A list of contexts approved for deletion.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
}
/**
* Delete multiple users within a single context.
*
* We never delete user agreements to the policies because they are part of privacy data.
* We never delete policy versions because they are part of privacy data.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
}
/**
* Export all policy agreements relating to the specified user context.
*
* @param \context_user $context The context to export
*/
protected static function export_policy_agreements_for_context(\context_user $context) {
global $DB;
$sysctx = \context_system::instance();
$fs = get_file_storage();
$agreementsql = "
SELECT
a.id AS agreementid, a.userid, a.timemodified, a.note, a.status,
a.policyversionid AS versionid, a.usermodified, a.timecreated,
v.id, v.archived, v.name, v.revision,
v.summary, v.summaryformat,
v.content, v.contentformat,
p.currentversionid
FROM {tool_policy_acceptances} a
JOIN {tool_policy_versions} v ON v.id = a.policyversionid
JOIN {tool_policy} p ON v.policyid = p.id
WHERE a.userid = :userid OR a.usermodified = :usermodified";
// Fetch all agreements related to this user.
$agreements = $DB->get_recordset_sql($agreementsql, [
'userid' => $context->instanceid,
'usermodified' => $context->instanceid,
]);
$basecontext = [
get_string('privacyandpolicies', 'admin'),
get_string('useracceptances', 'tool_policy'),
];
foreach ($agreements as $agreement) {
$subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $agreement)]);
$summary = writer::with_context($context)->rewrite_pluginfile_urls(
$subcontext,
'tool_policy',
'policydocumentsummary',
$agreement->versionid,
$agreement->summary
);
$content = writer::with_context($context)->rewrite_pluginfile_urls(
$subcontext,
'tool_policy',
'policydocumentcontent',
$agreement->versionid,
$agreement->content
);
$agreementcontent = (object) [
'name' => $agreement->name,
'revision' => $agreement->revision,
'isactive' => transform::yesno($agreement->versionid == $agreement->currentversionid),
'isagreed' => transform::yesno($agreement->status),
'agreedby' => transform::user($agreement->usermodified),
'timecreated' => transform::datetime($agreement->timecreated),
'timemodified' => transform::datetime($agreement->timemodified),
'note' => $agreement->note,
'summary' => format_text($summary, $agreement->summaryformat),
'content' => format_text($content, $agreement->contentformat),
];
writer::with_context($context)->export_data($subcontext, $agreementcontent);
// Manually export the files as they reside in the system context so we can't use
// the write's helper methods.
foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentsummary', $agreement->versionid) as $file) {
writer::with_context($context)->export_file($subcontext, $file);
}
foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentcontent', $agreement->versionid) as $file) {
writer::with_context($context)->export_file($subcontext, $file);
}
}
$agreements->close();
}
/**
* Export all policy agreements that the user authored.
*
* @param stdClass $user The user who has created the policies to export.
*/
protected static function export_authored_policies(\stdClass $user) {
global $DB;
// Authored policies are exported against the system.
$context = \context_system::instance();
$basecontext = [
get_string('policydocuments', 'tool_policy'),
];
$sql = "SELECT v.id,
v.name,
v.revision,
v.summary,
v.content,
v.archived,
v.usermodified,
v.timecreated,
v.timemodified,
p.currentversionid
FROM {tool_policy_versions} v
JOIN {tool_policy} p ON p.id = v.policyid
WHERE v.usermodified = :userid";
$versions = $DB->get_recordset_sql($sql, ['userid' => $user->id]);
foreach ($versions as $version) {
$subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $version)]);
$versioncontent = (object) [
'name' => $version->name,
'revision' => $version->revision,
'summary' => writer::with_context($context)->rewrite_pluginfile_urls(
$subcontext,
'tool_policy',
'policydocumentsummary',
$version->id,
$version->summary
),
'content' => writer::with_context($context)->rewrite_pluginfile_urls(
$subcontext,
'tool_policy',
'policydocumentcontent',
$version->id,
$version->content
),
'isactive' => transform::yesno($version->id == $version->currentversionid),
'isarchived' => transform::yesno($version->archived),
'createdbyme' => transform::yesno($version->usermodified == $user->id),
'timecreated' => transform::datetime($version->timecreated),
'timemodified' => transform::datetime($version->timemodified),
];
writer::with_context($context)
->export_data($subcontext, $versioncontent)
->export_area_files($subcontext, 'tool_policy', 'policydocumentsummary', $version->id)
->export_area_files($subcontext, 'tool_policy', 'policydocumentcontent', $version->id);
}
$versions->close();
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
// This file is part of Moodle - https://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/>.
/**
* Provides the {@link \tool_policy\test\helper} class.
*
* @package tool_policy
* @category test
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\test;
use tool_policy\api;
use tool_policy\policy_version;
defined('MOODLE_INTERNAL') || die();
/**
* Provides some helper methods for unit-tests.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/**
* Helper method that creates a new policy for testing
*
* @param array $params
* @return policy_version
*/
public static function add_policy($params = []) {
static $counter = 0;
$counter++;
$defaults = [
'name' => 'Policy '.$counter,
'summary_editor' => ['text' => "P$counter summary", 'format' => FORMAT_HTML, 'itemid' => 0],
'content_editor' => ['text' => "P$counter content", 'format' => FORMAT_HTML, 'itemid' => 0],
];
$params = (array)$params + $defaults;
$formdata = api::form_policydoc_data(new policy_version(0));
foreach ($params as $key => $value) {
$formdata->$key = $value;
}
return api::form_policydoc_add($formdata);
}
/**
* Helper method that prepare a policy document with some versions.
*
* @param int $numversions The number of policy versions to create.
* @return array Array with all the policy versions created.
*/
public static function create_versions($numversions = 2) {
// Prepare a policy document with some versions.
$policy = self::add_policy([
'name' => 'Test policy',
'revision' => 'v1',
]);
for ($i = 2; $i <= $numversions; $i++) {
$formdata = api::form_policydoc_data($policy);
$formdata->revision = 'v'.$i;
api::form_policydoc_update_new($formdata);
}
$list = api::list_policies($policy->get('policyid'));
return $list[0]->draftversions;
}
}
+61
View File
@@ -0,0 +1,61 @@
<?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/>.
/**
* Plugin capabilities are defined here.
*
* @package tool_policy
* @category access
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
'tool/policy:accept' => [
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'user' => CAP_ALLOW,
],
],
'tool/policy:acceptbehalf' => [
'riskbitmask' => RISK_PERSONAL,
'captype' => 'write',
'contextlevel' => CONTEXT_USER,
'archetypes' => [
],
],
'tool/policy:managedocs' => [
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'manager' => CAP_ALLOW,
],
],
'tool/policy:viewacceptances' => [
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'manager' => CAP_ALLOW,
],
],
];
+32
View File
@@ -0,0 +1,32 @@
<?php
// This file is part of Moodle - https://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/>.
/**
* Defined caches used internally by the plugin.
*
* @package tool_policy
* @category cache
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$definitions = [
'policy_optional' => [
'mode' => cache_store::MODE_REQUEST,
],
];
+32
View File
@@ -0,0 +1,32 @@
<?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/>.
/**
* This file defines observers needed by the plugin.
*
* @package tool_policy
* @copyright 2018 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$observers = [
[
'eventname' => '\core\event\user_created',
'callback' => '\tool_policy\api::create_acceptances_user_created',
],
];
+38
View File
@@ -0,0 +1,38 @@
<?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/>.
/**
* Hook callbacks for Policies
*
* @package tool_policy
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$callbacks = [
[
'hook' => \core\hook\output\before_standard_top_of_body_html_generation::class,
'callback' => \tool_policy\hook_callbacks::class . '::before_standard_top_of_body_html_generation',
'priority' => 0,
],
[
'hook' => \core\hook\output\before_standard_footer_html_generation::class,
'callback' => [\tool_policy\hook_callbacks::class, 'before_standard_footer_html_generation'],
'priority' => 0,
],
];
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="admin/tool/policy/db" VERSION="20180918" COMMENT="The plugin allows to manage various policy documents that users have to accept to use the site."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="tool_policy" COMMENT="Contains the list of policy documents defined on the site.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="999" SEQUENCE="false" COMMENT="Defines the order in which policies should be presented to users"/>
<FIELD NAME="currentversionid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="ID of the current policy version that applies on the site, NULL if the policy does not apply"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_currentversionid" TYPE="foreign" FIELDS="currentversionid" REFTABLE="tool_policy_versions" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="tool_policy_versions" COMMENT="Holds versions of the policy documents">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="name" TYPE="char" LENGTH="1333" NOTNULL="true" SEQUENCE="false" COMMENT="Name of the policy document"/>
<FIELD NAME="type" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Type of the policy: 0 - Site policy, 1 - Privacy policy, 2 - Third party policy, 99 - Other"/>
<FIELD NAME="audience" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Who is this policy targeted at: 0 - all users, 1 - logged in users only, 2 - guests only"/>
<FIELD NAME="archived" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should the version be considered as archived. All non-archived, non-current versions are considered to be drafts."/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the user who last edited this policy document version."/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when the policy version was created."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when the policy version was last modified."/>
<FIELD NAME="policyid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the policy document we are version of."/>
<FIELD NAME="agreementstyle" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="How this agreement should flow: 0 - on the consent page, 1 - on a separate page before reaching the consent page."/>
<FIELD NAME="optional" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="0 - the policy must be accepted to use the site, 1 - accepting the policy is optional"/>
<FIELD NAME="revision" TYPE="char" LENGTH="1333" NOTNULL="true" SEQUENCE="false" COMMENT="Human readable version of the policy document"/>
<FIELD NAME="summary" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Policy text summary"/>
<FIELD NAME="summaryformat" TYPE="int" LENGTH="3" NOTNULL="true" SEQUENCE="false" COMMENT="Format of the summary field"/>
<FIELD NAME="content" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Full policy text"/>
<FIELD NAME="contentformat" TYPE="int" LENGTH="3" NOTNULL="true" SEQUENCE="false" COMMENT="Format of the content field"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="fk_policyid" TYPE="foreign" FIELDS="policyid" REFTABLE="tool_policy" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="tool_policy_acceptances" COMMENT="Tracks users accepting the policy versions">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="policyversionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the policy document version"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the user this acceptance is relevant to"/>
<FIELD NAME="status" TYPE="int" LENGTH="1" NOTNULL="false" SEQUENCE="false" COMMENT="Acceptance status: 1 - accepted, 0 - not accepted"/>
<FIELD NAME="lang" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" COMMENT="Code of the language the user had when the policy document was displayed"/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the user who last modified the acceptance record"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when the acceptance record was created"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when the acceptance record was last modified"/>
<FIELD NAME="note" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Plain text note describing how the actual consent has been obtained if the policy has been accepted on other user's behalf."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_policyversionid" TYPE="foreign" FIELDS="policyversionid" REFTABLE="tool_policy_versions" REFFIELDS="id"/>
<KEY NAME="fk_userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="fk_usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="uq_versionuser" TYPE="unique" FIELDS="policyversionid, userid" COMMENT="There cannot exist more records for given user and policy document version."/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+62
View File
@@ -0,0 +1,62 @@
<?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/>.
/**
* Tool policy external functions and service definitions.
*
* @package tool_policy
* @category external
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$functions = [
'tool_policy_get_policy_version' => [
'classname' => 'tool_policy\external',
'methodname' => 'get_policy_version',
'classpath' => '',
'description' => 'Fetch the details of a policy version',
'type' => 'read',
'capabilities' => '',
'ajax' => true,
'loginrequired' => false,
],
'tool_policy_submit_accept_on_behalf' => [
'classname' => 'tool_policy\external',
'methodname' => 'submit_accept_on_behalf',
'classpath' => '',
'description' => 'Accept policies on behalf of other users',
'ajax' => true,
'type' => 'write',
],
'tool_policy_get_user_acceptances' => [
'classname' => '\tool_policy\external\get_user_acceptances',
'description' => 'Get user policies acceptances.',
'type' => 'read',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
],
'tool_policy_set_acceptances_status' => [
'classname' => '\tool_policy\external\set_acceptances_status',
'description' => 'Set the acceptance status (accept or decline only) for the indicated policies for the given user.',
'type' => 'write',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
],
];
+46
View File
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - https://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/>.
/**
* Plugin upgrade steps are defined here.
*
* @package tool_policy
* @category upgrade
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Execute the plugin upgrade steps from the given old version.
*
* @param int $oldversion
* @return bool
*/
function xmldb_tool_policy_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+190
View File
@@ -0,0 +1,190 @@
<?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/>.
/**
* Edit/create a policy document version.
*
* @package tool_policy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use tool_policy\api;
use tool_policy\policy_version;
require(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
$policyid = optional_param('policyid', null, PARAM_INT);
$versionid = optional_param('versionid', null, PARAM_INT);
$makecurrent = optional_param('makecurrent', null, PARAM_INT);
$inactivate = optional_param('inactivate', null, PARAM_INT);
$delete = optional_param('delete', null, PARAM_INT);
$confirm = optional_param('confirm', false, PARAM_BOOL);
$moveup = optional_param('moveup', null, PARAM_INT);
$movedown = optional_param('movedown', null, PARAM_INT);
admin_externalpage_setup('tool_policy_managedocs', '', ['policyid' => $policyid, 'versionid' => $versionid],
new moodle_url('/admin/tool/policy/editpolicydoc.php'));
require_capability('tool/policy:managedocs', context_system::instance());
$output = $PAGE->get_renderer('tool_policy');
$PAGE->navbar->add(get_string('editingpolicydocument', 'tool_policy'));
if ($makecurrent) {
$version = api::get_policy_version($makecurrent);
if ($confirm) {
require_sesskey();
api::make_current($makecurrent);
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
echo $output->header();
echo $output->heading(get_string('activating', 'tool_policy'));
echo $output->confirm(
get_string('activateconfirm', 'tool_policy', [
'name' => format_string($version->name),
'revision' => format_string($version->revision),
]),
new moodle_url($PAGE->url, ['makecurrent' => $makecurrent, 'confirm' => 1]),
new moodle_url('/admin/tool/policy/managedocs.php')
);
echo $output->footer();
die();
}
if ($inactivate) {
$policies = api::list_policies([$inactivate]);
if (empty($policies[0]->currentversionid)) {
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
if ($confirm) {
require_sesskey();
api::inactivate($inactivate);
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
echo $output->header();
echo $output->heading(get_string('inactivating', 'tool_policy'));
echo $output->confirm(
get_string('inactivatingconfirm', 'tool_policy', [
'name' => format_string($policies[0]->currentversion->name),
'revision' => format_string($policies[0]->currentversion->revision),
]),
new moodle_url($PAGE->url, ['inactivate' => $inactivate, 'confirm' => 1]),
new moodle_url('/admin/tool/policy/managedocs.php')
);
echo $output->footer();
die();
}
if ($delete) {
$version = api::get_policy_version($delete);
if ($confirm) {
require_sesskey();
api::delete($delete);
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
echo $output->header();
echo $output->heading(get_string('deleting', 'tool_policy'));
echo $output->confirm(
get_string('deleteconfirm', 'tool_policy', [
'name' => format_string($version->name),
'revision' => format_string($version->revision),
]),
new moodle_url($PAGE->url, ['delete' => $delete, 'confirm' => 1]),
new moodle_url('/admin/tool/policy/managedocs.php')
);
echo $output->footer();
die();
}
if ($moveup || $movedown) {
require_sesskey();
if ($moveup) {
api::move_up($moveup);
} else {
api::move_down($movedown);
}
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
if (!$versionid && $policyid) {
if (($policies = api::list_policies([$policyid])) && !empty($policies[0]->currentversionid)) {
$policy = $policies[0];
$policyversion = new policy_version($policy->currentversionid);
} else {
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
}
} else {
$policyversion = new policy_version($versionid);
if ($policyversion->get('policyid')) {
$policy = api::list_policies([$policyversion->get('policyid')])[0];
} else {
$policy = null;
}
}
$formdata = api::form_policydoc_data($policyversion);
if ($policy && $formdata->id && $policy->currentversionid == $formdata->id) {
// We are editing an active version.
$formdata->status = policy_version::STATUS_ACTIVE;
} else {
// We are editing a draft or archived version and the default next status is "draft".
$formdata->status = policy_version::STATUS_DRAFT;
// Archived versions can not be edited without creating a new version.
$formdata->minorchange = $policyversion->get('archived') ? 0 : 1;
}
$form = new \tool_policy\form\policydoc($PAGE->url, ['formdata' => $formdata]);
if ($form->is_cancelled()) {
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
} else if ($data = $form->get_data()) {
if (! $policyversion->get('id')) {
$policyversion = api::form_policydoc_add($data);
} else if (empty($data->minorchange)) {
$data->policyid = $policyversion->get('policyid');
$policyversion = api::form_policydoc_update_new($data);
} else {
$data->id = $policyversion->get('id');
$policyversion = api::form_policydoc_update_overwrite($data);
}
if ($data->status == policy_version::STATUS_ACTIVE) {
api::make_current($policyversion->get('id'));
}
redirect(new moodle_url('/admin/tool/policy/managedocs.php'));
} else {
echo $output->header();
echo $output->heading(get_string('editingpolicydocument', 'tool_policy'));
echo $form->render();
echo $output->footer();
}
+92
View File
@@ -0,0 +1,92 @@
<?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/>.
/**
* Show a user the policy documents to be agreed to.
*
* Script parameters:
* listdoc=<array> List of policy version ids that were displayed to the user to accept.
* statusXX=<int> Acceptance status to be set for the policy version with id XX.
* behalfid=<id> The user id to view the policy version as (such as child's id).
*
* @package tool_policy
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use tool_policy\api;
use tool_policy\output\page_agreedocs;
// Do not check for the site policies in require_login() to avoid the redirect loop.
define('NO_SITEPOLICY_CHECK', true);
// See the {@see page_agreedocs} for the access control checks.
require(__DIR__.'/../../../config.php'); // phpcs:ignore
$submit = optional_param('submit', null, PARAM_NOTAGS);
$cancel = optional_param('cancel', null, PARAM_NOTAGS);
$listdocs = optional_param_array('listdoc', [], PARAM_INT);
$behalfid = optional_param('userid', null, PARAM_INT);
$agreedocs = [];
$declinedocs = [];
foreach ($listdocs as $pvid) {
$status = optional_param('status'.$pvid, null, PARAM_INT);
if ($status === 1) {
$agreedocs[] = $pvid;
} else if ($status === 0) {
$declinedocs[] = $pvid;
}
}
$listdocs = array_values(array_unique($listdocs));
$agreedocs = array_values(array_unique($agreedocs));
$declinedocs = array_values(array_unique($declinedocs));
$PAGE->set_context(context_system::instance());
$PAGE->set_pagelayout('standard');
$PAGE->set_url('/admin/tool/policy/index.php');
$PAGE->set_popup_notification_allowed(false);
if (array_diff($agreedocs, $listdocs) || array_diff($declinedocs, $listdocs)) {
throw new moodle_exception('invalidaccessparameter');
}
if (isloggedin() && !isguestuser()) {
// Existing user.
$haspermissionagreedocs = api::can_accept_policies($listdocs, $behalfid);
} else {
// New user.
$haspermissionagreedocs = true;
}
if (!$haspermissionagreedocs) {
$outputpage = new \tool_policy\output\page_nopermission($listdocs, $behalfid);
} else if ($cancel) {
redirect(new moodle_url('/'));
} else {
if (!$behalfid && \core\session\manager::is_loggedinas()) {
$behalfid = $USER->id;
}
$outputpage = new \tool_policy\output\page_agreedocs($listdocs, $agreedocs, $declinedocs, $behalfid, $submit);
}
$output = $PAGE->get_renderer('tool_policy');
echo $output->header();
echo $output->render($outputpage);
echo $output->footer();
+197
View File
@@ -0,0 +1,197 @@
<?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/>.
/**
* Plugin strings are defined here.
*
* @package tool_policy
* @category string
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['acceptanceacknowledgement'] = 'I acknowledge that I have received a request to give consent on behalf of the above user(s).';
$string['acceptancenote'] = 'Remarks';
$string['acceptancepolicies'] = 'Policies';
$string['acceptancessavedsucessfully'] = 'The agreements have been saved successfully.';
$string['acceptancestatusaccepted'] = 'Accepted';
$string['acceptancestatusacceptedbehalf'] = 'Accepted on user\'s behalf';
$string['acceptancestatusdeclined'] = 'Declined';
$string['acceptancestatusdeclinedbehalf'] = 'Declined on user\'s behalf';
$string['acceptancestatusoverall'] = 'Overall';
$string['acceptancestatuspartial'] = 'Partially accepted';
$string['acceptancestatuspending'] = 'Pending';
$string['acceptanceusers'] = 'Users';
$string['actions'] = 'Actions';
$string['activate'] = 'Set status to "Active"';
$string['activating'] = 'Activating a policy';
$string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->name}\'</em> and make the version <em>\'{$a->revision}\'</em> the current one.</p><p>All users will be required to agree to this new policy version to be able to use the site.</p>';
$string['activateconfirmyes'] = 'Activate';
$string['agreepolicies'] = 'Please agree to the following policies';
$string['backtoprevious'] = 'Go back to previous page';
$string['backtotop'] = 'Back to top';
$string['cachedef_policy_optional'] = 'Cache of the optional/compulsory flag for policy versions';
$string['consentbulk'] = 'Consent';
$string['consentpagetitle'] = 'Consent';
$string['contactdpo'] = 'For any questions about the policies please contact the privacy officer.';
$string['dataproc'] = 'Personal data processing';
$string['declineacknowledgement'] = 'I acknowledge that I have received a request to decline consent on behalf of the above user(s).';
$string['declinethepolicy'] = 'Decline user consent';
$string['deleting'] = 'Deleting a version';
$string['deleteconfirm'] = '<p>Are you sure you want to delete policy <em>\'{$a->name}\'</em>?</p><p>This operation can not be undone.</p>';
$string['editingpolicydocument'] = 'Editing policy';
$string['errorpolicyversioncompulsory'] = 'Compulsory policies cannot be declined!';
$string['errorpolicyversionnotfound'] = 'There isn\'t any policy version with this identifier.';
$string['errorsaveasdraft'] = 'Minor change can not be saved as draft';
$string['errorusercantviewpolicyversion'] = 'The user doesn\'t have access to this policy version.';
$string['event_acceptance_created'] = 'User policy agreement created';
$string['event_acceptance_updated'] = 'User policy agreement updated';
$string['filtercapabilityno'] = 'Permission: Can not agree';
$string['filtercapabilityyes'] = 'Permission: Can agree';
$string['filterrevision'] = 'Version: {$a}';
$string['filterrevisionstatus'] = 'Version: {$a->name} ({$a->status})';
$string['filterrole'] = 'Role: {$a}';
$string['filters'] = 'Filters';
$string['filterstatusdeclined'] = 'Status: Declined';
$string['filterstatuspending'] = 'Status: Pending';
$string['filterstatusyes'] = 'Status: Agreed';
$string['filterplaceholder'] = 'Search keyword or select filter';
$string['filterpolicy'] = 'Policy: {$a}';
$string['guestconsent:continue'] = 'Continue';
$string['guestconsentmessage'] = 'If you continue browsing this website, you agree to our policies:';
$string['iagree'] = 'I agree to the {$a}';
$string['idontagree'] = 'No thanks, I decline {$a}';
$string['iagreetothepolicy'] = 'Give consent';
$string['inactivate'] = 'Set status to "Inactive"';
$string['inactivating'] = 'Inactivating a policy';
$string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p>';
$string['inactivatingconfirmyes'] = 'Inactivate';
$string['invalidversionid'] = 'There is no policy with this identifier!';
$string['irevokethepolicy'] = 'Withdraw user consent';
$string['listactivepolicies'] = 'List of active policies';
$string['minorchange'] = 'Minor change';
$string['minorchangeinfo'] = 'A minor change does not alter the meaning of the policy. Users are not required to agree to the policy again if the edit is marked as a minor change.';
$string['managepolicies'] = 'Manage policies';
$string['movedown'] = 'Move down';
$string['moveup'] = 'Move up';
$string['mustagreetocontinue'] = 'Before continuing you need to acknowledge all these policies.';
$string['newpolicy'] = 'New policy';
$string['newversion'] = 'New version';
$string['noactivepolicies'] = 'There are no policies with an active version.';
$string['nofiltersapplied'] = 'No filters applied';
$string['nopermissiontoagreedocs'] = 'No permission to agree to the policies';
$string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
$string['nopermissiontoagreedocsbehalf'] = 'No permission to agree to the policies on behalf of this user';
$string['nopermissiontoagreedocsbehalf_desc'] = 'Sorry, you do not have the required permission to agree to the following policies on behalf of {$a}:';
$string['nopermissiontoagreedocscontact'] = 'For more help:';
$string['nopermissiontoviewpolicyversion'] = 'You do not have permissions to view this policy version.';
$string['nopolicies'] = 'There are no policies for registered users with an active version.';
$string['selectpolicyandversion'] = 'Use the filter above to select policy and/or version';
$string['steppolicies'] = 'Policy {$a->numpolicy} out of {$a->totalpolicies}';
$string['pluginname'] = 'Policies';
$string['policiesagreements'] = 'Policies and agreements';
$string['policy:accept'] = 'Agree to the policies';
$string['policy:acceptbehalf'] = 'Give consent for policies on someone else\'s behalf';
$string['policy:managedocs'] = 'Manage policies';
$string['policy:viewacceptances'] = 'View user agreement reports';
$string['policydocaudience'] = 'User consent';
$string['policydocaudience0'] = 'All users';
$string['policydocaudience1'] = 'Authenticated users';
$string['policydocaudience2'] = 'Guests';
$string['policydoccontent'] = 'Full policy';
$string['policydochdrpolicy'] = 'Policy';
$string['policydochdrversion'] = 'Document version';
$string['policydocname'] = 'Name';
$string['policydocoptional'] = 'Agreement optional';
$string['policydocoptionalyes'] = 'Optional';
$string['policydocoptionalno'] = 'Compulsory';
$string['policydocrevision'] = 'Version';
$string['policydocsummary'] = 'Summary';
$string['policydocsummary_help'] = 'This text should provide a summary of the policy, potentially in a simplified and easily accessible form, using clear and plain language.';
$string['policydoctype'] = 'Type';
$string['policydoctype0'] = 'Site policy';
$string['policydoctype1'] = 'Privacy policy';
$string['policydoctype2'] = 'Third parties policy';
$string['policydoctype99'] = 'Other policy';
$string['policydocuments'] = 'Policy documents';
$string['policynamedversion'] = 'Policy {$a->name} (version {$a->revision} - {$a->id})';
$string['policypriorityagreement'] = 'Show policy before showing other policies';
$string['policyversionacceptedinbehalf'] = 'Consent for this policy has been given on your behalf.';
$string['policyversionacceptedinotherlang'] = 'Consent for this policy version has been given in a different language.';
$string['previousversions'] = '{$a} previous versions';
$string['privacy:metadata:acceptances'] = 'Information about policy agreements made by users.';
$string['privacy:metadata:acceptances:policyversionid'] = 'The version of the policy for which consent was given.';
$string['privacy:metadata:acceptances:userid'] = 'The user for whom this policy agreement relates to.';
$string['privacy:metadata:acceptances:status'] = 'The status of the agreement.';
$string['privacy:metadata:acceptances:lang'] = 'The language used to display the policy when consent was given.';
$string['privacy:metadata:acceptances:usermodified'] = 'The user who gave consent for the policy, if made on behalf of another user.';
$string['privacy:metadata:acceptances:timecreated'] = 'The time when the user agreed to the policy.';
$string['privacy:metadata:acceptances:timemodified'] = 'The time when the user updated their agreement.';
$string['privacy:metadata:acceptances:note'] = 'Any comments added by a user when giving consent on behalf of another user.';
$string['privacy:metadata:subsystem:corefiles'] = 'The policy tool stores files included in the summary and full policy.';
$string['privacy:metadata:versions'] = 'Policy version information.';
$string['privacy:metadata:versions:name'] = 'The name of the policy.';
$string['privacy:metadata:versions:type'] = 'Policy type.';
$string['privacy:metadata:versions:audience'] = 'The type of users required to give their consent.';
$string['privacy:metadata:versions:archived'] = 'The policy status (active or inactive).';
$string['privacy:metadata:versions:usermodified'] = 'The user who modified the policy.';
$string['privacy:metadata:versions:timecreated'] = 'The time that this version of the policy was created.';
$string['privacy:metadata:versions:timemodified'] = 'The time that this version of the policy was updated.';
$string['privacy:metadata:versions:policyid'] = 'The policy that this version is associated with.';
$string['privacy:metadata:versions:revision'] = 'The revision name of this version of the policy.';
$string['privacy:metadata:versions:summary'] = 'The summary of this version of the policy.';
$string['privacy:metadata:versions:summaryformat'] = 'The format of the summary field.';
$string['privacy:metadata:versions:content'] = 'The content of this version of the policy.';
$string['privacy:metadata:versions:contentformat'] = 'The format of the content field.';
$string['privacysettings'] = 'Privacy settings';
$string['readpolicy'] = 'Please read our {$a}';
$string['refertofullpolicytext'] = 'Please refer to the full {$a} if you would like to review the text.';
$string['response'] = 'Response';
$string['responseby'] = 'Respondent';
$string['responseon'] = 'Date';
$string['revokeacknowledgement'] = 'I acknowledge that I have received a request to withdraw consent on behalf of the above user(s).';
$string['save'] = 'Save';
$string['saveasdraft'] = 'Save as draft';
$string['selectuser'] = 'Select user {$a}';
$string['selectusersforconsent'] = 'Select users to give consent on behalf of.';
$string['settodraft'] = 'Create a new draft';
$string['status'] = 'Policy status';
$string['statusformtitleaccept'] = 'Accepting policy';
$string['statusformtitledecline'] = 'Declining policy';
$string['statusformtitlerevoke'] = 'Withdrawing policy';
$string['statusinfo'] = 'A policy with \'Active\' status requires users to give their consent, either when they first log in, or in the case of existing users when they next log in.';
$string['status0'] = 'Draft';
$string['status1'] = 'Active';
$string['status2'] = 'Inactive';
$string['useracceptanceactionaccept'] = 'Accept';
$string['useracceptanceactionacceptone'] = 'Accept {$a}';
$string['useracceptanceactionacceptpending'] = 'Accept pending policies';
$string['useracceptanceactiondecline'] = 'Decline';
$string['useracceptanceactiondeclineone'] = 'Decline {$a}';
$string['useracceptanceactiondeclinepending'] = 'Decline pending policies';
$string['useracceptanceactiondetails'] = 'Details';
$string['useracceptanceactionrevoke'] = 'Withdraw';
$string['useracceptanceactionrevokeall'] = 'Withdraw accepted policies';
$string['useracceptanceactionrevokeone'] = 'Withdraw acceptance of {$a}';
$string['useracceptancecount'] = '{$a->agreedcount} of {$a->userscount} ({$a->percent}%)';
$string['useracceptancecountna'] = 'N/A';
$string['useracceptances'] = 'User agreements';
$string['userpolicysettings'] = 'Policies';
$string['usersaccepted'] = 'Agreements';
$string['viewarchived'] = 'View previous versions';
$string['viewconsentpageforuser'] = 'Viewing this page on behalf of {$a}';
+190
View File
@@ -0,0 +1,190 @@
<?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/>.
/**
* Plugin version and other meta-data are defined here.
*
* @package tool_policy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
use core_user\output\myprofile\tree;
use tool_policy\api;
use tool_policy\policy_version;
/**
* Add nodes to myprofile page.
*
* @param tree $tree Tree object
* @param stdClass $user User object
* @param bool $iscurrentuser
* @param stdClass $course Course object
* @return bool
* @throws coding_exception
* @throws dml_exception
* @throws moodle_exception
*/
function tool_policy_myprofile_navigation(tree $tree, $user, $iscurrentuser, $course) {
global $CFG;
// Do nothing if we are not set as the site policies handler.
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
return;
}
// Get the Privacy and policies category.
if (!array_key_exists('privacyandpolicies', $tree->__get('categories'))) {
// Create the category.
$categoryname = get_string('privacyandpolicies', 'admin');
$category = new core_user\output\myprofile\category('privacyandpolicies', $categoryname, 'contact');
$tree->add_category($category);
} else {
// Get the existing category.
$category = $tree->__get('categories')['privacyandpolicies'];
}
// Add "Policies and agreements" node only for current user or users who can accept on behalf of current user.
$usercontext = \context_user::instance($user->id);
if ($iscurrentuser || has_capability('tool/policy:acceptbehalf', $usercontext)) {
$url = new moodle_url('/admin/tool/policy/user.php', ['userid' => $user->id]);
$node = new core_user\output\myprofile\node('privacyandpolicies', 'tool_policy',
get_string('policiesagreements', 'tool_policy'), null, $url);
$category->add_node($node);
}
return true;
}
/**
* Hooks redirection to policy acceptance pages before sign up.
*/
function tool_policy_pre_signup_requests() {
global $CFG;
// Do nothing if we are not set as the site policies handler.
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
return;
}
$policies = api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN);
$userpolicyagreed = cache::make('core', 'presignup')->get('tool_policy_userpolicyagreed');
if (!empty($policies) && !$userpolicyagreed) {
// Redirect to "Policy" pages for consenting before creating the user.
cache::make('core', 'presignup')->set('tool_policy_issignup', 1);
redirect(new \moodle_url('/admin/tool/policy/index.php'));
}
}
/**
* Serve the embedded files.
*
* @param stdClass $course the course object
* @param stdClass $cm the course module object
* @param stdClass $context the context
* @param string $filearea the name of the file area
* @param array $args extra arguments (itemid, path)
* @param bool $forcedownload whether or not force download
* @param array $options additional options affecting the file serving
* @return bool false if the file not found, just send the file otherwise and do not return anything
*/
function tool_policy_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
global $CFG, $PAGE;
// Do not allow access to files if we are not set as the site policy handler.
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
return false;
}
if ($context->contextlevel != CONTEXT_SYSTEM) {
return false;
}
$PAGE->set_context($context);
if ($filearea !== 'policydocumentsummary' && $filearea !== 'policydocumentcontent') {
return false;
}
$itemid = array_shift($args);
$policy = api::get_policy_version($itemid);
if ($policy->status != policy_version::STATUS_ACTIVE) {
require_login();
}
if (!api::can_user_view_policy_version($policy)) {
return false;
}
$filename = array_pop($args);
if (!$args) {
$filepath = '/';
} else {
$filepath = '/'.implode('/', $args).'/';
}
$fs = get_file_storage();
$file = $fs->get_file($context->id, 'tool_policy', $filearea, $itemid, $filepath, $filename);
if (!$file) {
return false;
}
send_stored_file($file, null, 0, $forcedownload, $options);
}
/**
* Map icons for font-awesome themes.
*/
function tool_policy_get_fontawesome_icon_map() {
return [
'tool_policy:agreed' => 'fa-check text-success',
'tool_policy:declined' => 'fa-times text-danger',
'tool_policy:pending' => 'fa-clock-o text-warning',
'tool_policy:partial' => 'fa-exclamation-triangle text-warning',
'tool_policy:level' => 'fa-level-up fa-rotate-90 text-muted',
];
}
/**
* Serve the new group form as a fragment.
*
* @param array $args List of named arguments for the fragment loader.
* @return string
*/
function tool_policy_output_fragment_accept_on_behalf($args) {
$args = (object) $args;
$data = [];
if (!empty($args->jsonformdata)) {
$serialiseddata = json_decode($args->jsonformdata);
parse_str($serialiseddata, $data);
}
$mform = new \tool_policy\form\accept_policy(null, $data);
if (!empty($args->jsonformdata)) {
// If we were passed non-empty form data we want the mform to call validation functions and show errors.
$mform->is_validated();
}
return $mform->render();
}
+41
View File
@@ -0,0 +1,41 @@
<?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/>.
/**
* Manage policy documents used on the site.
*
* Script arguments:
* - archived=<int> Show only archived versions of the given policy document
*
* @package tool_policy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
$archived = optional_param('archived', 0, PARAM_INT);
admin_externalpage_setup('tool_policy_managedocs', '', ['archived' => $archived]);
$output = $PAGE->get_renderer('tool_policy');
$manpage = new \tool_policy\output\page_managedocs_list($archived);
echo $output->header();
echo $output->render($manpage);
echo $output->footer();
Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.1 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M6.4 11.1c-2-2.5-3.7-4-3.7-4L0 9.8C5 13.1 8.1 16 8.1 16s.2-.7.6-1.8c.9-2.7 3.2-8.1 7.1-14.2-4.6 3.7-7.7 8.2-9.4 11.1z" fill="#9C3"/></svg>

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1.6 -0.5 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M12.8 2.7L10.1 0S8.5 1.5 6.4 4C4.3 1.5 2.7 0 2.7 0L0 2.7S1.9 4 4.6 6.4C3 8.7 1.3 11.6 0 14.9c2.2-2.7 4.4-5 6.4-6.9 2 1.9 4.2 4.2 6.4 6.9-1.3-3.3-3-6.2-4.6-8.6 2.7-2.3 4.6-3.6 4.6-3.6z" fill="#FF403C"/></svg>

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

+60
View File
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg4554"
sodipodi:docname="level.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
inkscape:export-filename="/home/mudrd8mz/www/mdk/gdpr33/moodle/admin/tool/policy/pix/level.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008" preserveAspectRatio="xMinYMid meet">
<metadata
id="metadata4560">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4558" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview4556"
showgrid="false"
inkscape:showpageshadow="false"
inkscape:zoom="16"
inkscape:cx="10.805859"
inkscape:cy="8.40625"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg4554" />
<path
d="m 10.60981,13.748496 q -0.42076,-0.2046 -0.42076,-0.6595 v -2.1834 H 0.36389001 q -0.15919999,0 -0.26154001,-0.102297 Q 0,10.700897 0,10.541796 V 2.5361969 q 0,-0.2388 0.20469002,-0.3297 0.22743999,-0.091 0.39800999,0.045 L 2.7860701,4.0708971 q 0.1250916,0.1024 0.1250916,0.2842998 v 3.639 H 10.18905 V 5.810797 q 0,-0.4548999 0.42076,-0.6596001 0.42075,-0.1932998 0.77327,0.1024 L 15.749821,8.892496 Q 16,9.0971966 16,9.4496973 16,9.8022974 15.749821,10.006998 l -4.366741,3.638798 q -0.36389,0.307 -0.77327,0.102297 z"
id="path4552"
inkscape:connector-curvature="0"
style="fill:#888;fill-opacity:1;stroke-width:0.9999997" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
preserveAspectRatio="xMinYMid meet"
overflow="visible"
version="1.1"
id="svg956"
sodipodi:docname="warning.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
inkscape:export-filename="/home/mudrd8mz/www/html/mdk/m36/admin/tool/policy/pix/warning.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<metadata
id="metadata962">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs960" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1624"
inkscape:window-height="911"
id="namedview958"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="-0.06779661"
inkscape:cy="7.3559322"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg956" />
<path
d="M7 .7L.2 14.1c-.5 1 0 1.8 1.1 1.8h13.2c1.1 0 1.6-.8 1.1-1.8L8.8.7C8.3-.2 7.5-.2 7 .7zm2.4 12.7c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5.7-1.5 1.5-1.5 1.5.6 1.5 1.5zm-.2-7.5v4c0 .5-.5 1-1 1h-.6c-.5 0-1-.5-1-1v-4c0-.6.5-1 1-1h.6c.6 0 1 .4 1 1z"
fill="#999"
id="path954"
style="fill:#ffb844;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

+58
View File
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
preserveAspectRatio="xMinYMid meet"
overflow="visible"
version="1.1"
id="svg5917"
sodipodi:docname="agreedpending.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<metadata
id="metadata5923">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs5921" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview5919"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="-0.06779661"
inkscape:cy="7.3559322"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg5917" />
<path
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 14c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm4-6c0 .6-.4 1-1 1H8c-.6 0-1-.4-1-1V4c0-.6.4-1 1-1s1 .4 1 1v3h2c.6 0 1 .4 1 1z"
fill="#999"
id="path5915"
style="fill:#ffb844;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+63
View File
@@ -0,0 +1,63 @@
jQuery EU Cookie Law popups 1.1.3
-------------
https://github.com/wimagguc/jquery-eu-cookie-law-popup
Instructions to import 'jQuery EU Cookie Law popups' into Moodle:
1. Download the latest release from https://github.com/wimagguc/jquery-eu-cookie-law-popup/releases
2. Copy 'js/jquery-eu-cookie-law-popup.js' into 'amd/src/jquery-eu-cookie-law-popup.js':
2.a. Replace jquery reference
------------------
(function($) {
------------------
with
------------------
define(['jquery'],function($) {
------------------
2.b. Remove initialisation code. It will be added and configured only in the pages where is needed
------------------
$(document).ready( function() {
if ($(".eupopup").length > 0) {
$(document).euCookieLawPopup().init({
'info' : 'YOU_CAN_ADD_MORE_SETTINGS_HERE',
'popupTitle' : 'This website is using cookies. ',
'popupText' : 'We use them to give you the best experience. If you continue using our website, we\'ll assume that you are happy to receive all cookies on this website.'
});
}
});
------------------
2.c. Remove code
------------------
$(document).bind("user_cookie_consent_changed", function(event, object) {
console.log("User cookie consent changed: " + $(object).attr('consent') );
});
------------------
2.d. Replace
------------------
}(jQuery));
------------------
with
------------------
});
------------------
3. Copy the following styles from 'css/jquery-eu-cookie-law-popup.css' into the
"jquery-eu-cookie-law-popup styles" section in 'styles.css':
.eupopup-container
.eupopup-container-bottom
.eupopup-closebutton
.eupopup-buttons
.eupopup-button
.eupopup-button:hover
.eupopup-button:focus
Add "tool_policy styles" to the end of the styles file.
4. Execute grunt to compile js
grunt amd
5. Update version number in admin/tool/policy/thirdpartylibs.xml
+51
View File
@@ -0,0 +1,51 @@
<?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/>.
/**
* Plugin administration pages are defined here.
*
* @package tool_policy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// Do nothing if we are not set as the site policies handler.
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
return;
}
$managecaps = [
'tool/policy:managedocs',
'tool/policy:viewacceptances',
];
if ($hassiteconfig || has_any_capability($managecaps, context_system::instance())) {
$ADMIN->add('privacy', new admin_externalpage(
'tool_policy_managedocs',
new lang_string('managepolicies', 'tool_policy'),
new moodle_url('/admin/tool/policy/managedocs.php'),
['tool/policy:managedocs', 'tool/policy:viewacceptances']
));
$ADMIN->add('privacy', new admin_externalpage(
'tool_policy_acceptances',
new lang_string('useracceptances', 'tool_policy'),
new moodle_url('/admin/tool/policy/acceptances.php'),
['tool/policy:viewacceptances']
));
}
+104
View File
@@ -0,0 +1,104 @@
/* jquery-eu-cookie-law-popup styles */
.eupopup-container {
background-color: rgba(25, 25, 25, 0.9);
color: #efefef;
padding: 5px 20px;
font-size: 12px;
line-height: 1.2em;
text-align: center;
display: none;
z-index: 9999999;
}
.eupopup-container .eupopup-body a {
color: #fff;
text-decoration: underline;
}
.behat-site .eupopup-container-bottom {
position: absolute;
}
.eupopup-container-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
.eupopup-closebutton {
font-size: 16px;
font-weight: 100;
line-height: 1;
color: #a2a2a2;
opacity: 0.2;
position: absolute;
font-family: helvetica, arial, verdana, sans-serif;
top: 0;
right: 0;
padding: 5px 10px;
}
.eupopup-buttons {
padding: 7px 0 5px 0;
}
.eupopup-button {
margin: 0 10px;
}
.eupopup-button:hover,
.eupopup-button:focus {
text-decoration: underline;
color: #f6a21d;
}
/* tool_policy styles */
.eupopup-body li {
display: inline;
}
.eupopup-body li:before {
content: ", ";
}
.eupopup-body li:first-child:before {
content: "";
}
.eupopup-body ul {
padding: 0;
margin: 0 0 3px;
}
.eupopup-closebutton:hover,
.eupopup-closebutton:active {
text-decoration: none;
}
.eupopup-head {
font-size: 1.2em;
font-weight: bold;
padding: 7px;
}
.eupopup-button_1 {
font-weight: bold;
font-size: 14px;
}
.eupopup-button_2 {
display: none;
}
.eupopup-button {
color: #f6a21d;
}
.policy-heading .policy-viewdoc-buttons {
text-align: center;
margin: 15px;
}
#page-admin-tool-policy-acceptances .colselect {
width: 35px;
}
@@ -0,0 +1,158 @@
{{!
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/>.
}}
{{!
@template tool_policy/acceptances
Template for the user acceptances page.
Classes required for JS:
-
Data attributes required for JS:
-
Context variables required for this template:
* policies
Example context (json):
{
"hasonbehalfagreements": true,
"canrevoke": true,
"policies": [
{
"versions": [
{
"isfirst": true,
"policyid": 1,
"viewurl": "/",
"name": "Terms &amp; conditions",
"revision": "2.0",
"hasarchived": true,
"timeaccepted": "1 Mar 2018",
"iscurrent": true,
"isoptional": false,
"agreement": {
"onbehalf": false,
"status": false,
"canaccept": true,
"acceptlink": "#"
}
},
{
"isfirst": false,
"policyid": 1,
"viewurl": "/",
"name": "Terms &amp; conditions",
"revision": "1.0-beta",
"acceptedby": "<a href=\"#\">Mary Smith</a>",
"note": "Based on parent's agreement via email",
"hasarchived": false,
"timeaccepted": "15 Feb 2018",
"iscurrent": true,
"isoptional": false,
"agreement": {
"onbehalf": true,
"status": true,
"canaccept": false
}
}
]
}
]
}
}}
{{^canrevoke}}
<div class="alert alert-info">{{#str}} contactdpo, tool_policy {{/str}}</div>
{{/canrevoke}}
<table class="generaltable fullwidth">
<thead>
<tr>
<th>{{#str}} policydocname, tool_policy {{/str}}</th>
<th>{{#str}} policydocrevision, tool_policy {{/str}}</th>
<th>{{#str}} response, tool_policy {{/str}}</th>
<th>{{#str}} responseon, tool_policy {{/str}}</th>
{{#hasonbehalfagreements}}
<th>{{#str}} responseby, tool_policy {{/str}}</th>
<th>{{#str}} acceptancenote, tool_policy {{/str}}</th>
{{/hasonbehalfagreements}}
<th></th>
</tr>
</thead>
<tbody>
{{#policies}}
{{#versions}}
{{#agreement}}
<tr {{^isfirst}}class="archived{{policyid}}" style="display:none"{{/isfirst}}>
<td>
{{^isfirst}}
<div style="float:left">
{{#pix}} level, tool_policy {{/pix}}
</div>
{{/isfirst}}
<div {{^isfirst}}style="margin-left: 24px" {{/isfirst}}>
<div><a href="{{viewurl}}">{{{name}}}</a></div>
</div>
</td>
<td>
<a href="{{viewurl}}">{{{revision}}}</a>
{{#iscurrent}}<span class="badge bg-success text-white">{{#str}} status1, tool_policy {{/str}}</span>{{/iscurrent}}
{{#isoptional}}<span class="badge bg-info text-white">{{#str}} policydocoptionalyes, tool_policy {{/str}}</span>{{/isoptional}}
</td>
<td>
{{>tool_policy/user_agreement}}
</td>
<td>{{timeaccepted}}</td>
{{#hasonbehalfagreements}}
<td>{{{acceptedby}}}</td>
<td>{{{note}}}</td>
{{/hasonbehalfagreements}}
<td>
{{#hasarchived}}<a class="showarchived" data-target=".archived{{policyid}}" data-status="hidden" href="#">
<div class="toggleoff" style="display:none">{{#pix}}t/less, moodle, {{#str}}detailedless, moodle{{/str}}{{/pix}}</div>
<div class="toggleon">{{#pix}}t/more, moodle, {{#str}}detailedmore, moodle{{/str}}{{/pix}}</div>
</a>{{/hasarchived}}
</td>
</tr>
{{/agreement}}
{{/versions}}
{{/policies}}
</tbody>
</table>
{{#returnurl}}
<div><a role="button" href="{{returnurl}}" class="btn btn-primary">{{#str}} back {{/str}}</a></div>
{{/returnurl}}
{{#js}}
require(['jquery'], function($) {
$('body').on('click', '.showarchived', function(e) {
e.preventDefault();
var target = $(this).attr('data-target'),
status = $(this).attr('data-status');
if (status === 'hidden') {
$(target).show();
$(this).attr('data-status', 'shown');
$(this).find('.toggleoff').show();
$(this).find('.toggleon').hide();
} else {
$(target).hide();
$(this).attr('data-status', 'hidden');
$(this).find('.toggleon').show();
$(this).find('.toggleoff').hide();
}
});
});
{{/js}}
@@ -0,0 +1,67 @@
{{!
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/>.
}}
{{!
@template tool_policy/acceptances_filter
Template for the unified filter element.
Context variables required for this template:
* action string - The action URL for the form.
* filteroptions - Array of filter options.
* value string - The option value.
* label string - The option label.
* selected boolean - Whether the option is selected
Example context (json):
{
"action": "/admin/tool/policy/acceptances.php",
"filteroptions": [
{
"value": "1",
"label": "Option 1"
},
{
"value": "2",
"label": "Option 2",
"selected": true
},
{
"value": "3",
"label": "Option 3",
"selected": true
},
{
"value": "4",
"label": "Option 4"
}
]
}
}}
<form method="post" action="{{action}}" class="mb-1" role="search">
<label for="unified-filters" class="sr-only">{{#str}}filters , tool_policy{{/str}}</label>
<select name="unified-filters[]" id="unified-filters" multiple="multiple" data-originaloptionsjson="{{originaloptionsjson}}" class="form-autocomplete-original-select">
{{#filteroptions}}
<option value="{{value}}" {{#selected}}selected="selected"{{/selected}}>{{{label}}}</option>
{{/filteroptions}}
</select>
<input type="hidden" name="unified-filter-submitted" value="1">
</form>
{{#js}}
require(['tool_policy/acceptances_filter'], function(Filter) {
Filter.init();
});
{{/js}}
@@ -0,0 +1,94 @@
{{!
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/>.
}}
{{!
@template tool_policy/guestconsent
Template for the guest consent message.
Classes required for JS:
* eupopup
* policyactions
Data attributes required for JS:
-
Context variables required for this template:
* pluginbaseurl
* returnurl - urlencoded URL to return to
* haspolicies - true if there are guest policies; false otherwise
* policies - array of policy documents
Example context (json):
{
"pluginbaseurl": "https://example.com",
"returnurl": "/",
"haspolicies": true,
"policies": [
{
"id": 1,
"name": "Terms &amp; conditions"
}
]
}
}}
{{#haspolicies}}
{{! Only show message if there is some policy related to guests. }}
<div {{!
}}class="eupopup eupopup-container eupopup-container-block eupopup-container-bottom eupopup-block eupopup-style-compact" {{!
}}role="dialog" {{!
}}aria-label="{{# str }} acceptancepolicies, tool_policy {{/str}}"{{!
}}>
</div>
<div class="eupopup-markup d-none">
<div class="eupopup-head"></div>
<div class="eupopup-body">
{{# str }} guestconsentmessage, tool_policy {{/ str }}
<ul>
{{#policies}}
<li>
<a href="{{pluginbaseurl}}/view.php?versionid={{id}}{{#returnurl}}&amp;returnurl={{.}}{{/returnurl}}" {{!
}}data-action="view-guest" data-versionid="{{id}}" data-behalfid="1">
{{{name}}}
</a>
</li>
{{/policies}}
</ul>
</div>
<div class="eupopup-buttons">
<a href="#" class="eupopup-button eupopup-button_1">{{# str }} guestconsent:continue, tool_policy {{/ str }}</a>
</div>
<div class="clearfix"></div>
<a href="#" class="eupopup-closebutton">x</a>
</div>
{{#js}}
require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policyactions'], function($, Popup, ActionsMod) {
// Initialise the guest popup.
$(document).ready(function() {
// Initialize popup.
$(document.body).addClass('eupopup');
if ($(".eupopup").length > 0) {
$(document).euCookieLawPopup().init();
}
// Initialise the JS for the modal window which displays the policy versions.
ActionsMod.init('[data-action="view-guest"]');
});
});
{{/js}}
{{/haspolicies}}
@@ -0,0 +1,148 @@
{{!
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/>.
}}
{{!
@template tool_policy/page_agreedocs
Template for showing to the user the policy docs to agree.
Classes required for JS:
* policyactions
Data attributes required for JS:
-
Context variables required for this template:
* pluginbaseurl
* myurl
* sesskey
* policies - policy array
* behalfuser - If behalfid is defined and valid, full name of the behalf user with a link to his/her profile; null otherwise
Example context (json):
{
"myurl": "/admin/tool/policy/index.php",
"sesskey": "123456",
"behalfuser": "Sam Student",
"policies": [
{
"id": 1,
"name": "Terms &amp; conditions",
"policymodal": "<a href=\"#\">Terms &amp; conditions</a>",
"summary": "Policy <u>summary</u>",
"versionagreed": false,
"versionlangsagreed": "Agreed",
"versionbehalfsagreed": ""
}
]
}
}}
{{#messages}}{{{.}}}{{/messages}}
<form id="agreedocsform" method="post" action="{{myurl}}">
<input type="hidden" name="sesskey" value="{{sesskey}}">
{{#behalfuser}}
<div class="clearfix">
<div class="float-right mb-2">
{{# str }} viewconsentpageforuser, tool_policy, {{{ . }}} {{/ str }}
</div>
</div>
{{/behalfuser}}
<div class="clearfix">
<div class="float-left">
<h2>{{# str }}consentpagetitle, tool_policy{{/ str }}</h2>
</div>
</div>
<div class="clearfix mt-2">
<h3>{{# str }}agreepolicies, tool_policy {{/ str }}</h3>
</div>
<hr>
{{#policies}}
<input value="{{id}}" name="listdoc[]" type="hidden">
<div class="agreedoc-policy clearfix mt-2 mb-1">
<h3>{{{name}}}</h3>
<div class="agreedoc-content">
<div class="agreedoc-summary mb-2">
{{{summary}}}
</div>
<div class="agreedoc-msg">
{{# str }}refertofullpolicytext, tool_policy, {{{policymodal}}} {{/ str }}
</div>
<div class="agreedoc-form mt-1">
{{#optional}}
<div class="agreedoc-radios">
<div class="agreedoc-radios-1">
<label>
<input type="radio" name="status{{id}}" value="1" {{#versionagreed}}checked="{{.}}"{{/versionagreed}}>
{{# str }}iagree, tool_policy, {{{name}}} {{/ str }}
</label>
</div>
<div class="agreedoc-radios-0">
<label>
<input type="radio" name="status{{id}}" value="0" {{#versiondeclined}}checked="{{.}}"{{/versiondeclined}}>
{{# str }}idontagree, tool_policy, {{{name}}} {{/ str }}
</label>
</div>
</div>
{{/optional}}
{{^optional}}
<div class="agreedoc-checkbox">
<label>
<input value="1" name="status{{id}}" {{#versionagreed}}checked="{{.}}"{{/versionagreed}} type="checkbox">
{{# str }}iagree, tool_policy, {{{name}}} {{/ str }}
<i class="icon fa fa-exclamation-circle text-danger fa-fw" title="{{# str }} required {{/ str }}" ></i>
</label>
</div>
<ul class="agreedoc-msg list-unstyled">
{{#versionlangsagreed}}
<li><small>{{{.}}}</small></li>
{{/versionlangsagreed}}
{{#versionbehalfsagreed}}
<li><small>{{{.}}}</small></li>
{{/versionbehalfsagreed}}
</ul>
{{/optional}}
</div>
</div>
</div>
{{/policies}}
{{# str }}somefieldsrequired, form, <i class="icon fa fa-exclamation-circle text-danger fa-fw" title="{{# str }} required {{/ str }}" ></i>{{/ str }}
<hr>
<input type="submit" class="btn btn-primary" name="submit" value="{{#cleanstr}} next {{/cleanstr}}">
{{#cancancel}}
<input type="submit" class="btn btn-secondary" name="cancel" value="{{#cleanstr}} cancel {{/cleanstr}}">
{{/cancancel}}
</form>
{{#js}}
// Initialise the JS for the modal window which displays the policy versions.
require(['tool_policy/policyactions'], function(ActionsMod) {
ActionsMod.init('[data-action="view"]');
});
{{/js}}
@@ -0,0 +1,151 @@
{{!
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/>.
}}
{{!
@template tool_policy/page_managedocs_list
Template for the policy documents management page.
Classes required for JS:
-
Data attributes required for JS:
* data-policy-name
* data-policy-revision
* data-action
Context variables required for this template:
* haspolicies
* policies
Example context (json):
{
"title": "Manage policies",
"backurl": "/",
"pluginbaseurl": "/admin/tool/policy/",
"canviewacceptances": true,
"canmanage": true,
"versions": [
{
"id": 1,
"name": "Terms &amp; conditions",
"typetext": "Site policy",
"audiencetext": "All users",
"statustext": "Active",
"optionaltext": "Optional",
"revision": "1.0",
"timemodified": 1521531208,
"acceptancescounturl": "#",
"acceptancescounttext": "10 out of 1000 (1%)"
},
{
"indented": true,
"name": "Terms &amp; conditions",
"typetext": "Site policy",
"audiencetext": "All users",
"statustext": "Draft",
"optionaltext": "Compulsory",
"revision": "2.0",
"timemodified": 1521531208,
"acceptancescounttext": "N/A"
}
]
}
}}
<h2>{{{title}}}</h2>
{{#backurl}}
<div>
<div class="btn-group">
<a href="{{backurl}}" class="btn btn-secondary">{{#str}} back {{/str}}</a>
</div>
</div>
{{/backurl}}
{{#canaddnew}}
<div>
<div class="btn-group">
<a href="{{pluginbaseurl}}/editpolicydoc.php" class="btn btn-primary">{{#str}} newpolicy, tool_policy {{/str}}</a>
</div>
</div>
{{/canaddnew}}
<table id="tool-policy-managedocs-wrapper" class="generaltable fullwidth listpolicydocuments">
<thead>
<tr>
<th scope="col">{{#str}} policydocname, tool_policy {{/str}}</th>
<th scope="col">{{#str}} status, tool_policy {{/str}}</th>
<th scope="col">{{#str}} policydocrevision, tool_policy {{/str}}</th>
{{#canviewacceptances}}
<th scope="col">{{#str}} usersaccepted, tool_policy {{/str}}</th>
{{/canviewacceptances}}
{{#canmanage}}
<th scope="col"></th>
{{/canmanage}}
</tr>
</thead>
<tbody>
{{#versions}}
<tr data-policy-name="{{{name}}}" data-policy-revision="{{revision}}">
<td>
{{#indented}}
<div style="float:left">
{{#pix}} level, tool_policy {{/pix}}
</div>
{{/indented}}
<div {{#indented}}style="margin-left: 24px" {{/indented}}>
<div>{{{name}}}</div>
<div class="text-muted, muted"><small>{{{typetext}}}, {{{audiencetext}}}, {{{optionaltext}}}</small></div>
</div>
</td>
<td>
{{{statustext}}}
</td>
<td>
{{revision}}
<div class="text-muted, muted">
<small>
<time title="{{#str}} lastmodified, core {{/str}}" datetime="{{#userdate}} {{timemodified}}, %Y-%m-%dT%T%z {{/userdate}}">
{{#userdate}} {{timemodified}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}
</time>
</small>
</div>
</td>
{{#canviewacceptances}}
<td>
{{#acceptancescounturl}}
<a href="{{acceptancescounturl}}">{{acceptancescounttext}}</a>
{{/acceptancescounturl}}
{{^acceptancescounturl}}
{{acceptancescounttext}}
{{/acceptancescounturl}}
</td>
{{/canviewacceptances}}
{{#canmanage}}
<td>
{{#actionmenu}}
{{>core/action_menu}}
{{/actionmenu}}
</td>
{{/canmanage}}
</tr>
{{/versions}}
</tbody>
</table>
{{#js}}
require(['tool_policy/managedocsactions'], function(ManageDocsActions) {
ManageDocsActions.init('tool-policy-managedocs-wrapper');
});
{{/js}}
@@ -0,0 +1,75 @@
{{!
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/>.
}}
{{!
@template tool_policy/page_nopermission
Template for showing to the user the policy docs to agree.
Classes required for JS:
* policyactions
Data attributes required for JS:
-
Context variables required for this template:
* pluginbaseurl
* haspermissionagreedocs - status of the capability of the user to accept policy documents
* messagetitle - Message title to display, with the error.
* messagedesc - Message description to display, with the error.
* supportname - name of the support contact; displayed when the user does not have permission to accept policy documents
* supportemail - email of the support contact; displayed when the user does not have permission to accept policy documents
* behalfuser - If behalfid is defined and valid, full name of the behalf user with a link to his/her profile; null otherwise
* policies - Array with all the links to current policies (for showing them if needed).
Example context (json):
{
"haspermissionagreedocs": false,
"messagetitle": "You don't have permission",
"messagedesc": "Sorry you don't have permission to accept",
"policies": [
"Policy 1",
"Policy 2"
],
"supportname": "Admin",
"supportemail": "moodlesite/user/contactsitesupport.php"
}
}}
{{^haspermissionagreedocs}}
<h3>{{{messagetitle}}}</h3>
<p class="mt-1 mb-1">{{{messagedesc}}}</p>
<div class="policies">
<ul>
{{# policies }}
<li>{{{.}}}</li>
{{/ policies }}
</ul>
</div>
{{#supportemail}}
<p>{{#str}}nopermissiontoagreedocscontact, tool_policy{{/str}}</p>
<p>{{{supportemail}}}</p>
{{/supportemail}}
{{/haspermissionagreedocs}}
{{#js}}
// Initialise the JS for the modal window which displays the policy versions.
require(['tool_policy/policyactions'], function(ActionsMod) {
ActionsMod.init('[data-action="view"]');
});
{{/js}}
@@ -0,0 +1,111 @@
{{!
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/>.
}}
{{!
@template tool_policy/page_viewalldoc
Template for viewing all policy versions, one under another.
Classes required for JS:
-
Data attributes required for JS:
-
Context variables required for this template:
* returnurl - url to the previous page
* policies - policy array
Example context (json):
{
"returnurl": "#",
"policies": [
{
"id": "2",
"name": "Terms &amp; conditions",
"summary": "Policy <u>summary</u>",
"content": "Policy <em>content</em>",
"policytypestr": "Site policy",
"policyaudiencestr": "All users"
},
{
"id": "5",
"name": "Privacy",
"summary": "We keep your information private",
"content": "Very private",
"policytypestr": "Privacy policy",
"policyaudiencestr": "Authenticated users"
}
]
}
}}
{{#returnurl}}
<div class="text-right mb-1">
<a href="{{returnurl}}">{{# str }} backtoprevious, tool_policy {{/ str }}</a>
</div>
{{/returnurl}}
<a id="top"></a>
<div id="policies_index">
<h1>{{# str }} listactivepolicies, tool_policy {{/ str }}</h1>
<table class="table">
<thead>
<tr>
<th scope="col">{{# str }}policydocname, tool_policy {{/ str }}</th>
<th scope="col">{{# str }}policydoctype, tool_policy {{/ str }}</th>
<th scope="col">{{# str }}policydocaudience, tool_policy {{/ str }}</th>
</tr>
</thead>
<tbody>
{{#policies }}
<tr>
<td><a href="#policy-{{id}}">{{{name}}}</a></td>
<td>{{{ policytypestr }}}</td>
<td>{{{ policyaudiencestr }}}</td>
</tr>
{{/policies }}
</tbody>
</table>
</div>
{{^policies }}
{{# str }} noactivepolicies, tool_policy {{/ str }}
{{/policies }}
{{#policies }}
<hr>
<div class="policy_version mb-3">
<div class="clearfix mt-2">
<h2><a id="policy-{{id}}">{{{name}}}</a></h2>
</div>
<div class="policy_document_summary clearfix mb-1">
<h3>{{# str }} policydocsummary, tool_policy {{/ str }}</h3>
{{{summary}}}
</div>
<div class="policy_document_content mt-2">
<h3>{{# str }} policydoccontent, tool_policy {{/ str }}</h3>
{{{content}}}
</div>
<div class="float-right">
<a href="#top">
{{# str }} backtotop, tool_policy {{/ str }}
<i class="icon text-primary fa fa-caret-up" title="{{# str }} backtotop, tool_policy {{/ str }}" ></i>
</a>
</div>
</div>
{{/policies }}
@@ -0,0 +1,105 @@
{{!
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/>.
}}
{{!
@template tool_policy/page_viewdoc
Template for viewing a policy version document.
Classes required for JS:
-
Data attributes required for JS:
-
Context variables required for this template:
* policy - version document object.
Example context (json):
{
"numpolicy": 1,
"totalpolicies": 2,
"policy": {
"name": "Terms &amp; conditions",
"summary": "Policy <u>summary</u>",
"content": "Policy <em>content</em>"
},
"returnurl": "#",
"editurl": "#",
"accepturl": "#"
}
}}
<a id="top"></a>
<div class="clearfix">
<div class="float-left">
<h2>{{{policy.name}}}</h2>
</div>
{{# numpolicy }}
<div class="float-right">
{{# str }} steppolicies, tool_policy,
{ "numpolicy": {{# quote }}{{numpolicy}}{{/quote }}, "totalpolicies": {{# quote }}{{totalpolicies}}{{/ quote }} }
{{/ str }}
</div>
{{/ numpolicy }}
</div>
{{# numpolicy }}
<div class="clearfix mt-2">
<h3>{{# str }}readpolicy, tool_policy, {{{policy.name}}} {{/ str }}</h3>
</div>
{{/ numpolicy }}
<hr>
<div class="policy_document mt-1">
<div class="policy_document_summary clearfix mb-1">
{{{policy.summary}}}
</div>
<div class="policy_document_content mt-2">
{{{policy.content}}}
</div>
</div>
<hr>
<div class="policy_buttons">
{{#returnurl}}
{{# numpolicy }}
<a role="button" href="{{returnurl}}" class="btn btn-primary">{{#str}} next {{/str}}</a>
{{/ numpolicy }}
{{^ numpolicy }}
<a role="button" href="{{returnurl}}" class="btn btn-primary">{{#str}} back {{/str}}</a>
{{/ numpolicy }}
{{/returnurl}}
{{#editurl}}
<a role="button" href="{{editurl}}" class="btn">{{#str}} edit {{/str}}</a>
{{/editurl}}
{{#accepturl}}
<a role="button" href="{{accepturl}}" class="btn btn-primary">{{#str}} iagree, tool_policy, {{{policy.name}}} {{/str}}</a>
{{/accepturl}}
{{#declineurl}}
<a role="button" href="{{declineurl}}" class="btn btn-link">{{#str}} idontagree, tool_policy, {{{policy.name}}} {{/str}}</a>
{{/declineurl}}
<div class="float-right">
<a href="#top">
{{# str }} backtotop, tool_policy {{/ str }}
<i class="icon text-primary fa fa-caret-up" title="{{# str }} backtotop, tool_policy {{/ str }}" ></i>
</a>
</div>
</div>
@@ -0,0 +1,55 @@
{{!
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/>.
}}
{{!
@template tool_policy/user_agreement
Template for the user agreement icon.
Classes required for JS:
-
Data attributes required for JS:
-
Context variables required for this template:
* statusicon
* statustext
* actions
- url
- data
- text
- title
Example context (json):
{
"statusicon": "accepted",
"statustext": "Accepted",
"actions": {
"url": "/admin/tool/policy/accept.php?param=value",
"data": "acceptmodal",
"text": "Withdraw",
"title": "Withdraw acceptance of Site policy"
}
}
}}
<span class="tool_policy-user_agreement-icon">{{#pix}} {{statusicon}}, tool_policy{{/pix}}</span>
<span class="tool_policy-user_agreement-status">{{statustext}}</span>
<span class="tool_policy-user_agreement-actions">
{{#actions}}
<a href="{{url}}" title="{{title}}" data-action="{{data}}"><small>{{text}}</small></a>
{{/actions}}
</span>
+810
View File
@@ -0,0 +1,810 @@
<?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/>.
namespace tool_policy;
use tool_policy\test\helper;
/**
* Unit tests for the {@link \tool_policy\api} class.
*
* @package tool_policy
* @category test
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class api_test extends \advanced_testcase {
/**
* Test the common operations with a policy document and its versions.
*/
public function test_policy_document_life_cycle(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Prepare the form data for adding a new policy document.
$formdata = api::form_policydoc_data(new policy_version(0));
$this->assertObjectHasProperty('name', $formdata);
$this->assertArrayHasKey('text', $formdata->summary_editor);
$this->assertArrayHasKey('format', $formdata->content_editor);
// Save the form.
$formdata->name = 'Test terms & conditions';
$formdata->type = policy_version::TYPE_OTHER;
$policy = api::form_policydoc_add($formdata);
$record = $policy->to_record();
$this->assertNotEmpty($record->id);
$this->assertNotEmpty($record->policyid);
$this->assertNotEmpty($record->timecreated);
$this->assertNotEmpty($record->timemodified);
$this->assertNotNull($record->name);
$this->assertNotNull($record->summary);
$this->assertNotNull($record->summaryformat);
$this->assertNotNull($record->content);
$this->assertNotNull($record->contentformat);
// Update the policy document version.
$formdata = api::form_policydoc_data($policy);
$formdata->revision = '*** Unit test ***';
$formdata->summary_editor['text'] = '__Just a summary__';
$formdata->summary_editor['format'] = FORMAT_MARKDOWN;
$formdata->content_editor['text'] = '### Just a test ###';
$formdata->content_editor['format'] = FORMAT_MARKDOWN;
$updated = api::form_policydoc_update_overwrite($formdata);
$this->assertEquals($policy->get('id'), $updated->get('id'));
$this->assertEquals($policy->get('policyid'), $updated->get('policyid'));
// Save form as a new version.
$formdata = api::form_policydoc_data($policy);
$formdata->name = 'New terms & conditions';
$formdata->revision = '*** Unit test 2 ***';
$formdata->summary_editor['text'] = '<strong>Yet another summary</strong>';
$formdata->summary_editor['format'] = FORMAT_MOODLE;
$formdata->content_editor['text'] = '<h3>Yet another test</h3>';
$formdata->content_editor['format'] = FORMAT_HTML;
$new = api::form_policydoc_update_new($formdata);
$this->assertNotEquals($policy->get('id'), $new->get('id'));
$this->assertEquals($policy->get('policyid'), $new->get('policyid'));
// Add yet another policy document.
$formdata = api::form_policydoc_data(new policy_version(0));
$formdata->name = 'Privacy terms';
$formdata->type = policy_version::TYPE_PRIVACY;
$another = api::form_policydoc_add($formdata);
// Get the list of all policies and their versions.
$docs = api::list_policies();
$this->assertEquals(2, count($docs));
// Get just one policy and all its versions.
$docs = api::list_policies($another->get('policyid'));
$this->assertEquals(1, count($docs));
// Activate a policy.
$this->assertEquals(0, count(api::list_current_versions()));
api::make_current($updated->get('id'));
$current = api::list_current_versions();
$this->assertEquals(1, count($current));
$first = reset($current);
$this->assertEquals('Test terms &amp; conditions', $first->name);
// Activate another policy version.
api::make_current($new->get('id'));
$current = api::list_current_versions();
$this->assertEquals(1, count($current));
$first = reset($current);
$this->assertEquals('New terms &amp; conditions', $first->name);
// Inactivate the policy.
api::inactivate($new->get('policyid'));
$this->assertEmpty(api::list_current_versions());
$archived = api::get_policy_version($new->get('id'));
$this->assertEquals(policy_version::STATUS_ARCHIVED, $archived->status);
// Create a new draft from an archived version.
$draft = api::revert_to_draft($archived->id);
$draft = api::get_policy_version($draft->get('id'));
$archived = api::get_policy_version($archived->id);
$this->assertEmpty(api::list_current_versions());
$this->assertNotEquals($draft->id, $archived->id);
$this->assertEquals(policy_version::STATUS_DRAFT, $draft->status);
$this->assertEquals(policy_version::STATUS_ARCHIVED, $archived->status);
// An active policy can't be set to draft.
api::make_current($draft->id);
$this->expectException('coding_exception');
$this->expectExceptionMessage('Version not found or is not archived');
api::revert_to_draft($draft->id);
}
/**
* Test changing the sort order of the policy documents.
*/
public function test_policy_sortorder(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$formdata = api::form_policydoc_data(new policy_version(0));
$formdata->name = 'Policy1';
$formdata->summary_editor = ['text' => 'P1 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'P1 content', 'format' => FORMAT_HTML, 'itemid' => 0];
$policy1 = api::form_policydoc_add($formdata);
$policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
$formdata = api::form_policydoc_data(new policy_version(0));
$formdata->name = 'Policy2';
$formdata->summary_editor = ['text' => 'P2 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'P2 content', 'format' => FORMAT_HTML, 'itemid' => 0];
$policy2 = api::form_policydoc_add($formdata);
$policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
$this->assertTrue($policy1sortorder < $policy2sortorder);
$formdata = api::form_policydoc_data(new policy_version(0));
$formdata->name = 'Policy3';
$formdata->summary_editor = ['text' => 'P3 summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'P3 content', 'format' => FORMAT_HTML, 'itemid' => 0];
$policy3 = api::form_policydoc_add($formdata);
$policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
$this->assertTrue($policy1sortorder < $policy2sortorder);
$this->assertTrue($policy2sortorder < $policy3sortorder);
api::move_up($policy3->get('policyid'));
$policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
$policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
$policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
$this->assertTrue($policy1sortorder < $policy3sortorder);
$this->assertTrue($policy3sortorder < $policy2sortorder);
api::move_down($policy1->get('policyid'));
$policy1sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy1->get('policyid')]);
$policy2sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy2->get('policyid')]);
$policy3sortorder = $DB->get_field('tool_policy', 'sortorder', ['id' => $policy3->get('policyid')]);
$this->assertTrue($policy3sortorder < $policy1sortorder);
$this->assertTrue($policy1sortorder < $policy2sortorder);
$orderedlist = [];
foreach (api::list_policies() as $policy) {
$orderedlist[] = $policy->id;
}
$this->assertEquals([$policy3->get('policyid'), $policy1->get('policyid'), $policy2->get('policyid')], $orderedlist);
}
/**
* Test that list of policies can be filtered by audience
*/
public function test_list_policies_audience(): void {
$this->resetAfterTest();
$this->setAdminUser();
$policy1 = helper::add_policy(['audience' => policy_version::AUDIENCE_LOGGEDIN]);
$policy2 = helper::add_policy(['audience' => policy_version::AUDIENCE_GUESTS]);
$policy3 = helper::add_policy();
api::make_current($policy1->get('id'));
api::make_current($policy2->get('id'));
api::make_current($policy3->get('id'));
$list = array_map(function ($version) {
return $version->policyid;
}, api::list_current_versions());
$this->assertEquals([$policy1->get('policyid'), $policy2->get('policyid'), $policy3->get('policyid')],
array_values($list));
$ids = api::get_current_versions_ids();
$this->assertEquals([$policy1->get('policyid') => $policy1->get('id'),
$policy2->get('policyid') => $policy2->get('id'),
$policy3->get('policyid') => $policy3->get('id')], $ids);
$list = array_map(function ($version) {
return $version->policyid;
}, api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN));
$this->assertEquals([$policy1->get('policyid'), $policy3->get('policyid')], array_values($list));
$ids = api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN);
$this->assertEquals([$policy1->get('policyid') => $policy1->get('id'),
$policy3->get('policyid') => $policy3->get('id')], $ids);
$list = array_map(function ($version) {
return $version->policyid;
}, api::list_current_versions(policy_version::AUDIENCE_GUESTS));
$this->assertEquals([$policy2->get('policyid'), $policy3->get('policyid')], array_values($list));
$ids = api::get_current_versions_ids(policy_version::AUDIENCE_GUESTS);
$this->assertEquals([$policy2->get('policyid') => $policy2->get('id'),
$policy3->get('policyid') => $policy3->get('id')], $ids);
}
/**
* Test behaviour of the {@link api::can_user_view_policy_version()} method.
*/
public function test_can_user_view_policy_version(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$child = $this->getDataGenerator()->create_user();
$parent = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->create_user();
$officer = $this->getDataGenerator()->create_user();
$manager = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$childcontext = \context_user::instance($child->id);
$roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
$roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
$rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
role_assign($roleminorid, $child->id, $syscontext->id);
// Becoming a parent is easy. Being a good one is difficult.
role_assign($roleparentid, $parent->id, $childcontext->id);
role_assign($roleofficerid, $officer->id, $syscontext->id);
role_assign($rolemanagerid, $manager->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
// Prepare a policy document with some versions.
list($policy1, $policy2, $policy3) = helper::create_versions(3);
// Normally users do not have access to policy drafts.
$this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
$this->assertFalse(api::can_user_view_policy_version($policy2, null, $parent->id));
$this->assertFalse(api::can_user_view_policy_version($policy3, null, $CFG->siteguest));
// Officers and managers have access even to drafts.
$this->assertTrue(api::can_user_view_policy_version($policy1, null, $officer->id));
$this->assertTrue(api::can_user_view_policy_version($policy3, null, $manager->id));
// Current versions are public so that users can decide whether to even register on such a site.
api::make_current($policy2->id);
$policy1 = api::get_policy_version($policy1->id);
$policy2 = api::get_policy_version($policy2->id);
$policy3 = api::get_policy_version($policy3->id);
$this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
$this->assertTrue(api::can_user_view_policy_version($policy2, null, $child->id));
$this->assertTrue(api::can_user_view_policy_version($policy2, null, $CFG->siteguest));
$this->assertFalse(api::can_user_view_policy_version($policy3, null, $child->id));
// Let the parent accept the policy on behalf of her child.
$this->setUser($parent);
api::accept_policies($policy2->id, $child->id);
// Release a new version of the policy.
api::make_current($policy3->id);
$policy1 = api::get_policy_version($policy1->id);
$policy2 = api::get_policy_version($policy2->id);
$policy3 = api::get_policy_version($policy3->id);
api::get_user_minors($parent->id);
// They should now have access to the archived version (because they agreed) and the current one.
$this->assertFalse(api::can_user_view_policy_version($policy1, null, $child->id));
$this->assertFalse(api::can_user_view_policy_version($policy1, null, $parent->id));
$this->assertTrue(api::can_user_view_policy_version($policy2, null, $child->id));
$this->assertTrue(api::can_user_view_policy_version($policy2, null, $parent->id));
$this->assertTrue(api::can_user_view_policy_version($policy3, null, $child->id));
$this->assertTrue(api::can_user_view_policy_version($policy3, null, $parent->id));
}
/**
* Test behaviour of the {@link api::can_accept_policies()} method.
*/
public function test_can_accept_policies(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$child = $this->getDataGenerator()->create_user();
$parent = $this->getDataGenerator()->create_user();
$officer = $this->getDataGenerator()->create_user();
$manager = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$childcontext = \context_user::instance($child->id);
$roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
$roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
$rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
role_assign($roleminorid, $child->id, $syscontext->id);
role_assign($roleparentid, $parent->id, $childcontext->id);
role_assign($roleofficerid, $officer->id, $syscontext->id);
role_assign($rolemanagerid, $manager->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
$policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
$policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
$policy3 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
$policy4 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
$mixed = [$policy1->id, $policy2->id, $policy3->id, $policy4->id];
$compulsory = [$policy1->id, $policy2->id];
$optional = [$policy3->id, $policy4->id];
// Normally users can accept all policies.
$this->setUser($user);
$this->assertTrue(api::can_accept_policies($mixed));
$this->assertTrue(api::can_accept_policies($compulsory));
$this->assertTrue(api::can_accept_policies($optional));
// Digital minors can be set to not be able to accept policies themselves.
$this->setUser($child);
$this->assertFalse(api::can_accept_policies($mixed));
$this->assertFalse(api::can_accept_policies($compulsory));
$this->assertFalse(api::can_accept_policies($optional));
// The parent can accept optional policies on child's behalf.
$this->setUser($parent);
$this->assertTrue(api::can_accept_policies($mixed, $child->id));
$this->assertTrue(api::can_accept_policies($compulsory, $child->id));
$this->assertTrue(api::can_accept_policies($optional, $child->id));
// Officers and managers can accept on other user's behalf.
$this->setUser($officer);
$this->assertTrue(api::can_accept_policies($mixed, $parent->id));
$this->assertTrue(api::can_accept_policies($compulsory, $parent->id));
$this->assertTrue(api::can_accept_policies($optional, $parent->id));
$this->setUser($manager);
$this->assertTrue(api::can_accept_policies($mixed, $parent->id));
$this->assertTrue(api::can_accept_policies($compulsory, $parent->id));
$this->assertTrue(api::can_accept_policies($optional, $parent->id));
}
/**
* Test behaviour of the {@link api::can_decline_policies()} method.
*/
public function test_can_decline_policies(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$child = $this->getDataGenerator()->create_user();
$parent = $this->getDataGenerator()->create_user();
$officer = $this->getDataGenerator()->create_user();
$manager = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$childcontext = \context_user::instance($child->id);
$roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
$roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
$rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
role_assign($roleminorid, $child->id, $syscontext->id);
role_assign($roleparentid, $parent->id, $childcontext->id);
role_assign($roleofficerid, $officer->id, $syscontext->id);
role_assign($rolemanagerid, $manager->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
$policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
$policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
$policy3 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
$policy4 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
$mixed = [$policy1->id, $policy2->id, $policy3->id, $policy4->id];
$compulsory = [$policy1->id, $policy2->id];
$optional = [$policy3->id, $policy4->id];
// Normally users can decline only optional policies.
$this->setUser($user);
$this->assertFalse(api::can_decline_policies($mixed));
$this->assertFalse(api::can_decline_policies($compulsory));
$this->assertTrue(api::can_decline_policies($optional));
// If they can't accept them, they can't decline them too.
$this->setUser($child);
$this->assertFalse(api::can_decline_policies($mixed));
$this->assertFalse(api::can_decline_policies($compulsory));
$this->assertFalse(api::can_decline_policies($optional));
// The parent can decline optional policies on child's behalf.
$this->setUser($parent);
$this->assertFalse(api::can_decline_policies($mixed, $child->id));
$this->assertFalse(api::can_decline_policies($compulsory, $child->id));
$this->assertTrue(api::can_decline_policies($optional, $child->id));
// Even officers or managers cannot decline compulsory policies.
$this->setUser($officer);
$this->assertFalse(api::can_decline_policies($mixed));
$this->assertFalse(api::can_decline_policies($compulsory));
$this->assertTrue(api::can_decline_policies($optional));
$this->assertFalse(api::can_decline_policies($mixed, $child->id));
$this->assertFalse(api::can_decline_policies($compulsory, $child->id));
$this->assertTrue(api::can_decline_policies($optional, $child->id));
$this->setUser($manager);
$this->assertFalse(api::can_decline_policies($mixed));
$this->assertFalse(api::can_decline_policies($compulsory));
$this->assertTrue(api::can_decline_policies($optional));
$this->assertFalse(api::can_decline_policies($mixed, $child->id));
$this->assertFalse(api::can_decline_policies($compulsory, $child->id));
$this->assertTrue(api::can_decline_policies($optional, $child->id));
}
/**
* Test behaviour of the {@link api::can_revoke_policies()} method.
*/
public function test_can_revoke_policies(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$child = $this->getDataGenerator()->create_user();
$parent = $this->getDataGenerator()->create_user();
$officer = $this->getDataGenerator()->create_user();
$manager = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$childcontext = \context_user::instance($child->id);
$roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
$roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
$rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
role_assign($roleminorid, $child->id, $syscontext->id);
// Becoming a parent is easy. Being a good one is difficult.
role_assign($roleparentid, $parent->id, $childcontext->id);
role_assign($roleofficerid, $officer->id, $syscontext->id);
role_assign($rolemanagerid, $manager->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
$policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
$policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
$versionids = [$policy1->id, $policy2->id];
// Guests cannot revoke anything.
$this->setGuestUser();
$this->assertFalse(api::can_revoke_policies($versionids));
// Normally users do not have access to revoke policies.
$this->setUser($user);
$this->assertFalse(api::can_revoke_policies($versionids, $user->id));
$this->setUser($child);
$this->assertFalse(api::can_revoke_policies($versionids, $child->id));
// Optional policies can be revoked if the user can accept them.
$this->setUser($user);
$this->assertTrue(api::can_revoke_policies([$policy2->id]));
$this->assertTrue(api::can_revoke_policies([$policy2->id], $user->id));
$this->setUser($child);
$this->assertFalse(api::can_revoke_policies([$policy2->id]));
$this->assertFalse(api::can_revoke_policies([$policy2->id], $child->id));
// The parent can revoke the policy on behalf of her child (but not her own policies, unless they are optional).
$this->setUser($parent);
$this->assertFalse(api::can_revoke_policies($versionids, $parent->id));
$this->assertTrue(api::can_revoke_policies($versionids, $child->id));
$this->assertTrue(api::can_revoke_policies([$policy2->id]));
$this->assertTrue(api::can_revoke_policies([$policy2->id], $child->id));
// Officers and managers can revoke everything.
$this->setUser($officer);
$this->assertTrue(api::can_revoke_policies($versionids, $officer->id));
$this->assertTrue(api::can_revoke_policies($versionids, $child->id));
$this->assertTrue(api::can_revoke_policies($versionids, $parent->id));
$this->assertTrue(api::can_revoke_policies($versionids, $manager->id));
$this->setUser($manager);
$this->assertTrue(api::can_revoke_policies($versionids, $manager->id));
$this->assertTrue(api::can_revoke_policies($versionids, $child->id));
$this->assertTrue(api::can_revoke_policies($versionids, $parent->id));
$this->assertTrue(api::can_revoke_policies($versionids, $officer->id));
}
/**
* Test {@link api::fix_revision_values()} behaviour.
*/
public function test_fix_revision_values(): void {
$this->resetAfterTest();
$this->setAdminUser();
$versions = [
(object) ['id' => 80, 'timecreated' => mktime(1, 1, 1, 12, 28, 2018), 'revision' => '', 'e' => '28 December 2018'],
(object) ['id' => 70, 'timecreated' => mktime(1, 1, 1, 12, 27, 2018), 'revision' => '', 'e' => '27 December 2018 - v2'],
(object) ['id' => 60, 'timecreated' => mktime(1, 1, 1, 12, 27, 2018), 'revision' => '', 'e' => '27 December 2018 - v1'],
(object) ['id' => 50, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '0', 'e' => '0'],
(object) ['id' => 40, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '1.1', 'e' => '1.1 - v2'],
(object) ['id' => 30, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '1.1', 'e' => '1.1 - v1'],
(object) ['id' => 20, 'timecreated' => mktime(0, 0, 0, 12, 26, 2018), 'revision' => '', 'e' => '26 December 2018'],
(object) ['id' => 10, 'timecreated' => mktime(17, 57, 00, 12, 25, 2018), 'revision' => '1.0', 'e' => '1.0'],
];
api::fix_revision_values($versions);
foreach ($versions as $version) {
$this->assertSame($version->revision, $version->e);
}
}
/**
* Test that accepting policy updates 'policyagreed'
*/
public function test_accept_policies(): void {
global $DB, $USER;
$this->resetAfterTest();
$this->setAdminUser();
$policy1 = helper::add_policy()->to_record();
api::make_current($policy1->id);
$policy2 = helper::add_policy()->to_record();
api::make_current($policy2->id);
$policy3 = helper::add_policy(['optional' => true])->to_record();
api::make_current($policy3->id);
// Accept policy on behalf of somebody else.
$user1 = $this->getDataGenerator()->create_user();
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Accepting just compulsory policies is not enough, we want to hear explicitly about the optional one, too.
api::accept_policies([$policy1->id, $policy2->id], $user1->id);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Optional policy does not need to be accepted, but it must be answered explicitly.
api::decline_policies([$policy3->id], $user1->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Revoke previous agreement to a compulsory policy.
api::revoke_acceptance($policy1->id, $user1->id);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Accept policies for oneself.
$user2 = $this->getDataGenerator()->create_user();
$this->setUser($user2);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
api::accept_policies([$policy1->id]);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
api::accept_policies([$policy2->id]);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
api::decline_policies([$policy3->id]);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
api::accept_policies([$policy3->id]);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
// Ensure policies are always accepted when all are responded regardless the $USER->policyagreed value.
$USER->policyagreed = 1;
$DB->set_field('user', 'policyagreed', 0, ['id' => $user2->id]);
api::accept_policies([$policy3->id]);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user2->id]));
}
/**
* Test that activating a new policy resets everybody's policyagreed flag in the database.
*/
public function test_reset_policyagreed(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$user1 = $this->getDataGenerator()->create_user();
// Introducing a new policy.
list($policy1v1, $policy1v2) = helper::create_versions(2);
api::make_current($policy1v1->id);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
api::accept_policies([$policy1v1->id], $user1->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Introducing another policy.
$policy2v1 = helper::add_policy()->to_record();
api::make_current($policy2v1->id);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
api::accept_policies([$policy2v1->id], $user1->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Updating an existing policy (major update).
api::make_current($policy1v2->id);
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
api::accept_policies([$policy1v2->id], $user1->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Do not touch the flag if there is no new version (e.g. a minor update).
api::make_current($policy2v1->id);
api::make_current($policy1v2->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Do not touch the flag if inactivating a policy.
api::inactivate($policy1v2->policyid);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
// Do not touch the flag if setting to draft a policy.
api::revert_to_draft($policy1v2->id);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
}
/**
* Test behaviour of the {@link api::get_user_minors()} method.
*/
public function test_get_user_minors(): void {
$this->resetAfterTest();
// A mother having two children, each child having own father.
$mother1 = $this->getDataGenerator()->create_user();
$father1 = $this->getDataGenerator()->create_user();
$father2 = $this->getDataGenerator()->create_user();
$child1 = $this->getDataGenerator()->create_user();
$child2 = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$child1context = \context_user::instance($child1->id);
$child2context = \context_user::instance($child2->id);
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
role_assign($roleparentid, $mother1->id, $child1context->id);
role_assign($roleparentid, $mother1->id, $child2context->id);
role_assign($roleparentid, $father1->id, $child1context->id);
role_assign($roleparentid, $father2->id, $child2context->id);
accesslib_clear_all_caches_for_unit_testing();
$mother1minors = api::get_user_minors($mother1->id);
$this->assertEquals(2, count($mother1minors));
$father1minors = api::get_user_minors($father1->id);
$this->assertEquals(1, count($father1minors));
$this->assertEquals($child1->id, $father1minors[$child1->id]->id);
$father2minors = api::get_user_minors($father2->id);
$this->assertEquals(1, count($father2minors));
$this->assertEquals($child2->id, $father2minors[$child2->id]->id);
$this->assertEmpty(api::get_user_minors($child1->id));
$this->assertEmpty(api::get_user_minors($child2->id));
$extradata = api::get_user_minors($mother1->id, ['policyagreed', 'deleted']);
$this->assertTrue(property_exists($extradata[$child1->id], 'policyagreed'));
$this->assertTrue(property_exists($extradata[$child1->id], 'deleted'));
$this->assertTrue(property_exists($extradata[$child2->id], 'policyagreed'));
$this->assertTrue(property_exists($extradata[$child2->id], 'deleted'));
}
/**
* Test behaviour of the {@link api::create_acceptances_user_created()} method.
*/
public function test_create_acceptances_user_created(): void {
global $CFG, $DB;
$this->resetAfterTest();
$this->setAdminUser();
$CFG->sitepolicyhandler = 'tool_policy';
$policy = helper::add_policy()->to_record();
api::make_current($policy->id);
// User has not accepted any policies.
$user1 = $this->getDataGenerator()->create_user();
\core\event\user_created::create_from_userid($user1->id)->trigger();
$this->assertEquals(0, $DB->count_records('tool_policy_acceptances',
['userid' => $user1->id, 'policyversionid' => $policy->id]));
// User has accepted policies.
$user2 = $this->getDataGenerator()->create_user();
$DB->set_field('user', 'policyagreed', 1, ['id' => $user2->id]);
\core\event\user_created::create_from_userid($user2->id)->trigger();
$this->assertEquals(1, $DB->count_records('tool_policy_acceptances',
['userid' => $user2->id, 'policyversionid' => $policy->id]));
}
/**
* Test that user can login if sitepolicyhandler is set but there are no policies.
*/
public function test_login_with_handler_without_policies(): void {
global $CFG;
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$CFG->sitepolicyhandler = 'tool_policy';
require_login(null, false, null, false, true);
}
/**
* Test the three-state logic of the value returned by {@link api::is_user_version_accepted()}.
*/
public function test_is_user_version_accepted(): void {
$preloadedacceptances = [
4 => (object) [
'policyversionid' => 4,
'mainuserid' => 13,
'status' => 1,
],
6 => (object) [
'policyversionid' => 6,
'mainuserid' => 13,
'status' => 0,
],
];
$this->assertTrue(api::is_user_version_accepted(13, 4, $preloadedacceptances));
$this->assertFalse(api::is_user_version_accepted(13, 6, $preloadedacceptances));
$this->assertNull(api::is_user_version_accepted(13, 5, $preloadedacceptances));
}
/**
* Test the functionality of {@link api::get_agreement_optional()}.
*/
public function test_get_agreement_optional(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
api::make_current($policy1->id);
$policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
api::make_current($policy2->id);
$this->assertEquals(api::get_agreement_optional($policy1->id), policy_version::AGREEMENT_OPTIONAL);
$this->assertEquals(api::get_agreement_optional($policy2->id), policy_version::AGREEMENT_COMPULSORY);
}
}
@@ -0,0 +1,306 @@
@tool @tool_policy
Feature: Viewing acceptances reports and accepting on behalf of other users
In order to manage user acceptances
As a manager
I need to be able to view acceptances and accept on behalf of other users
Background:
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
# This is required for now to prevent the overflow region affecting the action menus.
And I change window size to "large"
And the following policies exist:
| Name | Revision | Content | Summary | Status |
| This site policy | | full text2 | short text2 | active |
| This privacy policy | | full text3 | short text3 | draft |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | one@example.com |
| user2 | User | Two | two@example.com |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
And the following "courses" exist:
| fullname | shortname |
| Course1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| user1 | C1 | student |
| user2 | C1 | student |
Scenario: View acceptances made by users on their own, single policy
When I log in as "user1"
Then I should see "This site policy"
And I should not see "Course overview"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Calendar"
And I log out
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And "Accepted" "text" should exist in the "User One" "table_row"
And "Accepted" "text" should exist in the "Max Manager" "table_row"
And "Pending" "text" should exist in the "User Two" "table_row"
Scenario: Agree on behalf of another user as a manager, single policy, javascript off
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
When I log in as "manager"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
And I click on "Accept This site policy" "link" in the "User One" "table_row"
Then I should see "Accepting policy"
And I should see "User One"
And I should see "This site policy"
And I should see "I acknowledge that I have received a request to give consent on behalf of the above user(s)."
And I set the field "Remarks" to "Consent received from a parent"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Max Manager" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
And "Pending" "text" should exist in the "User Two" "table_row"
@javascript
Scenario: Agree on behalf of another user as a manager, single policy, javascript on
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
When I log in as "manager"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Calendar"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
And I click on "Accept This site policy" "link" in the "User One" "table_row"
Then I should see "Give consent"
And I should see "User One"
And I should see "This site policy"
And I should see "I acknowledge that I have received a request to give consent on behalf of the above user(s)."
And I set the field "Remarks" to "Consent received from a parent"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Max Manager" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
And "Pending" "text" should exist in the "User Two" "table_row"
Scenario: View acceptances made by users on their own, multiple policies
Given I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
And I press "Continue"
And I log out
When I log in as "user1"
Then I should see "This site policy"
And I press "Next"
And I should see "This privacy policy"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I should see "Calendar"
And I log out
And I log in as "manager"
And I press "Next"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And "Accepted" "text" should exist in the "User One" "table_row"
And "Pending" "text" should not exist in the "User One" "table_row"
And "Accepted" "text" should exist in the "Max Manager" "table_row"
And "Pending" "text" should exist in the "User Two" "table_row"
And "Accepted" "text" should not exist in the "User Two" "table_row"
And I click on "Details" "link" in the "User One" "table_row"
And "Accepted" "text" should exist in the "This site policy" "table_row"
And "Accepted" "text" should exist in the "This privacy policy" "table_row"
And I am on site homepage
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And I click on "Details" "link" in the "User Two" "table_row"
And "Pending" "text" should exist in the "This site policy" "table_row"
And "Pending" "text" should exist in the "This privacy policy" "table_row"
Scenario: Agree on behalf of another user as a manager, multiple policies, javascript off
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
And I press "Continue"
And I log out
When I log in as "manager"
And I press "Next"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And I click on "Accept This site policy" "link" in the "User One" "table_row"
Then I should see "Accepting policy"
And I should see "User One"
And I should see "This site policy"
And I should see "I acknowledge that I have received a request to give consent on behalf of the above user(s)."
And I set the field "Remarks" to "Consent received from a parent"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Pending" "text" should exist in the "User One" "table_row"
And I click on "Details" "link" in the "User One" "table_row"
And "Accepted on user's behalf" "text" should exist in the "This site policy" "table_row"
And "Max Manager" "link" should exist in the "This site policy" "table_row"
And "Consent received from a parent" "text" should exist in the "This site policy" "table_row"
And "Pending" "text" should exist in the "This privacy policy" "table_row"
@javascript
Scenario: Agree on behalf of another user as a manager, multiple policies, javascript on
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Actions" "link_or_button" in the "This privacy policy" "table_row"
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
And I wait until "Activate" "button" exists
And I press "Activate"
And I log out
When I log in as "manager"
And I press "Next"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And I click on "Accept This site policy" "link" in the "User One" "table_row"
Then I should see "Give consent"
And I should see "User One"
And I should see "This site policy"
And I should see "I acknowledge that I have received a request to give consent on behalf of the above user(s)."
And I set the field "Remarks" to "Consent received from a parent"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Pending" "text" should exist in the "User One" "table_row"
And I click on "Details" "link" in the "User One" "table_row"
And "Accepted on user's behalf" "text" should exist in the "This site policy" "table_row"
And "Max Manager" "link" should exist in the "This site policy" "table_row"
And "Consent received from a parent" "text" should exist in the "This site policy" "table_row"
And "Pending" "text" should exist in the "This privacy policy" "table_row"
Scenario: Policies and agreements profile link visible for current user
Given I log in as "user1"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
When I follow "Profile" in the user menu
# User can see his own agreements link in the profile.
Then I should see "Policies and agreements"
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "This site policy" "table_row"
# User can't see agreements link in other user profiles.
And I am on "Course1" course homepage
And I navigate to course participants
And I follow "User Two"
And I should not see "Policies and agreements"
Scenario: Policies and agreements profile link visible also for users who can access on behalf of others
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
# User can see agreements link in other user profiles because has the capability for accepting on behalf of them.
When I am on "Course1" course homepage
And I navigate to course participants
And I follow "User Two"
Then I should see "Policies and agreements"
Scenario: Agree on behalf of another user as an admin who is logged in as a manager
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
When I log in as "manager"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I log out
And I am on the "manager" "user > profile" page logged in as "admin"
And I follow "Log in as"
And I press "Continue"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
And I click on "Accept This site policy" "link" in the "User One" "table_row"
Then I should see "Accepting policy"
And I should see "User One"
And I should see "This site policy"
And I should see "I acknowledge that I have received a request to give consent on behalf of the above user(s)."
And I set the field "Remarks" to "Consent received from a parent"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Max Manager" "link" should not exist in the "User One" "table_row"
And "Admin User" "link" should exist in the "User One" "table_row"
And "Consent received from a parent" "text" should exist in the "User One" "table_row"
And "Pending" "text" should exist in the "User Two" "table_row"
@javascript
Scenario: Bulk agree on behalf of another users as a manager, multiple policies, javascript on
Given the following "role capability" exists:
| role | manager |
| tool/policy:acceptbehalf | allow |
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Actions" "link_or_button" in the "This privacy policy" "table_row"
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
And I wait until "Activate" "button" exists
And I press "Activate"
And I log out
When I log in as "manager"
And I press "Next"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > User agreements" in site administration
And I click on "Select" "checkbox" in the "User One" "table_row"
And I press "Consent"
And I should see "Accepting policy"
And I should see "One"
And I click on "Cancel" "button" in the "Accepting policy" "dialogue"
And I should not see "Accepting policy"
And I click on "Select" "checkbox" in the "User Two" "table_row"
And I press "Consent"
And I should see "Accepting policy"
And I should see "User One, User Two"
When I press "Give consent"
Then "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Accepted on user's behalf" "text" should exist in the "User Two" "table_row"
Scenario: View acceptances made by users on their own after inactivating a policy
Given I log in as "user1"
And I should see "This site policy"
And I should not see "Course overview"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Calendar"
And I log out
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Actions" "link_or_button" in the "This privacy policy" "table_row"
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
And I press "Continue"
And I click on "Set status to \"Inactive\"" "link" in the "This privacy policy" "table_row"
And I press "Continue"
And I log out
When I log in as "user1"
Then I should see "Calendar"
@@ -0,0 +1,125 @@
<?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/>.
/**
* Step definition for tool_policy
*
* @package tool_policy
* @category test
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode;
/**
* Step definition for tool_policy
*
* @package tool_policy
* @category test
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_tool_policy extends behat_base {
/**
* Click on an entry in the edit menu.
*
* @Given /^the following policies exist:$/
*
* Supported table fields:
*
* - Name: Policy name (required).
* - Revision: Revision name (policy version).
* - Status: Policy version status - 'draft', 'active' or 'archived'. Defaults to 'active'.
* - Audience: Target users - 'guest', 'all' or 'loggedin'. Default to 'all'.
* - Type: 0 - site policy, 1 - privacy policy, 2 - third party policy, 99 - other.
* - Summary: Policy summary text.
* - Content: Policy full text.
* - Agreement style (agreementstyle): 0 - On the consent page, 1 - On its own page
* - Agreement optional (optional): 0 - Compulsory policy, 1 - Optional policy
*
* @param TableNode $data
*/
public function the_following_policies_exist(TableNode $data) {
global $CFG;
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
throw new Exception('Site policy handler is not set to "tool_policy"');
}
$fields = [
'name',
'revision',
'policy',
'status',
'audience',
'type',
'content',
'summary',
'agreementstyle',
'optional',
];
// Associative array "policy identifier" => id in the database .
$policies = [];
foreach ($data->getHash() as $elementdata) {
$data = (object)[
'audience' => \tool_policy\policy_version::AUDIENCE_ALL,
'archived' => 0,
'type' => 0
];
$elementdata = array_change_key_case($elementdata, CASE_LOWER);
foreach ($elementdata as $key => $value) {
if ($key === 'policy') {
if (array_key_exists($value, $policies)) {
$data->policyid = $policies[$value];
}
} else if ($key === 'status') {
$data->archived = ($value === 'archived');
} else if ($key === 'audience') {
if ($value === 'guest') {
$data->audience = \tool_policy\policy_version::AUDIENCE_GUESTS;
} else if ($value === 'loggedin') {
$data->audience = \tool_policy\policy_version::AUDIENCE_LOGGEDIN;
}
} else if (($key === 'summary' || $key === 'content') && !empty($value)) {
$data->{$key.'_editor'} = ['text' => $value, 'format' => FORMAT_MOODLE];
} else if (in_array($key, $fields) && $value !== '') {
$data->$key = $value;
}
}
if (empty($data->name) || empty($data->content_editor) || empty($data->summary_editor)) {
throw new Exception('Policy is missing at least one of the required fields: name, content, summary');
}
if (!empty($data->policyid)) {
$version = tool_policy\api::form_policydoc_update_new($data);
} else {
$version = \tool_policy\api::form_policydoc_add($data);
}
if (!empty($elementdata['policy'])) {
$policies[$elementdata['policy']] = $version->get('policyid');
}
if (empty($elementdata['status']) || $elementdata['status'] === 'active') {
\tool_policy\api::make_current($version->get('id'));
}
}
}
}
@@ -0,0 +1,877 @@
@tool @tool_policy
Feature: User must accept policy managed by this plugin when logging in and signing up
In order to record user agreement to use the site
As a user
I need to be able to accept site policy during sign up
Scenario: Accept policy on sign up, no site policy
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site (edit their profile).
And I log in as "user1"
And I open my profile in edit mode
And the field "First name" matches value "User1"
Scenario: Accept policy on sign up, only draft policy
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Policy | Name | Revision | Content | Summary | Status |
| P1 | This site policy | | full text1 | short text1 | draft |
| P1 | This privacy policy | | full text2 | short text2 | draft |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site (edit their profile).
And I log in as "user1"
And I open my profile in edit mode
And the field "First name" matches value "User1"
Scenario: Accept policy on sign up, one policy
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
Given the following policies exist:
| Policy | Name | Revision | Content | Summary | Status |
| P1 | This site policy | | full text1 | short text1 | archived |
| P1 | This site policy | | full text2 | short text2 | active |
| P1 | This site policy | | full text3 | short text3 | draft |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "This site policy" "table_row"
And I log out
Scenario: Accept policy on sign up, multiple policies
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
Given the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
| This guests policy | 0 | | full text4 | short text4 | active | guest |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "full text3"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I should not see "full text4"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "This site policy" "table_row"
And "Accepted" "text" should exist in the "This privacy policy" "table_row"
And I should not see "This guests policy"
And I log out
Scenario: Accept policy on sign up and age verification
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
| agedigitalconsentverification | 1 |
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| This site policy | | full text2 | short text2 | active |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should see "Age and location verification"
And I set the field "What is your age?" to "16"
And I set the field "In which country do you live?" to "DZ"
And I press "Proceed"
And I should see "This site policy"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "short text2"
And I should not see "full text2"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "This site policy" "table_row"
And I log out
Scenario: Accept policy on sign up, do not accept all policies
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And I am on site homepage
And I follow "Log in"
And I click on "Create new account" "link"
And I should see "This site policy"
And I press "Next"
And I should see "This privacy policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This privacy policy"
# Confirm that a notification is displayed if none of the policies are accepted.
When I set the field "I agree to the This site policy" to "0"
And I set the field "I agree to the This privacy policy" to "0"
And I press "Next"
Then I should see "Please agree to the following policies"
And I should see "Before continuing you need to acknowledge all these policies."
# Confirm that a notification is displayed if only some policies are accepted.
When I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "0"
Then I should see "Please agree to the following policies"
And I should see "Before continuing you need to acknowledge all these policies."
Scenario: Accept policy on login, do not accept all policies
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And I log in as "user1"
And I should see "This site policy"
And I press "Next"
And I should see "This privacy policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This privacy policy"
# Confirm that a notification is displayed if none of the policies are accepted.
When I set the field "I agree to the This site policy" to "0"
And I set the field "I agree to the This privacy policy" to "0"
And I press "Next"
Then I should see "Please agree to the following policies"
And I should see "Before continuing you need to acknowledge all these policies."
# Confirm that a notification is displayed if only some policies are accepted.
When I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "0"
Then I should see "Please agree to the following policies"
And I should see "Before continuing you need to acknowledge all these policies."
# Confirm that user can not browse the site (edit their profile).
When I follow "Profile" in the user menu
Then I should see "Please agree to the following policies"
Scenario: Accept policy on login, accept all policies
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And I log in as "user1"
And I should see "This site policy"
And I press "Next"
And I should see "This privacy policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This privacy policy"
# User accepts all policies.
When I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
Then I should not see "Please agree to the following policies"
And I should not see "Before continuing you need to acknowledge all these policies."
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
And I log out
# Confirm when logging again as user, the policies are not displayed.
When I log in as "user1"
Then I should not see "This site policy"
And I should not see "This privacy policy"
And I should not see "Please agree to the following policies"
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
Scenario: Accept policy on login, accept new policy documents
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And I log in as "user1"
And I should see "This site policy"
And I press "Next"
And I should see "This privacy policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This privacy policy"
# User accepts all policies.
When I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
Then I should not see "Please agree to the following policies"
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
And I log out
# Create new policy document.
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I should see "Policies and agreements"
And I should see "New policy"
And I follow "New policy"
And I set the following fields to these values:
| Name | This third parties policy |
| Type | Third parties policy |
| User consent | All users |
| Summary | short text4 |
| Full policy | full text4 |
| Active | 1 |
When I press "Save"
Then I should see "Policies and agreements"
And I should see "This third parties policy"
And I log out
# Confirm when logging again as user, the new policies are displayed.
When I log in as "user1"
And I should not see "This site policy"
And I should not see "This privacy policy"
Then I should see "This third parties policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This third parties policy"
And I set the field "This third parties policy" to "1"
And I press "Next"
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
Scenario: Accept policy on login, accept new policy version
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And I log in as "user1"
And I should see "This site policy"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
# User accepts policy.
When I set the field "I agree to the This site policy" to "1"
And I press "Next"
Then I should not see "Please agree to the following policies"
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
And I log out
# Create new version of the policy document.
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
# Menu is already open because javascript is disabled.
Then I should see "View"
And I should see "Edit"
And I should see "Set status to \"Inactive\""
When I follow "Edit"
Then I should see "Editing policy"
And I set the field "Name" to "This site policy new version"
And I set the field "Summary" to "short text2 new version"
And I set the field "Full policy" to "full text2 new version"
And I press "Save"
And I log out
# Confirm that the user has to agree to the new version of the policy.
When I log in as "user1"
Then I should see "This site policy new version"
And I should see "short text2 new version"
And I should see "full text2 new version"
When I press "Next"
Then I should see "Please agree to the following policies"
And I should see "This site policy new version"
And I should see "short text2 new version"
# User accepts policy.
And I set the field "I agree to the This site policy new version" to "1"
When I press "Next"
Then I should not see "Please agree to the following policies"
# Confirm that user can login and browse the site (edit their profile).
When I open my profile in edit mode
Then the field "First name" matches value "User"
@javascript
Scenario: Accept policy on login as guest
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
| This guests policy | 0 | | full text4 | short text4 | active | guest |
And I am on site homepage
And I change window size to "large"
And I follow "Log in"
When I press "Access as a guest"
Then I should see "If you continue browsing this website, you agree to our policies"
# Confirm when navigating, the pop-up policies are displayed.
When I am on the "My courses" page
Then I should see "If you continue browsing this website, you agree to our policies"
And I should see "This site policy"
And I should see "This guests policy"
And I should not see "This privacy policy"
# Confirm when clicking on the policy links, the policy content is displayed.
When I click on "This site policy" "link"
Then I should see "full text2"
And I click on "Close" "button" in the "This site policy" "dialogue"
And I should not see "full text2"
When I click on "This guests policy" "link"
Then I should see "full text4"
And I click on "Close" "button" in the "This guests policy" "dialogue"
And I should not see "full text4"
# Confirm when agreeing to policies the pop-up is no longer displayed.
When I follow "Continue"
Then I should not see "If you continue browsing this website, you agree to our policies"
Scenario: Accept policy on sign up, after completing sign up attempt to create another account
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
Given the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
| This guests policy | 0 | | full text4 | short text4 | active | guest |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
When I press "Next"
Then I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
When I press "Next"
Then I should see "Please agree to the following policies"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
When I press "Next"
Then I should not see "I understand and agree"
And I should see "New account"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
When I press "Create my new account"
Then I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I follow "Log in"
When I click on "Create new account" "link"
# Confirm that the user can view and accept policies when attempting to create another account.
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
When I press "Next"
Then I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
When I press "Next"
Then I should see "Please agree to the following policies"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "full text3"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I should not see "full text4"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
When I press "Next"
Then I should not see "I understand and agree"
And I should see "New account"
Scenario: Accept policy while being logged in as another user
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
When I am on the "user1" "user > profile" page logged in as "admin"
And I follow "Log in as"
Then I should see "You are logged in as User 1"
And I press "Continue"
And I should see "Please read our This site policy"
And I press "Next"
And I should see "Please read our This privacy policy"
And I press "Next"
And I should see "Viewing this page on behalf of User 1"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I log out
And I log in as "user1"
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Admin User" "link" should exist in the "This site policy" "table_row"
And "Admin User" "link" should exist in the "This privacy policy" "table_row"
Scenario: Log in as another user without capability to accept policies on their behalf
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following policies exist:
| Name | Type | Revision | Content | Summary | Status | Audience |
| This site policy | 0 | | full text2 | short text2 | active | all |
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
When I log in as "manager"
And I press "Next"
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I am on the "user1" "user > profile" page
And I follow "Log in as"
Then I should see "You are logged in as User 1"
And I press "Continue"
And I should see "Policies and agreements"
And I should see "No permission to agree to the policies on behalf of this user"
And I should see "Sorry, you do not have the required permission to agree to the following policies on behalf of User 1"
Scenario: Accept policy on sign up as a guest, one policy
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
Given the following policies exist:
| Policy | Name | Revision | Content | Summary | Status |
| P1 | This site policy | | full text1 | short text1 | archived |
| P1 | This site policy | | full text2 | short text2 | active |
| P1 | This site policy | | full text3 | short text3 | draft |
And I am on site homepage
And I follow "Log in"
# First log in as a guest
And I press "Access as a guest"
# Now sign up
And I follow "Log in"
When I click on "Create new account" "link"
Then I should see "This site policy"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "short text2"
And I should not see "full text2"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "This site policy" "table_row"
And I log out
Scenario: Accepting policies on sign up, multiple policies with different style of giving ageement.
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| name | summary | content | agreementstyle |
| Privacy policy | We scan your thoughts | Here goes content. | 0 |
| Digital maturity declaration | You declare be old enough | Here goes content. | 1 |
| Cookies policy | We eat cookies, srsly | Here goes content. | 0 |
| Terms of Service | We teach, you learn | Here goes content. | 1 |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
# The first policy with the agreement style "on its own page" must be accepted first.
Then I should see "Digital maturity declaration" in the "region-main" "region"
And I should see "You declare be old enough"
And I should see "Here goes content."
And I press "I agree to the Digital maturity declaration"
# The second policy with the agreement style "on its own page" must be accepted now.
And I should see "Terms of Service" in the "region-main" "region"
And I should see "We teach, you learn"
And I should see "Here goes content."
And I press "I agree to the Terms of Service"
# Only now we see the remaining consent page policies.
And I should see "Policy 1 out of 2"
And I should see "Privacy policy" in the "region-main" "region"
And I should see "We scan your thoughts"
And I should see "Here goes content."
And I press "Next"
And I should see "Policy 2 out of 2"
And I should see "Cookies policy" in the "region-main" "region"
And I should see "We eat cookies, srsly"
And I should see "Here goes content."
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "Privacy policy"
And I should see "Cookies policy"
And I should not see "Digital maturity declaration" in the "region-main" "region"
And I should not see "Terms of Service" in the "region-main" "region"
And I should not see "Here goes content."
And I set the field "I agree to the Privacy policy" to "1"
And I set the field "I agree to the Cookies policy" to "1"
And I press "Next"
And I should see "New account"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "Privacy policy" "table_row"
And "Accepted" "text" should exist in the "Cookies policy" "table_row"
And "Accepted" "text" should exist in the "Terms of Service" "table_row"
And "Accepted" "text" should exist in the "Digital maturity declaration" "table_row"
And I log out
Scenario: Accepting policies on login, multiple policies with different style of giving ageement.
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following policies exist:
| name | summary | content | agreementstyle |
| Digital maturity declaration | You declare be old enough | Here goes content. | 1 |
| Privacy policy | We scan your thoughts | Here goes content. | 0 |
| Terms of Service | We teach, you learn | Here goes content. | 1 |
| Cookies policy | We eat cookies, srsly | Here goes content. | 0 |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | user1@example.com |
And I log in as "user1"
# The first policy with the agreement style "on its own page" must be accepted first.
Then I should see "Digital maturity declaration" in the "region-main" "region"
And I should see "You declare be old enough"
And I should see "Here goes content."
And I press "I agree to the Digital maturity declaration"
# The second policy with the agreement style "on its own page" must be accepted now.
And I should see "Terms of Service" in the "region-main" "region"
And I should see "We teach, you learn"
And I should see "Here goes content."
# If the user logs out now, only the first policy is accepted and we return to the same page.
And I log out
And I log in as "user1"
And I should see "Terms of Service" in the "region-main" "region"
And I should see "We teach, you learn"
And I should see "Here goes content."
And I press "I agree to the Terms of Service"
# Only now we see the remaining consent page policies.
And I should see "Policy 1 out of 2"
And I should see "Privacy policy" in the "region-main" "region"
And I should see "We scan your thoughts"
And I should see "Here goes content."
And I press "Next"
And I should see "Policy 2 out of 2"
And I should see "Cookies policy" in the "region-main" "region"
And I should see "We eat cookies, srsly"
And I should see "Here goes content."
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "Privacy policy"
And I should see "Cookies policy"
And I should not see "Digital maturity declaration" in the "region-main" "region"
And I should not see "Terms of Service" in the "region-main" "region"
And I should not see "Here goes content."
And I set the field "I agree to the Privacy policy" to "1"
And I set the field "I agree to the Cookies policy" to "1"
And I press "Next"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "Privacy policy" "table_row"
And "Accepted" "text" should exist in the "Cookies policy" "table_row"
And "Accepted" "text" should exist in the "Terms of Service" "table_row"
And "Accepted" "text" should exist in the "Digital maturity declaration" "table_row"
And I log out
Scenario: Accepting policies on login, all and loggedin policies to be accepted on their own page.
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following policies exist:
| name | summary | content | agreementstyle | audience |
| Privacy policy | We scan your thoughts | Here goes content. | 1 | all |
| Digital maturity declaration | You declare be old enough | Here goes content. | 1 | loggedin |
| Cookies policy | We eat cookies, srsly | Here goes content. | 1 | guest |
| Terms of Service | We teach, you learn | Here goes content. | 1 | all |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | user1@example.com |
And I log in as "user1"
# All the policies to be displayed one by one with a button to accept each of them prior seeing the next.
Then I should see "Privacy policy" in the "region-main" "region"
And I should see "We scan your thoughts"
And I should see "Here goes content."
And I press "I agree to the Privacy policy"
And I should see "Digital maturity declaration" in the "region-main" "region"
And I should see "You declare be old enough"
And I should see "Here goes content."
And I press "I agree to the Digital maturity declaration"
And I should see "Terms of Service" in the "region-main" "region"
And I should see "We teach, you learn"
And I should see "Here goes content."
And I press "I agree to the Terms of Service"
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "Privacy policy" "table_row"
And "Accepted" "text" should exist in the "Terms of Service" "table_row"
And "Accepted" "text" should exist in the "Digital maturity declaration" "table_row"
And "Cookies policy" "table_row" should not exist
And I log out
Scenario: Accepting policies on sign up, policies to be accepted on their own page.
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
And the following policies exist:
| name | summary | content | agreementstyle | audience |
| Privacy policy | We scan your thoughts | Here goes content. | 1 | guest |
| Digital maturity declaration | You declare be old enough | Here goes content. | 1 | all |
| Cookies policy | We eat cookies, srsly | Here goes content. | 1 | loggedin |
| Terms of Service | We teach, you learn | Here goes content. | 1 | guest |
And I am on site homepage
And I follow "Log in"
When I click on "Create new account" "link"
# All the policies to be displayed one by one with a button to accept each of them prior seeing the next.
Then I should see "Digital maturity declaration" in the "region-main" "region"
And I should see "You declare be old enough"
And I should see "Here goes content."
And I press "I agree to the Digital maturity declaration"
And I should see "Cookies policy" in the "region-main" "region"
And I should see "We eat cookies, srsly"
And I press "I agree to the Cookies policy"
And I should see "New account"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Last name | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "Digital maturity declaration" "table_row"
And "Accepted" "text" should exist in the "Cookies policy" "table_row"
And "Privacy policy" "table_row" should not exist
And "Terms of Service" "table_row" should not exist
And I log out
@@ -0,0 +1,260 @@
@tool @tool_policy
Feature: Manage policies
In order to manage policies
As a manager
I need to be able to create and edit site policies
Background:
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | one@example.com |
| user2 | User | Two | two@example.com |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
Scenario: Create new policy and save as draft
When I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I follow "New policy"
And I set the following fields to these values:
| Name | Policy1 |
| Version | v1 |
| Summary | Policy summary |
| Full policy | Full text |
And the field "Type" matches value "Site policy"
And the field "User consent" matches value "All users"
And the field "status" matches value "0"
And "Draft" "field" should exist
And "Active" "field" should exist
And "Minor change" "field" should not exist
And I should not see "Minor change"
And "Save as draft" "button" should not exist
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Draft | v1 | N/A |
And I log out
Scenario: Create new policy and save as active
When I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I follow "New policy"
And I set the following fields to these values:
| Name | Policy1 |
| Version | v1 |
| Summary | Policy summary |
| Full policy | Full text |
| Active | 1 |
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Active | v1 | 0 of 4 (0%) |
And I log out
Scenario: Edit active policy and save as minor change
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Edit" "link" in the "Policy1" "table_row"
And "Draft" "field" should not exist
And "Active" "field" should not exist
And "Minor change" "field" should exist
And "Save as draft" "button" should exist
And I set the field "Version" to "v1 amended"
And I set the field "Minor change" to "1"
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Active | v1 amended | 1 of 4 (25%) |
And I log out
Scenario: Edit active policy and save as draft
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Edit" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I press "Save as draft"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Active | v1 | 1 of 4 (25%) |
| Policy1 Site policy, All users, Compulsory | Draft | v2 | N/A |
And I log out
Scenario: Edit active policy and save as new active version
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Edit" "link" in the "Policy1" "table_row"
And I set the field "Name" to "Policy2"
And I set the field "Version" to "v2"
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy2 Site policy, All users, Compulsory | Active | v2 | 0 of 4 (0%) |
And I should not see "Policy1"
And I should not see "v1"
And I click on "View previous versions" "link" in the "Policy2" "table_row"
And I should see "Policy2 previous versions"
And I should not see "v2"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Inactive | v1 | 1 of 4 (25%) |
And I log out
Scenario: Edit draft policy and save as draft
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | draft |
And I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Edit" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And "Draft" "field" should exist
And "Active" "field" should exist
And "Minor change" "field" should not exist
And I should not see "Minor change"
And "Save as draft" "button" should not exist
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Draft | v2 | N/A |
And I should not see "v1"
And "View previous versions" "link" should not exist
And I log out
Scenario: Edit draft policy and save as active
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | draft |
And I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Edit" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I set the field "Active" to "1"
And I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Active | v2 | 0 of 4 (0%) |
And I should not see "v1"
And "View previous versions" "link" should not exist
And I log out
Scenario: Activate draft policy
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | draft |
And I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Set status to \"Active\"" "link" in the "Policy1" "table_row"
Then I should see "All users will be required to agree to this new policy version to be able to use the site."
And I press "Continue"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Active | v1 | 0 of 4 (0%) |
And "View previous versions" "link" should not exist
And I log out
Scenario: Edit archived policy and save as draft
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Set status to \"Inactive\"" "link" in the "Policy1" "table_row"
Then I should see "You are about to inactivate policy"
And I press "Continue"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Inactive | v1 | 1 of 4 (25%) |
And I click on "Create a new draft" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I set the field "Name" to "Policy2"
And the field "status" matches value "0"
And "Draft" "field" should exist
And "Active" "field" should exist
And "Minor change" "field" should not exist
And I should not see "Minor change"
And "Save as draft" "button" should not exist
And I press "Save"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy2 Site policy, All users, Compulsory | Draft | v2 | N/A |
And I should not see "v1"
And I should not see "Policy1"
And I click on "View previous versions" "link" in the "Policy2" "table_row"
And I should see "Policy2 previous versions"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Inactive | v1 | 1 of 4 (25%) |
And I should not see "v2"
And I log out
Scenario: Edit archived policy and save as active
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "manager"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "Set status to \"Inactive\"" "link" in the "Policy1" "table_row"
And I press "Continue"
And I click on "Create a new draft" "link" in the "Policy1" "table_row"
And I set the field "Version" to "v2"
And I set the field "Name" to "Policy2"
And I set the field "Active" to "1"
And I press "Save"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy2 Site policy, All users, Compulsory | Active | v2 | 0 of 4 (0%) |
And I should not see "v1"
And I should not see "Policy1"
And I click on "View previous versions" "link" in the "Policy2" "table_row"
And I should see "Policy2 previous versions"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version | Agreements |
| Policy1 Site policy, All users, Compulsory | Inactive | v1 | 1 of 4 (25%) |
And I should not see "v2"
And I log out
Scenario: Current user can go back to previous page in List of active policies page
Given the following policies exist:
| Name | Revision | Content | Summary | Status |
| Policy1 | v1 | full text2 | short text2 | active |
And I log in as "user1"
And I press "Next"
And I set the field "I agree to the Policy1" to "1"
And I press "Next"
And I follow "Preferences" in the user menu
And I should see "Preferences"
And I should see "Policies"
# User should see a redirect back to previous page link.
And I click on "Policies" "link"
And I should see "List of active policies"
And I should see "Go back to previous page"
When I click on "Go back to previous page" "link"
Then I should see "Preferences"
@@ -0,0 +1,264 @@
@tool @tool_policy
Feature: Optional policies
In order to exercise my privacy rights
As a user
I should be able to decline policy statements and withdraw my previously given consent to them
Background:
Given the following config values are set as admin:
| sitepolicyhandler | tool_policy |
And the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | One | one@example.com |
| user2 | User | Two | two@example.com |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
And the following "courses" exist:
| fullname | shortname |
| Course1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| user1 | C1 | student |
| user2 | C1 | student |
Scenario: Configuring a policy as optional
Given I log in as "manager"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I follow "New policy"
# Policies are compulsory by default.
And the field "Agreement optional" matches value "No"
# Optional status can be set when creating a new policy.
And I set the following fields to these values:
| Name | ConsentPageOptional1 |
| Version | v1 |
| Summary | Policy summary |
| Full policy | Full text |
| Active | 1 |
| Show policy before showing other policies | No |
| Agreement optional | Yes |
When I press "Save"
Then the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version |
| ConsentPageOptional1 Site policy, All users, Optional | Active | v1 |
# Optional status can be edited.
And I open the action menu in "ConsentPageOptional1" "table_row"
And I click on "Edit" "link" in the "ConsentPageOptional1" "table_row"
And I set the field "Agreement optional" to "No"
And I set the field "Minor change" to "1"
And I press "Save"
And the following should exist in the "tool-policy-managedocs-wrapper" table:
| Name | Policy status | Version |
| ConsentPageOptional1 Site policy, All users, Compulsory | Active | v1 |
Scenario: Compulsory policies must be accepted prior signup, optional policies just after it
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
And the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| ConsentPageOptional1 | full text1 | short text1 | 0 | 1 |
| ConsentPageOptional2 | full text2 | short text2 | 0 | 1 |
| ConsentPageCompulsory1 | full text3 | short text3 | 0 | 0 |
| OwnPageCompulsory1 | full text4 | short text4 | 1 | 0 |
| OwnPageOptional1 | full text5 | short text5 | 1 | 1 |
And I am on site homepage
And I follow "Log in"
And I click on "Create new account" "link"
# Compulsory policies displayed on own page are shown first and must be agreed.
And I should see "OwnPageCompulsory1" in the "region-main" "region"
And I should see "short text4" in the "region-main" "region"
And I should see "full text4" in the "region-main" "region"
And I press "I agree to the OwnPageCompulsory1"
# Compulsory policies displayed on the consent page are shown next and must be agreed.
And I should see "ConsentPageCompulsory1"
And I should see "short text3" in the "region-main" "region"
And I should see "full text3" in the "region-main" "region"
And I press "Next"
And I should see "Please agree to the following policies"
And I set the field "I agree to the ConsentPageCompulsory1" to "1"
And I press "Next"
# The signup form can be submitted and a new account created.
And I set the following fields to these values:
| Username | user3 |
| Password | user3 |
| Email address | user3@address.invalid |
| Email (again) | user3@address.invalid |
| First name | User3 |
| Last name | L3 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user3@address.invalid"
And I confirm email for "user3"
And I should see "Thanks, User3 L3"
And I should see "Your registration has been confirmed"
When I press "Continue"
# After confirming the new account, the user is logged in and asked to accept or decline the optional policies.
# First come policies displayed on their own page.
Then I should see "OwnPageOptional1"
And I should see "short text5" in the "region-main" "region"
And I should see "full text5" in the "region-main" "region"
And I press "No thanks, I decline OwnPageOptional1"
# Then come policies displayed on the consent page.
And I should see "ConsentPageOptional1" in the "region-main" "region"
And I should see "short text1" in the "region-main" "region"
And I should see "full text1" in the "region-main" "region"
And I press "Next"
And I should see "ConsentPageOptional2" in the "region-main" "region"
And I should see "short text2" in the "region-main" "region"
And I should see "full text2" in the "region-main" "region"
And I press "Next"
And I should see "Please agree to the following policies"
And I set the field "I agree to the ConsentPageOptional1" to "1"
And I set the field "No thanks, I decline ConsentPageOptional2" to "0"
And I press "Next"
# Accepted and declined policies are shown in the profile.
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "ConsentPageCompulsory1" "table_row"
And "Accepted" "text" should exist in the "ConsentPageOptional1" "table_row"
And "Accepted" "text" should exist in the "OwnPageCompulsory1" "table_row"
And "Declined" "text" should exist in the "OwnPageOptional1" "table_row"
And "Declined" "text" should exist in the "ConsentPageOptional2" "table_row"
Scenario: When a new optional policy is added, users are asked to accept/decline it on their next login
Given the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| ConsentPageOptional1 | full text1 | short text1 | 0 | 1 |
| OwnPageOptional1 | full text5 | short text5 | 1 | 1 |
When I log in as "user1"
# First come policies displayed on their own page.
Then I should see "OwnPageOptional1"
And I should see "short text5" in the "region-main" "region"
And I should see "full text5" in the "region-main" "region"
And I press "I agree to the OwnPageOptional1"
# Then come policies displayed on the consent page.
And I should see "ConsentPageOptional1" in the "region-main" "region"
And I should see "short text1" in the "region-main" "region"
And I should see "full text1" in the "region-main" "region"
And I press "Next"
And I should see "Please agree to the following policies"
And I set the field "No thanks, I decline ConsentPageOptional1" to "0"
And I press "Next"
# Accepted and declined policies are shown in the profile.
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "OwnPageOptional1" "table_row"
And "Declined" "text" should exist in the "ConsentPageOptional1" "table_row"
Scenario: Users can withdraw an accepted optional policy and re-accept it again (js off)
Given the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| OwnPageOptional1 | full text1 | short text1 | 1 | 1 |
And I log in as "user1"
And I press "I agree to the OwnPageOptional1"
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "OwnPageOptional1" "table_row"
And "Withdraw" "link" should exist in the "OwnPageOptional1" "table_row"
When I click on "Withdraw acceptance of OwnPageOptional1" "link" in the "OwnPageOptional1" "table_row"
Then I should see "Withdrawing policy"
And I should see "User One"
And I should see "OwnPageOptional1"
And I press "Withdraw user consent"
And "Declined" "text" should exist in the "OwnPageOptional1" "table_row"
And "Accept" "link" should exist in the "OwnPageOptional1" "table_row"
And I click on "Accept OwnPageOptional1" "link" in the "OwnPageOptional1" "table_row"
And I should see "Accepting policy"
And I should see "User One"
And I should see "OwnPageOptional1"
And I press "Give consent"
And "Accepted" "text" should exist in the "OwnPageOptional1" "table_row"
@javascript
Scenario: Users can withdraw an accepted optional policy and re-accept it again (js on)
Given the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| OwnPageOptional1 | full text1 | short text1 | 1 | 1 |
And I log in as "user1"
And I press "I agree to the OwnPageOptional1"
And I follow "Profile" in the user menu
And I follow "Policies and agreements"
And "Accepted" "text" should exist in the "OwnPageOptional1" "table_row"
And "Withdraw" "link" should exist in the "OwnPageOptional1" "table_row"
When I click on "Withdraw acceptance of OwnPageOptional1" "link" in the "OwnPageOptional1" "table_row"
Then I should see "Withdrawing policy"
And I should see "User One"
And I should see "OwnPageOptional1"
And I press "Withdraw user consent"
And "Declined" "text" should exist in the "OwnPageOptional1" "table_row"
And "Accept" "link" should exist in the "OwnPageOptional1" "table_row"
And I click on "Accept OwnPageOptional1" "link" in the "OwnPageOptional1" "table_row"
And I should see "Accepting policy"
And I should see "User One"
And I should see "OwnPageOptional1"
And I press "Give consent"
And "Accepted" "text" should exist in the "OwnPageOptional1" "table_row"
Scenario: Managers can see accepted, declined and pending acceptances of optional policies
Given the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| OwnPageOptional1 | full text1 | short text1 | 1 | 1 |
| OwnPageOptional2 | full text2 | short text2 | 1 | 1 |
And I log in as "user1"
And I press "I agree to the OwnPageOptional1"
And I press "No thanks, I decline OwnPageOptional2"
And I log out
And I log in as "manager"
And I press "I agree to the OwnPageOptional1"
And I press "I agree to the OwnPageOptional2"
When I navigate to "Users > Privacy and policies > User agreements" in site administration
# User One has accepted just some policies.
Then "Partially accepted" "text" should exist in the "User One" "table_row"
And "Details" "link" should exist in the "User One" "table_row"
# User Two did not have a chance to respond to the new policies yet.
And "Pending" "text" should exist in the "User Two" "table_row"
And "Details" "link" should exist in the "User Two" "table_row"
# Max Manager accepted all and can also change status of own acceptances.
And "Accepted" "text" should exist in the "Max Manager" "table_row"
And "Details" "link" should exist in the "Max Manager" "table_row"
And "Withdraw accepted policies" "link" should exist in the "Max Manager" "table_row"
And "Withdraw acceptance of OwnPageOptional1" "link" should exist in the "Max Manager" "table_row"
And "Withdraw acceptance of OwnPageOptional2" "link" should exist in the "Max Manager" "table_row"
Scenario: Administrators can see accepted, declined and pending acceptances of optional policies and also change them on behalf of other users
Given the following policies exist:
| Name | Content | Summary | Agreementstyle | Optional |
| OwnPageOptional1 | full text1 | short text1 | 1 | 1 |
| OwnPageOptional2 | full text2 | short text2 | 1 | 1 |
And I log in as "user1"
And I press "I agree to the OwnPageOptional1"
And I press "No thanks, I decline OwnPageOptional2"
And I log out
And I log in as "admin"
When I navigate to "Users > Privacy and policies > User agreements" in site administration
# User One has accepted just some policies.
Then "Partially accepted" "text" should exist in the "User One" "table_row"
And "Details" "link" should exist in the "User One" "table_row"
And "Withdraw acceptance of OwnPageOptional1" "link" should exist in the "User One" "table_row"
And "Accept OwnPageOptional2" "link" should exist in the "User One" "table_row"
# User Two did not have a chance to respond to the new policies yet.
And "Pending" "text" should exist in the "User Two" "table_row"
And "Accept pending policies" "link" should exist in the "User Two" "table_row"
And "Decline pending policies" "link" should exist in the "User Two" "table_row"
And "Accept OwnPageOptional1" "link" should exist in the "User Two" "table_row"
And "Decline OwnPageOptional1" "link" should exist in the "User Two" "table_row"
And "Accept OwnPageOptional2" "link" should exist in the "User Two" "table_row"
And "Decline OwnPageOptional2" "link" should exist in the "User Two" "table_row"
# Accept all policies on Max Manager's behalf.
And I click on "Accept pending policies" "link" in the "Max Manager" "table_row"
And I press "Give consent"
And "Accepted" "text" should exist in the "Max Manager" "table_row"
# Decline all policies on User Two's behalf.
And I click on "Decline pending policies" "link" in the "User Two" "table_row"
And I press "Decline user consent"
And "Declined on user's behalf" "text" should exist in the "User Two" "table_row"
And "Accepted" "text" should not exist in the "User Two" "table_row"
And "Pending" "text" should not exist in the "User Two" "table_row"
# Accept policy on User One's behalf.
And I click on "Accept OwnPageOptional2" "link" in the "User One" "table_row"
And I press "Give consent"
And "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
And "Declined" "text" should not exist in the "User One" "table_row"
And "Pending" "text" should not exist in the "User One" "table_row"
@@ -0,0 +1,447 @@
<?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/>.
namespace tool_policy;
use externallib_advanced_testcase;
use tool_mobile\external as external_mobile;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
require_once($CFG->dirroot . '/user/externallib.php');
/**
* External policy webservice API tests.
*
* @package tool_policy
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class externallib_test extends externallib_advanced_testcase {
/** @var \tool_policy\policy_version $policy1 Policy document 1. */
protected $policy1;
/** @var \tool_policy\policy_version $policy2 Policy document 2. */
protected $policy2;
/** @var \tool_policy\policy_version $policy3 Policy document 3. */
protected $policy3;
/** @var \stdClass $child user record. */
protected $child;
/** @var \stdClass $parent user record. */
protected $parent;
/** @var \stdClass $adult user record. */
protected $adult;
/**
* Setup function- we will create some policy docs.
*/
public function setUp(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
// Prepare a policy document with some versions.
$formdata = api::form_policydoc_data(new \tool_policy\policy_version(0));
$formdata->name = 'Test policy';
$formdata->revision = 'v1';
$formdata->summary_editor = ['text' => 'summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'content', 'format' => FORMAT_HTML, 'itemid' => 0];
$this->policy1 = api::form_policydoc_add($formdata);
$formdata = api::form_policydoc_data($this->policy1);
$formdata->revision = 'v2';
$this->policy2 = api::form_policydoc_update_new($formdata);
$formdata = api::form_policydoc_data($this->policy1);
$formdata->revision = 'v3';
$this->policy3 = api::form_policydoc_update_new($formdata);
api::make_current($this->policy2->get('id'));
// Create users.
$this->child = $this->getDataGenerator()->create_user();
$this->parent = $this->getDataGenerator()->create_user();
$this->adult = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$childcontext = \context_user::instance($this->child->id);
$roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
role_assign($roleminorid, $this->child->id, $syscontext->id);
role_assign($roleparentid, $this->parent->id, $childcontext->id);
}
/**
* Test for the get_policy_version() function.
*/
public function test_get_policy_version(): void {
$this->setUser($this->adult);
// View current policy version.
$result = external::get_policy_version($this->policy2->get('id'));
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(1, $result['result']);
$this->assertEquals($this->policy1->get('name'), $result['result']['policy']['name']);
$this->assertEquals($this->policy1->get('content'), $result['result']['policy']['content']);
// View draft policy version.
$result = external::get_policy_version($this->policy3->get('id'));
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(0, $result['result']);
$this->assertCount(1, $result['warnings']);
$this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
// Add test for non existing versionid.
$result = external::get_policy_version(999);
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(0, $result['result']);
$this->assertCount(1, $result['warnings']);
$this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorpolicyversionnotfound');
// View previous non-accepted version in behalf of a child.
$this->setUser($this->parent);
$result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(0, $result['result']);
$this->assertCount(1, $result['warnings']);
$this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
// Let the parent accept the policy on behalf of her child and view it again.
api::accept_policies($this->policy1->get('id'), $this->child->id);
$result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(1, $result['result']);
$this->assertEquals($this->policy1->get('name'), $result['result']['policy']['name']);
$this->assertEquals($this->policy1->get('content'), $result['result']['policy']['content']);
// Only parent is able to view the child policy version accepted by her child.
$this->setUser($this->adult);
$result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
$result = \core_external\external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
$this->assertCount(0, $result['result']);
$this->assertCount(1, $result['warnings']);
$this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
}
/**
* Test tool_mobile\external callback to site_policy_handler.
*/
public function test_get_config_with_site_policy_handler(): void {
global $CFG;
$this->resetAfterTest(true);
// Set the handler for the site policy, make sure it substitutes link to the sitepolicy.
$CFG->sitepolicyhandler = 'tool_policy';
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
$result = external_mobile::get_config();
$result = \core_external\external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
$toolsitepolicy = $sitepolicymanager->get_embed_url();
foreach (array_values($result['settings']) as $r) {
if ($r['name'] == 'sitepolicy') {
$configsitepolicy = $r['value'];
}
}
$this->assertEquals($toolsitepolicy, $configsitepolicy);
}
/**
* Test for core_privacy\sitepolicy\manager::accept() when site policy handler is set.
*/
public function test_agree_site_policy_with_handler(): void {
global $CFG, $DB, $USER;
$this->resetAfterTest(true);
$user = self::getDataGenerator()->create_user();
$this->setUser($user);
// Set mock site policy handler. See function tool_phpunit_site_policy_handler() below.
$CFG->sitepolicyhandler = 'tool_policy';
$this->assertEquals(0, $USER->policyagreed);
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
// Make sure user can not login.
$toolconsentpage = $sitepolicymanager->get_redirect_url();
$this->expectException(\moodle_exception::class);
$this->expectExceptionMessage(get_string('sitepolicynotagreed', 'error', $toolconsentpage->out()));
\core_user_external::validate_context(\context_system::instance());
// Call WS to agree to the site policy. It will call tool_policy handler.
$result = \core_user_external::agree_site_policy();
$result = \core_external\external_api::clean_returnvalue(\core_user_external::agree_site_policy_returns(), $result);
$this->assertTrue($result['status']);
$this->assertCount(0, $result['warnings']);
$this->assertEquals(1, $USER->policyagreed);
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id)));
// Try again, we should get a warning.
$result = \core_user_external::agree_site_policy();
$result = \core_external\external_api::clean_returnvalue(\core_user_external::agree_site_policy_returns(), $result);
$this->assertFalse($result['status']);
$this->assertCount(1, $result['warnings']);
$this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']);
}
/**
* Test for core_privacy\sitepolicy\manager::accept() when site policy handler is set.
*/
public function test_checkcanaccept_with_handler(): void {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'tool_policy';
$syscontext = \context_system::instance();
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
$adult = $this->getDataGenerator()->create_user();
$child = $this->getDataGenerator()->create_user();
$rolechildid = create_role('Child', 'child', 'Not old enough to accept site policies themselves');
assign_capability('tool/policy:accept', CAP_PROHIBIT, $rolechildid, $syscontext->id);
role_assign($rolechildid, $child->id, $syscontext->id);
// Default user can accept policies.
$this->setUser($adult);
$result = external_mobile::get_config();
$result = \core_external\external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
$toolsitepolicy = $sitepolicymanager->accept();
$this->assertTrue($toolsitepolicy);
// Child user can not accept policies.
$this->setUser($child);
$result = external_mobile::get_config();
$result = \core_external\external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
$this->expectException(\required_capability_exception::class);
$sitepolicymanager->accept();
}
/**
* Test for external function get_user_acceptances().
*/
public function test_external_get_user_acceptances(): void {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'tool_policy';
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Create optional policy.
$formdata = api::form_policydoc_data(new \tool_policy\policy_version(0));
$formdata->name = 'Test optional policy';
$formdata->revision = 'v1';
$formdata->optional = 1;
$formdata->summary_editor = ['text' => 'summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'content', 'format' => FORMAT_HTML, 'itemid' => 0];
$optionalpolicy = api::form_policydoc_add($formdata);
api::make_current($optionalpolicy->get('id'));
// Accept this version.
api::accept_policies([$optionalpolicy->get('id')], $user->id, null);
// Generate new version.
$formdata = api::form_policydoc_data($optionalpolicy);
$formdata->revision = 'v2';
$optionalpolicynew = api::form_policydoc_update_new($formdata);
api::make_current($optionalpolicynew->get('id'));
// Now return all policies the user should be able to see, including previous versions of existing policies, if accepted/declined.
$policies = \tool_policy\external\get_user_acceptances::execute();
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\get_user_acceptances::execute_returns(), $policies);
$this->assertCount(3, $policies['policies']);
$this->assertCount(0, $policies['warnings']);
foreach ($policies['policies'] as $policy) {
if ($policy['versionid'] == $this->policy2->get('id')) {
$this->assertEquals($this->policy2->get('name'), $policy['name']);
$this->assertEquals(0, $policy['optional']);
$this->assertTrue($policy['canaccept']);
$this->assertFalse($policy['candecline']); // Cannot decline or revoke mandatory for myself.
$this->assertFalse($policy['canrevoke']);
} else {
$this->assertEquals($optionalpolicy->get('name'), $policy['name']);
$this->assertEquals(1, $policy['optional']);
$this->assertTrue($policy['canaccept']);
$this->assertTrue($policy['candecline']); // Can decline or revoke optional for myself.
$this->assertTrue($policy['canrevoke']);
}
$this->assertNotContains('acceptance', $policy); // Nothing accepted yet.
}
// Get other user acceptances.
$this->parent->policyagreed = 1;
$this->setUser($this->parent);
$policies = \tool_policy\external\get_user_acceptances::execute($this->child->id);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\get_user_acceptances::execute_returns(), $policies);
$this->assertCount(2, $policies['policies']);
foreach ($policies['policies'] as $policy) {
if ($policy['versionid'] == $this->policy2->get('id')) {
$this->assertTrue($policy['canaccept']);
$this->assertFalse($policy['candecline']); // Cannot decline mandatory in general.
$this->assertTrue($policy['canrevoke']);
} else {
$this->assertTrue($policy['canaccept']);
$this->assertTrue($policy['candecline']);
$this->assertTrue($policy['canrevoke']);
}
$this->assertNotContains('acceptance', $policy); // Nothing accepted yet.
}
// Get other user acceptances without permission.
$this->expectException(\required_capability_exception::class);
$policies = \tool_policy\external\get_user_acceptances::execute($user->id);
}
/**
* Test for external function set_acceptances_status().
*/
public function test_external_set_acceptances_status(): void {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'tool_policy';
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Create optional policy.
$formdata = api::form_policydoc_data(new \tool_policy\policy_version(0));
$formdata->name = 'Test optional policy';
$formdata->revision = 'v1';
$formdata->optional = 1;
$formdata->summary_editor = ['text' => 'summary', 'format' => FORMAT_HTML, 'itemid' => 0];
$formdata->content_editor = ['text' => 'content', 'format' => FORMAT_HTML, 'itemid' => 0];
$optionalpolicy = api::form_policydoc_add($formdata);
api::make_current($optionalpolicy->get('id'));
// Decline this version.
api::decline_policies([$optionalpolicy->get('id')], $user->id, null);
// Generate new version and make it current.
$formdata = api::form_policydoc_data($optionalpolicy);
$formdata->revision = 'v2';
$optionalpolicynew = api::form_policydoc_update_new($formdata);
api::make_current($optionalpolicynew->get('id'));
// Accept all the current policies.
$ids = [
['versionid' => $this->policy2->get('id'), 'status' => 1],
['versionid' => $optionalpolicynew->get('id'), 'status' => 1],
];
$policies = \tool_policy\external\set_acceptances_status::execute($ids);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\set_acceptances_status::execute_returns(), $policies);
$this->assertEquals(1, $policies['policyagreed']);
$this->assertCount(0, $policies['warnings']);
// And now accept and old one.
$ids = [['versionid' => $optionalpolicy->get('id'), 'status' => 1, 'note' => 'I accept for me.']]; // The note will be ignored.
$policies = \tool_policy\external\set_acceptances_status::execute($ids);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\set_acceptances_status::execute_returns(), $policies);
// Retrieve and check all are accepted now.
$policies = \tool_policy\external\get_user_acceptances::execute();
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\get_user_acceptances::execute_returns(), $policies);
$this->assertCount(3, $policies['policies']);
foreach ($policies['policies'] as $policy) {
$this->assertEquals(1, $policy['acceptance']['status']); // Check all accepted.
$this->assertEmpty($policy['acceptance']['note']); // The note was not recorded because it was for itself.
}
// Decline optional only.
$policies = \tool_policy\external\set_acceptances_status::execute([['versionid' => $optionalpolicynew->get('id'), 'status' => 0]]);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\set_acceptances_status::execute_returns(), $policies);
$this->assertEquals(1, $policies['policyagreed']);
$this->assertCount(0, $policies['warnings']);
$policies = \tool_policy\external\get_user_acceptances::execute();
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\get_user_acceptances::execute_returns(), $policies);
$this->assertCount(3, $policies['policies']);
foreach ($policies['policies'] as $policy) {
if ($policy['versionid'] == $optionalpolicynew->get('id')) {
$this->assertEquals(0, $policy['acceptance']['status']); // Not accepted.
} else {
$this->assertEquals(1, $policy['acceptance']['status']); // Accepted.
}
}
// Parent & child case now. Accept the optional ONLY on behalf of someone else.
$this->parent->policyagreed = 1;
$this->setUser($this->parent);
$notetext = 'I accept this on behalf of my child Santiago.';
$policies = \tool_policy\external\set_acceptances_status::execute(
[['versionid' => $optionalpolicynew->get('id'), 'status' => 1, 'note' => $notetext]], $this->child->id);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\set_acceptances_status::execute_returns(), $policies);
$this->assertEquals(0, $policies['policyagreed']); // Mandatory missing.
$this->assertCount(0, $policies['warnings']);
$policies = \tool_policy\external\get_user_acceptances::execute($this->child->id);
$policies = \core_external\external_api::clean_returnvalue(
\tool_policy\external\get_user_acceptances::execute_returns(), $policies);
$this->assertCount(2, $policies['policies']);
foreach ($policies['policies'] as $policy) {
if ($policy['versionid'] == $this->policy2->get('id')) {
$this->assertNotContains('acceptance', $policy); // Not yet accepted.
$this->assertArrayNotHasKey('acceptance', $policy);
} else {
$this->assertEquals(1, $policy['acceptance']['status']); // Accepted.
$this->assertEquals($notetext, $policy['acceptance']['note']);
}
}
// Try to accept on behalf of other user with no permissions.
$this->expectException(\required_capability_exception::class);
$policies = \tool_policy\external\set_acceptances_status::execute([['versionid' => $optionalpolicynew->get('id'), 'status' => 1]], $user->id);
}
/**
* Test for external function set_acceptances_status decline mandatory.
*/
public function test_external_set_acceptances_status_decline_mandatory(): void {
global $CFG;
$this->resetAfterTest(true);
$CFG->sitepolicyhandler = 'tool_policy';
$this->parent->policyagreed = 1;
$this->setUser($this->parent);
$this->expectException(\moodle_exception::class);
$this->expectExceptionMessage(get_string('errorpolicyversioncompulsory', 'tool_policy'));
$ids = [['versionid' => $this->policy2->get('id'), 'status' => 0]];
$policies = \tool_policy\external\set_acceptances_status::execute($ids, $this->child->id);
}
}
@@ -0,0 +1,395 @@
<?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/>.
/**
* Privacy provider tests.
*
* @package tool_policy
* @category test
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_policy\privacy;
use core_privacy\local\metadata\collection;
use tool_policy\privacy\provider;
use tool_policy\api;
use tool_policy\policy_version;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy provider tests class.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends \core_privacy\tests\provider_testcase {
/** @var stdClass The user object. */
protected $user;
/** @var stdClass The manager user object. */
protected $manager;
/** @var context_system The system context instance. */
protected $syscontext;
/**
* Setup function. Will create a user.
*/
protected function setUp(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$this->user = $generator->create_user();
// Create manager user.
$this->manager = $generator->create_user();
$this->syscontext = \context_system::instance();
$rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $this->syscontext->id);
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $this->syscontext->id);
role_assign($rolemanagerid, $this->manager->id, $this->syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
}
/**
* Test getting the context for the user ID related to this plugin.
*/
public function test_get_contexts_for_userid(): void {
global $CFG;
// When there are no policies or agreements context list is empty.
$contextlist = \tool_policy\privacy\provider::get_contexts_for_userid($this->manager->id);
$this->assertEmpty($contextlist);
$contextlist = \tool_policy\privacy\provider::get_contexts_for_userid($this->user->id);
$this->assertEmpty($contextlist);
// Create a policy.
$this->setUser($this->manager);
$CFG->sitepolicyhandler = 'tool_policy';
$policy = $this->add_policy();
api::make_current($policy->get('id'));
// After creating a policy, there should be manager context.
$contextlist = \tool_policy\privacy\provider::get_contexts_for_userid($this->manager->id);
$this->assertEquals(1, $contextlist->count());
// But when there are no agreements, user context list is empty.
$contextlist = \tool_policy\privacy\provider::get_contexts_for_userid($this->user->id);
$this->assertEmpty($contextlist);
// Agree to the policy.
$this->setUser($this->user);
api::accept_policies([$policy->get('id')]);
// There should be user context.
$contextlist = \tool_policy\privacy\provider::get_contexts_for_userid($this->user->id);
$this->assertEquals(1, $contextlist->count());
}
/**
* Test getting the user IDs within the context related to this plugin.
*/
public function test_get_users_in_context(): void {
global $CFG;
$component = 'tool_policy';
// System context should have nothing before a policy is added.
$userlist = new \core_privacy\local\request\userlist($this->syscontext, $component);
provider::get_users_in_context($userlist);
$this->assertEmpty($userlist);
// Create parent and child users.
$generator = $this->getDataGenerator();
$parentuser = $generator->create_user();
$childuser = $generator->create_user();
// Fetch relevant contexts.
$managercontext = \context_user::instance($this->manager->id);
$usercontext = $managercontext = \context_user::instance($this->user->id);
$parentcontext = $managercontext = \context_user::instance($parentuser->id);
$childcontext = $managercontext = \context_user::instance($childuser->id);
// Assign parent to accept on behalf of the child.
$roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $this->syscontext->id);
role_assign($roleparentid, $parentuser->id, $childcontext->id);
// Create a policy.
$this->setUser($this->manager);
$CFG->sitepolicyhandler = 'tool_policy';
$policy = $this->add_policy();
api::make_current($policy->get('id'));
// Manager should exist in system context now they have created a policy.
$userlist = new \core_privacy\local\request\userlist($this->syscontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(1, $userlist);
$this->assertEquals([$this->manager->id], $userlist->get_userids());
// User contexts should be empty before policy acceptances.
$userlist = new \core_privacy\local\request\userlist($usercontext, $component);
provider::get_users_in_context($userlist);
$this->assertEmpty($userlist);
$userlist = new \core_privacy\local\request\userlist($parentcontext, $component);
provider::get_users_in_context($userlist);
$this->assertEmpty($userlist);
$userlist = new \core_privacy\local\request\userlist($childcontext, $component);
provider::get_users_in_context($userlist);
$this->assertEmpty($userlist);
// User accepts policy, parent accepts on behalf of child only.
$this->setUser($this->user);
api::accept_policies([$policy->get('id')]);
$this->setUser($parentuser);
api::accept_policies([$policy->get('id')], $childuser->id);
// Ensure user is fetched within its user context.
$userlist = new \core_privacy\local\request\userlist($usercontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(1, $userlist);
$this->assertEquals([$this->user->id], $userlist->get_userids());
// Ensure parent and child are both found within child's user context.
$userlist = new \core_privacy\local\request\userlist($childcontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(2, $userlist);
$expected = [$parentuser->id, $childuser->id];
$actual = $userlist->get_userids();
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
// Parent has not accepted for itself, so should not be found within its user context.
$userlist = new \core_privacy\local\request\userlist($parentcontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist);
}
public function test_export_agreements(): void {
global $CFG;
$otheruser = $this->getDataGenerator()->create_user();
$otherusercontext = \context_user::instance($otheruser->id);
// Create policies and agree to them as manager.
$this->setUser($this->manager);
$managercontext = \context_user::instance($this->manager->id);
$systemcontext = \context_system::instance();
$agreementsubcontext = [
get_string('privacyandpolicies', 'admin'),
get_string('useracceptances', 'tool_policy')
];
$versionsubcontext = [
get_string('policydocuments', 'tool_policy')
];
$CFG->sitepolicyhandler = 'tool_policy';
$policy1 = $this->add_policy();
api::make_current($policy1->get('id'));
$policy2 = $this->add_policy();
api::make_current($policy2->get('id'));
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
// Agree to the policies for oneself.
$this->setUser($this->user);
$usercontext = \context_user::instance($this->user->id);
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
// Request export for this user.
$contextlist = provider::get_contexts_for_userid($this->user->id);
$this->assertCount(1, $contextlist);
$this->assertEquals([$usercontext->id], $contextlist->get_contextids());
$approvedcontextlist = new approved_contextlist($this->user, 'tool_policy', [$usercontext->id]);
provider::export_user_data($approvedcontextlist);
// User can not see manager's agreements but can see his own.
$writer = writer::with_context($managercontext);
$this->assertFalse($writer->has_any_data());
$writer = writer::with_context($usercontext);
$this->assertTrue($writer->has_any_data());
// Test policy 1.
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy1->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy1->get('name'), $datauser->name);
$this->assertEquals($this->user->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy1->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy1->get('content')), strip_tags($datauser->content));
// Test policy 2.
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy2->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy2->get('name'), $datauser->name);
$this->assertEquals($this->user->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy2->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy2->get('content')), strip_tags($datauser->content));
}
public function test_export_agreements_for_other(): void {
global $CFG;
$managercontext = \context_user::instance($this->manager->id);
$systemcontext = \context_system::instance();
$usercontext = \context_user::instance($this->user->id);
// Create policies and agree to them as manager.
$this->setUser($this->manager);
$agreementsubcontext = [
get_string('privacyandpolicies', 'admin'),
get_string('useracceptances', 'tool_policy')
];
$versionsubcontext = [
get_string('policydocuments', 'tool_policy')
];
$CFG->sitepolicyhandler = 'tool_policy';
$policy1 = $this->add_policy();
api::make_current($policy1->get('id'));
$policy2 = $this->add_policy();
api::make_current($policy2->get('id'));
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
// Agree to the other user's policies.
api::accept_policies([$policy1->get('id'), $policy2->get('id')], $this->user->id, 'My note');
// Request export for the manager.
$contextlist = provider::get_contexts_for_userid($this->manager->id);
$this->assertCount(3, $contextlist);
$this->assertEqualsCanonicalizing(
[$managercontext->id, $usercontext->id, $systemcontext->id],
$contextlist->get_contextids()
);
$approvedcontextlist = new approved_contextlist($this->user, 'tool_policy', [$usercontext->id]);
provider::export_user_data($approvedcontextlist);
// The user context has data.
$writer = writer::with_context($usercontext);
$this->assertTrue($writer->has_any_data());
// Test policy 1.
$writer = writer::with_context($usercontext);
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy1->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy1->get('name'), $datauser->name);
$this->assertEquals($this->manager->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy1->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy1->get('content')), strip_tags($datauser->content));
// Test policy 2.
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy2->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy2->get('name'), $datauser->name);
$this->assertEquals($this->manager->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy2->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy2->get('content')), strip_tags($datauser->content));
}
public function test_export_created_policies(): void {
global $CFG;
// Create policies and agree to them as manager.
$this->setUser($this->manager);
$managercontext = \context_user::instance($this->manager->id);
$systemcontext = \context_system::instance();
$agreementsubcontext = [
get_string('privacyandpolicies', 'admin'),
get_string('useracceptances', 'tool_policy')
];
$versionsubcontext = [
get_string('policydocuments', 'tool_policy')
];
$CFG->sitepolicyhandler = 'tool_policy';
$policy1 = $this->add_policy();
api::make_current($policy1->get('id'));
$policy2 = $this->add_policy();
api::make_current($policy2->get('id'));
api::accept_policies([$policy1->get('id'), $policy2->get('id')]);
// Agree to the policies for oneself.
$contextlist = provider::get_contexts_for_userid($this->manager->id);
$this->assertCount(2, $contextlist);
$this->assertEqualsCanonicalizing([$managercontext->id, $systemcontext->id], $contextlist->get_contextids());
$approvedcontextlist = new approved_contextlist($this->manager, 'tool_policy', $contextlist->get_contextids());
provider::export_user_data($approvedcontextlist);
// User has agreed to policies.
$writer = writer::with_context($managercontext);
$this->assertTrue($writer->has_any_data());
// Test policy 1.
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy1->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy1->get('name'), $datauser->name);
$this->assertEquals($this->manager->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy1->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy1->get('content')), strip_tags($datauser->content));
// Test policy 2.
$subcontext = array_merge($agreementsubcontext, [get_string('policynamedversion', 'tool_policy', $policy2->to_record())]);
$datauser = $writer->get_data($subcontext);
$this->assertEquals($policy2->get('name'), $datauser->name);
$this->assertEquals($this->manager->id, $datauser->agreedby);
$this->assertEquals(strip_tags($policy2->get('summary')), strip_tags($datauser->summary));
$this->assertEquals(strip_tags($policy2->get('content')), strip_tags($datauser->content));
// User can see policy documents.
$writer = writer::with_context($systemcontext);
$this->assertTrue($writer->has_any_data());
$subcontext = array_merge($versionsubcontext, [get_string('policynamedversion', 'tool_policy', $policy1->to_record())]);
$dataversion = $writer->get_data($subcontext);
$this->assertEquals($policy1->get('name'), $dataversion->name);
$this->assertEquals(get_string('yes'), $dataversion->createdbyme);
$subcontext = array_merge($versionsubcontext, [get_string('policynamedversion', 'tool_policy', $policy2->to_record())]);
$dataversion = $writer->get_data($subcontext);
$this->assertEquals($policy2->get('name'), $dataversion->name);
$this->assertEquals(get_string('yes'), $dataversion->createdbyme);
}
/**
* Helper method that creates a new policy for testing
*
* @param array $params
* @return policy_version
*/
protected function add_policy($params = []) {
static $counter = 0;
$counter++;
$defaults = [
'name' => 'Policy '.$counter,
'summary_editor' => ['text' => "P$counter summary", 'format' => FORMAT_HTML, 'itemid' => 0],
'content_editor' => ['text' => "P$counter content", 'format' => FORMAT_HTML, 'itemid' => 0],
];
$params = (array)$params + $defaults;
$formdata = \tool_policy\api::form_policydoc_data(new policy_version(0));
foreach ($params as $key => $value) {
$formdata->$key = $value;
}
return api::form_policydoc_add($formdata);
}
}
@@ -0,0 +1,126 @@
<?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/>.
namespace tool_policy;
use tool_policy\privacy\local\sitepolicy\handler;
use tool_policy\test\helper;
/**
* Unit tests for the {@link \tool_policy\privacy\local\sitepolicy\handler} class.
*
* @package tool_policy
* @category test
* @copyright 2018 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sitepolicy_handler_test extends \advanced_testcase {
/**
* Test behaviour of the {@link \tool_policy\privacy\local\sitepolicy\handler::get_redirect_url()} method.
*/
public function test_get_redirect_url(): void {
$this->resetAfterTest();
$this->setAdminUser();
// No redirect for guests.
$this->assertNull(handler::get_redirect_url(true));
// No redirect if there is no policy.
$this->assertNull(handler::get_redirect_url());
// No redirect if no policy for logged in users.
$policy1 = helper::add_policy(['audience' => policy_version::AUDIENCE_GUESTS])->to_record();
api::make_current($policy1->id);
$this->assertNull(handler::get_redirect_url());
// URL only when there is actually some policy to show.
$policy2 = helper::add_policy(['audience' => policy_version::AUDIENCE_LOGGEDIN])->to_record();
api::make_current($policy2->id);
$this->assertInstanceOf('moodle_url', handler::get_redirect_url());
}
/**
* Test behaviour of the {@link \tool_policy\privacy\local\sitepolicy\handler::get_embed_url()} method.
*/
public function test_get_embed_url(): void {
$this->resetAfterTest();
$this->setAdminUser();
// No embed if there is no policy.
$this->assertNull(handler::get_embed_url());
$this->assertNull(handler::get_embed_url(true));
$policy1 = helper::add_policy(['audience' => policy_version::AUDIENCE_GUESTS])->to_record();
api::make_current($policy1->id);
// Policy exists for guests only.
$this->assertNull(handler::get_embed_url());
$this->assertInstanceOf('moodle_url', handler::get_embed_url(true));
$policy2 = helper::add_policy(['audience' => policy_version::AUDIENCE_LOGGEDIN])->to_record();
api::make_current($policy2->id);
// Some policy exists for all users.
$this->assertInstanceOf('moodle_url', handler::get_embed_url());
$this->assertInstanceOf('moodle_url', handler::get_embed_url(true));
}
/**
* Test behaviour of the {@link \tool_policy\privacy\local\sitepolicy\handler::accept()} method.
*/
public function test_accept(): void {
global $DB, $USER;
$this->resetAfterTest();
// False if not logged in.
$this->setUser(0);
$this->assertFalse(handler::accept());
// Guests accept policies implicitly by continuing to use the site.
$this->setGuestUser();
$this->assertTrue(handler::accept());
// Create one compulsory and one optional policy.
$this->setAdminUser();
$policy1 = helper::add_policy(['optional' => policy_version::AGREEMENT_COMPULSORY])->to_record();
api::make_current($policy1->id);
$policy2 = helper::add_policy(['optional' => policy_version::AGREEMENT_OPTIONAL])->to_record();
api::make_current($policy2->id);
$user1 = $this->getDataGenerator()->create_user();
$this->assertEquals(0, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
$this->assertEmpty($DB->get_records('tool_policy_acceptances', ['userid' => $user1->id]));
$this->setUser($user1->id);
$this->assertEquals(0, $USER->policyagreed);
// Only the compulsory policy is marked as accepted when accepting via the handler.
$this->assertTrue(handler::accept());
$this->assertEquals(1, $DB->get_field('user', 'policyagreed', ['id' => $user1->id]));
$this->assertEquals(1, $USER->policyagreed);
$this->assertEquals(1, $DB->count_records('tool_policy_acceptances', ['userid' => $user1->id]));
$this->assertTrue($DB->record_exists('tool_policy_acceptances', ['userid' => $user1->id,
'policyversionid' => $policy1->id]));
}
/**
* Test presence of the {@link \tool_policy\privacy\local\sitepolicy\handler::signup_form()} method.
*/
public function test_signup_form(): void {
$this->assertTrue(method_exists('\tool_policy\privacy\local\sitepolicy\handler', 'signup_form'));
}
}
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<libraries>
<library>
<location>amd/src/jquery-eu-cookie-law-popup.js</location>
<name>jQuery EU Cookie Law popups</name>
<description>An easy-to-install jQuery plugin to create EU Cookie Law popups and for GDPR compliance.</description>
<version>1.1.3</version>
<license>MIT</license>
<repository>https://github.com/wimagguc/jquery-eu-cookie-law-popup</repository>
<copyrights>
<copyright>2015 Richard Dancsi</copyright>
</copyrights>
<customised/>
</library>
</libraries>
+68
View File
@@ -0,0 +1,68 @@
<?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/>.
/**
* View user acceptances to the policies
*
* @package tool_policy
* @copyright 2018 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__.'/../../../config.php');
require_once($CFG->dirroot.'/user/editlib.php');
$userid = optional_param('userid', null, PARAM_INT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
require_login();
$userid = $userid ?: $USER->id;
if (isguestuser() || isguestuser($userid)) {
throw new \moodle_exception('noguest');
}
$context = context_user::instance($userid);
if ($userid != $USER->id) {
// Check capability to view acceptances. No capability is needed to view your own acceptances.
if (!has_capability('tool/policy:acceptbehalf', $context)) {
require_capability('tool/policy:viewacceptances', $context);
}
$user = core_user::get_user($userid);
$PAGE->navigation->extend_for_user($user);
}
$title = get_string('policiesagreements', 'tool_policy');
$PAGE->set_context($context);
$PAGE->set_pagelayout('standard');
$PAGE->set_url(new moodle_url('/admin/tool/policy/user.php', ['userid' => $userid]));
$PAGE->set_title($title);
if ($userid == $USER->id &&
($profilenode = $PAGE->settingsnav->find('myprofile', null))) {
$profilenode->make_active();
}
$PAGE->navbar->add($title);
$output = $PAGE->get_renderer('tool_policy');
echo $output->header();
echo $output->heading($title);
$acceptances = new \tool_policy\output\acceptances($userid, $returnurl);
echo $output->render($acceptances);
$PAGE->requires->js_call_amd('tool_policy/acceptmodal', 'getInstance', [context_system::instance()->id]);
echo $output->footer();
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Plugin version and other meta-data are defined here.
*
* @package tool_policy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'tool_policy'; // Full name of the plugin (used for diagnostics).
+58
View File
@@ -0,0 +1,58 @@
<?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/>.
/**
* View current document policy version.
*
* Script parameters:
* versionid=<int> Policy version id, defaults to the current one.
* policyid=<int> Policy document id, defaults to the one matching the version.
* returnurl=<local url> URL to continue to after reading the policy document.
* behalfid=<id> The user id to view the policy version as (such as child's id).
* manage=<bool> View the policy as a part of the management UI (managedocs.php).
*
* @package tool_policy
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use tool_policy\api;
use tool_policy\output\page_viewdoc;
// Do not check for the site policies in require_login() to avoid the redirect loop.
define('NO_SITEPOLICY_CHECK', true);
// See the {@see page_viewdoc} for the access control checks.
require(__DIR__.'/../../../config.php'); // phpcs:ignore
$versionid = optional_param('versionid', null, PARAM_INT);
$policyid = $versionid ? optional_param('policyid', null, PARAM_INT) : required_param('policyid', PARAM_INT);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
$behalfid = optional_param('behalfid', null, PARAM_INT);
$manage = optional_param('manage', false, PARAM_BOOL);
$numpolicy = optional_param('numpolicy', null, PARAM_INT);
$totalpolicies = optional_param('totalpolicies', null, PARAM_INT);
$PAGE->set_context(context_system::instance());
$PAGE->set_pagelayout('standard');
$viewpage = new page_viewdoc($policyid, $versionid, $returnurl, $behalfid, $manage, $numpolicy, $totalpolicies);
$output = $PAGE->get_renderer('tool_policy');
echo $output->header();
echo $output->render($viewpage);
echo $output->footer();
+45
View File
@@ -0,0 +1,45 @@
<?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/>.
/**
* View all document policy with a version, one under another.
*
* Script parameters:
* -
*
* @package tool_policy
* @copyright 2018 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use tool_policy\api;
use tool_policy\output\page_viewalldoc;
// Do not check for the site policies in require_login() to avoid the redirect loop.
define('NO_SITEPOLICY_CHECK', true);
// See the {@see page_viewalldoc} for the access control checks.
require(__DIR__.'/../../../config.php'); // phpcs:ignore
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL); // A return URL.
$viewallpage = new page_viewalldoc($returnurl);
$output = $PAGE->get_renderer('tool_policy');
echo $output->header();
echo $output->render($viewallpage);
echo $output->footer();