Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
declare(strict_types=1);
18
 
19
namespace core_reportbuilder;
20
 
21
use action_menu_filler;
22
use coding_exception;
1441 ariadna 23
use core_reportbuilder\exception\report_access_exception;
1 efrain 24
use html_writer;
25
use stdClass;
26
use core\output\checkbox_toggleall;
27
use core_reportbuilder\local\models\report;
28
use core_reportbuilder\local\report\action;
29
use core_reportbuilder\local\report\base;
30
use core_reportbuilder\local\report\column;
31
 
32
/**
33
 * Base class for system reports
34
 *
35
 * @package     core_reportbuilder
36
 * @copyright   2020 Paul Holden <paulh@moodle.com>
37
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
abstract class system_report extends base {
40
 
41
    /** @var array $parameters */
42
    private $parameters;
43
 
44
    /** @var string[] $basefields List of base fields */
45
    private $basefields = [];
46
 
47
    /** @var callable $checkboxcallback */
48
    private $checkboxcallback = null;
49
 
50
    /** @var bool $filterformdefault Whether to use the default filters form */
51
    private $filterformdefault = true;
52
 
53
    /** @var action|action_menu_filler[] $actions */
54
    private $actions = [];
55
 
56
    /** @var column $initialsortcolumn */
57
    private $initialsortcolumn;
58
 
59
    /** @var int $initialsortdirection */
60
    private $initialsortdirection;
61
 
62
    /**
63
     * System report constructor.
64
     *
65
     * @param report $report
66
     * @param array $parameters
67
     */
68
    final public function __construct(report $report, array $parameters) {
69
        $this->parameters = $parameters;
70
 
71
        parent::__construct($report);
72
    }
73
 
74
    /**
75
     * Provide default implementation of the report name. Extending classes can implement this method to provide their own name
76
     *
77
     * @return string
78
     */
79
    public static function get_name(): string {
80
        $classparts = explode('\\', get_called_class());
81
        $classname = end($classparts);
82
 
83
        // Try to make human readable, capitalized and with spaces.
84
        return ucfirst(str_replace('_', ' ', $classname));
85
    }
86
 
87
    /**
88
     * Validates access to view this report
89
     *
90
     * This is necessary to implement independently of the page that would typically embed the report because
91
     * subsequent pages are requested via AJAX requests, and access should be validated each time
92
     *
1441 ariadna 93
     * Report parameters should also be considered when implementing this method
94
     *
1 efrain 95
     * @return bool
96
     */
97
    abstract protected function can_view(): bool;
98
 
99
    /**
100
     * Validate access to the report
101
     *
102
     * @throws report_access_exception
103
     */
104
    final public function require_can_view(): void {
105
        if (!$this->can_view()) {
106
            throw new report_access_exception();
107
        }
108
    }
109
 
110
    /**
111
     * Report validation
112
     *
113
     * @throws report_access_exception If user cannot access the report
114
     * @throws coding_exception If no default column are specified
115
     */
116
    protected function validate(): void {
117
        parent::validate();
118
 
119
        $this->require_can_view();
120
 
121
        // Ensure the report has some default columns specified.
122
        if (empty($this->get_columns())) {
123
            throw new coding_exception('No columns added');
124
        }
125
    }
126
 
127
    /**
128
     * Add list of fields that have to be always included in SQL query for actions and row classes
129
     *
130
     * @param string $sql SQL clause for the list of fields that only uses main table or base joins
131
     */
132
    final protected function add_base_fields(string $sql): void {
133
        $this->basefields[] = $sql;
134
    }
135
 
136
    /**
137
     * Return report base fields
138
     *
139
     * @return array
140
     */
141
    final public function get_base_fields(): array {
142
        return $this->basefields;
143
    }
144
 
145
    /**
146
     * Define toggle all checkbox for the report, required row data should be defined by calling {@see add_base_fields}
147
     *
148
     * @param callable $callback Callback to return value/label for each checkbox, implementing the following signature:
1441 ariadna 149
     *      function(stdClass $row): ?array containing value/label pair, or null if the checkbox should not be shown for the row
1 efrain 150
     */
151
    final protected function set_checkbox_toggleall(callable $callback): void {
152
        $this->checkboxcallback = $callback;
153
    }
154
 
155
    /**
156
     * Return instance of toggle all checkbox, if previously defined by {@see set_checkbox_toggleall}
157
     *
158
     * @param bool $ismaster
159
     * @param stdClass|null $row
160
     * @return checkbox_toggleall|null
161
     */
162
    final public function get_checkbox_toggleall(bool $ismaster, ?stdClass $row = null): ?checkbox_toggleall {
163
        if (!is_callable($this->checkboxcallback)) {
164
            return null;
165
        }
166
 
167
        // Generic content for the master checkbox, execute callback for those belonging to each row.
168
        if ($ismaster) {
169
            $value = '';
170
            $label = get_string('selectall');
171
        } else {
1441 ariadna 172
            $checkboxdata = ($this->checkboxcallback)($row);
173
            if ($checkboxdata === null) {
174
                return null;
175
            }
176
            [$value, $label] = $checkboxdata;
1 efrain 177
        }
178
 
179
        return new checkbox_toggleall('report-select-all', $ismaster, [
180
            'id' => html_writer::random_id(),
181
            'name' => 'report-select-row[]',
182
            'value' => $value,
183
            'label' => $label,
184
            'labelclasses' => 'accesshide',
185
        ]);
186
    }
