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
@@ -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');
}
}
}
}