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
 * Database module external API
19
 *
20
 * @package    mod_data
21
 * @category   external
22
 * @copyright  2015 Juan Leyva <juan@moodle.com>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 * @since      Moodle 2.9
25
 */
26
 
27
defined('MOODLE_INTERNAL') || die;
28
 
29
require_once($CFG->dirroot . "/mod/data/locallib.php");
30
 
31
use core_external\external_api;
32
use core_external\external_function_parameters;
33
use core_external\external_multiple_structure;
34
use core_external\external_single_structure;
35
use core_external\external_value;
36
use core_external\external_warnings;
37
use core_external\util;
38
use mod_data\external\database_summary_exporter;
39
use mod_data\external\record_exporter;
40
use mod_data\external\field_exporter;
41
use mod_data\manager;
42
 
43
/**
44
 * Database module external functions
45
 *
46
 * @package    mod_data
47
 * @category   external
48
 * @copyright  2015 Juan Leyva <juan@moodle.com>
49
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
50
 * @since      Moodle 2.9
51
 */
52
class mod_data_external extends external_api {
53
 
54
    /**
55
     * Describes the parameters for get_databases_by_courses.
56
     *
57
     * @return external_function_parameters
58
     * @since Moodle 2.9
59
     */
60
    public static function get_databases_by_courses_parameters() {
61
        return new external_function_parameters (
62
            array(
63
                'courseids' => new external_multiple_structure(
64
                    new external_value(PARAM_INT, 'course id', VALUE_REQUIRED),
65
                    'Array of course ids', VALUE_DEFAULT, array()
66
                ),
67
            )
68
        );
69
    }
70
 
71
    /**
72
     * Returns a list of databases in a provided list of courses,
73
     * if no list is provided all databases that the user can view will be returned.
74
     *
75
     * @param array $courseids the course ids
76
     * @return array the database details
77
     * @since Moodle 2.9
78
     */
79
    public static function get_databases_by_courses($courseids = array()) {
80
        global $PAGE;
81
 
82
        $params = self::validate_parameters(self::get_databases_by_courses_parameters(), array('courseids' => $courseids));
83
        $warnings = array();
84
 
85
        $mycourses = array();
86
        if (empty($params['courseids'])) {
87
            $mycourses = enrol_get_my_courses();
88
            $params['courseids'] = array_keys($mycourses);
89
        }
90
 
91
        // Array to store the databases to return.
92
        $arrdatabases = array();
93
 
94
        // Ensure there are courseids to loop through.
95
        if (!empty($params['courseids'])) {
96
 
97
            list($dbcourses, $warnings) = util::validate_courses($params['courseids'], $mycourses);
98
 
99
            // Get the databases in this course, this function checks users visibility permissions.
100
            // We can avoid then additional validate_context calls.
101
            $databases = get_all_instances_in_courses("data", $dbcourses);
102
 
103
            foreach ($databases as $database) {
104
 
105
                $context = context_module::instance($database->coursemodule);
106
                // Remove fields added by get_all_instances_in_courses.
107
                unset($database->coursemodule, $database->section, $database->visible, $database->groupmode, $database->groupingid);
108
 
109
                // This information should be only available if the user can see the database entries.
110
                if (!has_capability('mod/data:viewentry', $context)) {
111
                    $fields = array('comments', 'timeavailablefrom', 'timeavailableto', 'timeviewfrom',
112
                                    'timeviewto', 'requiredentries', 'requiredentriestoview', 'maxentries', 'rssarticles',
113
                                    'singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter', 'addtemplate',
114
                                    'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate', 'asearchtemplate', 'approval',
115
                                    'manageapproved', 'defaultsort', 'defaultsortdir');
116
 
117
                    foreach ($fields as $field) {
118
                        unset($database->{$field});
119
                    }
120
                }
121
 
122
                // Check additional permissions for returning optional private settings.
123
                // I avoid intentionally to use can_[add|update]_moduleinfo.
124
                if (!has_capability('moodle/course:manageactivities', $context)) {
125
 
126
                    $fields = array('scale', 'assessed', 'assesstimestart', 'assesstimefinish', 'editany', 'notification',
127
                                    'timemodified');
128
 
129
                    foreach ($fields as $field) {
130
                        unset($database->{$field});
131
                    }
132
                }
133
                $exporter = new database_summary_exporter($database, array('context' => $context));
134
                $data = $exporter->export($PAGE->get_renderer('core'));
135
                $data->name = \core_external\util::format_string($data->name, $context);
136
                $arrdatabases[] = $data;
137
            }
138
        }
139
 
140
        $result = array();
141
        $result['databases'] = $arrdatabases;
142
        $result['warnings'] = $warnings;
143
        return $result;
144
    }
145
 
146
    /**
147
     * Describes the get_databases_by_courses return value.
148
     *
149
     * @return external_single_structure
150
     * @since Moodle 2.9
151
     */
152
    public static function get_databases_by_courses_returns() {
153
 
154
        return new external_single_structure(
155
            array(
156
                'databases' => new external_multiple_structure(
157
                    database_summary_exporter::get_read_structure()
158
                ),
159
                'warnings' => new external_warnings(),
160
            )
161
        );
162
    }
163
 
164
    /**
165
     * Utility function for validating a database.
166
     *
167
     * @param int $databaseid database instance id
168
     * @return array array containing the database object, course, context and course module objects
169
     * @since  Moodle 3.3
170
     */
171
    protected static function validate_database($databaseid) {
172
        global $DB;
173
 
174
        // Request and permission validation.
175
        $database = $DB->get_record('data', array('id' => $databaseid), '*', MUST_EXIST);
176
        list($course, $cm) = get_course_and_cm_from_instance($database, 'data');
177
 
178
        $context = context_module::instance($cm->id);
179
        self::validate_context($context);
180
        require_capability('mod/data:viewentry', $context);
181
 
182
        return array($database, $course, $cm, $context);
183
    }
184
 
185
    /**
186
     * Returns description of method parameters
187
     *
188
     * @return external_function_parameters
189
     * @since Moodle 3.3
190
     */
191
    public static function view_database_parameters() {
192
        return new external_function_parameters(
193
            array(
194
                'databaseid' => new external_value(PARAM_INT, 'data instance id')
195
            )
196
        );
197
    }
198
 
199
    /**
200
     * Simulate the data/view.php web interface page: trigger events, completion, etc...
201
     *
202
     * @param int $databaseid the data instance id
203
     * @return array of warnings and status result
204
     * @since Moodle 3.3
205
     * @throws moodle_exception
206
     */
207
    public static function view_database($databaseid) {
208
 
209
        $params = self::validate_parameters(self::view_database_parameters(), array('databaseid' => $databaseid));
210
        $warnings = array();
211
 
212
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
213
 
214
        // Call the data/lib API.
215
        $manager = manager::create_from_coursemodule($cm);
216
        $manager->set_module_viewed($course);
217
 
218
        $result = [
219
            'status' => true,
220
            'warnings' => $warnings,
221
        ];
222
        return $result;
223
    }
224
 
225
    /**
226
     * Returns description of method result value
227
     *
228
     * @return \core_external\external_description
229
     * @since Moodle 3.3
230
     */
231
    public static function view_database_returns() {
232
        return new external_single_structure(
233
            array(
234
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
235
                'warnings' => new external_warnings()
236
            )
237
        );
238
    }
239
 
240
    /**
241
     * Returns description of method parameters.
242
     *
243
     * @return external_function_parameters
244
     * @since Moodle 3.3
245
     */
246
    public static function get_data_access_information_parameters() {
247
        return new external_function_parameters(
248
            array(
249
                'databaseid' => new external_value(PARAM_INT, 'Database instance id.'),
250
                'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group.',
251
                                                   VALUE_DEFAULT, 0),
252
            )
253
        );
254
    }
