Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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