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