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 |
* View user acceptances to the policies
|
|
|
19 |
*
|
|
|
20 |
* @package tool_policy
|
|
|
21 |
* @copyright 2018 Marina Glancy
|
|
|
22 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
namespace tool_policy;
|
|
|
26 |
|
|
|
27 |
use tool_policy\output\acceptances_filter;
|
|
|
28 |
use tool_policy\output\renderer;
|
|
|
29 |
use tool_policy\output\user_agreement;
|
|
|
30 |
use core_user;
|
|
|
31 |
use stdClass;
|
|
|
32 |
|
|
|
33 |
defined('MOODLE_INTERNAL') || die();
|
|
|
34 |
|
|
|
35 |
global $CFG;
|
|
|
36 |
require_once($CFG->dirroot.'/lib/tablelib.php');
|
|
|
37 |
|
|
|
38 |
/**
|
|
|
39 |
* Class acceptances_table
|
|
|
40 |
*
|
|
|
41 |
* @package tool_policy
|
|
|
42 |
* @copyright 2018 Marina Glancy
|
|
|
43 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
44 |
*/
|
|
|
45 |
class acceptances_table extends \table_sql {
|
|
|
46 |
|
|
|
47 |
/** @var array */
|
|
|
48 |
protected $versionids;
|
|
|
49 |
|
|
|
50 |
/** @var acceptances_filter */
|
|
|
51 |
protected $acceptancesfilter;
|
|
|
52 |
|
|
|
53 |
/** @var renderer */
|
|
|
54 |
protected $output;
|
|
|
55 |
|
|
|
56 |
/**
|
|
|
57 |
* @var string[] The list of countries.
|
|
|
58 |
*/
|
|
|
59 |
protected $countries;
|
|
|
60 |
|
|
|
61 |
/** @var bool are there any users that this user can agree on behalf of */
|
|
|
62 |
protected $canagreeany = false;
|
|
|
63 |
|
|
|
64 |
/**
|
|
|
65 |
* Constructor.
|
|
|
66 |
*
|
|
|
67 |
* @param string $uniqueid Table identifier.
|
|
|
68 |
* @param acceptances_filter $acceptancesfilter
|
|
|
69 |
* @param renderer $output
|
|
|
70 |
*/
|
|
|
71 |
public function __construct($uniqueid, acceptances_filter $acceptancesfilter, renderer $output) {
|
|
|
72 |
global $CFG;
|
|
|
73 |
parent::__construct($uniqueid);
|
|
|
74 |
$this->set_attribute('id', 'acceptancetable');
|
|
|
75 |
$this->acceptancesfilter = $acceptancesfilter;
|
|
|
76 |
$this->is_downloading(optional_param('download', 0, PARAM_ALPHA), 'user_acceptances');
|
|
|
77 |
$this->baseurl = $acceptancesfilter->get_url();
|
|
|
78 |
$this->output = $output;
|
|
|
79 |
|
|
|
80 |
$this->versionids = [];
|
|
|
81 |
$versions = $acceptancesfilter->get_versions();
|
|
|
82 |
if (count($versions) > 1) {
|
|
|
83 |
foreach ($versions as $version) {
|
|
|
84 |
$this->versionids[$version->id] = $version->name;
|
|
|
85 |
}
|
|
|
86 |
} else {
|
|
|
87 |
$version = reset($versions);
|
|
|
88 |
$this->versionids[$version->id] = $version->name;
|
|
|
89 |
if ($version->status != policy_version::STATUS_ACTIVE) {
|
|
|
90 |
$this->versionids[$version->id] .= '<br>' . $version->revision;
|
|
|
91 |
}
|
|
|
92 |
}
|
|
|
93 |
|
|
|
94 |
// TODO Does not support custom user profile fields (MDL-70456).
|
|
|
95 |
$userfieldsapi = \core_user\fields::for_identity(\context_system::instance(), false)->with_userpic();
|
|
|
96 |
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
|
|
97 |
$extrafields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
|
|
|
98 |
|
|
|
99 |
$this->set_sql("$userfields",
|
|
|
100 |
"{user} u",
|
|
|
101 |
'u.id <> :siteguestid AND u.deleted = 0',
|
|
|
102 |
['siteguestid' => $CFG->siteguest]);
|
|
|
103 |
if (!$this->is_downloading()) {
|
|
|
104 |
$this->add_column_header('select', get_string('select'), false, 'colselect');
|
|
|
105 |
}
|
|
|
106 |
$this->add_column_header('fullname', get_string('fullnameuser', 'core'));
|
|
|
107 |
foreach ($extrafields as $field) {
|
|
|
108 |
$this->add_column_header($field, \core_user\fields::get_display_name($field));
|
|
|
109 |
}
|
|
|
110 |
|
|
|
111 |
if (!$this->is_downloading() && !has_capability('tool/policy:acceptbehalf', \context_system::instance())) {
|
|
|
112 |
// We will need to check capability to accept on behalf in each user's context, preload users contexts.
|
|
|
113 |
$this->sql->fields .= ',' . \context_helper::get_preload_record_columns_sql('ctx');
|
|
|
114 |
$this->sql->from .= ' JOIN {context} ctx ON ctx.contextlevel = :usercontextlevel AND ctx.instanceid = u.id';
|
|
|
115 |
$this->sql->params['usercontextlevel'] = CONTEXT_USER;
|
|
|
116 |
}
|
|
|
117 |
|
|
|
118 |
if ($this->acceptancesfilter->get_single_version()) {
|
|
|
119 |
$this->configure_for_single_version();
|
|
|
120 |
} else {
|
|
|
121 |
$this->configure_for_multiple_versions();
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
$this->build_sql_for_search_string($extrafields);
|
|
|
125 |
$this->build_sql_for_capability_filter();
|
|
|
126 |
$this->build_sql_for_roles_filter();
|
|
|
127 |
|
|
|
128 |
$this->sortable(true, 'firstname');
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
/**
|
|
|
132 |
* Remove randomness from the list by always sorting by user id in the end
|
|
|
133 |
*
|
|
|
134 |
* @return array
|
|
|
135 |
*/
|
|
|
136 |
public function get_sort_columns() {
|
|
|
137 |
$c = parent::get_sort_columns();
|
|
|
138 |
$c['u.id'] = SORT_ASC;
|
|
|
139 |
return $c;
|
|
|
140 |
}
|
|
|
141 |
|
|
|
142 |
/**
|
|
|
143 |
* Allows to add only one column name and header to the table (parent class methods only allow to set all).
|
|
|
144 |
*
|
|
|
145 |
* @param string $key
|
|
|
146 |
* @param string $label
|
|
|
147 |
* @param bool $sortable
|
|
|
148 |
* @param string $columnclass
|
|
|
149 |
*/
|
|
|
150 |
protected function add_column_header($key, $label, $sortable = true, $columnclass = '') {
|
|
|
151 |
if (empty($this->columns)) {
|
|
|
152 |
$this->define_columns([$key]);
|
|
|
153 |
$this->define_headers([$label]);
|
|
|
154 |
} else {
|
|
|
155 |
$this->columns[$key] = count($this->columns);
|
|
|
156 |
$this->column_style[$key] = array();
|
|
|
157 |
$this->column_class[$key] = $columnclass;
|
|
|
158 |
$this->columnsticky[$key] = '';
|
|
|
159 |
$this->column_suppress[$key] = false;
|
|
|
160 |
$this->headers[] = $label;
|
|
|
161 |
}
|
|
|
162 |
if ($columnclass !== null) {
|
|
|
163 |
$this->column_class($key, $columnclass);
|
|
|
164 |
}
|
|
|
165 |
if (!$sortable) {
|
|
|
166 |
$this->no_sorting($key);
|
|
|
167 |
}
|
|
|
168 |
}
|
|
|
169 |
|
|
|
170 |
/**
|
|
|
171 |
* Helper configuration method.
|
|
|
172 |
*/
|
|
|
173 |
protected function configure_for_single_version() {
|
|
|
174 |
$userfieldsapi = \core_user\fields::for_name();
|
|
|
175 |
$userfieldsmod = $userfieldsapi->get_sql('m', false, 'mod', '', false)->selects;
|
|
|
176 |
$v = key($this->versionids);
|
|
|
177 |
$this->sql->fields .= ", $userfieldsmod, a{$v}.status AS status{$v}, a{$v}.note, ".
|
|
|
178 |
"a{$v}.timemodified, a{$v}.usermodified AS usermodified{$v}";
|
|
|
179 |
|
|
|
180 |
$join = "JOIN {tool_policy_acceptances} a{$v} ON a{$v}.userid = u.id AND a{$v}.policyversionid=:versionid{$v}";
|
|
|
181 |
$filterstatus = $this->acceptancesfilter->get_status_filter();
|
|
|
182 |
if ($filterstatus == 1) {
|
|
|
183 |
$this->sql->from .= " $join AND a{$v}.status=1";
|
|
|
184 |
} else if ($filterstatus == 2) {
|
|
|
185 |
$this->sql->from .= " $join AND a{$v}.status=0";
|
|
|
186 |
} else {
|
|
|
187 |
$this->sql->from .= " LEFT $join";
|
|
|
188 |
}
|
|
|
189 |
|
|
|
190 |
$this->sql->from .= " LEFT JOIN {user} m ON m.id = a{$v}.usermodified AND m.id <> u.id AND a{$v}.status IS NOT NULL";
|
|
|
191 |
|
|
|
192 |
$this->sql->params['versionid' . $v] = $v;
|
|
|
193 |
|
|
|
194 |
if ($filterstatus === 0) {
|
|
|
195 |
$this->sql->where .= " AND a{$v}.status IS NULL";
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
$this->add_column_header('status' . $v, get_string('response', 'tool_policy'));
|
|
|
199 |
$this->add_column_header('timemodified', get_string('responseon', 'tool_policy'));
|
|
|
200 |
$this->add_column_header('usermodified' . $v, get_string('responseby', 'tool_policy'));
|
|
|
201 |
$this->add_column_header('note', get_string('acceptancenote', 'tool_policy'), false);
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
/**
|
|
|
205 |
* Helper configuration method.
|
|
|
206 |
*/
|
|
|
207 |
protected function configure_for_multiple_versions() {
|
|
|
208 |
$this->add_column_header('statusall', get_string('acceptancestatusoverall', 'tool_policy'));
|
|
|
209 |
$filterstatus = $this->acceptancesfilter->get_status_filter();
|
|
|
210 |
$statusall = [];
|
|
|
211 |
foreach ($this->versionids as $v => $versionname) {
|
|
|
212 |
$this->sql->fields .= ", a{$v}.status AS status{$v}, a{$v}.usermodified AS usermodified{$v}";
|
|
|
213 |
$join = "JOIN {tool_policy_acceptances} a{$v} ON a{$v}.userid = u.id AND a{$v}.policyversionid=:versionid{$v}";
|
|
|
214 |
if ($filterstatus == 1) {
|
|
|
215 |
$this->sql->from .= " {$join} AND a{$v}.status=1";
|
|
|
216 |
} else if ($filterstatus == 2) {
|
|
|
217 |
$this->sql->from .= " {$join} AND a{$v}.status=0";
|
|
|
218 |
} else {
|
|
|
219 |
$this->sql->from .= " LEFT {$join}";
|
|
|
220 |
}
|
|
|
221 |
$this->sql->params['versionid' . $v] = $v;
|
|
|
222 |
$this->add_column_header('status' . $v, $versionname);
|
|
|
223 |
$statusall[] = "COALESCE(a{$v}.status, 0)";
|
|
|
224 |
}
|
|
|
225 |
$this->sql->fields .= ",".join('+', $statusall)." AS statusall";
|
|
|
226 |
|
|
|
227 |
if ($filterstatus === 0) {
|
|
|
228 |
$statussql = [];
|
|
|
229 |
foreach ($this->versionids as $v => $versionname) {
|
|
|
230 |
$statussql[] = "a{$v}.status IS NULL";
|
|
|
231 |
}
|
|
|
232 |
$this->sql->where .= " AND (u.policyagreed = 0 OR ".join(" OR ", $statussql).")";
|
|
|
233 |
}
|
|
|
234 |
}
|
|
|
235 |
|
|
|
236 |
/**
|
|
|
237 |
* Download the data.
|
|
|
238 |
*/
|
|
|
239 |
public function download() {
|
|
|
240 |
\core\session\manager::write_close();
|
|
|
241 |
$this->out(0, false);
|
|
|
242 |
exit;
|
|
|
243 |
}
|
|
|
244 |
|
|
|
245 |
/**
|
|
|
246 |
* Get sql to add to where statement.
|
|
|
247 |
*
|
|
|
248 |
* @return string
|
|
|
249 |
*/
|
|
|
250 |
public function get_sql_where() {
|
|
|
251 |
list($where, $params) = parent::get_sql_where();
|
|
|
252 |
$where = preg_replace('/firstname/', 'u.firstname', $where);
|
|
|
253 |
$where = preg_replace('/lastname/', 'u.lastname', $where);
|
|
|
254 |
return [$where, $params];
|
|
|
255 |
}
|
|
|
256 |
|
|
|
257 |
/**
|
|
|
258 |
* Helper SQL query builder.
|
|
|
259 |
*
|
|
|
260 |
* @param array $userfields
|
|
|
261 |
*/
|
|
|
262 |
protected function build_sql_for_search_string($userfields) {
|
|
|
263 |
global $DB, $USER;
|
|
|
264 |
|
|
|
265 |
$search = $this->acceptancesfilter->get_search_strings();
|
|
|
266 |
if (empty($search)) {
|
|
|
267 |
return;
|
|
|
268 |
}
|
|
|
269 |
|
|
|
270 |
$wheres = [];
|
|
|
271 |
$params = [];
|
|
|
272 |
foreach ($search as $index => $keyword) {
|
|
|
273 |
$searchkey1 = 'search' . $index . '1';
|
|
|
274 |
$searchkey2 = 'search' . $index . '2';
|
|
|
275 |
$searchkey3 = 'search' . $index . '3';
|
|
|
276 |
$searchkey4 = 'search' . $index . '4';
|
|
|
277 |
$searchkey5 = 'search' . $index . '5';
|
|
|
278 |
$searchkey6 = 'search' . $index . '6';
|
|
|
279 |
$searchkey7 = 'search' . $index . '7';
|
|
|
280 |
|
|
|
281 |
$conditions = array();
|
|
|
282 |
// Search by fullname.
|
|
|
283 |
$fullname = $DB->sql_fullname('u.firstname', 'u.lastname');
|
|
|
284 |
$conditions[] = $DB->sql_like($fullname, ':' . $searchkey1, false, false);
|
|
|
285 |
|
|
|
286 |
// Search by email.
|
|
|
287 |
$email = $DB->sql_like('u.email', ':' . $searchkey2, false, false);
|
|
|
288 |
if (!in_array('email', $userfields)) {
|
|
|
289 |
$maildisplay = 'maildisplay' . $index;
|
|
|
290 |
$userid1 = 'userid' . $index . '1';
|
|
|
291 |
// Prevent users who hide their email address from being found by others
|
|
|
292 |
// who aren't allowed to see hidden email addresses.
|
|
|
293 |
$email = "(". $email ." AND (" .
|
|
|
294 |
"u.maildisplay <> :$maildisplay " .
|
|
|
295 |
"OR u.id = :$userid1". // User can always find himself.
|
|
|
296 |
"))";
|
|
|
297 |
$params[$maildisplay] = core_user::MAILDISPLAY_HIDE;
|
|
|
298 |
$params[$userid1] = $USER->id;
|
|
|
299 |
}
|
|
|
300 |
$conditions[] = $email;
|
|
|
301 |
|
|
|
302 |
// Search by idnumber.
|
|
|
303 |
$idnumber = $DB->sql_like('u.idnumber', ':' . $searchkey3, false, false);
|
|
|
304 |
if (!in_array('idnumber', $userfields)) {
|
|
|
305 |
$userid2 = 'userid' . $index . '2';
|
|
|
306 |
// Users who aren't allowed to see idnumbers should at most find themselves
|
|
|
307 |
// when searching for an idnumber.
|
|
|
308 |
$idnumber = "(". $idnumber . " AND u.id = :$userid2)";
|
|
|
309 |
$params[$userid2] = $USER->id;
|
|
|
310 |
}
|
|
|
311 |
$conditions[] = $idnumber;
|
|
|
312 |
|
|
|
313 |
// Search by middlename.
|
|
|
314 |
$middlename = $DB->sql_like('u.middlename', ':' . $searchkey4, false, false);
|
|
|
315 |
$conditions[] = $middlename;
|
|
|
316 |
|
|
|
317 |
// Search by alternatename.
|
|
|
318 |
$alternatename = $DB->sql_like('u.alternatename', ':' . $searchkey5, false, false);
|
|
|
319 |
$conditions[] = $alternatename;
|
|
|
320 |
|
|
|
321 |
// Search by firstnamephonetic.
|
|
|
322 |
$firstnamephonetic = $DB->sql_like('u.firstnamephonetic', ':' . $searchkey6, false, false);
|
|
|
323 |
$conditions[] = $firstnamephonetic;
|
|
|
324 |
|
|
|
325 |
// Search by lastnamephonetic.
|
|
|
326 |
$lastnamephonetic = $DB->sql_like('u.lastnamephonetic', ':' . $searchkey7, false, false);
|
|
|
327 |
$conditions[] = $lastnamephonetic;
|
|
|
328 |
|
|
|
329 |
$wheres[] = "(". implode(" OR ", $conditions) .") ";
|
|
|
330 |
$params[$searchkey1] = "%$keyword%";
|
|
|
331 |
$params[$searchkey2] = "%$keyword%";
|
|
|
332 |
$params[$searchkey3] = "%$keyword%";
|
|
|
333 |
$params[$searchkey4] = "%$keyword%";
|
|
|
334 |
$params[$searchkey5] = "%$keyword%";
|
|
|
335 |
$params[$searchkey6] = "%$keyword%";
|
|
|
336 |
$params[$searchkey7] = "%$keyword%";
|
|
|
337 |
}
|
|
|
338 |
|
|
|
339 |
$this->sql->where .= ' AND '.join(' AND ', $wheres);
|
|
|
340 |
$this->sql->params += $params;
|
|
|
341 |
}
|
|
|
342 |
|
|
|
343 |
/**
|
|
|
344 |
* If there is a filter to find users who can/cannot accept on their own behalf add it to the SQL query
|
|
|
345 |
*/
|
|
|
346 |
protected function build_sql_for_capability_filter() {
|
|
|
347 |
global $CFG;
|
|
|
348 |
$hascapability = $this->acceptancesfilter->get_capability_accept_filter();
|
|
|
349 |
if ($hascapability === null) {
|
|
|
350 |
return;
|
|
|
351 |
}
|
|
|
352 |
|
|
|
353 |
list($neededroles, $forbiddenroles) = get_roles_with_cap_in_context(\context_system::instance(), 'tool/policy:accept');
|
|
|
354 |
|
|
|
355 |
if (empty($neededroles)) {
|
|
|
356 |
// There are no roles that allow to accept agreement on one own's behalf.
|
|
|
357 |
$this->sql->where .= $hascapability ? ' AND 1=0' : '';
|
|
|
358 |
return;
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
if (empty($forbiddenroles)) {
|
|
|
362 |
// There are no roles that prohibit to accept agreement on one own's behalf.
|
|
|
363 |
$this->sql->where .= ' AND ' . $this->sql_has_role($neededroles, $hascapability);
|
|
|
364 |
return;
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
|
|
|
368 |
if (!empty($neededroles[$defaultuserroleid])) {
|
|
|
369 |
// Default role allows to accept agreement. Make sure user has/does not have one of the roles prohibiting it.
|
|
|
370 |
$this->sql->where .= ' AND ' . $this->sql_has_role($forbiddenroles, !$hascapability);
|
|
|
371 |
return;
|
|
|
372 |
}
|
|
|
373 |
|
|
|
374 |
if ($hascapability) {
|
|
|
375 |
// User has at least one role allowing to accept and no roles prohibiting.
|
|
|
376 |
$this->sql->where .= ' AND ' . $this->sql_has_role($neededroles);
|
|
|
377 |
$this->sql->where .= ' AND ' . $this->sql_has_role($forbiddenroles, false);
|
|
|
378 |
} else {
|
|
|
379 |
// Option 1: User has one of the roles prohibiting to accept.
|
|
|
380 |
$this->sql->where .= ' AND (' . $this->sql_has_role($forbiddenroles);
|
|
|
381 |
// Option 2: User has none of the roles allowing to accept.
|
|
|
382 |
$this->sql->where .= ' OR ' . $this->sql_has_role($neededroles, false) . ")";
|
|
|
383 |
}
|
|
|
384 |
}
|
|
|
385 |
|
|
|
386 |
/**
|
|
|
387 |
* Returns SQL snippet for users that have (do not have) one of the given roles in the system context
|
|
|
388 |
*
|
|
|
389 |
* @param array $roles list of roles indexed by role id
|
|
|
390 |
* @param bool $positive true: return users who HAVE roles; false: return users who DO NOT HAVE roles
|
|
|
391 |
* @return string
|
|
|
392 |
*/
|
|
|
393 |
protected function sql_has_role($roles, $positive = true) {
|
|
|
394 |
global $CFG;
|
|
|
395 |
if (empty($roles)) {
|
|
|
396 |
return $positive ? '1=0' : '1=1';
|
|
|
397 |
}
|
|
|
398 |
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
|
|
|
399 |
if (!empty($roles[$defaultuserroleid])) {
|
|
|
400 |
// No need to query, everybody has the default role.
|
|
|
401 |
return $positive ? '1=1' : '1=0';
|
|
|
402 |
}
|
|
|
403 |
return "u.id " . ($positive ? "" : "NOT") . " IN (
|
|
|
404 |
SELECT userid
|
|
|
405 |
FROM {role_assignments}
|
|
|
406 |
WHERE contextid = " . SYSCONTEXTID . " AND roleid IN (" . implode(',', array_keys($roles)) . ")
|
|
|
407 |
)";
|
|
|
408 |
}
|
|
|
409 |
|
|
|
410 |
/**
|
|
|
411 |
* If there is a filter by user roles add it to the SQL query.
|
|
|
412 |
*/
|
|
|
413 |
protected function build_sql_for_roles_filter() {
|
|
|
414 |
foreach ($this->acceptancesfilter->get_role_filters() as $roleid) {
|
|
|
415 |
$this->sql->where .= ' AND ' . $this->sql_has_role([$roleid => $roleid]);
|
|
|
416 |
}
|
|
|
417 |
}
|
|
|
418 |
|
|
|
419 |
/**
|
|
|
420 |
* Hook that can be overridden in child classes to wrap a table in a form
|
|
|
421 |
* for example. Called only when there is data to display and not
|
|
|
422 |
* downloading.
|
|
|
423 |
*/
|
|
|
424 |
public function wrap_html_start() {
|
|
|
425 |
echo \html_writer::start_tag('form',
|
|
|
426 |
['action' => new \moodle_url('/admin/tool/policy/accept.php')]);
|
|
|
427 |
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]);
|
|
|
428 |
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'returnurl',
|
|
|
429 |
'value' => $this->get_return_url()]);
|
|
|
430 |
foreach (array_keys($this->versionids) as $versionid) {
|
|
|
431 |
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'versionids[]',
|
|
|
432 |
'value' => $versionid]);
|
|
|
433 |
}
|
|
|
434 |
}
|
|
|
435 |
|
|
|
436 |
/**
|
|
|
437 |
* Hook that can be overridden in child classes to wrap a table in a form
|
|
|
438 |
* for example. Called only when there is data to display and not
|
|
|
439 |
* downloading.
|
|
|
440 |
*/
|
|
|
441 |
public function wrap_html_finish() {
|
|
|
442 |
global $PAGE;
|
|
|
443 |
if ($this->canagreeany) {
|
|
|
444 |
echo \html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'accept']);
|
|
|
445 |
echo \html_writer::empty_tag('input', ['type' => 'submit', 'data-action' => 'acceptmodal',
|
|
|
446 |
'value' => get_string('consentbulk', 'tool_policy'), 'class' => 'btn btn-primary mt-1']);
|
|
|
447 |
$PAGE->requires->js_call_amd('tool_policy/acceptmodal', 'getInstance', [\context_system::instance()->id]);
|
|
|
448 |
}
|
|
|
449 |
echo "</form>\n";
|
|
|
450 |
}
|
|
|
451 |
|
|
|
452 |
/**
|
|
|
453 |
* Render the table.
|
|
|
454 |
*/
|
|
|
455 |
public function display() {
|
|
|
456 |
$this->out(100, true);
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
/**
|
|
|
460 |
* Call appropriate methods on this table class to perform any processing on values before displaying in table.
|
|
|
461 |
* Takes raw data from the database and process it into human readable format, perhaps also adding html linking when
|
|
|
462 |
* displaying table as html, adding a div wrap, etc.
|
|
|
463 |
*
|
|
|
464 |
* See for example col_fullname below which will be called for a column whose name is 'fullname'.
|
|
|
465 |
*
|
|
|
466 |
* @param array|object $row row of data from db used to make one row of the table.
|
|
|
467 |
* @return array one row for the table, added using add_data_keyed method.
|
|
|
468 |
*/
|
|
|
469 |
public function format_row($row) {
|
|
|
470 |
\context_helper::preload_from_record($row);
|
|
|
471 |
$row->canaccept = false;
|
|
|
472 |
$row->user = \user_picture::unalias($row, [], $this->useridfield);
|
|
|
473 |
$row->select = null;
|
|
|
474 |
if (!$this->is_downloading()) {
|
|
|
475 |
if (has_capability('tool/policy:acceptbehalf', \context_system::instance()) ||
|
|
|
476 |
has_capability('tool/policy:acceptbehalf', \context_user::instance($row->id))) {
|
|
|
477 |
$row->canaccept = true;
|
|
|
478 |
$row->select = \html_writer::empty_tag('input',
|
|
|
479 |
['type' => 'checkbox', 'name' => 'userids[]', 'value' => $row->id, 'class' => 'usercheckbox',
|
|
|
480 |
'id' => 'selectuser' . $row->id]) .
|
|
|
481 |
\html_writer::tag('label', get_string('selectuser', 'tool_policy', $this->username($row->user, false)),
|
|
|
482 |
['for' => 'selectuser' . $row->id, 'class' => 'accesshide']);
|
|
|
483 |
$this->canagreeany = true;
|
|
|
484 |
}
|
|
|
485 |
}
|
|
|
486 |
return parent::format_row($row);
|
|
|
487 |
}
|
|
|
488 |
|
|
|
489 |
/**
|
|
|
490 |
* Get the column fullname value.
|
|
|
491 |
*
|
|
|
492 |
* @param stdClass $row
|
|
|
493 |
* @return string
|
|
|
494 |
*/
|
|
|
495 |
public function col_fullname($row) {
|
|
|
496 |
global $OUTPUT;
|
|
|
497 |
$userpic = $this->is_downloading() ? '' : $OUTPUT->user_picture($row->user);
|
|
|
498 |
return $userpic . $this->username($row->user, true);
|
|
|
499 |
}
|
|
|
500 |
|
|
|
501 |
/**
|
|
|
502 |
* User name with a link to profile
|
|
|
503 |
*
|
|
|
504 |
* @param stdClass $user
|
|
|
505 |
* @param bool $profilelink show link to profile (when we are downloading never show links)
|
|
|
506 |
* @return string
|
|
|
507 |
*/
|
|
|
508 |
protected function username($user, $profilelink = true) {
|
|
|
509 |
$canviewfullnames = has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
|
|
|
510 |
has_capability('moodle/site:viewfullnames', \context_user::instance($user->id));
|
|
|
511 |
$name = fullname($user, $canviewfullnames);
|
|
|
512 |
if (!$this->is_downloading() && $profilelink) {
|
|
|
513 |
$profileurl = new \moodle_url('/user/profile.php', array('id' => $user->id));
|
|
|
514 |
return \html_writer::link($profileurl, $name);
|
|
|
515 |
}
|
|
|
516 |
return $name;
|
|
|
517 |
}
|
|
|
518 |
|
|
|
519 |
/**
|
|
|
520 |
* Helper.
|
|
|
521 |
*/
|
|
|
522 |
protected function get_return_url() {
|
|
|
523 |
$pageurl = $this->baseurl;
|
|
|
524 |
if ($this->currpage) {
|
|
|
525 |
$pageurl = new \moodle_url($pageurl, [$this->request[TABLE_VAR_PAGE] => $this->currpage]);
|
|
|
526 |
}
|
|
|
527 |
return $pageurl;
|
|
|
528 |
}
|
|
|
529 |
|
|
|
530 |
/**
|
|
|
531 |
* Return agreement status
|
|
|
532 |
*
|
|
|
533 |
* @param int $versionid either id of an individual version or empty for overall status
|
|
|
534 |
* @param stdClass $row
|
|
|
535 |
* @return string
|
|
|
536 |
*/
|
|
|
537 |
protected function status($versionid, $row) {
|
|
|
538 |
$onbehalf = false;
|
|
|
539 |
$versions = $versionid ? [$versionid => $this->versionids[$versionid]] : $this->versionids; // List of versions.
|
|
|
540 |
$accepted = []; // List of versionids that user has accepted.
|
|
|
541 |
$declined = [];
|
|
|
542 |
|
|
|
543 |
foreach ($versions as $v => $name) {
|
|
|
544 |
if ($row->{'status' . $v} !== null) {
|
|
|
545 |
if (empty($row->{'status' . $v})) {
|
|
|
546 |
$declined[] = $v;
|
|
|
547 |
} else {
|
|
|
548 |
$accepted[] = $v;
|
|
|
549 |
}
|
|
|
550 |
$agreedby = $row->{'usermodified' . $v};
|
|
|
551 |
if ($agreedby && $agreedby != $row->id) {
|
|
|
552 |
$onbehalf = true;
|
|
|
553 |
}
|
|
|
554 |
}
|
|
|
555 |
}
|
|
|
556 |
|
|
|
557 |
$ua = new user_agreement($row->id, $accepted, $declined, $this->get_return_url(), $versions, $onbehalf, $row->canaccept);
|
|
|
558 |
|
|
|
559 |
if ($this->is_downloading()) {
|
|
|
560 |
return $ua->export_for_download();
|
|
|
561 |
|
|
|
562 |
} else {
|
|
|
563 |
return $this->output->render($ua);
|
|
|
564 |
}
|
|
|
565 |
}
|
|
|
566 |
|
|
|
567 |
/**
|
|
|
568 |
* Get the column timemodified value.
|
|
|
569 |
*
|
|
|
570 |
* @param stdClass $row
|
|
|
571 |
* @return string
|
|
|
572 |
*/
|
|
|
573 |
public function col_timemodified($row) {
|
|
|
574 |
if ($row->timemodified) {
|
|
|
575 |
if ($this->is_downloading()) {
|
|
|
576 |
// Use timestamp format readable for both machines and humans.
|
|
|
577 |
return date_format_string((int) $row->timemodified, '%Y-%m-%d %H:%M:%S %Z');
|
|
|
578 |
} else {
|
|
|
579 |
// Use localised calendar format.
|
|
|
580 |
return userdate($row->timemodified, get_string('strftimedatetime'));
|
|
|
581 |
}
|
|
|
582 |
} else {
|
|
|
583 |
return null;
|
|
|
584 |
}
|
|
|
585 |
}
|
|
|
586 |
|
|
|
587 |
/**
|
|
|
588 |
* Get the column note value.
|
|
|
589 |
*
|
|
|
590 |
* @param stdClass $row
|
|
|
591 |
* @return string
|
|
|
592 |
*/
|
|
|
593 |
public function col_note($row) {
|
|
|
594 |
if ($this->is_downloading()) {
|
|
|
595 |
return $row->note;
|
|
|
596 |
} else {
|
|
|
597 |
return format_text($row->note, FORMAT_MOODLE);
|
|
|
598 |
}
|
|
|
599 |
}
|
|
|
600 |
|
|
|
601 |
/**
|
|
|
602 |
* Get the column statusall value.
|
|
|
603 |
*
|
|
|
604 |
* @param stdClass $row
|
|
|
605 |
* @return string
|
|
|
606 |
*/
|
|
|
607 |
public function col_statusall($row) {
|
|
|
608 |
return $this->status(0, $row);
|
|
|
609 |
}
|
|
|
610 |
|
|
|
611 |
/**
|
|
|
612 |
* Generate the country column.
|
|
|
613 |
*
|
|
|
614 |
* @param \stdClass $data
|
|
|
615 |
* @return string
|
|
|
616 |
*/
|
|
|
617 |
public function col_country($data) {
|
|
|
618 |
if ($data->country && $this->countries === null) {
|
|
|
619 |
$this->countries = get_string_manager()->get_list_of_countries();
|
|
|
620 |
}
|
|
|
621 |
if (!empty($this->countries[$data->country])) {
|
|
|
622 |
return $this->countries[$data->country];
|
|
|
623 |
}
|
|
|
624 |
return '';
|
|
|
625 |
}
|
|
|
626 |
|
|
|
627 |
/**
|
|
|
628 |
* You can override this method in a child class. See the description of
|
|
|
629 |
* build_table which calls this method.
|
|
|
630 |
*
|
|
|
631 |
* @param string $column
|
|
|
632 |
* @param stdClass $row
|
|
|
633 |
* @return string
|
|
|
634 |
*/
|
|
|
635 |
public function other_cols($column, $row) {
|
|
|
636 |
if (preg_match('/^status([\d]+)$/', $column, $matches)) {
|
|
|
637 |
$versionid = $matches[1];
|
|
|
638 |
return $this->status($versionid, $row);
|
|
|
639 |
}
|
|
|
640 |
if (preg_match('/^usermodified([\d]+)$/', $column, $matches)) {
|
|
|
641 |
if ($row->$column && $row->$column != $row->id) {
|
|
|
642 |
$user = (object)['id' => $row->$column];
|
|
|
643 |
username_load_fields_from_object($user, $row, 'mod');
|
|
|
644 |
return $this->username($user, true);
|
|
|
645 |
}
|
|
|
646 |
return ''; // User agreed by themselves.
|
|
|
647 |
}
|
|
|
648 |
return parent::other_cols($column, $row);
|
|
|
649 |
}
|
|
|
650 |
}
|