255
 
256
    /**
257
     * Return access information for a given database.
258
     *
259
     * @param int $databaseid the database instance id
260
     * @param int $groupid (optional) group id, 0 means that the function will determine the user group
261
     * @return array of warnings and access information
262
     * @since Moodle 3.3
263
     * @throws moodle_exception
264
     */
265
    public static function get_data_access_information($databaseid, $groupid = 0) {
266
 
267
        $params = array('databaseid' => $databaseid, 'groupid' => $groupid);
268
        $params = self::validate_parameters(self::get_data_access_information_parameters(), $params);
269
        $warnings = array();
270
 
271
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
272
 
273
        $result = array(
274
            'warnings' => $warnings
275
        );
276
 
277
        $groupmode = groups_get_activity_groupmode($cm);
278
        if (!empty($params['groupid'])) {
279
            $groupid = $params['groupid'];
280
            // Determine is the group is visible to user.
281
            if (!groups_group_visible($groupid, $course, $cm)) {
282
                throw new moodle_exception('notingroup');
283
            }
284
        } else {
285
            // Check to see if groups are being used here.
286
            if ($groupmode) {
287
                $groupid = groups_get_activity_group($cm);
288
            } else {
289
                $groupid = 0;
290
            }
291
        }
292
        // Group related information.
293
        $result['groupid'] = $groupid;
294
        $result['canaddentry'] = data_user_can_add_entry($database, $groupid, $groupmode, $context);
295
 
296
        // Now capabilities.
297
        $result['canmanageentries'] = has_capability('mod/data:manageentries', $context);
298
        $result['canapprove'] = has_capability('mod/data:approve', $context);
299
 
300
        // Now time access restrictions.
301
        list($result['timeavailable'], $warnings) = data_get_time_availability_status($database, $result['canmanageentries']);
302
 
303
        // Other information.
304
        $result['numentries'] = data_numentries($database);
305
        $result['entrieslefttoadd'] = data_get_entries_left_to_add($database, $result['numentries'], $result['canmanageentries']);
306
        $result['entrieslefttoview'] = data_get_entries_left_to_view($database, $result['numentries'], $result['canmanageentries']);
307
        $result['inreadonlyperiod'] = data_in_readonly_period($database);
308
 
309
        return $result;
310
    }
311
 
312
    /**
313
     * Returns description of method result value.
314
     *
315
     * @return \core_external\external_description
316
     * @since Moodle 3.3
317
     */
318
    public static function get_data_access_information_returns() {
319
        return new external_single_structure(
320
            array(
321
                'groupid' => new external_value(PARAM_INT, 'User current group id (calculated)'),
322
                'canaddentry' => new external_value(PARAM_BOOL, 'Whether the user can add entries or not.'),
323
                'canmanageentries' => new external_value(PARAM_BOOL, 'Whether the user can manage entries or not.'),
324
                'canapprove' => new external_value(PARAM_BOOL, 'Whether the user can approve entries or not.'),
325
                'timeavailable' => new external_value(PARAM_BOOL, 'Whether the database is available or not by time restrictions.'),
326
                'inreadonlyperiod' => new external_value(PARAM_BOOL, 'Whether the database is in read mode only.'),
327
                'numentries' => new external_value(PARAM_INT, 'The number of entries the current user added.'),
328
                'entrieslefttoadd' => new external_value(PARAM_INT, 'The number of entries left to complete the activity.'),
329
                'entrieslefttoview' => new external_value(PARAM_INT, 'The number of entries left to view other users entries.'),
330
                'warnings' => new external_warnings()
331
            )
332
        );
333
    }
