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\local\helpers;
20
 
21
use stdClass;
22
use invalid_parameter_exception;
23
use core\persistent;
24
use core_reportbuilder\datasource;
25
use core_reportbuilder\manager;
1441 ariadna 26
use core_reportbuilder\table\{custom_report_table_view, system_report_table};
27
use core_reportbuilder\local\models\{audience as audience_model, column, filter, report as report_model, schedule};
1 efrain 28
use core_tag_tag;
29
 
30
/**
31
 * Helper class for manipulating custom reports and their elements (columns, filters, conditions, etc)
32
 *
33
 * @package     core_reportbuilder
34
 * @copyright   2021 Paul Holden <paulh@moodle.com>
35
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class report {
38
 
39
    /**
40
     * Create custom report
41
     *
42
     * @param stdClass $data
43
     * @param bool $default If $default is set to true it will populate report with default layout as defined by the selected
44
     *                      source. These include pre-defined columns, filters and conditions.
45
     * @return report_model
46
     */
47
    public static function create_report(stdClass $data, bool $default = true): report_model {
48
        $data->name = trim($data->name);
49
        $data->type = datasource::TYPE_CUSTOM_REPORT;
50
 
51
        // Create report persistent.
52
        $report = manager::create_report_persistent($data);
53
 
54
        // Add datasource default columns, filters and conditions to the report.
55
        if ($default) {
56
            $source = $report->get('source');
57
            /** @var datasource $datasource */
58
            $datasource = new $source($report);
59
            $datasource->add_default_columns();
60
            $datasource->add_default_filters();
61
            $datasource->add_default_conditions();
62
        }
63
 
64
        // Report tags.
65
        if (property_exists($data, "tags")) {
66
            core_tag_tag::set_item_tags('core_reportbuilder', 'reportbuilder_report', $report->get('id'),
67
                $report->get_context(), $data->tags);
68
        }
69
 
1441 ariadna 70
        // Report custom fields.
71
        $data->id = $report->get('id');
72
        \core_reportbuilder\customfield\report_handler::create()->instance_form_save($data, true);
73
 
1 efrain 74
        return $report;
75
    }
76
 
77
    /**
78
     * Update custom report
79
     *
80
     * @param stdClass $data
81
     * @return report_model
82
     */
83
    public static function update_report(stdClass $data): report_model {
84
        $report = report_model::get_record(['id' => $data->id, 'type' => datasource::TYPE_CUSTOM_REPORT]);
85
        if ($report === false) {
86
            throw new invalid_parameter_exception('Invalid report');
87
        }
88
 
89
        $report->set_many([
90
            'name' => trim($data->name),
91
            'uniquerows' => $data->uniquerows,
92
        ])->update();
93
 
94
        // Report tags.
95
        if (property_exists($data, "tags")) {
96
            core_tag_tag::set_item_tags('core_reportbuilder', 'reportbuilder_report', $report->get('id'),
97
                $report->get_context(), $data->tags);
98
        }
99
 
1441 ariadna 100
        // Report custom fields.
101
        \core_reportbuilder\customfield\report_handler::create()->instance_form_save($data, false);
102
 
1 efrain 103
        return $report;
104
    }
105
 
1441 ariadna 106
 
1 efrain 107
    /**
1441 ariadna 108
     * Duplicate custom report
109
     *
110
     * @param report_model $report The report to duplicate.
111
     * @param string $reportname
112
     * @param bool $duplicateaudiences
113
     * @param bool $duplicateschedules
114
     * @return report_model The duplicated report.
115
     */
