Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

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

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