334
 
335
    /**
336
     * Returns description of method parameters
337
     *
338
     * @return external_function_parameters
339
     * @since Moodle 3.3
340
     */
341
    public static function get_entries_parameters() {
342
        return new external_function_parameters(
343
            array(
344
                'databaseid' => new external_value(PARAM_INT, 'data instance id'),
345
                'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
346
                                                   VALUE_DEFAULT, 0),
347
                'returncontents' => new external_value(PARAM_BOOL, 'Whether to return contents or not. This will return each entry
348
                                                        raw contents and the complete list view (using the template).',
349
                                                        VALUE_DEFAULT, false),
350
                'sort' => new external_value(PARAM_INT, 'Sort the records by this field id, reserved ids are:
351
                                                0: timeadded
352
                                                -1: firstname
353
                                                -2: lastname
354
                                                -3: approved
355
                                                -4: timemodified.
356
                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
357
                'order' => new external_value(PARAM_ALPHA, 'The direction of the sorting: \'ASC\' or \'DESC\'.
358
                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
359
                'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
360
                'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
361
            )
362
        );
363
    }
364
 
365
    /**
366
     * Return access information for a given feedback
367
     *
368
     * @param int $databaseid       the data instance id
369
     * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
370
     * @param bool $returncontents  Whether to return the entries contents or not
371
     * @param str $sort             sort by this field
372
     * @param int $order            the direction of the sorting
373
     * @param int $page             page of records to return
374
     * @param int $perpage          number of records to return per page
375
     * @return array of warnings and the entries
376
     * @since Moodle 3.3
377
     * @throws moodle_exception
378
     */
379
    public static function get_entries($databaseid, $groupid = 0, $returncontents = false, $sort = null, $order = null,
380
            $page = 0, $perpage = 0) {
381
        global $PAGE, $DB;
382
 
383
        $params = array('databaseid' => $databaseid, 'groupid' => $groupid, 'returncontents' => $returncontents ,
384
                        'sort' => $sort, 'order' => $order, 'page' => $page, 'perpage' => $perpage);
385
        $params = self::validate_parameters(self::get_entries_parameters(), $params);
386
        $warnings = array();
387
 
388
        if (!empty($params['order'])) {
389
            $params['order'] = strtoupper($params['order']);
390
            if ($params['order'] != 'ASC' && $params['order'] != 'DESC') {
391
                throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $params['order'] . ')');
392
            }
393
        }
394
 
395
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
396
        // Check database is open in time.
397
        data_require_time_available($database, null, $context);
398
 
399
        if (!empty($params['groupid'])) {
400
            $groupid = $params['groupid'];
401
            // Determine is the group is visible to user.
402
            if (!groups_group_visible($groupid, $course, $cm)) {
403
                throw new moodle_exception('notingroup');
404
            }
405
        } else {
406
            // Check to see if groups are being used here.
407
            if ($groupmode = groups_get_activity_groupmode($cm)) {
408
                // We don't need to validate a possible groupid = 0 since it would be handled by data_search_entries.
409
                $groupid = groups_get_activity_group($cm);
410
            } else {
411
                $groupid = 0;
412
            }
413
        }
414
 
415
        $manager = manager::create_from_instance($database);
416
 
417
        list($records, $maxcount, $totalcount, $page, $nowperpage, $sort, $mode) =
418
            data_search_entries($database, $cm, $context, 'list', $groupid, '', $params['sort'], $params['order'],
419
                $params['page'], $params['perpage']);
420
 
421
        $entries = [];
422
        $contentsids = [];  // Store here the content ids of the records returned.
423
        foreach ($records as $record) {
424
            $user = user_picture::unalias($record, null, 'userid');
425
            $related = array('context' => $context, 'database' => $database, 'user' => $user);
426
 
427
            $contents = $DB->get_records('data_content', array('recordid' => $record->id));
428
            $contentsids = array_merge($contentsids, array_keys($contents));
429
            if ($params['returncontents']) {
430
                $related['contents'] = $contents;
431
            } else {
432
                $related['contents'] = null;
433
            }
434
 
435
            $exporter = new record_exporter($record, $related);
436
            $entries[] = $exporter->export($PAGE->get_renderer('core'));
437
        }
438
 
439
        // Retrieve total files size for the records retrieved.
440
        $totalfilesize = 0;
441
        $fs = get_file_storage();
442
        $files = $fs->get_area_files($context->id, 'mod_data', 'content');
443
        foreach ($files as $file) {
444
            if ($file->is_directory() || !in_array($file->get_itemid(), $contentsids)) {
445
                continue;
446
            }
447
            $totalfilesize += $file->get_filesize();
448
        }
449
 
450
        $result = array(
451
            'entries' => $entries,
452
            'totalcount' => $totalcount,
453
            'totalfilesize' => $totalfilesize,
454
            'warnings' => $warnings
455
        );
456
 
457
        // Check if we should return the list rendered.
458
        if ($params['returncontents']) {
459
            $parser = $manager->get_template('listtemplate', ['page' => $page]);
460
            $result['listviewcontents'] = $parser->parse_entries($records);
461
        }
462
 
463
        return $result;
464
    }
