2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Provides {@link tool_policy\output\renderer} class.
20 * @package tool_policy
22 * @copyright 2018 Sara Arjona <sara@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 namespace tool_policy\output;
28 defined('MOODLE_INTERNAL') || die();
31 use core\output\notification;
32 use core\session\manager;
41 use tool_policy\policy_version;
44 * Represents a page for showing all the policy documents which a user has to agree to.
46 * @copyright 2018 Sara Arjona <sara@moodle.com>
47 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
49 class page_agreedocs implements renderable, templatable {
51 /** @var array $policies List of public policies objects with information about the user acceptance. */
52 protected $policies = null;
54 /** @var array $agreedocs List of policy identifiers which the user has agreed using the form. */
55 protected $agreedocs = null;
57 /** @var string $action Form action to identify when user agreeds policies. */
58 protected $action = null;
60 /** @var int User id who wants to accept this page. */
61 protected $behalfid = null;
63 /** @var object User who wants to accept this page. */
64 protected $behalfuser = null;
66 /** @var boolean True if signup user has agreed to all the policies; false otherwise. */
67 protected $signupuserpolicyagreed = false;
69 /** @var array Info or error messages to show. */
70 protected $messages = [];
72 /** @var bool This is an existing user (rather than non-loggedin/guest). */
73 protected $isexistinguser;
76 * Prepare the page for rendering.
78 * @param array $agreedocs Array with the policy identifiers which the user has agreed using the form.
79 * @param int $behalfid The userid to accept the policy versions as (such as child's id).
80 * @param string $action Form action to identify when user agreeds policies.
82 public function __construct($agreedocs = null, $behalfid = 0, $action = null) {
84 $realuser = manager::get_realuser();
86 $this->agreedocs = $agreedocs;
87 if (empty($this->agreedocs)) {
88 $this->agreedocs = [];
91 $this->action = $action;
93 $this->isexistinguser = isloggedin() && !isguestuser();
94 $behalfid = $behalfid ?: $USER->id;
95 if ($realuser->id != $behalfid) {
96 $this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
97 $this->behalfid = $this->behalfuser->id;
100 $this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
101 if (empty($this->behalfid)) {
104 $userid = $this->behalfid;
106 $this->accept_and_revoke_policies();
107 $this->prepare_global_page_access($userid);
108 $this->prepare_user_acceptances($userid);
112 * Accept and revoke the policy versions.
113 * The capabilities for accepting/revoking policies are checked into the api functions.
116 protected function accept_and_revoke_policies() {
119 if ($this->isexistinguser) {
121 if (!empty($this->action) && confirm_sesskey()) {
122 // The form has been sent. Update policies acceptances according to $this->agreedocs.
123 $lang = current_language();
124 // Accept / revoke policies.
125 $acceptversionids = array();
126 foreach ($this->policies as $policy) {
127 if (in_array($policy->id, $this->agreedocs)) {
128 // Save policy version doc to accept it.
129 $acceptversionids[] = $policy->id;
131 // Revoke policy doc.
132 api::revoke_acceptance($policy->id, $this->behalfid);
135 // Accept all policy docs saved in $acceptversionids.
136 api::accept_policies($acceptversionids, $this->behalfid, null, $lang);
137 // Show a message to let know the user he/she must agree all the policies.
138 if (count($acceptversionids) != count($this->policies)) {
139 $message = (object) [
141 'text' => get_string('mustagreetocontinue', 'tool_policy')
144 $message = (object) [
146 'text' => get_string('acceptancessavedsucessfully', 'tool_policy')
149 $this->messages[] = $message;
150 } else if (!empty($this->policies) && empty($USER->policyagreed)) {
151 // Inform users they must agree to all policies before continuing.
152 $message = (object) [
154 'text' => get_string('mustagreetocontinue', 'tool_policy')
156 $this->messages[] = $message;
160 if (!empty($this->action) && confirm_sesskey()) {
161 // The form has been sent.
162 $currentpolicyversionids = [];
163 foreach ($this->policies as $policy) {
164 $currentpolicyversionids[] = $policy->id;
166 // If the user has accepted all the policies, add it to the session to let continue with the signup process.
167 $this->signupuserpolicyagreed = empty(array_diff($currentpolicyversionids, $this->agreedocs));
168 \cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed',
169 $this->signupuserpolicyagreed);
170 } else if (empty($this->policies)) {
171 // There are no policies to agree to. Update the policyagreed value to avoid show empty consent page.
172 \cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed', 1);
174 if (!empty($this->policies) && !$this->signupuserpolicyagreed) {
175 // During the signup process, inform users they must agree to all policies before continuing.
176 $message = (object) [
178 'text' => get_string('mustagreetocontinue', 'tool_policy')
180 $this->messages[] = $message;
186 * Before display the consent page, the user has to view all the still-non-accepted policy docs.
187 * This function checks if the non-accepted policy docs have been shown and redirect to them.
189 * @param int $userid User identifier who wants to access to the consent page.
190 * @param moodle_url $returnurl URL to return after shown the policy docs.
192 protected function redirect_to_policies($userid, $returnurl = null) {
193 $allpolicies = $this->policies;
194 if ($this->isexistinguser) {
195 $acceptances = api::get_user_acceptances($userid);
196 foreach ($allpolicies as $policy) {
197 if (api::is_user_version_accepted($userid, $policy->id, $acceptances)) {
198 // If this version is accepted by the user, remove from the pending policies list.
199 unset($allpolicies[array_search($policy, $allpolicies)]);
204 if (!empty($allpolicies)) {
205 $currentpolicyversionids = [];
206 foreach ($allpolicies as $policy) {
207 $currentpolicyversionids[] = $policy->id;
210 $cache = \cache::make('core', 'presignup');
211 $cachekey = 'tool_policy_viewedpolicies';
213 $viewedpolicies = $cache->get($cachekey);
214 if (!empty($viewedpolicies)) {
215 // Get the list of the policies docs which the user haven't viewed during this session.
216 $pendingpolicies = array_diff($currentpolicyversionids, $viewedpolicies);
218 $pendingpolicies = $currentpolicyversionids;
220 if (count($pendingpolicies) > 0) {
221 // Still is needed to show some policies docs. Save in the session and redirect.
222 $policyversionid = array_shift($pendingpolicies);
223 $viewedpolicies[] = $policyversionid;
224 $cache->set($cachekey, $viewedpolicies);
225 if (empty($returnurl)) {
226 $returnurl = new moodle_url('/admin/tool/policy/index.php');
228 $urlparams = ['versionid' => $policyversionid,
229 'returnurl' => $returnurl,
230 'numpolicy' => count($currentpolicyversionids) - count($pendingpolicies),
231 'totalpolicies' => count($currentpolicyversionids),
233 redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
239 * Redirect to signup page if defined or to $CFG->wwwroot if not.
241 protected function redirect_to_previous_url() {
244 if ($this->isexistinguser) {
246 if (!empty($SESSION->wantsurl)) {
247 $returnurl = $SESSION->wantsurl;
248 unset($SESSION->wantsurl);
250 $returnurl = new moodle_url('/admin/tool/policy/user.php');
253 // Non-authenticated user.
254 $issignup = \cache::make('core', 'presignup')->get('tool_policy_issignup');
256 // User came here from signup page - redirect back there.
257 $returnurl = new moodle_url('/login/signup.php');
258 \cache::make('core', 'presignup')->set('tool_policy_issignup', false);
260 // Guests should not be on this page unless it's part of signup - redirect home.
261 $returnurl = new moodle_url('/');
265 redirect($returnurl);
269 * Sets up the global $PAGE and performs the access checks.
273 protected function prepare_global_page_access($userid) {
274 global $PAGE, $SITE, $USER;
276 // Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
277 $newsignupuser = \cache::make('core', 'presignup')->get('tool_policy_issignup');
278 if (!$this->isexistinguser && !$newsignupuser) {
279 $this->redirect_to_previous_url();
282 // Check for correct user capabilities.
283 if ($this->isexistinguser) {
284 // For existing users, it's needed to check if they have the capability for accepting policies.
285 api::can_accept_policies($this->behalfid, true);
287 // For new users, the behalfid parameter is ignored.
288 if ($this->behalfid) {
289 redirect(new moodle_url('/admin/tool/policy/index.php'));
293 // If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
294 // redirect to the return page.
295 $hasagreedsignupuser = !$this->isexistinguser && $this->signupuserpolicyagreed;
296 $hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
297 if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
298 $this->redirect_to_previous_url();
302 if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
303 $myparams['userid'] = $this->behalfid;
305 $myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
307 // Redirect to policy docs before the consent page.
308 $this->redirect_to_policies($userid, $myurl);
311 $PAGE->set_context(context_system::instance());
312 $PAGE->set_url($myurl);
313 $PAGE->set_heading($SITE->fullname);
314 $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
315 $PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
319 * Prepare user acceptances.
323 protected function prepare_user_acceptances($userid) {
326 // Get all the policy version acceptances for this user.
327 $lang = current_language();
328 foreach ($this->policies as $policy) {
329 // Get a link to display the full policy document.
330 $policy->url = new moodle_url('/admin/tool/policy/view.php',
331 array('policyid' => $policy->policyid, 'returnurl' => qualified_me()));
332 $policyattributes = array('data-action' => 'view',
333 'data-versionid' => $policy->id,
334 'data-behalfid' => $this->behalfid);
335 $policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
337 // Check if this policy version has been agreed or not.
338 if ($this->isexistinguser) {
340 $versionagreed = false;
341 $acceptances = api::get_user_acceptances($userid);
342 $policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
343 if (!empty($policy->versionacceptance)) {
344 // The policy version has ever been agreed. Check if status = 1 to know if still is accepted.
345 $versionagreed = $policy->versionacceptance->status;
346 if ($versionagreed) {
347 if ($policy->versionacceptance->lang != $lang) {
348 // Add a message because this version has been accepted in a different language than the current one.
349 $policy->versionlangsagreed = get_string('policyversionacceptedinotherlang', 'tool_policy');
351 if ($policy->versionacceptance->usermodified != $userid && $USER->id == $userid) {
352 // Add a message because this version has been accepted in behalf of current user.
353 $policy->versionbehalfsagreed = get_string('policyversionacceptedinbehalf', 'tool_policy');
359 $versionagreed = in_array($policy->id, $this->agreedocs);
361 $policy->versionagreed = $versionagreed;
362 $policy->policylink = html_writer::link($policy->url, $policy->name);
363 $policy->policymodal = $policymodal;
368 * Export the page data for the mustache template.
370 * @param renderer_base $output renderer to be used to render the page elements.
373 public function export_for_template(renderer_base $output) {
377 if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
378 $myparams['userid'] = $this->behalfid;
381 'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
382 'myurl' => (new moodle_url('/admin/tool/policy/index.php', $myparams))->out(false),
383 'sesskey' => sesskey(),
386 if (!empty($this->messages)) {
387 foreach ($this->messages as $message) {
388 switch ($message->type) {
390 $data->messages[] = $output->notification($message->text, notification::NOTIFY_ERROR);
394 $data->messages[] = $output->notification($message->text, notification::NOTIFY_SUCCESS);
398 $data->messages[] = $output->notification($message->text, notification::NOTIFY_INFO);
404 $data->policies = array_values($this->policies);
406 // If viewing docs in behalf of other user, get his/her full name and profile link.
407 if (!empty($this->behalfuser)) {
408 $userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
409 has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
410 $data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
413 // User can cancel accepting policies only if it is a part of signup.
414 $data->cancancel = !isloggedin() || isguestuser();