Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
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.
8
//
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.
13
//
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/>.
16
 
17
/**
18
 * Provides {@link tool_policy\output\renderer} class.
19
 *
20
 * @package     tool_policy
21
 * @category    output
22
 * @copyright   2018 Sara Arjona <sara@moodle.com>
23
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace tool_policy\output;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
 
30
use context_system;
31
use core\output\notification;
32
use core\session\manager;
33
use core_user;
34
use html_writer;
35
use moodle_url;
36
use renderable;
37
use renderer_base;
38
use single_button;
39
use templatable;
40
use tool_policy\api;
41
use tool_policy\policy_version;
42
 
43
/**
44
 * Represents a page for showing all the policy documents which a user has to agree to.
45
 *
46
 * @copyright 2018 Sara Arjona <sara@moodle.com>
47
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
48
 */
49
class page_agreedocs implements renderable, templatable {
50
 
51
    /** @var array $policies List of public policies objects with information about the user acceptance. */
52
    protected $policies = null;
53
 
54
    /** @var array List of policy version ids that were displayed to the user to agree with. */
55
    protected $listdocs = null;
56
 
57
    /** @var array $agreedocs List of policy identifiers which the user has agreed using the form. */
58
    protected $agreedocs = null;
59
 
60
    /** @var array $declinedocs List of policy identifiers that the user declined. */
61
    protected $declinedocs = null;
62
 
63
    /** @var string $action Form action to identify when user agreeds policies. */
64
    protected $action = null;
65
 
66
    /** @var int User id who wants to accept this page. */
67
    protected $behalfid = null;
68
 
69
    /** @var object User who wants to accept this page. */
70
    protected $behalfuser = null;
71
 
72
    /** @var boolean True if signup user has agreed to all the policies; false otherwise. */
73
    protected $signupuserpolicyagreed = false;
74
 
75
    /** @var array Info or error messages to show. */
76
    protected $messages = [];
77
 
78
    /** @var bool This is an existing user (rather than non-loggedin/guest). */
79
    protected $isexistinguser;
80
 
81
    /**
82
     * Prepare the page for rendering.
83
     *
84
     * @param array $listdocs List of policy version ids that were displayed to the user to agree with.
85
     * @param array $agreedocs List of policy version ids that the user actually agreed with.
86
     * @param array $declinedocs List of policy version ids that the user declined.
87
     * @param int $behalfid The userid to accept the policy versions as (such as child's id).
88
     * @param string $action Form action to identify when user agreeds policies.
89
     */
90
    public function __construct(array $listdocs, array $agreedocs = [], array $declinedocs = [], $behalfid = 0, $action = null) {
91
        global $USER;
92
        $realuser = manager::get_realuser();
93
 
94
        $this->listdocs = $listdocs;
95
        $this->agreedocs = $agreedocs;
96
        $this->declinedocs = $declinedocs;
97
        $this->action = $action;
98
        $this->isexistinguser = isloggedin() && !isguestuser();
99
 
100
        $behalfid = $behalfid ?: $USER->id;
101
        if ($realuser->id != $behalfid) {
102
            $this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
103
            $this->behalfid = $this->behalfuser->id;
104
        }
105
 
106
        $this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
107
 
108
        if (!$this->isexistinguser) {
109
            // During the signup, show compulsory policies only.
110
            foreach ($this->policies as $ix => $policyversion) {
111
                if ($policyversion->optional == policy_version::AGREEMENT_OPTIONAL) {
112
                    unset($this->policies[$ix]);
113
                }
114
            }
115
            $this->policies = array_values($this->policies);
116
        }
117
 
118
        if (empty($this->behalfid)) {
119
            $userid = $USER->id;
120
        } else {
121
            $userid = $this->behalfid;
122
        }
123
 
124
        $this->accept_and_revoke_policies();
125
        $this->prepare_global_page_access($userid);
126
        $this->prepare_user_acceptances($userid);
127
    }
128
 
129
    /**
130
     * Accept and revoke the policy versions.
131
     * The capabilities for accepting/revoking policies are checked into the api functions.
132
     *
133
     */
134
    protected function accept_and_revoke_policies() {
135
        global $USER;
136
 
137
        if ($this->isexistinguser) {
138
            // Existing user.
139
            if (!empty($this->action) && confirm_sesskey()) {
140
                // The form has been sent, update policies acceptances.
141
                $lang = current_language();
142
                // Accept / revoke policies.
143
                $acceptversionids = [];
144
                $declineversionids = [];
145
 
146
                foreach ($this->policies as $policy) {
147
                    if (in_array($policy->id, $this->listdocs)) {
148
                        if (in_array($policy->id, $this->agreedocs)) {
149
                            $acceptversionids[] = $policy->id;
150
                        } else if (in_array($policy->id, $this->declinedocs)) {
151
                            $declineversionids[] = $policy->id;
152
                        } else {
153
                            // If the policy was displayed but not answered, revoke the eventually given acceptance.
154
                            api::revoke_acceptance($policy->id, $this->behalfid);
155
                        }
156
                    }
157
                }
158
 
159
                api::accept_policies($acceptversionids, $this->behalfid, null, $lang);
160
                api::decline_policies($declineversionids, $this->behalfid, null, $lang);
161
 
162
                // Show a message to let know the user he/she must agree all the policies.
163
                if ((count($acceptversionids) + count($declineversionids)) != count($this->policies)) {
164
                    $message = (object) [
165
                        'type' => 'error',
166
                        'text' => get_string('mustagreetocontinue', 'tool_policy')
167
                    ];
168
                } else {
169
                    $message = (object) [
170
                        'type' => 'success',
171
                        'text' => get_string('acceptancessavedsucessfully', 'tool_policy')
172
                    ];
173
                }
174
                $this->messages[] = $message;
175
            } else if (!empty($this->policies) && empty($USER->policyagreed)) {
176
                // Inform users they must agree to all policies before continuing.
177
                $message = (object) [
178
                    'type' => 'error',
179
                    'text' => get_string('mustagreetocontinue', 'tool_policy')
180
                ];
181
                $this->messages[] = $message;
182
            }
183
        } else {
184
            // New user.
185
            if (!empty($this->action) && confirm_sesskey()) {
186
                $currentpolicyversionids = [];
187
                $presignupcache = \cache::make('core', 'presignup');
188
                $acceptances = $presignupcache->get('tool_policy_policyversionidsagreed');
189
                if (!$acceptances) {
190
                    $acceptances = [];
191
                }
192
                foreach ($this->policies as $policy) {
193
                    $currentpolicyversionids[] = $policy->id;
194
                    if (in_array($policy->id, $this->listdocs)) {
195
                        if (in_array($policy->id, $this->agreedocs)) {
196
                            $acceptances[] = $policy->id;
197
                        } else {
198
                            $acceptances = array_values(array_diff($acceptances, [$policy->id]));
199
                        }
200
                    }
201
                }
202
                // If the user has accepted all the policies, add it to the session to let continue with the signup process.
203
                $this->signupuserpolicyagreed = empty(array_diff($currentpolicyversionids, $acceptances));
204
                $presignupcache->set('tool_policy_userpolicyagreed', $this->signupuserpolicyagreed);
205
                $presignupcache->set('tool_policy_policyversionidsagreed', $acceptances);
206
            } else if (empty($this->policies)) {
207
                // There are no policies to agree to. Update the policyagreed value to avoid show empty consent page.
208
                \cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed', 1);
209
            }
210
            if (!empty($this->policies) && !$this->signupuserpolicyagreed) {
211
                // During the signup process, inform users they must agree to all policies before continuing.
212
                $message = (object) [
213
                    'type' => 'error',
214
                    'text' => get_string('mustagreetocontinue', 'tool_policy')
215
                ];
216
                $this->messages[] = $message;
217
            }
218
        }
219
    }
220
 
221
    /**
222
     * Before display the consent page, the user has to view all the still-non-accepted policy docs.
223
     * This function checks if the non-accepted policy docs have been shown and redirect to them.
224
     *
225
     * @param int $userid User identifier who wants to access to the consent page.
226
     * @param moodle_url $returnurl URL to return after shown the policy docs.
227
     */
228
    protected function redirect_to_policies($userid, $returnurl = null) {
229
 
230
        // Make a list of all policies that the user has not answered yet.
231
        $allpolicies = $this->policies;
232
 
233
        if ($this->isexistinguser) {
234
            $acceptances = api::get_user_acceptances($userid);
235
            foreach ($allpolicies as $ix => $policy) {
236
                $isaccepted = api::is_user_version_accepted($userid, $policy->id, $acceptances);
237
                if ($isaccepted) {
238
                    // The user has accepted this policy, do not show it again.
239
                    unset($allpolicies[$ix]);
240
                } else if ($isaccepted === false && $policy->optional == policy_version::AGREEMENT_OPTIONAL) {
241
                    // The user declined this policy but the agreement was optional, do not show it.
242
                    unset($allpolicies[$ix]);
243
                } else {
244
                    // The user has not answered the policy yet, or the agreement is compulsory. Show it.
245
                    continue;
246
                }
247
            }
248
 
249
        } else {
250
            $presignupcache = \cache::make('core', 'presignup');
251
            $acceptances = $presignupcache->get('tool_policy_policyversionidsagreed');
252
            if ($acceptances) {
253
                foreach ($allpolicies as $ix => $policy) {
254
                    if (in_array($policy->id, $acceptances)) {
255
                        unset($allpolicies[$ix]);
256
                    }
257
                }
258
            }
259
        }
260
 
261
        if (!empty($allpolicies)) {
262
            // Check if some of the to-be-accepted policies should be agreed on their own page.
263
            foreach ($allpolicies as $policy) {
264
                if ($policy->agreementstyle == policy_version::AGREEMENTSTYLE_OWNPAGE) {
265
                    if (empty($returnurl)) {
266
                        $returnurl = (new moodle_url('/admin/tool/policy/index.php'))->out_as_local_url(false);
267
                    }
268
                    $urlparams = ['versionid' => $policy->id, 'returnurl' => $returnurl];
269
                    redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
270
                }
271
            }
272
 
273
            $currentpolicyversionids = [];
274
            foreach ($allpolicies as $policy) {
275
                $currentpolicyversionids[] = $policy->id;
276
            }
277
 
278
            $cache = \cache::make('core', 'presignup');
279
            $cachekey = 'tool_policy_viewedpolicies';
280
 
281
            $viewedpolicies = $cache->get($cachekey) ?: [];
282
            if (!empty($viewedpolicies)) {
283
                // Get the list of the policies docs which the user haven't viewed during this session.
284
                $pendingpolicies = array_diff($currentpolicyversionids, $viewedpolicies);
285
            } else {
286
                $pendingpolicies = $currentpolicyversionids;
287
            }
288
            if (count($pendingpolicies) > 0) {
289
                // Still is needed to show some policies docs. Save in the session and redirect.
290
                $policyversionid = array_shift($pendingpolicies);
291
                $viewedpolicies[] = $policyversionid;
292
                $cache->set($cachekey, $viewedpolicies);
293
                if (empty($returnurl)) {
294
                    $returnurl = new moodle_url('/admin/tool/policy/index.php');
295
                }
296
                $urlparams = ['versionid' => $policyversionid,
297
                              'returnurl' => $returnurl,
298
                              'numpolicy' => count($currentpolicyversionids) - count($pendingpolicies),
299
                              'totalpolicies' => count($currentpolicyversionids),
300
                ];
301
                redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
302
            }
303
        } else {
304
            // Update the policyagreed for the user to avoid infinite loop because there are no policies to-be-accepted.
305
            api::update_policyagreed($userid);
306
            $this->redirect_to_previous_url();
307
        }
308
    }
309
 
310
    /**
311
     * Redirect to signup page if defined or to $CFG->wwwroot if not.
312
     */
313
    protected function redirect_to_previous_url() {
314
        global $SESSION;
315
 
316
        if ($this->isexistinguser) {
317
            // Existing user.
318
            if (!empty($SESSION->wantsurl)) {
319
                $returnurl = $SESSION->wantsurl;
320
                unset($SESSION->wantsurl);
321
            } else {
322
                $returnurl = new moodle_url('/admin/tool/policy/user.php');
323
            }
324
        } else {
325
            // Non-authenticated user.
326
            $issignup = \cache::make('core', 'presignup')->get('tool_policy_issignup');
327
            if ($issignup) {
328
                // User came here from signup page - redirect back there.
329
                $returnurl = new moodle_url('/login/signup.php');
330
                \cache::make('core', 'presignup')->set('tool_policy_issignup', false);
331
            } else {
332
                // Guests should not be on this page unless it's part of signup - redirect home.
333
                $returnurl = new moodle_url('/');
334
            }
335
        }
336
 
337
        redirect($returnurl);
338
    }
339
 
340
    /**
341
     * Sets up the global $PAGE and performs the access checks.
342
     *
343
     * @param int $userid
344
     */
345
    protected function prepare_global_page_access($userid) {
346
        global $PAGE, $SITE, $USER;
347
 
348
        // Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
349
        $newsignupuser = \cache::make('core', 'presignup')->get('tool_policy_issignup');
350
        if (!$this->isexistinguser && !$newsignupuser) {
351
            $this->redirect_to_previous_url();
352
        }
353
 
354
        // Check for correct user capabilities.
355
        if ($this->isexistinguser) {
356
            // For existing users, it's needed to check if they have the capability for accepting policies.
357
            api::can_accept_policies($this->listdocs, $this->behalfid, true);
358
        } else {
359
            // For new users, the behalfid parameter is ignored.
360
            if ($this->behalfid) {
361
                redirect(new moodle_url('/admin/tool/policy/index.php'));
362
            }
363
        }
364
 
365
        // If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
366
        // redirect to the return page.
367
        $hasagreedsignupuser = !$this->isexistinguser && $this->signupuserpolicyagreed;
368
        $hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
369
        if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
370
            $this->redirect_to_previous_url();
371
        }
372
 
373
        $myparams = [];
374
        if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
375
            $myparams['userid'] = $this->behalfid;
376
        }
