MDL-63013 tool_policy: Change acceptance flow for policies with own page

The idea of the patch is to check the list of policies that are shown
before and on the consent page. If that list contains a policy that is
supposed to be accepted on its own page, the existing flow is
interrupted and the user is redirected to a view.php to display that
particular policy.

The view template for such a policy contains a button for getting the
user agreement. When pressed, the policy is marked as accepted and we
can go to start again.

To make this working during the signup, we need to extend the existing
logic and start tracking which particular policies the visitor accepted
prior reaching the signup form. Similarly, we need to start track which
particular policies were displayed and use that list when evaluating
which policies were unchecked on the consent page.
This commit is contained in:
David Mudrák 2018-08-29 23:43:57 +02:00
parent 23b663ed55
commit 362ae1b443
5 changed files with 91 additions and 25 deletions

View file

@ -51,6 +51,9 @@ 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;
@ -75,22 +78,20 @@ class page_agreedocs implements renderable, templatable {
/**
* Prepare the page for rendering.
*
* @param array $agreedocs Array with the policy identifiers which the user has agreed using the form.
* @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 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($agreedocs = null, $behalfid = 0, $action = null) {
public function __construct(array $listdocs, array $agreedocs = [], $behalfid = 0, $action = null) {
global $USER;
$realuser = manager::get_realuser();
$this->listdocs = $listdocs;
$this->agreedocs = $agreedocs;
if (empty($this->agreedocs)) {
$this->agreedocs = [];
}
$this->action = $action;
$this->isexistinguser = isloggedin() && !isguestuser();
$behalfid = $behalfid ?: $USER->id;
if ($realuser->id != $behalfid) {
$this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
@ -124,12 +125,14 @@ class page_agreedocs implements renderable, templatable {
// Accept / revoke policies.
$acceptversionids = array();
foreach ($this->policies as $policy) {
if (in_array($policy->id, $this->agreedocs)) {
// Save policy version doc to accept it.
$acceptversionids[] = $policy->id;
} else {
// Revoke policy doc.
api::revoke_acceptance($policy->id, $this->behalfid);
if (in_array($policy->id, $this->listdocs)) {
if (in_array($policy->id, $this->agreedocs)) {
// Save policy version doc to accept it.
$acceptversionids[] = $policy->id;
} else {
// If the policy was displayed but not agreed, revoke the eventually given acceptance.
api::revoke_acceptance($policy->id, $this->behalfid);
}
}
}
// Accept all policy docs saved in $acceptversionids.
@ -158,15 +161,26 @@ class page_agreedocs implements renderable, templatable {
} else {
// New user.
if (!empty($this->action) && confirm_sesskey()) {
// The form has been sent.
$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, $this->agreedocs));
\cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed',
$this->signupuserpolicyagreed);
$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);
@ -190,18 +204,41 @@ class page_agreedocs implements renderable, templatable {
* @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 accepted yet.
$allpolicies = $this->policies;
if ($this->isexistinguser) {
$acceptances = api::get_user_acceptances($userid);
foreach ($allpolicies as $policy) {
foreach ($allpolicies as $ix => $policy) {
if (api::is_user_version_accepted($userid, $policy->id, $acceptances)) {
// If this version is accepted by the user, remove from the pending policies list.
unset($allpolicies[array_search($policy, $allpolicies)]);
unset($allpolicies[$ix]);
}
}
} 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;
@ -232,6 +269,8 @@ class page_agreedocs implements renderable, templatable {
];
redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
}
} else {
$this->redirect_to_previous_url();
}
}
@ -401,7 +440,10 @@ class page_agreedocs implements renderable, templatable {
}
}
$data->policies = array_values($this->policies);
// 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)) {

View file

@ -151,6 +151,7 @@ class page_viewdoc implements renderable, templatable {
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
global $USER;
$data = (object) [
'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
@ -163,6 +164,18 @@ class page_viewdoc implements renderable, templatable {
$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,
'agreedoc[]' => $this->policy->id,
'submit' => 'accept',
'sesskey' => sesskey(),
]))->out(false);
}
}
$data->policy = clone($this->policy);
return $data;

View file

@ -18,7 +18,8 @@
* Show a user the policy documents to be agreed to.
*
* Script parameters:
* agreedoc=<array> Policy version id which have been accepted by the user.
* listdoc=<array> List of policy version ids that were displayed to the user to accept.
* agreedoc=<array> List of policy version ids that were accepted by the user.
* behalfid=<id> The user id to view the policy version as (such as child's id).
*
* @package tool_policy
@ -37,7 +38,8 @@ require(__DIR__.'/../../../config.php');
$submit = optional_param('submit', null, PARAM_NOTAGS);
$cancel = optional_param('cancel', null, PARAM_NOTAGS);
$agreedocs = optional_param_array('agreedoc', null, PARAM_INT);
$listdocs = optional_param_array('listdoc', [], PARAM_INT);
$agreedocs = optional_param_array('agreedoc', [], PARAM_INT);
$behalfid = optional_param('userid', null, PARAM_INT);
$PAGE->set_context(context_system::instance());
@ -45,6 +47,10 @@ $PAGE->set_pagelayout('standard');
$PAGE->set_url('/admin/tool/policy/index.php');
$PAGE->set_popup_notification_allowed(false);
if (array_diff($agreedocs, $listdocs)) {
throw new moodle_exception('invalidaccessparameter');
}
if (isloggedin() && !isguestuser()) {
// Existing user.
$haspermissionagreedocs = api::can_accept_policies($behalfid);
@ -61,7 +67,7 @@ if (!$haspermissionagreedocs) {
if (!$behalfid && \core\session\manager::is_loggedinas()) {
$behalfid = $USER->id;
}
$outputpage = new \tool_policy\output\page_agreedocs($agreedocs, $behalfid, $submit);
$outputpage = new \tool_policy\output\page_agreedocs($listdocs, $agreedocs, $behalfid, $submit);
}
$output = $PAGE->get_renderer('tool_policy');

View file

@ -92,6 +92,7 @@
<div class="agreedoc-form m-t-1">
<div class="agreedoc-checkbox">
<label>
<input value="{{id}}" name="listdoc[]" type="hidden" >
<input value="{{id}}" name="agreedoc[]" {{#versionagreed}}checked="{{.}}"{{/versionagreed}} type="checkbox">
<strong>{{# str }}iagree, tool_policy, {{{name}}} {{/ str }}</strong>
<i class="icon fa fa-exclamation-circle text-danger fa-fw" title="{{# str }} required {{/ str }}" ></i>

View file

@ -38,7 +38,8 @@
"content": "Policy <em>content</em>"
},
"returnurl": "#",
"editurl": "#"
"editurl": "#",
"accepturl": "#"
}
}}
@ -88,6 +89,9 @@
{{#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}}
<div class="pull-right">
<a href="#top">