116
    public static function duplicate_report(
117
        report_model $report,
118
        string $reportname,
119
        bool $duplicateaudiences,
120
        bool $duplicateschedules,
121
    ): report_model {
122
        $reportinstance = manager::get_report_from_persistent($report);
123
 
124
        // Copy the original report, removing properties to be re-created when duplicating.
125
        $record = $report->to_record();
126
        unset($record->id, $record->usercreated);
127
 
128
        // Create new report.
129
        $record->name = $reportname;
130
        $record->tags = core_tag_tag::get_item_tags_array('core_reportbuilder', 'reportbuilder_report', $report->get('id'));
131
        $newreport = static::create_report($record, false);
132
 
133
        // Duplicate report content.
134
        $columns = array_map(fn($column) => $column->get_persistent(), $reportinstance->get_active_columns());
135
        static::duplicate_report_content($columns, $newreport->get('id'));
136
 
137
        $conditions = array_map(fn($condition) => $condition->get_persistent(), $reportinstance->get_active_conditions());
138
        static::duplicate_report_content($conditions, $newreport->get('id'));
139
 
140
        $filters = array_map(fn($filter) => $filter->get_persistent(), $reportinstance->get_active_filters());
141
        static::duplicate_report_content($filters, $newreport->get('id'));
142
 
143
        // Duplicate audiences.
144
        if ($duplicateaudiences) {
145
            $audiencemap = [];
146
 
147
            foreach (audience::get_base_records($report->get('id')) as $audienceinstance) {
148
 
149
                // If user can't edit the current audience then do not copy it.
150
                if (!$audienceinstance->user_can_edit()) {
151
                    continue;
152
                }
153
 
154
                $audiencerecord = $audienceinstance->get_persistent()->to_record();
155
                $audiencerecordid = $audiencerecord->id;
156
                unset($audiencerecord->id, $audiencerecord->usercreated);
157
 
158
                $newaudience = (new audience_model(0, $audiencerecord))
159
                    ->set('reportid', $newreport->get('id'))
160
                    ->create();
161
 
162
                $audiencemap[$audiencerecordid] = $newaudience->get('id');
163
            }
164
 
165
            // Duplicate schedules and map them to the new audience ids.
166
            if ($duplicateschedules) {
167
                foreach (schedule::get_records(['reportid' => $report->get('id')]) as $schedule) {
168
                    $schedulerecord = $schedule->to_record();
169
                    unset($schedulerecord->id, $schedulerecord->usercreated);
170
 
171
                    // Map new audience ids with the old ones.
172
                    $audiences = array_map(
173
                        fn($audienceid) => $audiencemap[$audienceid] ?? 0,
174
                        (array) json_decode($schedulerecord->audiences),
175
                    );
176
 
177
                    (new schedule(0, $schedulerecord))
178
                        ->set_many([
179
                            'reportid' => $newreport->get('id'),
180
                            'audiences' => json_encode($audiences),
181
                        ])
182
                        ->create();
183
                }
184
            }
185
        }
186
 
187
        // Duplicate custom fields.
188
        $reportdata = $report->to_record();
189
        \core_reportbuilder\customfield\report_handler::create()->instance_form_before_set_data($reportdata);
190
        $reportdata->id = $newreport->get('id');
191
        \core_reportbuilder\customfield\report_handler::create()->instance_form_save($reportdata);
192
 
193
        return $newreport;
194
    }
195
 
196
    /**
1 efrain 197
     * Delete custom report
198
     *
199
     * @param int $reportid
200
     * @return bool
201
     * @throws invalid_parameter_exception
202
     */
203
    public static function delete_report(int $reportid): bool {
204
        $report = report_model::get_record(['id' => $reportid, 'type' => datasource::TYPE_CUSTOM_REPORT]);
205
        if ($report === false) {
206
            throw new invalid_parameter_exception('Invalid report');
207
        }
208
 
209
        // Report tags.
210
        core_tag_tag::remove_all_item_tags('core_reportbuilder', 'reportbuilder_report', $report->get('id'));
211
 
212
        return $report->delete();
213
    }
214
 
215
    /**
216
     * Add given column to report
217
     *
218
     * @param int $reportid
219
     * @param string $uniqueidentifier
220
     * @return column
221
     * @throws invalid_parameter_exception
222
     */
