Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

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