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