223
    public static function add_report_column(int $reportid, string $uniqueidentifier): column {
224
        $report = manager::get_report_from_id($reportid);
225
 
226
        // Ensure column is available.
227
        if (!array_key_exists($uniqueidentifier, $report->get_columns())) {
228
            throw new invalid_parameter_exception('Invalid column');
229
        }
230
 
231
        $column = new column(0, (object) [
232
            'reportid' => $reportid,
233
            'uniqueidentifier' => $uniqueidentifier,
234
            'columnorder' => column::get_max_columnorder($reportid, 'columnorder') + 1,
235
            'sortorder' => column::get_max_columnorder($reportid, 'sortorder') + 1,
236
        ]);
237
 
238
        return $column->create();
239
    }
240
 
241
    /**
242
     * Delete given column from report
243
     *
244
     * @param int $reportid
245
     * @param int $columnid
246
     * @return bool
247
     * @throws invalid_parameter_exception
248
     */
249
    public static function delete_report_column(int $reportid, int $columnid): bool {
250
        global $DB;
251
 
252
        $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
253
        if ($column === false) {
254
            throw new invalid_parameter_exception('Invalid column');
255
        }
256
 
257
        // After deletion, re-index remaining report columns.
258
        if ($result = $column->delete()) {
259
            $sqlupdateorder = '
260
                UPDATE {' . column::TABLE . '}
261
                   SET columnorder = columnorder - 1
262
                 WHERE reportid = :reportid
263
                   AND columnorder > :columnorder';
264
 
265
            $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'columnorder' => $column->get('columnorder')]);
266
        }
267
 
268
        return $result;
269
    }
270
 
271
    /**
272
     * Re-order given column within a report
273
     *
274
     * @param int $reportid
275
     * @param int $columnid
276
     * @param int $position
277
     * @return bool
278
     * @throws invalid_parameter_exception
279
     */
280
    public static function reorder_report_column(int $reportid, int $columnid, int $position): bool {
281
        $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
282
        if ($column === false) {
283
            throw new invalid_parameter_exception('Invalid column');
284
        }
285
 
286
        // Get the rest of the report columns, excluding the one we are moving.
287
        $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
288
            'reportid' => $reportid,
289
            'id' => $columnid,
290
        ], 'columnorder');
291
 
292
        return static::reorder_persistents_by_field($column, $columns, $position, 'columnorder');
293
    }
294
 
295
    /**
296
     * Re-order given column sorting within a report
297
     *
298
     * @param int $reportid
299
     * @param int $columnid
300
     * @param int $position
301
     * @return bool
302
     * @throws invalid_parameter_exception
303
     */
304
    public static function reorder_report_column_sorting(int $reportid, int $columnid, int $position): bool {
305
        $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
306
        if ($column === false) {
307
            throw new invalid_parameter_exception('Invalid column');
308
        }
309
 
310
        // Get the rest of the report columns, excluding the one we are moving.
311
        $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
312
            'reportid' => $reportid,
313
            'id' => $columnid,
314
        ], 'sortorder');
315
 
316
        return static::reorder_persistents_by_field($column, $columns, $position, 'sortorder');
317
    }
318
 
319
    /**
320
     * Toggle sorting options for given column within a report
321
     *
322
     * @param int $reportid
323
     * @param int $columnid
324
     * @param bool $enabled
325
     * @param int $direction
326
     * @return bool
327
     * @throws invalid_parameter_exception
328
     */
329
    public static function toggle_report_column_sorting(int $reportid, int $columnid, bool $enabled,
330
            int $direction = SORT_ASC): bool {
331
 
332
        $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
333
        if ($column === false) {
334
            throw new invalid_parameter_exception('Invalid column');
335
        }
336
 
337
        return $column->set_many([
338
            'sortenabled' => $enabled,
339
            'sortdirection' => $direction,
340
        ])->update();
341
    }
342
 
343
    /**
344
     * Add given condition to report
345
     *
346
     * @param int $reportid
347
     * @param string $uniqueidentifier
348
     * @return filter
349
     * @throws invalid_parameter_exception
350
     */