187
 
188
    /**
189
     * Override whether to use the default system report filters form, for instance this can be disabled if the UI requires
190
     * it's own custom filter management form for a specific report
191
     *
192
     * @param bool $filterformdefault
193
     */
194
    final public function set_filter_form_default(bool $filterformdefault = true): void {
195
        $this->filterformdefault = $filterformdefault;
196
    }
197
 
198
    /**
199
     * Whether to use the default filters form
200
     *
201
     * @return bool
202
     */
203
    final public function get_filter_form_default(): bool {
204
        return $this->filterformdefault;
205
    }
206
 
207
    /**
208
     * Adds an action to the report
209
     *
210
     * @param action $action
211
     */
212
    final public function add_action(action $action): void {
213
        $this->actions[] = $action;
214
    }
215
 
216
    /**
217
     * Adds action divider to the report
218
     *
219
     */
220
    final public function add_action_divider(): void {
221
        $divider = new action_menu_filler();
222
        // We need to set as not primary action because we just need add an action divider, not a new action item.
223
        $divider->primary = false;
224
        $this->actions[] = $divider;
225
    }
226
 
227
    /**
228
     * Whether report has any actions
229
     *
230
     * @return bool
231
     */
232
    final public function has_actions(): bool {
233
        return !empty($this->actions);
234
    }
235
 
236
    /**
237
     * Return report actions
238
     *
239
     * @return action|action_menu_filler[]
240
     */
241
    final public function get_actions(): array {
242
        return $this->actions;
243
    }
244
 
245
    /**
246
     * Set all report parameters
247
     *
248
     * @param array $parameters
249
     */
250
    final public function set_parameters(array $parameters): void {
251
        $this->parameters = $parameters;
252
    }
253
 
254
    /**
255
     * Return all report parameters
256
     *
257
     * @return array
258
     */
259
    final public function get_parameters(): array {
260
        return $this->parameters;
261
    }
262
 
263
    /**
264
     * Return specific report parameter
265
     *
1441 ariadna 266
     * Capability/permission checks relating to parameters retrieved here should also be considered in your {@see can_view} method
267
     *
1 efrain 268
     * @param string $param
269
     * @param mixed $default
270
     * @param string $type
271
     * @return mixed
272
     */
273
    final public function get_parameter(string $param, $default, string $type) {
274
        if (!array_key_exists($param, $this->parameters)) {
275
            return $default;
276
        }
277
 
278
        return clean_param($this->parameters[$param], $type);
279
    }
280
 
281
    /**
282
     * Output the report
283
     *
284
     * @uses \core_reportbuilder\output\renderer::render_system_report()
285
     *
286
     * @return string
287
     */
288
    final public function output(): string {
289
        global $PAGE;
290
 
291
        /** @var \core_reportbuilder\output\renderer $renderer */
292
        $renderer = $PAGE->get_renderer('core_reportbuilder');
293
        $report = new \core_reportbuilder\output\system_report($this->get_report_persistent(), $this, $this->parameters);
294
 
295
        return $renderer->render($report);
296
    }
297
 
298
    /**
299
     * CSS classes to add to the row. Can be overridden by system reports do define class to be added to output according to
300
     * content of each row
301
     *
302
     * @param stdClass $row
303
     * @return string
304
     */
305
    public function get_row_class(stdClass $row): string {
306
        return '';
307
    }
308
 
309
    /**
310
     * Called before rendering each row. Can be overridden to pre-fetch/create objects and store them in the class, which can
311
     * later be used in column and action callbacks
312
     *
313
     * @param stdClass $row
314
     */
315
    public function row_callback(stdClass $row): void {
316
        return;
317
    }
318
 
319
    /**
320
     * Validates access to download this report.
321
     *
322
     * @return bool
323
     */
324
    final public function can_be_downloaded(): bool {
325
        return $this->can_view() && $this->is_downloadable();
326
    }
327
 
328
    /**
329
     * Return list of column names that will be excluded when table is downloaded. Extending classes should override this method
330
     * as appropriate
331
     *
332
     * @return string[] Array of column unique identifiers
333
     */
334
    public function get_exclude_columns_for_download(): array {
335
        return [];
336
    }
337
 
338
    /**
339
     * Set initial sort column and sort direction for the report
340
     *
341
     * @param string $uniqueidentifier
342
     * @param int $sortdirection One of SORT_ASC or SORT_DESC
343
     * @throws coding_exception
344
     */
345
    public function set_initial_sort_column(string $uniqueidentifier, int $sortdirection): void {
346
        if (!$sortcolumn = $this->get_column($uniqueidentifier)) {
347
            throw new coding_exception('Unknown column identifier', $uniqueidentifier);
348
        }
349
 
350
        $this->initialsortcolumn = $sortcolumn;
351
        $this->initialsortdirection = $sortdirection;
352
    }
353
 
354
    /**
355
     * Get initial sort column
356
     *
357
     * @return column|null
358
     */
359
    public function get_initial_sort_column(): ?column {
360
        return $this->initialsortcolumn;
361
    }
362
 
363
    /**
364
     * Get initial sort column direction
365
     *
366
     * @return int
367
     */
368
    public function get_initial_sort_direction(): int {
369
        return $this->initialsortdirection;
370
    }
371
}