Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * 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
}