351
    public static function add_report_condition(int $reportid, string $uniqueidentifier): filter {
352
        $report = manager::get_report_from_id($reportid);
353
 
354
        // Ensure condition is available.
355
        if (!array_key_exists($uniqueidentifier, $report->get_conditions())) {
356
            throw new invalid_parameter_exception('Invalid condition');
357
        }
358
 
359
        // Ensure condition wasn't already added.
360
        if (array_key_exists($uniqueidentifier, $report->get_active_conditions())) {
361
            throw new invalid_parameter_exception('Duplicate condition');
362
        }
363
 
364
        $condition = new filter(0, (object) [
365
            'reportid' => $reportid,
366
            'uniqueidentifier' => $uniqueidentifier,
367
            'iscondition' => true,
368
            'filterorder' => filter::get_max_filterorder($reportid, true) + 1,
369
        ]);
370
 
371
        return $condition->create();
372
    }
373
 
374
    /**
375
     * Delete given condition from report
376
     *
377
     * @param int $reportid
378
     * @param int $conditionid
379
     * @return bool
380
     * @throws invalid_parameter_exception
381
     */
382
    public static function delete_report_condition(int $reportid, int $conditionid): bool {
383
        global $DB;
384
 
385
        $condition = filter::get_condition_record($reportid, $conditionid);
386
        if ($condition === false) {
387
            throw new invalid_parameter_exception('Invalid condition');
388
        }
389
 
390
        // After deletion, re-index remaining report conditions.
391
        if ($result = $condition->delete()) {
392
            $sqlupdateorder = '
393
                UPDATE {' . filter::TABLE . '}
394
                   SET filterorder = filterorder - 1
395
                 WHERE reportid = :reportid
396
                   AND filterorder > :filterorder
397
                   AND iscondition = 1';
398
 
399
            $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $condition->get('filterorder')]);
400
        }
401
 
402
        return $result;
403
    }
404
 
405
    /**
406
     * Re-order given condition within a report
407
     *
408
     * @param int $reportid
409
     * @param int $conditionid
410
     * @param int $position
411
     * @return bool
412
     * @throws invalid_parameter_exception
413
     */
414
    public static function reorder_report_condition(int $reportid, int $conditionid, int $position): bool {
415
        $condition = filter::get_condition_record($reportid, $conditionid);
416
        if ($condition === false) {
417
            throw new invalid_parameter_exception('Invalid condition');
418
        }
419
 
420
        // Get the rest of the report conditions, excluding the one we are moving.
421
        $conditions = filter::get_records_select('reportid = :reportid AND iscondition = 1 AND id <> :id', [
422
            'reportid' => $reportid,
423
            'id' => $conditionid,
424
        ], 'filterorder');
425
 
426
        return static::reorder_persistents_by_field($condition, $conditions, $position, 'filterorder');
427
    }
428
 
429
    /**
430
     * Add given filter to report
431
     *
432
     * @param int $reportid
433
     * @param string $uniqueidentifier
434
     * @return filter
435
     * @throws invalid_parameter_exception
436
     */
437
    public static function add_report_filter(int $reportid, string $uniqueidentifier): filter {
438
        $report = manager::get_report_from_id($reportid);
439
 
440
        // Ensure filter is available.
441
        if (!array_key_exists($uniqueidentifier, $report->get_filters())) {
442
            throw new invalid_parameter_exception('Invalid filter');
443
        }
444
 
445
        // Ensure filter wasn't already added.
446
        if (array_key_exists($uniqueidentifier, $report->get_active_filters())) {
447
            throw new invalid_parameter_exception('Duplicate filter');
448
        }
449
 
450
        $filter = new filter(0, (object) [
451
            'reportid' => $reportid,
452
            'uniqueidentifier' => $uniqueidentifier,
453
            'filterorder' => filter::get_max_filterorder($reportid) + 1,
454
        ]);
455
 
456
        return $filter->create();
457
    }
458
 
459
    /**
460
     * Delete given filter from report
461
     *
462
     * @param int $reportid
463
     * @param int $filterid
464
     * @return bool
465
     * @throws invalid_parameter_exception
466
     */