377
        $myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
378
 
379
        // Redirect to policy docs before the consent page.
380
        $this->redirect_to_policies($userid, $myurl);
381
 
382
        // Page setup.
383
        $PAGE->set_context(context_system::instance());
384
        $PAGE->set_url($myurl);
385
        $PAGE->set_heading($SITE->fullname);
386
        $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
387
        $PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
388
    }
389
 
390
    /**
391
     * Prepare user acceptances.
392
     *
393
     * @param int $userid
394
     */
395
    protected function prepare_user_acceptances($userid) {
396
        global $USER;
397
 
398
        // Get all the policy version acceptances for this user.
399
        $lang = current_language();
400
        foreach ($this->policies as $policy) {
401
            // Get a link to display the full policy document.
402
            $policy->url = new moodle_url('/admin/tool/policy/view.php',
403
                array('policyid' => $policy->policyid, 'returnurl' => qualified_me()));
404
            $policyattributes = array('data-action' => 'view',
405
                                      'data-versionid' => $policy->id,
406
                                      'data-behalfid' => $this->behalfid);
407
            $policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
408
 
409
            // Check if this policy version has been agreed or not.
410
            if ($this->isexistinguser) {
411
                // Existing user.
412
                $versionagreed = false;
413
                $versiondeclined = false;
414
                $acceptances = api::get_user_acceptances($userid);
415
                $policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
416
                if (!empty($policy->versionacceptance)) {
417
                    // The policy version has ever been replied to before. Check if status = 1 to know if still is accepted.
418
                    if ($policy->versionacceptance->status) {
419
                        $versionagreed = true;
420
                    } else {
421
                        $versiondeclined = true;
422
                    }
423
                    if ($versionagreed) {
424
                        if ($policy->versionacceptance->lang != $lang) {
425
                            // Add a message because this version has been accepted in a different language than the current one.
426
                            $policy->versionlangsagreed = get_string('policyversionacceptedinotherlang', 'tool_policy');
427
                        }
428
                        $usermodified = $policy->versionacceptance->usermodified;
429
                        if ($usermodified && $usermodified != $userid && $USER->id == $userid) {
430
                            // Add a message because this version has been accepted on behalf of current user.
431
                            $policy->versionbehalfsagreed = get_string('policyversionacceptedinbehalf', 'tool_policy');
432
                        }
433
                    }
434
                }
435
            } else {
436
                // New user.
437
                $versionagreed = in_array($policy->id, $this->agreedocs);
438
                $versiondeclined = false;
439
            }
440
            $policy->versionagreed = $versionagreed;
441
            $policy->versiondeclined = $versiondeclined;
442
            $policy->policylink = html_writer::link($policy->url, $policy->name);
443
            $policy->policymodal = $policymodal;
444
        }
445
    }