465
 
466
    /**
467
     * Returns description of method result value
468
     *
469
     * @return \core_external\external_description
470
     * @since Moodle 3.3
471
     */
472
    public static function get_entries_returns() {
473
        return new external_single_structure(
474
            array(
475
                'entries' => new external_multiple_structure(
476
                    record_exporter::get_read_structure()
477
                ),
478
                'totalcount' => new external_value(PARAM_INT, 'Total count of records.'),
479
                'totalfilesize' => new external_value(PARAM_INT, 'Total size (bytes) of the files included in the records.'),
480
                'listviewcontents' => new external_value(PARAM_RAW, 'The list view contents as is rendered in the site.',
481
                                                            VALUE_OPTIONAL),
482
                'warnings' => new external_warnings()
483
            )
484
        );
485
    }
486
 
487
    /**
488
     * Returns description of method parameters
489
     *
490
     * @return external_function_parameters
491
     * @since Moodle 3.3
492
     */
493
    public static function get_entry_parameters() {
494
        return new external_function_parameters(
495
            array(
496
                'entryid' => new external_value(PARAM_INT, 'record entry id'),
497
                'returncontents' => new external_value(PARAM_BOOL, 'Whether to return contents or not.', VALUE_DEFAULT, false),
498
            )
499
        );
500
    }
501
 
502
    /**
503
     * Return one entry record from the database, including contents optionally.
504
     *
505
     * @param int $entryid          the record entry id id
506
     * @param bool $returncontents  whether to return the entries contents or not
507
     * @return array of warnings and the entries
508
     * @since Moodle 3.3
509
     * @throws moodle_exception
510
     */
511
    public static function get_entry($entryid, $returncontents = false) {
512
        global $PAGE, $DB;
513
 
514
        $params = array('entryid' => $entryid, 'returncontents' => $returncontents);
515
        $params = self::validate_parameters(self::get_entry_parameters(), $params);
516
        $warnings = array();
517
 
518
        $record = $DB->get_record('data_records', array('id' => $params['entryid']), '*', MUST_EXIST);
519
        list($database, $course, $cm, $context) = self::validate_database($record->dataid);
520
 
521
        // Check database is open in time.
522
        $canmanageentries = has_capability('mod/data:manageentries', $context);
523
        data_require_time_available($database, $canmanageentries);
524
 
525
        $manager = manager::create_from_instance($database);
526
 
527
        if ($record->groupid != 0) {
528
            if (!groups_group_visible($record->groupid, $course, $cm)) {
529
                throw new moodle_exception('notingroup');
530
            }
531
        }
532
 
533
        // Check correct record entry. Group check was done before.
534
        if (!data_can_view_record($database, $record, $record->groupid, $canmanageentries)) {
535
            throw new moodle_exception('notapprovederror', 'data');
536
        }
537
 
538
        $related = array('context' => $context, 'database' => $database, 'user' => null);
539
        if ($params['returncontents']) {
540
            $related['contents'] = $DB->get_records('data_content', array('recordid' => $record->id));
541
        } else {
542
            $related['contents'] = null;
543
        }
544
        $exporter = new record_exporter($record, $related);
545
        $entry = $exporter->export($PAGE->get_renderer('core'));
546
 
547
        $result = array(
548
            'entry' => $entry,
549
            'ratinginfo' => \core_rating\external\util::get_rating_info($database, $context, 'mod_data', 'entry', array($record)),
550
            'warnings' => $warnings
551
        );
552
        // Check if we should return the entry rendered.
553
        if ($params['returncontents']) {
554
            $records = [$record];
555
            $parser = $manager->get_template('singletemplate');
556
            $result['entryviewcontents'] = $parser->parse_entries($records);
557
        }
558
 
559
        return $result;
560
    }
561
 
562
    /**
563
     * Returns description of method result value
564
     *
565
     * @return \core_external\external_description
566
     * @since Moodle 3.3
567
     */
568
    public static function get_entry_returns() {
569
        return new external_single_structure(
570
            array(
571
                'entry' => record_exporter::get_read_structure(),
572
                'entryviewcontents' => new external_value(PARAM_RAW, 'The entry as is rendered in the site.', VALUE_OPTIONAL),
573
                'ratinginfo' => \core_rating\external\util::external_ratings_structure(),
574
                'warnings' => new external_warnings()
575
            )
576
        );
577
    }
578
 
579
    /**
580
     * Returns description of method parameters
581
     *
582
     * @return external_function_parameters
583
     * @since Moodle 3.3
584
     */
585
    public static function get_fields_parameters() {
586
        return new external_function_parameters(
587
            array(
588
                'databaseid' => new external_value(PARAM_INT, 'Database instance id.'),
589
            )
590
        );
591
    }
592
 
593
    /**
594
     * Return the list of configured fields for the given database.
595
     *
596
     * @param int $databaseid the database id
597
     * @return array of warnings and the fields
598
     * @since Moodle 3.3
599
     * @throws moodle_exception
600
     */