467
    public static function delete_report_filter(int $reportid, int $filterid): bool {
468
        global $DB;
469
 
470
        $filter = filter::get_filter_record($reportid, $filterid);
471
        if ($filter === false) {
472
            throw new invalid_parameter_exception('Invalid filter');
473
        }
474
 
475
        // After deletion, re-index remaining report filters.
476
        if ($result = $filter->delete()) {
477
            $sqlupdateorder = '
478
                UPDATE {' . filter::TABLE . '}
479
                   SET filterorder = filterorder - 1
480
                 WHERE reportid = :reportid
481
                   AND filterorder > :filterorder
482
                   AND iscondition = 0';
483
 
484
            $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $filter->get('filterorder')]);
485
        }
486
 
487
        return $result;
488
    }
489
 
490
    /**
491
     * Re-order given filter within a report
492
     *
493
     * @param int $reportid
494
     * @param int $filterid
495
     * @param int $position
496
     * @return bool
497
     * @throws invalid_parameter_exception
498
     */
499
    public static function reorder_report_filter(int $reportid, int $filterid, int $position): bool {
500
        $filter = filter::get_filter_record($reportid, $filterid);
501
        if ($filter === false) {
502
            throw new invalid_parameter_exception('Invalid filter');
503
        }
504
 
505
        // Get the rest of the report filters, excluding the one we are moving.
506
        $filters = filter::get_records_select('reportid = :reportid AND iscondition = 0 AND id <> :id', [
507
            'reportid' => $reportid,
508
            'id' => $filterid,
509
        ], 'filterorder');
510
 
511
        return static::reorder_persistents_by_field($filter, $filters, $position, 'filterorder');
512
    }
513
 
514
    /**
1441 ariadna 515
     * Get total row count for given custom or system report
1 efrain 516
     *
1441 ariadna 517
     * @param int $reportid
518
     * @param array $parameters Applicable for system reports only
519
     * @return int
520
     */
521
    public static function get_report_row_count(int $reportid, array $parameters = []): int {
522
        $report = new report_model($reportid);
523
        if ($report->get('type') === datasource::TYPE_CUSTOM_REPORT) {
524
            $table = custom_report_table_view::create($report->get('id'));
525
        } else {
526
            $table = system_report_table::create($report->get('id'), $parameters);
527
            $table->guess_base_url();
528
        }
529
        $table->setup();
530
        return $table->get_total_row_count();
531
    }
532
 
533
    /**
1 efrain 534
     * @deprecated since Moodle 4.1 - please do not use this function any more, {@see custom_report_column_cards_exporter}
535
     */
1441 ariadna 536
    #[\core\attribute\deprecated('custom_report_column_cards_exporter', since: '4.1', final: true)]
537
    public static function get_available_columns() {
538
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
539
    }
1 efrain 540
 
1441 ariadna 541
    /**
542
     * Duplicate report content to given report ID
543
     *
544
     * @param persistent[] $persistents
545
     * @param int $reportid
546
     */
547
    private static function duplicate_report_content(array $persistents, int $reportid): void {
548
        foreach ($persistents as $persistent) {
549
            $record = $persistent->to_record();
550
            unset($record->id, $record->usercreated);
1 efrain 551
 
1441 ariadna 552
            /** @var persistent $persistentclass */
553
            $persistentclass = get_class($persistent);
1 efrain 554
 
1441 ariadna 555
            (new $persistentclass(0, $record))
556
                ->set('reportid', $reportid)
557
                ->create();
1 efrain 558
        }
559
    }
560
 
561
    /**
562
     * Helper method for re-ordering given persistents (columns, filters, etc)
563
     *
564
     * @param persistent $persistent The persistent we are moving
565
     * @param persistent[] $persistents The rest of the persistents
566
     * @param int $position
567
     * @param string $field The field we need to update
568
     * @return bool
569
     */
570
    private static function reorder_persistents_by_field(persistent $persistent, array $persistents, int $position,
571
            string $field): bool {
572
 
573
        // Splice into new position.
574
        array_splice($persistents, $position - 1, 0, [$persistent]);
575
 
576
        $fieldorder = 1;
577
        foreach ($persistents as $persistent) {
578
            $persistent->set($field, $fieldorder++)
579
                ->update();
580
        }
581
 
582
        return true;
583
    }
584
}