446
 
447
    /**
448
     * Export the page data for the mustache template.
449
     *
450
     * @param renderer_base $output renderer to be used to render the page elements.
451
     * @return \stdClass
452
     */
453
    public function export_for_template(renderer_base $output) {
454
        global $USER;
455
 
456
        $myparams = [];
457
        if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
458
            $myparams['userid'] = $this->behalfid;
459
        }
460
        $data = (object) [
461
            'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
462
            'myurl' => (new moodle_url('/admin/tool/policy/index.php', $myparams))->out(false),
463
            'sesskey' => sesskey(),
464
        ];
465
 
466
        if (!empty($this->messages)) {
467
            foreach ($this->messages as $message) {
468
                switch ($message->type) {
469
                    case 'error':
470
                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_ERROR);
471
                        break;
472
 
473
                    case 'success':
474
                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_SUCCESS);
475
                        break;
476
 
477
                    default:
478
                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_INFO);
479
                        break;
480
                }
481
            }
482
        }
483
 
484
        // Filter out policies already shown on their own page, keep just policies to be shown here on the consent page.
485
        $data->policies = array_values(array_filter($this->policies, function ($policy) {
486
            return $policy->agreementstyle == policy_version::AGREEMENTSTYLE_CONSENTPAGE;
487
        }));
488
 
489
        // If viewing docs in behalf of other user, get his/her full name and profile link.
490
        if (!empty($this->behalfuser)) {
491
            $userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
492
                        has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
493
            $data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
494
        }
495
 
496
        // User can cancel accepting policies only if it is a part of signup.
497
        $data->cancancel = !isloggedin() || isguestuser();
498
 
499
        return $data;
500
    }
501
 
502
}