601
    public static function get_fields($databaseid) {
602
        global $PAGE;
603
 
604
        $params = array('databaseid' => $databaseid);
605
        $params = self::validate_parameters(self::get_fields_parameters(), $params);
606
        $fields = $warnings = array();
607
 
608
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
609
 
610
        // Check database is open in time.
611
        $canmanageentries = has_capability('mod/data:manageentries', $context);
612
        data_require_time_available($database, $canmanageentries);
613
 
614
        $fieldinstances = data_get_field_instances($database);
615
 
616
        foreach ($fieldinstances as $fieldinstance) {
617
            $record = $fieldinstance->field;
618
            // Now get the configs the user can see with his current permissions.
619
            $configs = $fieldinstance->get_config_for_external();
620
            foreach ($configs as $name => $value) {
621
                // Overwrite.
622
                $record->{$name} = $value;
623
            }
624
 
625
            $exporter = new field_exporter($record, array('context' => $context));
626
            $fields[] = $exporter->export($PAGE->get_renderer('core'));
627
        }
628
 
629
        $result = array(
630
            'fields' => $fields,
631
            'warnings' => $warnings
632
        );
633
        return $result;
634
    }
635
 
636
    /**
637
     * Returns description of method result value
638
     *
639
     * @return \core_external\external_description
640
     * @since Moodle 3.3
641
     */
642
    public static function get_fields_returns() {
643
        return new external_single_structure(
644
            array(
645
                'fields' => new external_multiple_structure(
646
                    field_exporter::get_read_structure()
647
                ),
648
                'warnings' => new external_warnings()
649
            )
650
        );
651
    }
652
 
653
    /**
654
     * Returns description of method parameters
655
     *
656
     * @return external_function_parameters
657
     * @since Moodle 3.3
658
     */
659
    public static function search_entries_parameters() {
660
        return new external_function_parameters(
661
            array(
662
                'databaseid' => new external_value(PARAM_INT, 'data instance id'),
663
                'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
664
                                                   VALUE_DEFAULT, 0),
665
                'returncontents' => new external_value(PARAM_BOOL, 'Whether to return contents or not.', VALUE_DEFAULT, false),
666
                'search' => new external_value(PARAM_NOTAGS, 'search string (empty when using advanced)', VALUE_DEFAULT, ''),
667
                'advsearch' => new external_multiple_structure(
668
                    new external_single_structure(
669
                        array(
670
                            'name' => new external_value(PARAM_ALPHANUMEXT, 'Field key for search.
671
                                                            Use fn or ln for first or last name'),
672
                            'value' => new external_value(PARAM_RAW, 'JSON encoded value for search'),
673
                        )
674
                    ), 'Advanced search', VALUE_DEFAULT, array()
675
                ),
676
                'sort' => new external_value(PARAM_INT, 'Sort the records by this field id, reserved ids are:
677
                                                0: timeadded
678
                                                -1: firstname
679
                                                -2: lastname
680
                                                -3: approved
681
                                                -4: timemodified.
682
                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
683
                'order' => new external_value(PARAM_ALPHA, 'The direction of the sorting: \'ASC\' or \'DESC\'.
684
                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
685
                'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
686
                'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
687
            )
688
        );
689
    }
690
 
691
    /**
692
     * Return access information for a given feedback
693
     *
694
     * @param int $databaseid       the data instance id
695
     * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
696
     * @param bool $returncontents  whether to return contents or not
697
     * @param str $search           search text
698
     * @param array $advsearch      advanced search data
699
     * @param str $sort             sort by this field
700
     * @param int $order            the direction of the sorting
701
     * @param int $page             page of records to return
702
     * @param int $perpage          number of records to return per page
703
     * @return array of warnings and the entries
704
     * @since Moodle 3.3
705
     * @throws moodle_exception
706
     */
707
    public static function search_entries($databaseid, $groupid = 0, $returncontents = false, $search = '', $advsearch = [],
708
            $sort = null, $order = null, $page = 0, $perpage = 0) {
709
        global $PAGE, $DB;
710
 
711
        $params = array('databaseid' => $databaseid, 'groupid' => $groupid, 'returncontents' => $returncontents, 'search' => $search,
712
                        'advsearch' => $advsearch, 'sort' => $sort, 'order' => $order, 'page' => $page, 'perpage' => $perpage);
713
        $params = self::validate_parameters(self::search_entries_parameters(), $params);
714
        $warnings = array();
715
 
716
        if (!empty($params['order'])) {
717
            $params['order'] = strtoupper($params['order']);
718
            if ($params['order'] != 'ASC' && $params['order'] != 'DESC') {
719
                throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $params['order'] . ')');
720
            }
721
        }
722
 
723
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
724
        // Check database is open in time.
725
        data_require_time_available($database, null, $context);
726
 
727
        $manager = manager::create_from_instance($database);
728
 
729
        if (!empty($params['groupid'])) {
730
            $groupid = $params['groupid'];
731
            // Determine is the group is visible to user.
732
            if (!groups_group_visible($groupid, $course, $cm)) {
733
                throw new moodle_exception('notingroup');
734
            }
735
        } else {
736
            // Check to see if groups are being used here.
737
            if ($groupmode = groups_get_activity_groupmode($cm)) {
738
                // We don't need to validate a possible groupid = 0 since it would be handled by data_search_entries.
739
                $groupid = groups_get_activity_group($cm);
740
            } else {
741
                $groupid = 0;
742
            }
743
        }
744
 
745
        if (!empty($params['advsearch'])) {
746
            $advanced = true;
747
            $defaults = [];
748
            $fn = $ln = ''; // Defaults for first and last name.
749
            // Force defaults for advanced search.
750
            foreach ($params['advsearch'] as $adv) {
751
                if ($adv['name'] == 'fn') {
752
                    $fn = json_decode($adv['value']);
753
                    continue;
754
                }
755
                if ($adv['name'] == 'ln') {
756
                    $ln = json_decode($adv['value']);
757
                    continue;
758
                }
759
                $defaults[$adv['name']] = json_decode($adv['value']);
760
            }
761
            list($searcharray, $params['search']) = data_build_search_array($database, false, [], $defaults, $fn, $ln);
762
        } else {
763
            $advanced = null;
764
            $searcharray = null;
765
        }
766
 
767
        list($records, $maxcount, $totalcount, $page, $nowperpage, $sort, $mode) =
768
            data_search_entries($database, $cm, $context, 'list', $groupid, $params['search'], $params['sort'], $params['order'],
769
                $params['page'], $params['perpage'], $advanced, $searcharray);
770
 
771
        $entries = [];
772
        foreach ($records as $record) {
773
            $user = user_picture::unalias($record, null, 'userid');
774
            $related = array('context' => $context, 'database' => $database, 'user' => $user);
775
            if ($params['returncontents']) {
776
                $related['contents'] = $DB->get_records('data_content', array('recordid' => $record->id));
777
            } else {
778
                $related['contents'] = null;
779
            }
780
 
781
            $exporter = new record_exporter($record, $related);
782
            $entries[] = $exporter->export($PAGE->get_renderer('core'));
783
        }
784
 
785
        $result = array(
786
            'entries' => $entries,
787
            'totalcount' => $totalcount,
788
            'maxcount' => $maxcount,
789
            'warnings' => $warnings
790
        );
791
 
792
        // Check if we should return the list rendered.
793
        if ($params['returncontents']) {
794
            $parser = $manager->get_template('listtemplate', ['page' => $page]);
795
            $result['listviewcontents'] = $parser->parse_entries($records);
796
        }
797
 
798
        return $result;
799
    }
800
 
801
    /**
802
     * Returns description of method result value
803
     *
804
     * @return \core_external\external_description
805
     * @since Moodle 3.3
806
     */
807
    public static function search_entries_returns() {
808
        return new external_single_structure(
809
            array(
810
                'entries' => new external_multiple_structure(
811
                    record_exporter::get_read_structure()
812
                ),
813
                'totalcount' => new external_value(PARAM_INT, 'Total count of records returned by the search.'),
814
                'maxcount' => new external_value(PARAM_INT, 'Total count of records that the user could see in the database
815
                    (if all the search criterias were removed).', VALUE_OPTIONAL),
816
                'listviewcontents' => new external_value(PARAM_RAW, 'The list view contents as is rendered in the site.',
817
                                                            VALUE_OPTIONAL),
818
                'warnings' => new external_warnings()
819
            )
820
        );
821
    }
822
 
823
    /**
824
     * Returns description of method parameters
825
     *
826
     * @return external_function_parameters
827
     * @since Moodle 3.3
828
     */
829
    public static function approve_entry_parameters() {
830
        return new external_function_parameters(
831
            array(
832
                'entryid' => new external_value(PARAM_INT, 'Record entry id.'),
833
                'approve' => new external_value(PARAM_BOOL, 'Whether to approve (true) or unapprove the entry.',
834
                                                VALUE_DEFAULT, true),
835
            )
836
        );
837
    }
838
 
839
    /**
840
     * Approves or unapproves an entry.
841
     *
842
     * @param int $entryid          the record entry id id
843
     * @param bool $approve         whether to approve (true) or unapprove the entry
844
     * @return array of warnings and the entries
845
     * @since Moodle 3.3
846
     * @throws moodle_exception
847
     */
848
    public static function approve_entry($entryid, $approve = true) {
849
        global $PAGE, $DB;
850
 
851
        $params = array('entryid' => $entryid, 'approve' => $approve);
852
        $params = self::validate_parameters(self::approve_entry_parameters(), $params);
853
        $warnings = array();
854
 
855
        $record = $DB->get_record('data_records', array('id' => $params['entryid']), '*', MUST_EXIST);
856
        list($database, $course, $cm, $context) = self::validate_database($record->dataid);
857
        // Check database is open in time.
858
        data_require_time_available($database, null, $context);
859
        // Check specific capabilities.
860
        require_capability('mod/data:approve', $context);
861
 
862
        data_approve_entry($record->id, $params['approve']);
863
 
864
        $result = array(
865
            'status' => true,
866
            'warnings' => $warnings
867
        );
868
        return $result;
869
    }
870
 
871
    /**
872
     * Returns description of method result value
873
     *
874
     * @return \core_external\external_description
875
     * @since Moodle 3.3
876
     */
877
    public static function approve_entry_returns() {
878
        return new external_single_structure(
879
            array(
880
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
881
                'warnings' => new external_warnings()
882
            )
883
        );
884
    }
885
 
886
    /**
887
     * Returns description of method parameters
888
     *
889
     * @return external_function_parameters
890
     * @since Moodle 3.3
891
     */
892
    public static function delete_entry_parameters() {
893
        return new external_function_parameters(
894
            array(
895
                'entryid' => new external_value(PARAM_INT, 'Record entry id.'),
896
            )
897
        );
898
    }
899
 
900
    /**
901
     * Deletes an entry.
902
     *
903
     * @param int $entryid the record entry id
904
     * @return array of warnings success status
905
     * @since Moodle 3.3
906
     * @throws moodle_exception
907
     */
908
    public static function delete_entry($entryid) {
909
        global $PAGE, $DB;
910
 
911
        $params = array('entryid' => $entryid);
912
        $params = self::validate_parameters(self::delete_entry_parameters(), $params);
913
        $warnings = array();
914
 
915
        $record = $DB->get_record('data_records', array('id' => $params['entryid']), '*', MUST_EXIST);
916
        list($database, $course, $cm, $context) = self::validate_database($record->dataid);
917
 
918
        if (data_user_can_manage_entry($record, $database, $context)) {
919
            data_delete_record($record->id, $database, $course->id, $cm->id);
920
        } else {
921
            throw new moodle_exception('noaccess', 'data');
922
        }
923
 
924
        $result = array(
925
            'status' => true,
926
            'warnings' => $warnings
927
        );
928
        return $result;
929
    }
930
 
931
    /**
932
     * Returns description of method result value
933
     *
934
     * @return \core_external\external_description
935
     * @since Moodle 3.3
936
     */
937
    public static function delete_entry_returns() {
938
        return new external_single_structure(
939
            array(
940
                'status' => new external_value(PARAM_BOOL, 'Always true. If we see this field it means that the entry was deleted.'),
941
                'warnings' => new external_warnings()
942
            )
943
        );
944
    }
945
 
946
    /**
947
     * Returns description of method parameters
948
     *
949
     * @return external_function_parameters
950
     * @since Moodle 3.3
951
     */
952
    public static function add_entry_parameters() {
953
        return new external_function_parameters(
954
            array(
955
                'databaseid' => new external_value(PARAM_INT, 'data instance id'),
956
                'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group',
957
                                                   VALUE_DEFAULT, 0),
958
                'data' => new external_multiple_structure(
959
                    new external_single_structure(
960
                        array(
961
                            'fieldid' => new external_value(PARAM_INT, 'The field id.'),
962
                            'subfield' => new external_value(PARAM_NOTAGS, 'The subfield name (if required).', VALUE_DEFAULT, ''),
963
                            'value' => new external_value(PARAM_RAW, 'The contents for the field always JSON encoded.'),
964
                        )
965
                    ), 'The fields data to be created'
966
                ),
967
            )
968
        );
969
    }
970
 
971
    /**
972
     * Adds a new entry to a database
973
     *
974
     * @param int $databaseid the data instance id
975
     * @param int $groupid (optional) group id, 0 means that the function will determine the user group
976
     * @param array $data the fields data to be created
977
     * @return array of warnings and status result
978
     * @since Moodle 3.3
979
     * @throws moodle_exception
980
     */
981
    public static function add_entry($databaseid, $groupid, $data) {
982
        global $DB;
983
 
984
        $params = array('databaseid' => $databaseid, 'groupid' => $groupid, 'data' => $data);
985
        $params = self::validate_parameters(self::add_entry_parameters(), $params);
986
        $warnings = array();
987
        $fieldnotifications = array();
988
 
989
        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
990
 
991
        $fields = $DB->get_records('data_fields', ['dataid' => $database->id]);
992
        if (empty($fields)) {
993
            throw new moodle_exception('nofieldindatabase', 'data');
994
        }
995
 
996
        // Check database is open in time.
997
        data_require_time_available($database, null, $context);
998
 
999
        $groupmode = groups_get_activity_groupmode($cm);
1000
        // Determine default group.
1001
        if (empty($params['groupid'])) {
1002
            // Check to see if groups are being used here.
1003
            if ($groupmode) {
1004
                $groupid = groups_get_activity_group($cm);
1005
            } else {
1006
                $groupid = 0;
1007
            }
1008
        }
1009
 
1010
        // Group is validated inside the function.
1011
        if (!data_user_can_add_entry($database, $groupid, $groupmode, $context)) {
1012
            throw new moodle_exception('noaccess', 'data');
1013
        }
1014
 
1015
        // Prepare the data as is expected by the API.
1016
        $datarecord = new stdClass;
1017
        foreach ($params['data'] as $data) {
1018
            $subfield = ($data['subfield'] !== '') ? '_' . $data['subfield'] : '';
1019
            // We ask for JSON encoded values because of multiple choice forms or checkboxes that use array parameters.
1020
            $datarecord->{'field_' . $data['fieldid'] . $subfield} = json_decode($data['value']);
1021
        }
1022
        // Validate to ensure that enough data was submitted.
1023
        $processeddata = data_process_submission($database, $fields, $datarecord);
1024
 
1025
        // Format notifications.
1026
        if (!empty($processeddata->fieldnotifications)) {
1027
            foreach ($processeddata->fieldnotifications as $field => $notififications) {
1028
                foreach ($notififications as $notif) {
1029
                    $fieldnotifications[] = [
1030
                        'fieldname' => $field,
1031
                        'notification' => $notif,
1032
                    ];
1033
                }
1034
            }
1035
        }
1036
 
1037
        // Create a new (empty) record.
1038
        $newentryid = 0;
1039
        if ($processeddata->validated && $recordid = data_add_record($database, $groupid)) {
1040
            $newentryid = $recordid;
1041
            // Now populate the fields contents of the new record.
1042
            data_add_fields_contents_to_new_record($database, $context, $recordid, $fields, $datarecord, $processeddata);
1043
        }
1044
 
1045
        $result = array(
1046
            'newentryid' => $newentryid,
1047
            'generalnotifications' => $processeddata->generalnotifications,
1048
            'fieldnotifications' => $fieldnotifications,
1049
        );
1050
        return $result;
1051
    }
1052
 
1053
    /**
1054
     * Returns description of method result value
1055
     *
1056
     * @return \core_external\external_description
1057
     * @since Moodle 3.3
1058
     */
1059
    public static function add_entry_returns() {
1060
        return new external_single_structure(
1061
            array(
1062
                'newentryid' => new external_value(PARAM_INT, 'True new created entry id. 0 if the entry was not created.'),
1063
                'generalnotifications' => new external_multiple_structure(
1064
                    new external_value(PARAM_RAW, 'General notifications')
1065
                ),
1066
                'fieldnotifications' => new external_multiple_structure(
1067
                    new external_single_structure(
1068
                        array(
1069
                            'fieldname' => new external_value(PARAM_TEXT, 'The field name.'),
1070
                            'notification' => new external_value(PARAM_RAW, 'The notification for the field.'),
1071
                        )
1072
                    )
1073
                ),
1074
                'warnings' => new external_warnings()
1075
            )
1076
        );
1077
    }
1078
 
1079
    /**
1080
     * Returns description of method parameters
1081
     *
1082
     * @return external_function_parameters
1083
     * @since Moodle 3.3
1084
     */
1085
    public static function update_entry_parameters() {
1086
        return new external_function_parameters(
1087
            array(
1088
                'entryid' => new external_value(PARAM_INT, 'The entry record id.'),
1089
                'data' => new external_multiple_structure(
1090
                    new external_single_structure(
1091
                        array(
1092
                            'fieldid' => new external_value(PARAM_INT, 'The field id.'),
1093
                            'subfield' => new external_value(PARAM_NOTAGS, 'The subfield name (if required).', VALUE_DEFAULT, null),
1094
                            'value' => new external_value(PARAM_RAW, 'The new contents for the field always JSON encoded.'),
1095
                        )
1096
                    ), 'The fields data to be updated'
1097
                ),
1098
            )
1099
        );
1100
    }
1101
 
1102
    /**
1103
     * Updates an existing entry.
1104
     *
1105
     * @param int $entryid the data instance id
1106
     * @param array $data the fields data to be created
1107
     * @return array of warnings and status result
1108
     * @since Moodle 3.3
1109
     * @throws moodle_exception
1110
     */
1111
    public static function update_entry($entryid, $data) {
1112
        global $DB;
1113
 
1114
        $params = array('entryid' => $entryid, 'data' => $data);
1115
        $params = self::validate_parameters(self::update_entry_parameters(), $params);
1116
        $warnings = array();
1117
        $fieldnotifications = array();
1118
        $updated = false;
1119
 
1120
        $record = $DB->get_record('data_records', array('id' => $params['entryid']), '*', MUST_EXIST);
1121
        list($database, $course, $cm, $context) = self::validate_database($record->dataid);
1122
        // Check database is open in time.
1123
        data_require_time_available($database, null, $context);
1124
 
1125
        if (!data_user_can_manage_entry($record, $database, $context)) {
1126
            throw new moodle_exception('noaccess', 'data');
1127
        }
1128
 
1129
        // Prepare the data as is expected by the API.
1130
        $datarecord = new stdClass;
1131
        foreach ($params['data'] as $data) {
1132
            $subfield = ($data['subfield'] !== '') ? '_' . $data['subfield'] : '';
1133
            // We ask for JSON encoded values because of multiple choice forms or checkboxes that use array parameters.
1134
            $datarecord->{'field_' . $data['fieldid'] . $subfield} = json_decode($data['value']);
1135
        }
1136
        // Validate to ensure that enough data was submitted.
1137
        $fields = $DB->get_records('data_fields', array('dataid' => $database->id));
1138
        $processeddata = data_process_submission($database, $fields, $datarecord);
1139
 
1140
        // Format notifications.
1141
        if (!empty($processeddata->fieldnotifications)) {
1142
            foreach ($processeddata->fieldnotifications as $field => $notififications) {
1143
                foreach ($notififications as $notif) {
1144
                    $fieldnotifications[] = [
1145
                        'fieldname' => $field,
1146
                        'notification' => $notif,
1147
                    ];
1148
                }
1149
            }
1150
        }
1151
 
1152
        if ($processeddata->validated) {
1153
            // Now update the fields contents.
1154
            data_update_record_fields_contents($database, $record, $context, $datarecord, $processeddata);
1155
            $updated = true;
1156
        }
1157
 
1158
        $result = array(
1159
            'updated' => $updated,
1160
            'generalnotifications' => $processeddata->generalnotifications,
1161
            'fieldnotifications' => $fieldnotifications,
1162
            'warnings' => $warnings,
1163
        );
1164
        return $result;
1165
    }
1166
 
1167
    /**
1168
     * Returns description of method result value
1169
     *
1170
     * @return \core_external\external_description
1171
     * @since Moodle 3.3
1172
     */
1173
    public static function update_entry_returns() {
1174
        return new external_single_structure(
1175
            array(
1176
                'updated' => new external_value(PARAM_BOOL, 'True if the entry was successfully updated, false other wise.'),
1177
                'generalnotifications' => new external_multiple_structure(
1178
                    new external_value(PARAM_RAW, 'General notifications')
1179
                ),
1180
                'fieldnotifications' => new external_multiple_structure(
1181
                    new external_single_structure(
1182
                        array(
1183
                            'fieldname' => new external_value(PARAM_TEXT, 'The field name.'),
1184
                            'notification' => new external_value(PARAM_RAW, 'The notification for the field.'),
1185
                        )
1186
                    )
1187
                ),
1188
                'warnings' => new external_warnings()
1189
            )
1190
        );
1191
    }
1192
}