[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/data/ -> lib.php (source)

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package   mod_data
  20   * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  // Some constants
  27  define ('DATA_MAX_ENTRIES', 50);
  28  define ('DATA_PERPAGE_SINGLE', 1);
  29  
  30  define ('DATA_FIRSTNAME', -1);
  31  define ('DATA_LASTNAME', -2);
  32  define ('DATA_APPROVED', -3);
  33  define ('DATA_TIMEADDED', 0);
  34  define ('DATA_TIMEMODIFIED', -4);
  35  
  36  define ('DATA_CAP_EXPORT', 'mod/data:viewalluserpresets');
  37  
  38  define('DATA_PRESET_COMPONENT', 'mod_data');
  39  define('DATA_PRESET_FILEAREA', 'site_presets');
  40  define('DATA_PRESET_CONTEXT', SYSCONTEXTID);
  41  
  42  // Users having assigned the default role "Non-editing teacher" can export database records
  43  // Using the mod/data capability "viewalluserpresets" existing in Moodle 1.9.x.
  44  // In Moodle >= 2, new roles may be introduced and used instead.
  45  
  46  /**
  47   * @package   mod_data
  48   * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  49   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  50   */
  51  class data_field_base {     // Base class for Database Field Types (see field/*/field.class.php)
  52  
  53      /** @var string Subclasses must override the type with their name */
  54      var $type = 'unknown';
  55      /** @var object The database object that this field belongs to */
  56      var $data = NULL;
  57      /** @var object The field object itself, if we know it */
  58      var $field = NULL;
  59      /** @var int Width of the icon for this fieldtype */
  60      var $iconwidth = 16;
  61      /** @var int Width of the icon for this fieldtype */
  62      var $iconheight = 16;
  63      /** @var object course module or cmifno */
  64      var $cm;
  65      /** @var object activity context */
  66      var $context;
  67  
  68      /**
  69       * Constructor function
  70       *
  71       * @global object
  72       * @uses CONTEXT_MODULE
  73       * @param int $field
  74       * @param int $data
  75       * @param int $cm
  76       */
  77      function __construct($field=0, $data=0, $cm=0) {   // Field or data or both, each can be id or object
  78          global $DB;
  79  
  80          if (empty($field) && empty($data)) {
  81              print_error('missingfield', 'data');
  82          }
  83  
  84          if (!empty($field)) {
  85              if (is_object($field)) {
  86                  $this->field = $field;  // Programmer knows what they are doing, we hope
  87              } else if (!$this->field = $DB->get_record('data_fields', array('id'=>$field))) {
  88                  print_error('invalidfieldid', 'data');
  89              }
  90              if (empty($data)) {
  91                  if (!$this->data = $DB->get_record('data', array('id'=>$this->field->dataid))) {
  92                      print_error('invalidid', 'data');
  93                  }
  94              }
  95          }
  96  
  97          if (empty($this->data)) {         // We need to define this properly
  98              if (!empty($data)) {
  99                  if (is_object($data)) {
 100                      $this->data = $data;  // Programmer knows what they are doing, we hope
 101                  } else if (!$this->data = $DB->get_record('data', array('id'=>$data))) {
 102                      print_error('invalidid', 'data');
 103                  }
 104              } else {                      // No way to define it!
 105                  print_error('missingdata', 'data');
 106              }
 107          }
 108  
 109          if ($cm) {
 110              $this->cm = $cm;
 111          } else {
 112              $this->cm = get_coursemodule_from_instance('data', $this->data->id);
 113          }
 114  
 115          if (empty($this->field)) {         // We need to define some default values
 116              $this->define_default_field();
 117          }
 118  
 119          $this->context = context_module::instance($this->cm->id);
 120      }
 121  
 122  
 123      /**
 124       * This field just sets up a default field object
 125       *
 126       * @return bool
 127       */
 128      function define_default_field() {
 129          global $OUTPUT;
 130          if (empty($this->data->id)) {
 131              echo $OUTPUT->notification('Programmer error: dataid not defined in field class');
 132          }
 133          $this->field = new stdClass();
 134          $this->field->id = 0;
 135          $this->field->dataid = $this->data->id;
 136          $this->field->type   = $this->type;
 137          $this->field->param1 = '';
 138          $this->field->param2 = '';
 139          $this->field->param3 = '';
 140          $this->field->name = '';
 141          $this->field->description = '';
 142          $this->field->required = false;
 143  
 144          return true;
 145      }
 146  
 147      /**
 148       * Set up the field object according to data in an object.  Now is the time to clean it!
 149       *
 150       * @return bool
 151       */
 152      function define_field($data) {
 153          $this->field->type        = $this->type;
 154          $this->field->dataid      = $this->data->id;
 155  
 156          $this->field->name        = trim($data->name);
 157          $this->field->description = trim($data->description);
 158          $this->field->required    = !empty($data->required) ? 1 : 0;
 159  
 160          if (isset($data->param1)) {
 161              $this->field->param1 = trim($data->param1);
 162          }
 163          if (isset($data->param2)) {
 164              $this->field->param2 = trim($data->param2);
 165          }
 166          if (isset($data->param3)) {
 167              $this->field->param3 = trim($data->param3);
 168          }
 169          if (isset($data->param4)) {
 170              $this->field->param4 = trim($data->param4);
 171          }
 172          if (isset($data->param5)) {
 173              $this->field->param5 = trim($data->param5);
 174          }
 175  
 176          return true;
 177      }
 178  
 179      /**
 180       * Insert a new field in the database
 181       * We assume the field object is already defined as $this->field
 182       *
 183       * @global object
 184       * @return bool
 185       */
 186      function insert_field() {
 187          global $DB, $OUTPUT;
 188  
 189          if (empty($this->field)) {
 190              echo $OUTPUT->notification('Programmer error: Field has not been defined yet!  See define_field()');
 191              return false;
 192          }
 193  
 194          $this->field->id = $DB->insert_record('data_fields',$this->field);
 195  
 196          // Trigger an event for creating this field.
 197          $event = \mod_data\event\field_created::create(array(
 198              'objectid' => $this->field->id,
 199              'context' => $this->context,
 200              'other' => array(
 201                  'fieldname' => $this->field->name,
 202                  'dataid' => $this->data->id
 203              )
 204          ));
 205          $event->trigger();
 206  
 207          return true;
 208      }
 209  
 210  
 211      /**
 212       * Update a field in the database
 213       *
 214       * @global object
 215       * @return bool
 216       */
 217      function update_field() {
 218          global $DB;
 219  
 220          $DB->update_record('data_fields', $this->field);
 221  
 222          // Trigger an event for updating this field.
 223          $event = \mod_data\event\field_updated::create(array(
 224              'objectid' => $this->field->id,
 225              'context' => $this->context,
 226              'other' => array(
 227                  'fieldname' => $this->field->name,
 228                  'dataid' => $this->data->id
 229              )
 230          ));
 231          $event->trigger();
 232  
 233          return true;
 234      }
 235  
 236      /**
 237       * Delete a field completely
 238       *
 239       * @global object
 240       * @return bool
 241       */
 242      function delete_field() {
 243          global $DB;
 244  
 245          if (!empty($this->field->id)) {
 246              // Get the field before we delete it.
 247              $field = $DB->get_record('data_fields', array('id' => $this->field->id));
 248  
 249              $this->delete_content();
 250              $DB->delete_records('data_fields', array('id'=>$this->field->id));
 251  
 252              // Trigger an event for deleting this field.
 253              $event = \mod_data\event\field_deleted::create(array(
 254                  'objectid' => $this->field->id,
 255                  'context' => $this->context,
 256                  'other' => array(
 257                      'fieldname' => $this->field->name,
 258                      'dataid' => $this->data->id
 259                   )
 260              ));
 261              $event->add_record_snapshot('data_fields', $field);
 262              $event->trigger();
 263          }
 264  
 265          return true;
 266      }
 267  
 268      /**
 269       * Print the relevant form element in the ADD template for this field
 270       *
 271       * @global object
 272       * @param int $recordid
 273       * @return string
 274       */
 275      function display_add_field($recordid=0, $formdata=null) {
 276          global $DB, $OUTPUT;
 277  
 278          if ($formdata) {
 279              $fieldname = 'field_' . $this->field->id;
 280              $content = $formdata->$fieldname;
 281          } else if ($recordid) {
 282              $content = $DB->get_field('data_content', 'content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid));
 283          } else {
 284              $content = '';
 285          }
 286  
 287          // beware get_field returns false for new, empty records MDL-18567
 288          if ($content===false) {
 289              $content='';
 290          }
 291  
 292          $str = '<div title="' . s($this->field->description) . '">';
 293          $str .= '<label for="field_'.$this->field->id.'"><span class="accesshide">'.$this->field->name.'</span>';
 294          if ($this->field->required) {
 295              $image = html_writer::img($OUTPUT->pix_url('req'), get_string('requiredelement', 'form'),
 296                                       array('class' => 'req', 'title' => get_string('requiredelement', 'form')));
 297              $str .= html_writer::div($image, 'inline-req');
 298          }
 299          $str .= '</label><input class="basefieldinput mod-data-input" type="text" name="field_'.$this->field->id.'"';
 300          $str .= ' id="field_' . $this->field->id . '" value="'.s($content).'" />';
 301          $str .= '</div>';
 302  
 303          return $str;
 304      }
 305  
 306      /**
 307       * Print the relevant form element to define the attributes for this field
 308       * viewable by teachers only.
 309       *
 310       * @global object
 311       * @global object
 312       * @return void Output is echo'd
 313       */
 314      function display_edit_field() {
 315          global $CFG, $DB, $OUTPUT;
 316  
 317          if (empty($this->field)) {   // No field has been defined yet, try and make one
 318              $this->define_default_field();
 319          }
 320          echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
 321  
 322          echo '<form id="editfield" action="'.$CFG->wwwroot.'/mod/data/field.php" method="post">'."\n";
 323          echo '<input type="hidden" name="d" value="'.$this->data->id.'" />'."\n";
 324          if (empty($this->field->id)) {
 325              echo '<input type="hidden" name="mode" value="add" />'."\n";
 326              $savebutton = get_string('add');
 327          } else {
 328              echo '<input type="hidden" name="fid" value="'.$this->field->id.'" />'."\n";
 329              echo '<input type="hidden" name="mode" value="update" />'."\n";
 330              $savebutton = get_string('savechanges');
 331          }
 332          echo '<input type="hidden" name="type" value="'.$this->type.'" />'."\n";
 333          echo '<input name="sesskey" value="'.sesskey().'" type="hidden" />'."\n";
 334  
 335          echo $OUTPUT->heading($this->name(), 3);
 336  
 337          require_once($CFG->dirroot.'/mod/data/field/'.$this->type.'/mod.html');
 338  
 339          echo '<div class="mdl-align">';
 340          echo '<input type="submit" value="'.$savebutton.'" />'."\n";
 341          echo '<input type="submit" name="cancel" value="'.get_string('cancel').'" />'."\n";
 342          echo '</div>';
 343  
 344          echo '</form>';
 345  
 346          echo $OUTPUT->box_end();
 347      }
 348  
 349      /**
 350       * Display the content of the field in browse mode
 351       *
 352       * @global object
 353       * @param int $recordid
 354       * @param object $template
 355       * @return bool|string
 356       */
 357      function display_browse_field($recordid, $template) {
 358          global $DB;
 359  
 360          if ($content = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
 361              if (isset($content->content)) {
 362                  $options = new stdClass();
 363                  if ($this->field->param1 == '1') {  // We are autolinking this field, so disable linking within us
 364                      //$content->content = '<span class="nolink">'.$content->content.'</span>';
 365                      //$content->content1 = FORMAT_HTML;
 366                      $options->filter=false;
 367                  }
 368                  $options->para = false;
 369                  $str = format_text($content->content, $content->content1, $options);
 370              } else {
 371                  $str = '';
 372              }
 373              return $str;
 374          }
 375          return false;
 376      }
 377  
 378      /**
 379       * Update the content of one data field in the data_content table
 380       * @global object
 381       * @param int $recordid
 382       * @param mixed $value
 383       * @param string $name
 384       * @return bool
 385       */
 386      function update_content($recordid, $value, $name=''){
 387          global $DB;
 388  
 389          $content = new stdClass();
 390          $content->fieldid = $this->field->id;
 391          $content->recordid = $recordid;
 392          $content->content = clean_param($value, PARAM_NOTAGS);
 393  
 394          if ($oldcontent = $DB->get_record('data_content', array('fieldid'=>$this->field->id, 'recordid'=>$recordid))) {
 395              $content->id = $oldcontent->id;
 396              return $DB->update_record('data_content', $content);
 397          } else {
 398              return $DB->insert_record('data_content', $content);
 399          }
 400      }
 401  
 402      /**
 403       * Delete all content associated with the field
 404       *
 405       * @global object
 406       * @param int $recordid
 407       * @return bool
 408       */
 409      function delete_content($recordid=0) {
 410          global $DB;
 411  
 412          if ($recordid) {
 413              $conditions = array('fieldid'=>$this->field->id, 'recordid'=>$recordid);
 414          } else {
 415              $conditions = array('fieldid'=>$this->field->id);
 416          }
 417  
 418          $rs = $DB->get_recordset('data_content', $conditions);
 419          if ($rs->valid()) {
 420              $fs = get_file_storage();
 421              foreach ($rs as $content) {
 422                  $fs->delete_area_files($this->context->id, 'mod_data', 'content', $content->id);
 423              }
 424          }
 425          $rs->close();
 426  
 427          return $DB->delete_records('data_content', $conditions);
 428      }
 429  
 430      /**
 431       * Check if a field from an add form is empty
 432       *
 433       * @param mixed $value
 434       * @param mixed $name
 435       * @return bool
 436       */
 437      function notemptyfield($value, $name) {
 438          return !empty($value);
 439      }
 440  
 441      /**
 442       * Just in case a field needs to print something before the whole form
 443       */
 444      function print_before_form() {
 445      }
 446  
 447      /**
 448       * Just in case a field needs to print something after the whole form
 449       */
 450      function print_after_form() {
 451      }
 452  
 453  
 454      /**
 455       * Returns the sortable field for the content. By default, it's just content
 456       * but for some plugins, it could be content 1 - content4
 457       *
 458       * @return string
 459       */
 460      function get_sort_field() {
 461          return 'content';
 462      }
 463  
 464      /**
 465       * Returns the SQL needed to refer to the column.  Some fields may need to CAST() etc.
 466       *
 467       * @param string $fieldname
 468       * @return string $fieldname
 469       */
 470      function get_sort_sql($fieldname) {
 471          return $fieldname;
 472      }
 473  
 474      /**
 475       * Returns the name/type of the field
 476       *
 477       * @return string
 478       */
 479      function name() {
 480          return get_string('name'.$this->type, 'data');
 481      }
 482  
 483      /**
 484       * Prints the respective type icon
 485       *
 486       * @global object
 487       * @return string
 488       */
 489      function image() {
 490          global $OUTPUT;
 491  
 492          $params = array('d'=>$this->data->id, 'fid'=>$this->field->id, 'mode'=>'display', 'sesskey'=>sesskey());
 493          $link = new moodle_url('/mod/data/field.php', $params);
 494          $str = '<a href="'.$link->out().'">';
 495          $str .= '<img src="'.$OUTPUT->pix_url('field/'.$this->type, 'data') . '" ';
 496          $str .= 'height="'.$this->iconheight.'" width="'.$this->iconwidth.'" alt="'.$this->type.'" title="'.$this->type.'" /></a>';
 497          return $str;
 498      }
 499  
 500      /**
 501       * Per default, it is assumed that fields support text exporting.
 502       * Override this (return false) on fields not supporting text exporting.
 503       *
 504       * @return bool true
 505       */
 506      function text_export_supported() {
 507          return true;
 508      }
 509  
 510      /**
 511       * Per default, return the record's text value only from the "content" field.
 512       * Override this in fields class if necesarry.
 513       *
 514       * @param string $record
 515       * @return string
 516       */
 517      function export_text_value($record) {
 518          if ($this->text_export_supported()) {
 519              return $record->content;
 520          }
 521      }
 522  
 523      /**
 524       * @param string $relativepath
 525       * @return bool false
 526       */
 527      function file_ok($relativepath) {
 528          return false;
 529      }
 530  }
 531  
 532  
 533  /**
 534   * Given a template and a dataid, generate a default case template
 535   *
 536   * @global object
 537   * @param object $data
 538   * @param string template [addtemplate, singletemplate, listtempalte, rsstemplate]
 539   * @param int $recordid
 540   * @param bool $form
 541   * @param bool $update
 542   * @return bool|string
 543   */
 544  function data_generate_default_template(&$data, $template, $recordid=0, $form=false, $update=true) {
 545      global $DB;
 546  
 547      if (!$data && !$template) {
 548          return false;
 549      }
 550      if ($template == 'csstemplate' or $template == 'jstemplate' ) {
 551          return '';
 552      }
 553  
 554      // get all the fields for that database
 555      if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'id')) {
 556  
 557          $table = new html_table();
 558          $table->attributes['class'] = 'mod-data-default-template ##approvalstatus##';
 559          $table->colclasses = array('template-field', 'template-token');
 560          $table->data = array();
 561          foreach ($fields as $field) {
 562              if ($form) {   // Print forms instead of data
 563                  $fieldobj = data_get_field($field, $data);
 564                  $token = $fieldobj->display_add_field($recordid, null);
 565              } else {           // Just print the tag
 566                  $token = '[['.$field->name.']]';
 567              }
 568              $table->data[] = array(
 569                  $field->name.': ',
 570                  $token
 571              );
 572          }
 573          if ($template == 'listtemplate') {
 574              $cell = new html_table_cell('##edit##  ##more##  ##delete##  ##approve##  ##disapprove##  ##export##');
 575              $cell->colspan = 2;
 576              $cell->attributes['class'] = 'controls';
 577              $table->data[] = new html_table_row(array($cell));
 578          } else if ($template == 'singletemplate') {
 579              $cell = new html_table_cell('##edit##  ##delete##  ##approve##  ##disapprove##  ##export##');
 580              $cell->colspan = 2;
 581              $cell->attributes['class'] = 'controls';
 582              $table->data[] = new html_table_row(array($cell));
 583          } else if ($template == 'asearchtemplate') {
 584              $row = new html_table_row(array(get_string('authorfirstname', 'data').': ', '##firstname##'));
 585              $row->attributes['class'] = 'searchcontrols';
 586              $table->data[] = $row;
 587              $row = new html_table_row(array(get_string('authorlastname', 'data').': ', '##lastname##'));
 588              $row->attributes['class'] = 'searchcontrols';
 589              $table->data[] = $row;
 590          }
 591  
 592          $str = '';
 593          if ($template == 'listtemplate'){
 594              $str .= '##delcheck##';
 595              $str .= html_writer::empty_tag('br');
 596          }
 597  
 598          $str .= html_writer::start_tag('div', array('class' => 'defaulttemplate'));
 599          $str .= html_writer::table($table);
 600          $str .= html_writer::end_tag('div');
 601          if ($template == 'listtemplate'){
 602              $str .= html_writer::empty_tag('hr');
 603          }
 604  
 605          if ($update) {
 606              $newdata = new stdClass();
 607              $newdata->id = $data->id;
 608              $newdata->{$template} = $str;
 609              $DB->update_record('data', $newdata);
 610              $data->{$template} = $str;
 611          }
 612  
 613          return $str;
 614      }
 615  }
 616  
 617  
 618  /**
 619   * Search for a field name and replaces it with another one in all the
 620   * form templates. Set $newfieldname as '' if you want to delete the
 621   * field from the form.
 622   *
 623   * @global object
 624   * @param object $data
 625   * @param string $searchfieldname
 626   * @param string $newfieldname
 627   * @return bool
 628   */
 629  function data_replace_field_in_templates($data, $searchfieldname, $newfieldname) {
 630      global $DB;
 631  
 632      if (!empty($newfieldname)) {
 633          $prestring = '[[';
 634          $poststring = ']]';
 635          $idpart = '#id';
 636  
 637      } else {
 638          $prestring = '';
 639          $poststring = '';
 640          $idpart = '';
 641      }
 642  
 643      $newdata = new stdClass();
 644      $newdata->id = $data->id;
 645      $newdata->singletemplate = str_ireplace('[['.$searchfieldname.']]',
 646              $prestring.$newfieldname.$poststring, $data->singletemplate);
 647  
 648      $newdata->listtemplate = str_ireplace('[['.$searchfieldname.']]',
 649              $prestring.$newfieldname.$poststring, $data->listtemplate);
 650  
 651      $newdata->addtemplate = str_ireplace('[['.$searchfieldname.']]',
 652              $prestring.$newfieldname.$poststring, $data->addtemplate);
 653  
 654      $newdata->addtemplate = str_ireplace('[['.$searchfieldname.'#id]]',
 655              $prestring.$newfieldname.$idpart.$poststring, $data->addtemplate);
 656  
 657      $newdata->rsstemplate = str_ireplace('[['.$searchfieldname.']]',
 658              $prestring.$newfieldname.$poststring, $data->rsstemplate);
 659  
 660      return $DB->update_record('data', $newdata);
 661  }
 662  
 663  
 664  /**
 665   * Appends a new field at the end of the form template.
 666   *
 667   * @global object
 668   * @param object $data
 669   * @param string $newfieldname
 670   */
 671  function data_append_new_field_to_templates($data, $newfieldname) {
 672      global $DB;
 673  
 674      $newdata = new stdClass();
 675      $newdata->id = $data->id;
 676      $change = false;
 677  
 678      if (!empty($data->singletemplate)) {
 679          $newdata->singletemplate = $data->singletemplate.' [[' . $newfieldname .']]';
 680          $change = true;
 681      }
 682      if (!empty($data->addtemplate)) {
 683          $newdata->addtemplate = $data->addtemplate.' [[' . $newfieldname . ']]';
 684          $change = true;
 685      }
 686      if (!empty($data->rsstemplate)) {
 687          $newdata->rsstemplate = $data->singletemplate.' [[' . $newfieldname . ']]';
 688          $change = true;
 689      }
 690      if ($change) {
 691          $DB->update_record('data', $newdata);
 692      }
 693  }
 694  
 695  
 696  /**
 697   * given a field name
 698   * this function creates an instance of the particular subfield class
 699   *
 700   * @global object
 701   * @param string $name
 702   * @param object $data
 703   * @return object|bool
 704   */
 705  function data_get_field_from_name($name, $data){
 706      global $DB;
 707  
 708      $field = $DB->get_record('data_fields', array('name'=>$name, 'dataid'=>$data->id));
 709  
 710      if ($field) {
 711          return data_get_field($field, $data);
 712      } else {
 713          return false;
 714      }
 715  }
 716  
 717  /**
 718   * given a field id
 719   * this function creates an instance of the particular subfield class
 720   *
 721   * @global object
 722   * @param int $fieldid
 723   * @param object $data
 724   * @return bool|object
 725   */
 726  function data_get_field_from_id($fieldid, $data){
 727      global $DB;
 728  
 729      $field = $DB->get_record('data_fields', array('id'=>$fieldid, 'dataid'=>$data->id));
 730  
 731      if ($field) {
 732          return data_get_field($field, $data);
 733      } else {
 734          return false;
 735      }
 736  }
 737  
 738  /**
 739   * given a field id
 740   * this function creates an instance of the particular subfield class
 741   *
 742   * @global object
 743   * @param string $type
 744   * @param object $data
 745   * @return object
 746   */
 747  function data_get_field_new($type, $data) {
 748      global $CFG;
 749  
 750      require_once($CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php');
 751      $newfield = 'data_field_'.$type;
 752      $newfield = new $newfield(0, $data);
 753      return $newfield;
 754  }
 755  
 756  /**
 757   * returns a subclass field object given a record of the field, used to
 758   * invoke plugin methods
 759   * input: $param $field - record from db
 760   *
 761   * @global object
 762   * @param object $field
 763   * @param object $data
 764   * @param object $cm
 765   * @return object
 766   */
 767  function data_get_field($field, $data, $cm=null) {
 768      global $CFG;
 769  
 770      if ($field) {
 771          require_once('field/'.$field->type.'/field.class.php');
 772          $newfield = 'data_field_'.$field->type;
 773          $newfield = new $newfield($field, $data, $cm);
 774          return $newfield;
 775      }
 776  }
 777  
 778  
 779  /**
 780   * Given record object (or id), returns true if the record belongs to the current user
 781   *
 782   * @global object
 783   * @global object
 784   * @param mixed $record record object or id
 785   * @return bool
 786   */
 787  function data_isowner($record) {
 788      global $USER, $DB;
 789  
 790      if (!isloggedin()) { // perf shortcut
 791          return false;
 792      }
 793  
 794      if (!is_object($record)) {
 795          if (!$record = $DB->get_record('data_records', array('id'=>$record))) {
 796              return false;
 797          }
 798      }
 799  
 800      return ($record->userid == $USER->id);
 801  }
 802  
 803  /**
 804   * has a user reached the max number of entries?
 805   *
 806   * @param object $data
 807   * @return bool
 808   */
 809  function data_atmaxentries($data){
 810      if (!$data->maxentries){
 811          return false;
 812  
 813      } else {
 814          return (data_numentries($data) >= $data->maxentries);
 815      }
 816  }
 817  
 818  /**
 819   * returns the number of entries already made by this user
 820   *
 821   * @global object
 822   * @global object
 823   * @param object $data
 824   * @return int
 825   */
 826  function data_numentries($data){
 827      global $USER, $DB;
 828      $sql = 'SELECT COUNT(*) FROM {data_records} WHERE dataid=? AND userid=?';
 829      return $DB->count_records_sql($sql, array($data->id, $USER->id));
 830  }
 831  
 832  /**
 833   * function that takes in a dataid and adds a record
 834   * this is used everytime an add template is submitted
 835   *
 836   * @global object
 837   * @global object
 838   * @param object $data
 839   * @param int $groupid
 840   * @return bool
 841   */
 842  function data_add_record($data, $groupid=0){
 843      global $USER, $DB;
 844  
 845      $cm = get_coursemodule_from_instance('data', $data->id);
 846      $context = context_module::instance($cm->id);
 847  
 848      $record = new stdClass();
 849      $record->userid = $USER->id;
 850      $record->dataid = $data->id;
 851      $record->groupid = $groupid;
 852      $record->timecreated = $record->timemodified = time();
 853      if (has_capability('mod/data:approve', $context)) {
 854          $record->approved = 1;
 855      } else {
 856          $record->approved = 0;
 857      }
 858      $record->id = $DB->insert_record('data_records', $record);
 859  
 860      // Trigger an event for creating this record.
 861      $event = \mod_data\event\record_created::create(array(
 862          'objectid' => $record->id,
 863          'context' => $context,
 864          'other' => array(
 865              'dataid' => $data->id
 866          )
 867      ));
 868      $event->trigger();
 869  
 870      return $record->id;
 871  }
 872  
 873  /**
 874   * check the multple existence any tag in a template
 875   *
 876   * check to see if there are 2 or more of the same tag being used.
 877   *
 878   * @global object
 879   * @param int $dataid,
 880   * @param string $template
 881   * @return bool
 882   */
 883  function data_tags_check($dataid, $template) {
 884      global $DB, $OUTPUT;
 885  
 886      // first get all the possible tags
 887      $fields = $DB->get_records('data_fields', array('dataid'=>$dataid));
 888      // then we generate strings to replace
 889      $tagsok = true; // let's be optimistic
 890      foreach ($fields as $field){
 891          $pattern="/\[\[" . preg_quote($field->name, '/') . "\]\]/i";
 892          if (preg_match_all($pattern, $template, $dummy)>1){
 893              $tagsok = false;
 894              echo $OUTPUT->notification('[['.$field->name.']] - '.get_string('multipletags','data'));
 895          }
 896      }
 897      // else return true
 898      return $tagsok;
 899  }
 900  
 901  /**
 902   * Adds an instance of a data
 903   *
 904   * @param stdClass $data
 905   * @param mod_data_mod_form $mform
 906   * @return int intance id
 907   */
 908  function data_add_instance($data, $mform = null) {
 909      global $DB, $CFG;
 910      require_once($CFG->dirroot.'/mod/data/locallib.php');
 911  
 912      if (empty($data->assessed)) {
 913          $data->assessed = 0;
 914      }
 915  
 916      if (empty($data->ratingtime) || empty($data->assessed)) {
 917          $data->assesstimestart  = 0;
 918          $data->assesstimefinish = 0;
 919      }
 920  
 921      $data->timemodified = time();
 922  
 923      $data->id = $DB->insert_record('data', $data);
 924  
 925      // Add calendar events if necessary.
 926      data_set_events($data);
 927  
 928      data_grade_item_update($data);
 929  
 930      return $data->id;
 931  }
 932  
 933  /**
 934   * updates an instance of a data
 935   *
 936   * @global object
 937   * @param object $data
 938   * @return bool
 939   */
 940  function data_update_instance($data) {
 941      global $DB, $CFG;
 942      require_once($CFG->dirroot.'/mod/data/locallib.php');
 943  
 944      $data->timemodified = time();
 945      $data->id           = $data->instance;
 946  
 947      if (empty($data->assessed)) {
 948          $data->assessed = 0;
 949      }
 950  
 951      if (empty($data->ratingtime) or empty($data->assessed)) {
 952          $data->assesstimestart  = 0;
 953          $data->assesstimefinish = 0;
 954      }
 955  
 956      if (empty($data->notification)) {
 957          $data->notification = 0;
 958      }
 959  
 960      $DB->update_record('data', $data);
 961  
 962      // Add calendar events if necessary.
 963      data_set_events($data);
 964  
 965      data_grade_item_update($data);
 966  
 967      return true;
 968  
 969  }
 970  
 971  /**
 972   * deletes an instance of a data
 973   *
 974   * @global object
 975   * @param int $id
 976   * @return bool
 977   */
 978  function data_delete_instance($id) {    // takes the dataid
 979      global $DB, $CFG;
 980  
 981      if (!$data = $DB->get_record('data', array('id'=>$id))) {
 982          return false;
 983      }
 984  
 985      $cm = get_coursemodule_from_instance('data', $data->id);
 986      $context = context_module::instance($cm->id);
 987  
 988  /// Delete all the associated information
 989  
 990      // files
 991      $fs = get_file_storage();
 992      $fs->delete_area_files($context->id, 'mod_data');
 993  
 994      // get all the records in this data
 995      $sql = "SELECT r.id
 996                FROM {data_records} r
 997               WHERE r.dataid = ?";
 998  
 999      $DB->delete_records_select('data_content', "recordid IN ($sql)", array($id));
1000  
1001      // delete all the records and fields
1002      $DB->delete_records('data_records', array('dataid'=>$id));
1003      $DB->delete_records('data_fields', array('dataid'=>$id));
1004  
1005      // Remove old calendar events.
1006      $events = $DB->get_records('event', array('modulename' => 'data', 'instance' => $id));
1007      foreach ($events as $event) {
1008          $event = calendar_event::load($event);
1009          $event->delete();
1010      }
1011  
1012      // Delete the instance itself
1013      $result = $DB->delete_records('data', array('id'=>$id));
1014  
1015      // cleanup gradebook
1016      data_grade_item_delete($data);
1017  
1018      return $result;
1019  }
1020  
1021  /**
1022   * returns a summary of data activity of this user
1023   *
1024   * @global object
1025   * @param object $course
1026   * @param object $user
1027   * @param object $mod
1028   * @param object $data
1029   * @return object|null
1030   */
1031  function data_user_outline($course, $user, $mod, $data) {
1032      global $DB, $CFG;
1033      require_once("$CFG->libdir/gradelib.php");
1034  
1035      $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
1036      if (empty($grades->items[0]->grades)) {
1037          $grade = false;
1038      } else {
1039          $grade = reset($grades->items[0]->grades);
1040      }
1041  
1042  
1043      if ($countrecords = $DB->count_records('data_records', array('dataid'=>$data->id, 'userid'=>$user->id))) {
1044          $result = new stdClass();
1045          $result->info = get_string('numrecords', 'data', $countrecords);
1046          $lastrecord   = $DB->get_record_sql('SELECT id,timemodified FROM {data_records}
1047                                                WHERE dataid = ? AND userid = ?
1048                                             ORDER BY timemodified DESC', array($data->id, $user->id), true);
1049          $result->time = $lastrecord->timemodified;
1050          if ($grade) {
1051              $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
1052          }
1053          return $result;
1054      } else if ($grade) {
1055          $result = new stdClass();
1056          $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
1057  
1058          //datesubmitted == time created. dategraded == time modified or time overridden
1059          //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
1060          //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
1061          if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
1062              $result->time = $grade->dategraded;
1063          } else {
1064              $result->time = $grade->datesubmitted;
1065          }
1066  
1067          return $result;
1068      }
1069      return NULL;
1070  }
1071  
1072  /**
1073   * Prints all the records uploaded by this user
1074   *
1075   * @global object
1076   * @param object $course
1077   * @param object $user
1078   * @param object $mod
1079   * @param object $data
1080   */
1081  function data_user_complete($course, $user, $mod, $data) {
1082      global $DB, $CFG, $OUTPUT;
1083      require_once("$CFG->libdir/gradelib.php");
1084  
1085      $grades = grade_get_grades($course->id, 'mod', 'data', $data->id, $user->id);
1086      if (!empty($grades->items[0]->grades)) {
1087          $grade = reset($grades->items[0]->grades);
1088          echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
1089          if ($grade->str_feedback) {
1090              echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
1091          }
1092      }
1093  
1094      if ($records = $DB->get_records('data_records', array('dataid'=>$data->id,'userid'=>$user->id), 'timemodified DESC')) {
1095          data_print_template('singletemplate', $records, $data);
1096      }
1097  }
1098  
1099  /**
1100   * Return grade for given user or all users.
1101   *
1102   * @global object
1103   * @param object $data
1104   * @param int $userid optional user id, 0 means all users
1105   * @return array array of grades, false if none
1106   */
1107  function data_get_user_grades($data, $userid=0) {
1108      global $CFG;
1109  
1110      require_once($CFG->dirroot.'/rating/lib.php');
1111  
1112      $ratingoptions = new stdClass;
1113      $ratingoptions->component = 'mod_data';
1114      $ratingoptions->ratingarea = 'entry';
1115      $ratingoptions->modulename = 'data';
1116      $ratingoptions->moduleid   = $data->id;
1117  
1118      $ratingoptions->userid = $userid;
1119      $ratingoptions->aggregationmethod = $data->assessed;
1120      $ratingoptions->scaleid = $data->scale;
1121      $ratingoptions->itemtable = 'data_records';
1122      $ratingoptions->itemtableusercolumn = 'userid';
1123  
1124      $rm = new rating_manager();
1125      return $rm->get_user_grades($ratingoptions);
1126  }
1127  
1128  /**
1129   * Update activity grades
1130   *
1131   * @category grade
1132   * @param object $data
1133   * @param int $userid specific user only, 0 means all
1134   * @param bool $nullifnone
1135   */
1136  function data_update_grades($data, $userid=0, $nullifnone=true) {
1137      global $CFG, $DB;
1138      require_once($CFG->libdir.'/gradelib.php');
1139  
1140      if (!$data->assessed) {
1141          data_grade_item_update($data);
1142  
1143      } else if ($grades = data_get_user_grades($data, $userid)) {
1144          data_grade_item_update($data, $grades);
1145  
1146      } else if ($userid and $nullifnone) {
1147          $grade = new stdClass();
1148          $grade->userid   = $userid;
1149          $grade->rawgrade = NULL;
1150          data_grade_item_update($data, $grade);
1151  
1152      } else {
1153          data_grade_item_update($data);
1154      }
1155  }
1156  
1157  /**
1158   * Update/create grade item for given data
1159   *
1160   * @category grade
1161   * @param stdClass $data A database instance with extra cmidnumber property
1162   * @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook
1163   * @return object grade_item
1164   */
1165  function data_grade_item_update($data, $grades=NULL) {
1166      global $CFG;
1167      require_once($CFG->libdir.'/gradelib.php');
1168  
1169      $params = array('itemname'=>$data->name, 'idnumber'=>$data->cmidnumber);
1170  
1171      if (!$data->assessed or $data->scale == 0) {
1172          $params['gradetype'] = GRADE_TYPE_NONE;
1173  
1174      } else if ($data->scale > 0) {
1175          $params['gradetype'] = GRADE_TYPE_VALUE;
1176          $params['grademax']  = $data->scale;
1177          $params['grademin']  = 0;
1178  
1179      } else if ($data->scale < 0) {
1180          $params['gradetype'] = GRADE_TYPE_SCALE;
1181          $params['scaleid']   = -$data->scale;
1182      }
1183  
1184      if ($grades  === 'reset') {
1185          $params['reset'] = true;
1186          $grades = NULL;
1187      }
1188  
1189      return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, $grades, $params);
1190  }
1191  
1192  /**
1193   * Delete grade item for given data
1194   *
1195   * @category grade
1196   * @param object $data object
1197   * @return object grade_item
1198   */
1199  function data_grade_item_delete($data) {
1200      global $CFG;
1201      require_once($CFG->libdir.'/gradelib.php');
1202  
1203      return grade_update('mod/data', $data->course, 'mod', 'data', $data->id, 0, NULL, array('deleted'=>1));
1204  }
1205  
1206  // junk functions
1207  /**
1208   * takes a list of records, the current data, a search string,
1209   * and mode to display prints the translated template
1210   *
1211   * @global object
1212   * @global object
1213   * @param string $template
1214   * @param array $records
1215   * @param object $data
1216   * @param string $search
1217   * @param int $page
1218   * @param bool $return
1219   * @param object $jumpurl a moodle_url by which to jump back to the record list (can be null)
1220   * @return mixed
1221   */
1222  function data_print_template($template, $records, $data, $search='', $page=0, $return=false, moodle_url $jumpurl=null) {
1223      global $CFG, $DB, $OUTPUT;
1224  
1225      $cm = get_coursemodule_from_instance('data', $data->id);
1226      $context = context_module::instance($cm->id);
1227  
1228      static $fields = array();
1229      static $dataid = null;
1230  
1231      if (empty($dataid)) {
1232          $dataid = $data->id;
1233      } else if ($dataid != $data->id) {
1234          $fields = array();
1235      }
1236  
1237      if (empty($fields)) {
1238          $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id));
1239          foreach ($fieldrecords as $fieldrecord) {
1240              $fields[]= data_get_field($fieldrecord, $data);
1241          }
1242      }
1243  
1244      if (empty($records)) {
1245          return;
1246      }
1247  
1248      if (!$jumpurl) {
1249          $jumpurl = new moodle_url('/mod/data/view.php', array('d' => $data->id));
1250      }
1251      $jumpurl = new moodle_url($jumpurl, array('page' => $page, 'sesskey' => sesskey()));
1252  
1253      foreach ($records as $record) {   // Might be just one for the single template
1254  
1255      // Replacing tags
1256          $patterns = array();
1257          $replacement = array();
1258  
1259      // Then we generate strings to replace for normal tags
1260          foreach ($fields as $field) {
1261              $patterns[]='[['.$field->field->name.']]';
1262              $replacement[] = highlight($search, $field->display_browse_field($record->id, $template));
1263          }
1264  
1265          $canmanageentries = has_capability('mod/data:manageentries', $context);
1266  
1267      // Replacing special tags (##Edit##, ##Delete##, ##More##)
1268          $patterns[]='##edit##';
1269          $patterns[]='##delete##';
1270          if (data_user_can_manage_entry($record, $data, $context)) {
1271              $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/edit.php?d='
1272                               .$data->id.'&amp;rid='.$record->id.'&amp;sesskey='.sesskey().'"><img src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall" alt="'.get_string('edit').'" title="'.get_string('edit').'" /></a>';
1273              $replacement[] = '<a href="'.$CFG->wwwroot.'/mod/data/view.php?d='
1274                               .$data->id.'&amp;delete='.$record->id.'&amp;sesskey='.sesskey().'"><img src="'.$OUTPUT->pix_url('t/delete') . '" class="iconsmall" alt="'.get_string('delete').'" title="'.get_string('delete').'" /></a>';
1275          } else {
1276              $replacement[] = '';
1277              $replacement[] = '';
1278          }
1279  
1280          $moreurl = $CFG->wwwroot . '/mod/data/view.php?d=' . $data->id . '&amp;rid=' . $record->id;
1281          if ($search) {
1282              $moreurl .= '&amp;filter=1';
1283          }
1284          $patterns[]='##more##';
1285          $replacement[] = '<a href="'.$moreurl.'"><img src="'.$OUTPUT->pix_url('t/preview').
1286                          '" class="iconsmall" alt="'.get_string('more', 'data').'" title="'.get_string('more', 'data').
1287                          '" /></a>';
1288  
1289          $patterns[]='##moreurl##';
1290          $replacement[] = $moreurl;
1291  
1292          $patterns[]='##delcheck##';
1293          if ($canmanageentries) {
1294              $replacement[] = html_writer::checkbox('delcheck[]', $record->id, false, '', array('class' => 'recordcheckbox'));
1295          } else {
1296              $replacement[] = '';
1297          }
1298  
1299          $patterns[]='##user##';
1300          $replacement[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$record->userid.
1301                                 '&amp;course='.$data->course.'">'.fullname($record).'</a>';
1302  
1303          $patterns[] = '##userpicture##';
1304          $ruser = user_picture::unalias($record, null, 'userid');
1305          $replacement[] = $OUTPUT->user_picture($ruser, array('courseid' => $data->course));
1306  
1307          $patterns[]='##export##';
1308  
1309          if (!empty($CFG->enableportfolios) && ($template == 'singletemplate' || $template == 'listtemplate')
1310              && ((has_capability('mod/data:exportentry', $context)
1311                  || (data_isowner($record->id) && has_capability('mod/data:exportownentry', $context))))) {
1312              require_once($CFG->libdir . '/portfoliolib.php');
1313              $button = new portfolio_add_button();
1314              $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id, 'recordid' => $record->id), 'mod_data');
1315              list($formats, $files) = data_portfolio_caller::formats($fields, $record);
1316              $button->set_formats($formats);
1317              $replacement[] = $button->to_html(PORTFOLIO_ADD_ICON_LINK);
1318          } else {
1319              $replacement[] = '';
1320          }
1321  
1322          $patterns[] = '##timeadded##';
1323          $replacement[] = userdate($record->timecreated);
1324  
1325          $patterns[] = '##timemodified##';
1326          $replacement [] = userdate($record->timemodified);
1327  
1328          $patterns[]='##approve##';
1329          if (has_capability('mod/data:approve', $context) && ($data->approval) && (!$record->approved)) {
1330              $approveurl = new moodle_url($jumpurl, array('approve' => $record->id));
1331              $approveicon = new pix_icon('t/approve', get_string('approve', 'data'), '', array('class' => 'iconsmall'));
1332              $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($approveurl, $approveicon),
1333                      array('class' => 'approve'));
1334          } else {
1335              $replacement[] = '';
1336          }
1337  
1338          $patterns[]='##disapprove##';
1339          if (has_capability('mod/data:approve', $context) && ($data->approval) && ($record->approved)) {
1340              $disapproveurl = new moodle_url($jumpurl, array('disapprove' => $record->id));
1341              $disapproveicon = new pix_icon('t/block', get_string('disapprove', 'data'), '', array('class' => 'iconsmall'));
1342              $replacement[] = html_writer::tag('span', $OUTPUT->action_icon($disapproveurl, $disapproveicon),
1343                      array('class' => 'disapprove'));
1344          } else {
1345              $replacement[] = '';
1346          }
1347  
1348          $patterns[] = '##approvalstatus##';
1349          if (!$data->approval) {
1350              $replacement[] = '';
1351          } else if ($record->approved) {
1352              $replacement[] = get_string('approved', 'data');
1353          } else {
1354              $replacement[] = get_string('notapproved', 'data');
1355          }
1356  
1357          $patterns[]='##comments##';
1358          if (($template == 'listtemplate') && ($data->comments)) {
1359  
1360              if (!empty($CFG->usecomments)) {
1361                  require_once($CFG->dirroot  . '/comment/lib.php');
1362                  list($context, $course, $cm) = get_context_info_array($context->id);
1363                  $cmt = new stdClass();
1364                  $cmt->context = $context;
1365                  $cmt->course  = $course;
1366                  $cmt->cm      = $cm;
1367                  $cmt->area    = 'database_entry';
1368                  $cmt->itemid  = $record->id;
1369                  $cmt->showcount = true;
1370                  $cmt->component = 'mod_data';
1371                  $comment = new comment($cmt);
1372                  $replacement[] = $comment->output(true);
1373              }
1374          } else {
1375              $replacement[] = '';
1376          }
1377  
1378          // actual replacement of the tags
1379          $newtext = str_ireplace($patterns, $replacement, $data->{$template});
1380  
1381          // no more html formatting and filtering - see MDL-6635
1382          if ($return) {
1383              return $newtext;
1384          } else {
1385              echo $newtext;
1386  
1387              // hack alert - return is always false in singletemplate anyway ;-)
1388              /**********************************
1389               *    Printing Ratings Form       *
1390               *********************************/
1391              if ($template == 'singletemplate') {    //prints ratings options
1392                  data_print_ratings($data, $record);
1393              }
1394  
1395              /**********************************
1396               *    Printing Comments Form       *
1397               *********************************/
1398              if (($template == 'singletemplate') && ($data->comments)) {
1399                  if (!empty($CFG->usecomments)) {
1400                      require_once($CFG->dirroot . '/comment/lib.php');
1401                      list($context, $course, $cm) = get_context_info_array($context->id);
1402                      $cmt = new stdClass();
1403                      $cmt->context = $context;
1404                      $cmt->course  = $course;
1405                      $cmt->cm      = $cm;
1406                      $cmt->area    = 'database_entry';
1407                      $cmt->itemid  = $record->id;
1408                      $cmt->showcount = true;
1409                      $cmt->component = 'mod_data';
1410                      $comment = new comment($cmt);
1411                      $comment->output(false);
1412                  }
1413              }
1414          }
1415      }
1416  }
1417  
1418  /**
1419   * Return rating related permissions
1420   *
1421   * @param string $contextid the context id
1422   * @param string $component the component to get rating permissions for
1423   * @param string $ratingarea the rating area to get permissions for
1424   * @return array an associative array of the user's rating permissions
1425   */
1426  function data_rating_permissions($contextid, $component, $ratingarea) {
1427      $context = context::instance_by_id($contextid, MUST_EXIST);
1428      if ($component != 'mod_data' || $ratingarea != 'entry') {
1429          return null;
1430      }
1431      return array(
1432          'view'    => has_capability('mod/data:viewrating',$context),
1433          'viewany' => has_capability('mod/data:viewanyrating',$context),
1434          'viewall' => has_capability('mod/data:viewallratings',$context),
1435          'rate'    => has_capability('mod/data:rate',$context)
1436      );
1437  }
1438  
1439  /**
1440   * Validates a submitted rating
1441   * @param array $params submitted data
1442   *            context => object the context in which the rated items exists [required]
1443   *            itemid => int the ID of the object being rated
1444   *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
1445   *            rating => int the submitted rating
1446   *            rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
1447   *            aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
1448   * @return boolean true if the rating is valid. Will throw rating_exception if not
1449   */
1450  function data_rating_validate($params) {
1451      global $DB, $USER;
1452  
1453      // Check the component is mod_data
1454      if ($params['component'] != 'mod_data') {
1455          throw new rating_exception('invalidcomponent');
1456      }
1457  
1458      // Check the ratingarea is entry (the only rating area in data module)
1459      if ($params['ratingarea'] != 'entry') {
1460          throw new rating_exception('invalidratingarea');
1461      }
1462  
1463      // Check the rateduserid is not the current user .. you can't rate your own entries
1464      if ($params['rateduserid'] == $USER->id) {
1465          throw new rating_exception('nopermissiontorate');
1466      }
1467  
1468      $datasql = "SELECT d.id as dataid, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
1469                    FROM {data_records} r
1470                    JOIN {data} d ON r.dataid = d.id
1471                   WHERE r.id = :itemid";
1472      $dataparams = array('itemid'=>$params['itemid']);
1473      if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
1474          //item doesn't exist
1475          throw new rating_exception('invaliditemid');
1476      }
1477  
1478      if ($info->scale != $params['scaleid']) {
1479          //the scale being submitted doesnt match the one in the database
1480          throw new rating_exception('invalidscaleid');
1481      }
1482  
1483      //check that the submitted rating is valid for the scale
1484  
1485      // lower limit
1486      if ($params['rating'] < 0  && $params['rating'] != RATING_UNSET_RATING) {
1487          throw new rating_exception('invalidnum');
1488      }
1489  
1490      // upper limit
1491      if ($info->scale < 0) {
1492          //its a custom scale
1493          $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
1494          if ($scalerecord) {
1495              $scalearray = explode(',', $scalerecord->scale);
1496              if ($params['rating'] > count($scalearray)) {
1497                  throw new rating_exception('invalidnum');
1498              }
1499          } else {
1500              throw new rating_exception('invalidscaleid');
1501          }
1502      } else if ($params['rating'] > $info->scale) {
1503          //if its numeric and submitted rating is above maximum
1504          throw new rating_exception('invalidnum');
1505      }
1506  
1507      if ($info->approval && !$info->approved) {
1508          //database requires approval but this item isnt approved
1509          throw new rating_exception('nopermissiontorate');
1510      }
1511  
1512      // check the item we're rating was created in the assessable time window
1513      if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
1514          if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
1515              throw new rating_exception('notavailable');
1516          }
1517      }
1518  
1519      $course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST);
1520      $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
1521      $context = context_module::instance($cm->id);
1522  
1523      // if the supplied context doesnt match the item's context
1524      if ($context->id != $params['context']->id) {
1525          throw new rating_exception('invalidcontext');
1526      }
1527  
1528      // Make sure groups allow this user to see the item they're rating
1529      $groupid = $info->groupid;
1530      if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
1531          if (!groups_group_exists($groupid)) { // Can't find group
1532              throw new rating_exception('cannotfindgroup');//something is wrong
1533          }
1534  
1535          if (!groups_is_member($groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
1536              // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
1537              throw new rating_exception('notmemberofgroup');
1538          }
1539      }
1540  
1541      return true;
1542  }
1543  
1544  /**
1545   * Can the current user see ratings for a given itemid?
1546   *
1547   * @param array $params submitted data
1548   *            contextid => int contextid [required]
1549   *            component => The component for this module - should always be mod_data [required]
1550   *            ratingarea => object the context in which the rated items exists [required]
1551   *            itemid => int the ID of the object being rated [required]
1552   *            scaleid => int scale id [optional]
1553   * @return bool
1554   * @throws coding_exception
1555   * @throws rating_exception
1556   */
1557  function mod_data_rating_can_see_item_ratings($params) {
1558      global $DB;
1559  
1560      // Check the component is mod_data.
1561      if (!isset($params['component']) || $params['component'] != 'mod_data') {
1562          throw new rating_exception('invalidcomponent');
1563      }
1564  
1565      // Check the ratingarea is entry (the only rating area in data).
1566      if (!isset($params['ratingarea']) || $params['ratingarea'] != 'entry') {
1567          throw new rating_exception('invalidratingarea');
1568      }
1569  
1570      if (!isset($params['itemid'])) {
1571          throw new rating_exception('invaliditemid');
1572      }
1573  
1574      $datasql = "SELECT d.id as dataid, d.course, r.groupid
1575                    FROM {data_records} r
1576                    JOIN {data} d ON r.dataid = d.id
1577                   WHERE r.id = :itemid";
1578      $dataparams = array('itemid' => $params['itemid']);
1579      if (!$info = $DB->get_record_sql($datasql, $dataparams)) {
1580          // Item doesn't exist.
1581          throw new rating_exception('invaliditemid');
1582      }
1583  
1584      // User can see ratings of all participants.
1585      if ($info->groupid == 0) {
1586          return true;
1587      }
1588  
1589      $course = $DB->get_record('course', array('id' => $info->course), '*', MUST_EXIST);
1590      $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
1591  
1592      // Make sure groups allow this user to see the item they're rating.
1593      return groups_group_visible($info->groupid, $course, $cm);
1594  }
1595  
1596  
1597  /**
1598   * function that takes in the current data, number of items per page,
1599   * a search string and prints a preference box in view.php
1600   *
1601   * This preference box prints a searchable advanced search template if
1602   *     a) A template is defined
1603   *  b) The advanced search checkbox is checked.
1604   *
1605   * @global object
1606   * @global object
1607   * @param object $data
1608   * @param int $perpage
1609   * @param string $search
1610   * @param string $sort
1611   * @param string $order
1612   * @param array $search_array
1613   * @param int $advanced
1614   * @param string $mode
1615   * @return void
1616   */
1617  function data_print_preference_form($data, $perpage, $search, $sort='', $order='ASC', $search_array = '', $advanced = 0, $mode= ''){
1618      global $CFG, $DB, $PAGE, $OUTPUT;
1619  
1620      $cm = get_coursemodule_from_instance('data', $data->id);
1621      $context = context_module::instance($cm->id);
1622      echo '<br /><div class="datapreferences">';
1623      echo '<form id="options" action="view.php" method="get">';
1624      echo '<div>';
1625      echo '<input type="hidden" name="d" value="'.$data->id.'" />';
1626      if ($mode =='asearch') {
1627          $advanced = 1;
1628          echo '<input type="hidden" name="mode" value="list" />';
1629      }
1630      echo '<label for="pref_perpage">'.get_string('pagesize','data').'</label> ';
1631      $pagesizes = array(2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,15=>15,
1632                         20=>20,30=>30,40=>40,50=>50,100=>100,200=>200,300=>300,400=>400,500=>500,1000=>1000);
1633      echo html_writer::select($pagesizes, 'perpage', $perpage, false, array('id'=>'pref_perpage'));
1634  
1635      if ($advanced) {
1636          $regsearchclass = 'search_none';
1637          $advancedsearchclass = 'search_inline';
1638      } else {
1639          $regsearchclass = 'search_inline';
1640          $advancedsearchclass = 'search_none';
1641      }
1642      echo '<div id="reg_search" class="' . $regsearchclass . '" >&nbsp;&nbsp;&nbsp;';
1643      echo '<label for="pref_search">'.get_string('search').'</label> <input type="text" size="16" name="search" id= "pref_search" value="'.s($search).'" /></div>';
1644      echo '&nbsp;&nbsp;&nbsp;<label for="pref_sortby">'.get_string('sortby').'</label> ';
1645      // foreach field, print the option
1646      echo '<select name="sort" id="pref_sortby">';
1647      if ($fields = $DB->get_records('data_fields', array('dataid'=>$data->id), 'name')) {
1648          echo '<optgroup label="'.get_string('fields', 'data').'">';
1649          foreach ($fields as $field) {
1650              if ($field->id == $sort) {
1651                  echo '<option value="'.$field->id.'" selected="selected">'.$field->name.'</option>';
1652              } else {
1653                  echo '<option value="'.$field->id.'">'.$field->name.'</option>';
1654              }
1655          }
1656          echo '</optgroup>';
1657      }
1658      $options = array();
1659      $options[DATA_TIMEADDED]    = get_string('timeadded', 'data');
1660      $options[DATA_TIMEMODIFIED] = get_string('timemodified', 'data');
1661      $options[DATA_FIRSTNAME]    = get_string('authorfirstname', 'data');
1662      $options[DATA_LASTNAME]     = get_string('authorlastname', 'data');
1663      if ($data->approval and has_capability('mod/data:approve', $context)) {
1664          $options[DATA_APPROVED] = get_string('approved', 'data');
1665      }
1666      echo '<optgroup label="'.get_string('other', 'data').'">';
1667      foreach ($options as $key => $name) {
1668          if ($key == $sort) {
1669              echo '<option value="'.$key.'" selected="selected">'.$name.'</option>';
1670          } else {
1671              echo '<option value="'.$key.'">'.$name.'</option>';
1672          }
1673      }
1674      echo '</optgroup>';
1675      echo '</select>';
1676      echo '<label for="pref_order" class="accesshide">'.get_string('order').'</label>';
1677      echo '<select id="pref_order" name="order">';
1678      if ($order == 'ASC') {
1679          echo '<option value="ASC" selected="selected">'.get_string('ascending','data').'</option>';
1680      } else {
1681          echo '<option value="ASC">'.get_string('ascending','data').'</option>';
1682      }
1683      if ($order == 'DESC') {
1684          echo '<option value="DESC" selected="selected">'.get_string('descending','data').'</option>';
1685      } else {
1686          echo '<option value="DESC">'.get_string('descending','data').'</option>';
1687      }
1688      echo '</select>';
1689  
1690      if ($advanced) {
1691          $checked = ' checked="checked" ';
1692      }
1693      else {
1694          $checked = '';
1695      }
1696      $PAGE->requires->js('/mod/data/data.js');
1697      echo '&nbsp;<input type="hidden" name="advanced" value="0" />';
1698      echo '&nbsp;<input type="hidden" name="filter" value="1" />';
1699      echo '&nbsp;<input type="checkbox" id="advancedcheckbox" name="advanced" value="1" '.$checked.' onchange="showHideAdvSearch(this.checked);" /><label for="advancedcheckbox">'.get_string('advancedsearch', 'data').'</label>';
1700      echo '&nbsp;<input type="submit" value="'.get_string('savesettings','data').'" />';
1701  
1702      echo '<br />';
1703      echo '<div class="' . $advancedsearchclass . '" id="data_adv_form">';
1704      echo '<table class="boxaligncenter">';
1705  
1706      // print ASC or DESC
1707      echo '<tr><td colspan="2">&nbsp;</td></tr>';
1708      $i = 0;
1709  
1710      // Determine if we are printing all fields for advanced search, or the template for advanced search
1711      // If a template is not defined, use the deafault template and display all fields.
1712      if(empty($data->asearchtemplate)) {
1713          data_generate_default_template($data, 'asearchtemplate');
1714      }
1715  
1716      static $fields = array();
1717      static $dataid = null;
1718  
1719      if (empty($dataid)) {
1720          $dataid = $data->id;
1721      } else if ($dataid != $data->id) {
1722          $fields = array();
1723      }
1724  
1725      if (empty($fields)) {
1726          $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$data->id));
1727          foreach ($fieldrecords as $fieldrecord) {
1728              $fields[]= data_get_field($fieldrecord, $data);
1729          }
1730      }
1731  
1732      // Replacing tags
1733      $patterns = array();
1734      $replacement = array();
1735  
1736      // Then we generate strings to replace for normal tags
1737      foreach ($fields as $field) {
1738          $fieldname = $field->field->name;
1739          $fieldname = preg_quote($fieldname, '/');
1740          $patterns[] = "/\[\[$fieldname\]\]/i";
1741          $searchfield = data_get_field_from_id($field->field->id, $data);
1742          if (!empty($search_array[$field->field->id]->data)) {
1743              $replacement[] = $searchfield->display_search_field($search_array[$field->field->id]->data);
1744          } else {
1745              $replacement[] = $searchfield->display_search_field();
1746          }
1747      }
1748      $fn = !empty($search_array[DATA_FIRSTNAME]->data) ? $search_array[DATA_FIRSTNAME]->data : '';
1749      $ln = !empty($search_array[DATA_LASTNAME]->data) ? $search_array[DATA_LASTNAME]->data : '';
1750      $patterns[]    = '/##firstname##/';
1751      $replacement[] = '<label class="accesshide" for="u_fn">'.get_string('authorfirstname', 'data').'</label><input type="text" size="16" id="u_fn" name="u_fn" value="'.s($fn).'" />';
1752      $patterns[]    = '/##lastname##/';
1753      $replacement[] = '<label class="accesshide" for="u_ln">'.get_string('authorlastname', 'data').'</label><input type="text" size="16" id="u_ln" name="u_ln" value="'.s($ln).'" />';
1754  
1755      // actual replacement of the tags
1756      $newtext = preg_replace($patterns, $replacement, $data->asearchtemplate);
1757  
1758      $options = new stdClass();
1759      $options->para=false;
1760      $options->noclean=true;
1761      echo '<tr><td>';
1762      echo format_text($newtext, FORMAT_HTML, $options);
1763      echo '</td></tr>';
1764  
1765      echo '<tr><td colspan="4"><br/><input type="submit" value="'.get_string('savesettings','data').'" /><input type="submit" name="resetadv" value="'.get_string('resetsettings','data').'" /></td></tr>';
1766      echo '</table>';
1767      echo '</div>';
1768      echo '</div>';
1769      echo '</form>';
1770      echo '</div>';
1771  }
1772  
1773  /**
1774   * @global object
1775   * @global object
1776   * @param object $data
1777   * @param object $record
1778   * @return void Output echo'd
1779   */
1780  function data_print_ratings($data, $record) {
1781      global $OUTPUT;
1782      if (!empty($record->rating)){
1783          echo $OUTPUT->render($record->rating);
1784      }
1785  }
1786  
1787  /**
1788   * List the actions that correspond to a view of this module.
1789   * This is used by the participation report.
1790   *
1791   * Note: This is not used by new logging system. Event with
1792   *       crud = 'r' and edulevel = LEVEL_PARTICIPATING will
1793   *       be considered as view action.
1794   *
1795   * @return array
1796   */
1797  function data_get_view_actions() {
1798      return array('view');
1799  }
1800  
1801  /**
1802   * List the actions that correspond to a post of this module.
1803   * This is used by the participation report.
1804   *
1805   * Note: This is not used by new logging system. Event with
1806   *       crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
1807   *       will be considered as post action.
1808   *
1809   * @return array
1810   */
1811  function data_get_post_actions() {
1812      return array('add','update','record delete');
1813  }
1814  
1815  /**
1816   * @param string $name
1817   * @param int $dataid
1818   * @param int $fieldid
1819   * @return bool
1820   */
1821  function data_fieldname_exists($name, $dataid, $fieldid = 0) {
1822      global $DB;
1823  
1824      if (!is_numeric($name)) {
1825          $like = $DB->sql_like('df.name', ':name', false);
1826      } else {
1827          $like = "df.name = :name";
1828      }
1829      $params = array('name'=>$name);
1830      if ($fieldid) {
1831          $params['dataid']   = $dataid;
1832          $params['fieldid1'] = $fieldid;
1833          $params['fieldid2'] = $fieldid;
1834          return $DB->record_exists_sql("SELECT * FROM {data_fields} df
1835                                          WHERE $like AND df.dataid = :dataid
1836                                                AND ((df.id < :fieldid1) OR (df.id > :fieldid2))", $params);
1837      } else {
1838          $params['dataid']   = $dataid;
1839          return $DB->record_exists_sql("SELECT * FROM {data_fields} df
1840                                          WHERE $like AND df.dataid = :dataid", $params);
1841      }
1842  }
1843  
1844  /**
1845   * @param array $fieldinput
1846   */
1847  function data_convert_arrays_to_strings(&$fieldinput) {
1848      foreach ($fieldinput as $key => $val) {
1849          if (is_array($val)) {
1850              $str = '';
1851              foreach ($val as $inner) {
1852                  $str .= $inner . ',';
1853              }
1854              $str = substr($str, 0, -1);
1855  
1856              $fieldinput->$key = $str;
1857          }
1858      }
1859  }
1860  
1861  
1862  /**
1863   * Converts a database (module instance) to use the Roles System
1864   *
1865   * @global object
1866   * @global object
1867   * @uses CONTEXT_MODULE
1868   * @uses CAP_PREVENT
1869   * @uses CAP_ALLOW
1870   * @param object $data a data object with the same attributes as a record
1871   *                     from the data database table
1872   * @param int $datamodid the id of the data module, from the modules table
1873   * @param array $teacherroles array of roles that have archetype teacher
1874   * @param array $studentroles array of roles that have archetype student
1875   * @param array $guestroles array of roles that have archetype guest
1876   * @param int $cmid the course_module id for this data instance
1877   * @return boolean data module was converted or not
1878   */
1879  function data_convert_to_roles($data, $teacherroles=array(), $studentroles=array(), $cmid=NULL) {
1880      global $CFG, $DB, $OUTPUT;
1881  
1882      if (!isset($data->participants) && !isset($data->assesspublic)
1883              && !isset($data->groupmode)) {
1884          // We assume that this database has already been converted to use the
1885          // Roles System. above fields get dropped the data module has been
1886          // upgraded to use Roles.
1887          return false;
1888      }
1889  
1890      if (empty($cmid)) {
1891          // We were not given the course_module id. Try to find it.
1892          if (!$cm = get_coursemodule_from_instance('data', $data->id)) {
1893              echo $OUTPUT->notification('Could not get the course module for the data');
1894              return false;
1895          } else {
1896              $cmid = $cm->id;
1897          }
1898      }
1899      $context = context_module::instance($cmid);
1900  
1901  
1902      // $data->participants:
1903      // 1 - Only teachers can add entries
1904      // 3 - Teachers and students can add entries
1905      switch ($data->participants) {
1906          case 1:
1907              foreach ($studentroles as $studentrole) {
1908                  assign_capability('mod/data:writeentry', CAP_PREVENT, $studentrole->id, $context->id);
1909              }
1910              foreach ($teacherroles as $teacherrole) {
1911                  assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
1912              }
1913              break;
1914          case 3:
1915              foreach ($studentroles as $studentrole) {
1916                  assign_capability('mod/data:writeentry', CAP_ALLOW, $studentrole->id, $context->id);
1917              }
1918              foreach ($teacherroles as $teacherrole) {
1919                  assign_capability('mod/data:writeentry', CAP_ALLOW, $teacherrole->id, $context->id);
1920              }
1921              break;
1922      }
1923  
1924      // $data->assessed:
1925      // 2 - Only teachers can rate posts
1926      // 1 - Everyone can rate posts
1927      // 0 - No one can rate posts
1928      switch ($data->assessed) {
1929          case 0:
1930              foreach ($studentroles as $studentrole) {
1931                  assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
1932              }
1933              foreach ($teacherroles as $teacherrole) {
1934                  assign_capability('mod/data:rate', CAP_PREVENT, $teacherrole->id, $context->id);
1935              }
1936              break;
1937          case 1:
1938              foreach ($studentroles as $studentrole) {
1939                  assign_capability('mod/data:rate', CAP_ALLOW, $studentrole->id, $context->id);
1940              }
1941              foreach ($teacherroles as $teacherrole) {
1942                  assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
1943              }
1944              break;
1945          case 2:
1946              foreach ($studentroles as $studentrole) {
1947                  assign_capability('mod/data:rate', CAP_PREVENT, $studentrole->id, $context->id);
1948              }
1949              foreach ($teacherroles as $teacherrole) {
1950                  assign_capability('mod/data:rate', CAP_ALLOW, $teacherrole->id, $context->id);
1951              }
1952              break;
1953      }
1954  
1955      // $data->assesspublic:
1956      // 0 - Students can only see their own ratings
1957      // 1 - Students can see everyone's ratings
1958      switch ($data->assesspublic) {
1959          case 0:
1960              foreach ($studentroles as $studentrole) {
1961                  assign_capability('mod/data:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
1962              }
1963              foreach ($teacherroles as $teacherrole) {
1964                  assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
1965              }
1966              break;
1967          case 1:
1968              foreach ($studentroles as $studentrole) {
1969                  assign_capability('mod/data:viewrating', CAP_ALLOW, $studentrole->id, $context->id);
1970              }
1971              foreach ($teacherroles as $teacherrole) {
1972                  assign_capability('mod/data:viewrating', CAP_ALLOW, $teacherrole->id, $context->id);
1973              }
1974              break;
1975      }
1976  
1977      if (empty($cm)) {
1978          $cm = $DB->get_record('course_modules', array('id'=>$cmid));
1979      }
1980  
1981      switch ($cm->groupmode) {
1982          case NOGROUPS:
1983              break;
1984          case SEPARATEGROUPS:
1985              foreach ($studentroles as $studentrole) {
1986                  assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
1987              }
1988              foreach ($teacherroles as $teacherrole) {
1989                  assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
1990              }
1991              break;
1992          case VISIBLEGROUPS:
1993              foreach ($studentroles as $studentrole) {
1994                  assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
1995              }
1996              foreach ($teacherroles as $teacherrole) {
1997                  assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
1998              }
1999              break;
2000      }
2001      return true;
2002  }
2003  
2004  /**
2005   * Returns the best name to show for a preset
2006   *
2007   * @param string $shortname
2008   * @param  string $path
2009   * @return string
2010   */
2011  function data_preset_name($shortname, $path) {
2012  
2013      // We are looking inside the preset itself as a first choice, but also in normal data directory
2014      $string = get_string('modulename', 'datapreset_'.$shortname);
2015  
2016      if (substr($string, 0, 1) == '[') {
2017          return $shortname;
2018      } else {
2019          return $string;
2020      }
2021  }
2022  
2023  /**
2024   * Returns an array of all the available presets.
2025   *
2026   * @return array
2027   */
2028  function data_get_available_presets($context) {
2029      global $CFG, $USER;
2030  
2031      $presets = array();
2032  
2033      // First load the ratings sub plugins that exist within the modules preset dir
2034      if ($dirs = core_component::get_plugin_list('datapreset')) {
2035          foreach ($dirs as $dir=>$fulldir) {
2036              if (is_directory_a_preset($fulldir)) {
2037                  $preset = new stdClass();
2038                  $preset->path = $fulldir;
2039                  $preset->userid = 0;
2040                  $preset->shortname = $dir;
2041                  $preset->name = data_preset_name($dir, $fulldir);
2042                  if (file_exists($fulldir.'/screenshot.jpg')) {
2043                      $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.jpg';
2044                  } else if (file_exists($fulldir.'/screenshot.png')) {
2045                      $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.png';
2046                  } else if (file_exists($fulldir.'/screenshot.gif')) {
2047                      $preset->screenshot = $CFG->wwwroot.'/mod/data/preset/'.$dir.'/screenshot.gif';
2048                  }
2049                  $presets[] = $preset;
2050              }
2051          }
2052      }
2053      // Now add to that the site presets that people have saved
2054      $presets = data_get_available_site_presets($context, $presets);
2055      return $presets;
2056  }
2057  
2058  /**
2059   * Gets an array of all of the presets that users have saved to the site.
2060   *
2061   * @param stdClass $context The context that we are looking from.
2062   * @param array $presets
2063   * @return array An array of presets
2064   */
2065  function data_get_available_site_presets($context, array $presets=array()) {
2066      global $USER;
2067  
2068      $fs = get_file_storage();
2069      $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
2070      $canviewall = has_capability('mod/data:viewalluserpresets', $context);
2071      if (empty($files)) {
2072          return $presets;
2073      }
2074      foreach ($files as $file) {
2075          if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory() || (!$canviewall && $file->get_userid() != $USER->id)) {
2076              continue;
2077          }
2078          $preset = new stdClass;
2079          $preset->path = $file->get_filepath();
2080          $preset->name = trim($preset->path, '/');
2081          $preset->shortname = $preset->name;
2082          $preset->userid = $file->get_userid();
2083          $preset->id = $file->get_id();
2084          $preset->storedfile = $file;
2085          $presets[] = $preset;
2086      }
2087      return $presets;
2088  }
2089  
2090  /**
2091   * Deletes a saved preset.
2092   *
2093   * @param string $name
2094   * @return bool
2095   */
2096  function data_delete_site_preset($name) {
2097      $fs = get_file_storage();
2098  
2099      $files = $fs->get_directory_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/');
2100      if (!empty($files)) {
2101          foreach ($files as $file) {
2102              $file->delete();
2103          }
2104      }
2105  
2106      $dir = $fs->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, '/'.$name.'/', '.');
2107      if (!empty($dir)) {
2108          $dir->delete();
2109      }
2110      return true;
2111  }
2112  
2113  /**
2114   * Prints the heads for a page
2115   *
2116   * @param stdClass $course
2117   * @param stdClass $cm
2118   * @param stdClass $data
2119   * @param string $currenttab
2120   */
2121  function data_print_header($course, $cm, $data, $currenttab='') {
2122  
2123      global $CFG, $displaynoticegood, $displaynoticebad, $OUTPUT, $PAGE;
2124  
2125      $PAGE->set_title($data->name);
2126      echo $OUTPUT->header();
2127      echo $OUTPUT->heading(format_string($data->name), 2);
2128      echo $OUTPUT->box(format_module_intro('data', $data, $cm->id), 'generalbox', 'intro');
2129  
2130      // Groups needed for Add entry tab
2131      $currentgroup = groups_get_activity_group($cm);
2132      $groupmode = groups_get_activity_groupmode($cm);
2133  
2134      // Print the tabs
2135  
2136      if ($currenttab) {
2137          include ('tabs.php');
2138      }
2139  
2140      // Print any notices
2141  
2142      if (!empty($displaynoticegood)) {
2143          echo $OUTPUT->notification($displaynoticegood, 'notifysuccess');    // good (usually green)
2144      } else if (!empty($displaynoticebad)) {
2145          echo $OUTPUT->notification($displaynoticebad);                     // bad (usuually red)
2146      }
2147  }
2148  
2149  /**
2150   * Can user add more entries?
2151   *
2152   * @param object $data
2153   * @param mixed $currentgroup
2154   * @param int $groupmode
2155   * @param stdClass $context
2156   * @return bool
2157   */
2158  function data_user_can_add_entry($data, $currentgroup, $groupmode, $context = null) {
2159      global $USER;
2160  
2161      if (empty($context)) {
2162          $cm = get_coursemodule_from_instance('data', $data->id, 0, false, MUST_EXIST);
2163          $context = context_module::instance($cm->id);
2164      }
2165  
2166      if (has_capability('mod/data:manageentries', $context)) {
2167          // no entry limits apply if user can manage
2168  
2169      } else if (!has_capability('mod/data:writeentry', $context)) {
2170          return false;
2171  
2172      } else if (data_atmaxentries($data)) {
2173          return false;
2174      } else if (data_in_readonly_period($data)) {
2175          // Check whether we're in a read-only period
2176          return false;
2177      }
2178  
2179      if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
2180          return true;
2181      }
2182  
2183      if ($currentgroup) {
2184          return groups_is_member($currentgroup);
2185      } else {
2186          //else it might be group 0 in visible mode
2187          if ($groupmode == VISIBLEGROUPS){
2188              return true;
2189          } else {
2190              return false;
2191          }
2192      }
2193  }
2194  
2195  /**
2196   * Check whether the current user is allowed to manage the given record considering manageentries capability,
2197   * data_in_readonly_period() result, ownership (determined by data_isowner()) and manageapproved setting.
2198   * @param mixed $record record object or id
2199   * @param object $data data object
2200   * @param object $context context object
2201   * @return bool returns true if the user is allowd to edit the entry, false otherwise
2202   */
2203  function data_user_can_manage_entry($record, $data, $context) {
2204      global $DB;
2205  
2206      if (has_capability('mod/data:manageentries', $context)) {
2207          return true;
2208      }
2209  
2210      // Check whether this activity is read-only at present.
2211      $readonly = data_in_readonly_period($data);
2212  
2213      if (!$readonly) {
2214          // Get record object from db if just id given like in data_isowner.
2215          // ...done before calling data_isowner() to avoid querying db twice.
2216          if (!is_object($record)) {
2217              if (!$record = $DB->get_record('data_records', array('id' => $record))) {
2218                  return false;
2219              }
2220          }
2221          if (data_isowner($record)) {
2222              if ($data->approval && $record->approved) {
2223                  return $data->manageapproved == 1;
2224              } else {
2225                  return true;
2226              }
2227          }
2228      }
2229  
2230      return false;
2231  }
2232  
2233  /**
2234   * Check whether the specified database activity is currently in a read-only period
2235   *
2236   * @param object $data
2237   * @return bool returns true if the time fields in $data indicate a read-only period; false otherwise
2238   */
2239  function data_in_readonly_period($data) {
2240      $now = time();
2241      if (!$data->timeviewfrom && !$data->timeviewto) {
2242          return false;
2243      } else if (($data->timeviewfrom && $now < $data->timeviewfrom) || ($data->timeviewto && $now > $data->timeviewto)) {
2244          return false;
2245      }
2246      return true;
2247  }
2248  
2249  /**
2250   * @return bool
2251   */
2252  function is_directory_a_preset($directory) {
2253      $directory = rtrim($directory, '/\\') . '/';
2254      $status = file_exists($directory.'singletemplate.html') &&
2255                file_exists($directory.'listtemplate.html') &&
2256                file_exists($directory.'listtemplateheader.html') &&
2257                file_exists($directory.'listtemplatefooter.html') &&
2258                file_exists($directory.'addtemplate.html') &&
2259                file_exists($directory.'rsstemplate.html') &&
2260                file_exists($directory.'rsstitletemplate.html') &&
2261                file_exists($directory.'csstemplate.css') &&
2262                file_exists($directory.'jstemplate.js') &&
2263                file_exists($directory.'preset.xml');
2264  
2265      return $status;
2266  }
2267  
2268  /**
2269   * Abstract class used for data preset importers
2270   */
2271  abstract class data_preset_importer {
2272  
2273      protected $course;
2274      protected $cm;
2275      protected $module;
2276      protected $directory;
2277  
2278      /**
2279       * Constructor
2280       *
2281       * @param stdClass $course
2282       * @param stdClass $cm
2283       * @param stdClass $module
2284       * @param string $directory
2285       */
2286      public function __construct($course, $cm, $module, $directory) {
2287          $this->course = $course;
2288          $this->cm = $cm;
2289          $this->module = $module;
2290          $this->directory = $directory;
2291      }
2292  
2293      /**
2294       * Returns the name of the directory the preset is located in
2295       * @return string
2296       */
2297      public function get_directory() {
2298          return basename($this->directory);
2299      }
2300  
2301      /**
2302       * Retreive the contents of a file. That file may either be in a conventional directory of the Moodle file storage
2303       * @param file_storage $filestorage. should be null if using a conventional directory
2304       * @param stored_file $fileobj the directory to look in. null if using a conventional directory
2305       * @param string $dir the directory to look in. null if using the Moodle file storage
2306       * @param string $filename the name of the file we want
2307       * @return string the contents of the file or null if the file doesn't exist.
2308       */
2309      public function data_preset_get_file_contents(&$filestorage, &$fileobj, $dir, $filename) {
2310          if(empty($filestorage) || empty($fileobj)) {
2311              if (substr($dir, -1)!='/') {
2312                  $dir .= '/';
2313              }
2314              if (file_exists($dir.$filename)) {
2315                  return file_get_contents($dir.$filename);
2316              } else {
2317                  return null;
2318              }
2319          } else {
2320              if ($filestorage->file_exists(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename)) {
2321                  $file = $filestorage->get_file(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA, 0, $fileobj->get_filepath(), $filename);
2322                  return $file->get_content();
2323              } else {
2324                  return null;
2325              }
2326          }
2327  
2328      }
2329      /**
2330       * Gets the preset settings
2331       * @global moodle_database $DB
2332       * @return stdClass
2333       */
2334      public function get_preset_settings() {
2335          global $DB;
2336  
2337          $fs = $fileobj = null;
2338          if (!is_directory_a_preset($this->directory)) {
2339              //maybe the user requested a preset stored in the Moodle file storage
2340  
2341              $fs = get_file_storage();
2342              $files = $fs->get_area_files(DATA_PRESET_CONTEXT, DATA_PRESET_COMPONENT, DATA_PRESET_FILEAREA);
2343  
2344              //preset name to find will be the final element of the directory
2345              $explodeddirectory = explode('/', $this->directory);
2346              $presettofind = end($explodeddirectory);
2347  
2348              //now go through the available files available and see if we can find it
2349              foreach ($files as $file) {
2350                  if (($file->is_directory() && $file->get_filepath()=='/') || !$file->is_directory()) {
2351                      continue;
2352                  }
2353                  $presetname = trim($file->get_filepath(), '/');
2354                  if ($presetname==$presettofind) {
2355                      $this->directory = $presetname;
2356                      $fileobj = $file;
2357                  }
2358              }
2359  
2360              if (empty($fileobj)) {
2361                  print_error('invalidpreset', 'data', '', $this->directory);
2362              }
2363          }
2364  
2365          $allowed_settings = array(
2366              'intro',
2367              'comments',
2368              'requiredentries',
2369              'requiredentriestoview',
2370              'maxentries',
2371              'rssarticles',
2372              'approval',
2373              'defaultsortdir',
2374              'defaultsort');
2375  
2376          $result = new stdClass;
2377          $result->settings = new stdClass;
2378          $result->importfields = array();
2379          $result->currentfields = $DB->get_records('data_fields', array('dataid'=>$this->module->id));
2380          if (!$result->currentfields) {
2381              $result->currentfields = array();
2382          }
2383  
2384  
2385          /* Grab XML */
2386          $presetxml = $this->data_preset_get_file_contents($fs, $fileobj, $this->directory,'preset.xml');
2387          $parsedxml = xmlize($presetxml, 0);
2388  
2389          /* First, do settings. Put in user friendly array. */
2390          $settingsarray = $parsedxml['preset']['#']['settings'][0]['#'];
2391          $result->settings = new StdClass();
2392          foreach ($settingsarray as $setting => $value) {
2393              if (!is_array($value) || !in_array($setting, $allowed_settings)) {
2394                  // unsupported setting
2395                  continue;
2396              }
2397              $result->settings->$setting = $value[0]['#'];
2398          }
2399  
2400          /* Now work out fields to user friendly array */
2401          $fieldsarray = $parsedxml['preset']['#']['field'];
2402          foreach ($fieldsarray as $field) {
2403              if (!is_array($field)) {
2404                  continue;
2405              }
2406              $f = new StdClass();
2407              foreach ($field['#'] as $param => $value) {
2408                  if (!is_array($value)) {
2409                      continue;
2410                  }
2411                  $f->$param = $value[0]['#'];
2412              }
2413              $f->dataid = $this->module->id;
2414              $f->type = clean_param($f->type, PARAM_ALPHA);
2415              $result->importfields[] = $f;
2416          }
2417          /* Now add the HTML templates to the settings array so we can update d */
2418          $result->settings->singletemplate     = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"singletemplate.html");
2419          $result->settings->listtemplate       = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplate.html");
2420          $result->settings->listtemplateheader = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplateheader.html");
2421          $result->settings->listtemplatefooter = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"listtemplatefooter.html");
2422          $result->settings->addtemplate        = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"addtemplate.html");
2423          $result->settings->rsstemplate        = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstemplate.html");
2424          $result->settings->rsstitletemplate   = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"rsstitletemplate.html");
2425          $result->settings->csstemplate        = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"csstemplate.css");
2426          $result->settings->jstemplate         = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"jstemplate.js");
2427          $result->settings->asearchtemplate    = $this->data_preset_get_file_contents($fs, $fileobj,$this->directory,"asearchtemplate.html");
2428  
2429          $result->settings->instance = $this->module->id;
2430          return $result;
2431      }
2432  
2433      /**
2434       * Import the preset into the given database module
2435       * @return bool
2436       */
2437      function import($overwritesettings) {
2438          global $DB, $CFG;
2439  
2440          $params = $this->get_preset_settings();
2441          $settings = $params->settings;
2442          $newfields = $params->importfields;
2443          $currentfields = $params->currentfields;
2444          $preservedfields = array();
2445  
2446          /* Maps fields and makes new ones */
2447          if (!empty($newfields)) {
2448              /* We require an injective mapping, and need to know what to protect */
2449              foreach ($newfields as $nid => $newfield) {
2450                  $cid = optional_param("field_$nid", -1, PARAM_INT);
2451                  if ($cid == -1) {
2452                      continue;
2453                  }
2454                  if (array_key_exists($cid, $preservedfields)){
2455                      print_error('notinjectivemap', 'data');
2456                  }
2457                  else $preservedfields[$cid] = true;
2458              }
2459  
2460              foreach ($newfields as $nid => $newfield) {
2461                  $cid = optional_param("field_$nid", -1, PARAM_INT);
2462  
2463                  /* A mapping. Just need to change field params. Data kept. */
2464                  if ($cid != -1 and isset($currentfields[$cid])) {
2465                      $fieldobject = data_get_field_from_id($currentfields[$cid]->id, $this->module);
2466                      foreach ($newfield as $param => $value) {
2467                          if ($param != "id") {
2468                              $fieldobject->field->$param = $value;
2469                          }
2470                      }
2471                      unset($fieldobject->field->similarfield);
2472                      $fieldobject->update_field();
2473                      unset($fieldobject);
2474                  } else {
2475                      /* Make a new field */
2476                      include_once("field/$newfield->type/field.class.php");
2477  
2478                      if (!isset($newfield->description)) {
2479                          $newfield->description = '';
2480                      }
2481                      $classname = 'data_field_'.$newfield->type;
2482                      $fieldclass = new $classname($newfield, $this->module);
2483                      $fieldclass->insert_field();
2484                      unset($fieldclass);
2485                  }
2486              }
2487          }
2488  
2489          /* Get rid of all old unused data */
2490          if (!empty($preservedfields)) {
2491              foreach ($currentfields as $cid => $currentfield) {
2492                  if (!array_key_exists($cid, $preservedfields)) {
2493                      /* Data not used anymore so wipe! */
2494                      print "Deleting field $currentfield->name<br />";
2495  
2496                      $id = $currentfield->id;
2497                      //Why delete existing data records and related comments/ratings??
2498                      $DB->delete_records('data_content', array('fieldid'=>$id));
2499                      $DB->delete_records('data_fields', array('id'=>$id));
2500                  }
2501              }
2502          }
2503  
2504          // handle special settings here
2505          if (!empty($settings->defaultsort)) {
2506              if (is_numeric($settings->defaultsort)) {
2507                  // old broken value
2508                  $settings->defaultsort = 0;
2509              } else {
2510                  $settings->defaultsort = (int)$DB->get_field('data_fields', 'id', array('dataid'=>$this->module->id, 'name'=>$settings->defaultsort));
2511              }
2512          } else {
2513              $settings->defaultsort = 0;
2514          }
2515  
2516          // do we want to overwrite all current database settings?
2517          if ($overwritesettings) {
2518              // all supported settings
2519              $overwrite = array_keys((array)$settings);
2520          } else {
2521              // only templates and sorting
2522              $overwrite = array('singletemplate', 'listtemplate', 'listtemplateheader', 'listtemplatefooter',
2523                                 'addtemplate', 'rsstemplate', 'rsstitletemplate', 'csstemplate', 'jstemplate',
2524                                 'asearchtemplate', 'defaultsortdir', 'defaultsort');
2525          }
2526  
2527          // now overwrite current data settings
2528          foreach ($this->module as $prop=>$unused) {
2529              if (in_array($prop, $overwrite)) {
2530                  $this->module->$prop = $settings->$prop;
2531              }
2532          }
2533  
2534          data_update_instance($this->module);
2535  
2536          return $this->cleanup();
2537      }
2538  
2539      /**
2540       * Any clean up routines should go here
2541       * @return bool
2542       */
2543      public function cleanup() {
2544          return true;
2545      }
2546  }
2547  
2548  /**
2549   * Data preset importer for uploaded presets
2550   */
2551  class data_preset_upload_importer extends data_preset_importer {
2552      public function __construct($course, $cm, $module, $filepath) {
2553          global $USER;
2554          if (is_file($filepath)) {
2555              $fp = get_file_packer();
2556              if ($fp->extract_to_pathname($filepath, $filepath.'_extracted')) {
2557                  fulldelete($filepath);
2558              }
2559              $filepath .= '_extracted';
2560          }
2561          parent::__construct($course, $cm, $module, $filepath);
2562      }
2563      public function cleanup() {
2564          return fulldelete($this->directory);
2565      }
2566  }
2567  
2568  /**
2569   * Data preset importer for existing presets
2570   */
2571  class data_preset_existing_importer extends data_preset_importer {
2572      protected $userid;
2573      public function __construct($course, $cm, $module, $fullname) {
2574          global $USER;
2575          list($userid, $shortname) = explode('/', $fullname, 2);
2576          $context = context_module::instance($cm->id);
2577          if ($userid && ($userid != $USER->id) && !has_capability('mod/data:manageuserpresets', $context) && !has_capability('mod/data:viewalluserpresets', $context)) {
2578             throw new coding_exception('Invalid preset provided');
2579          }
2580  
2581          $this->userid = $userid;
2582          $filepath = data_preset_path($course, $userid, $shortname);
2583          parent::__construct($course, $cm, $module, $filepath);
2584      }
2585      public function get_userid() {
2586          return $this->userid;
2587      }
2588  }
2589  
2590  /**
2591   * @global object
2592   * @global object
2593   * @param object $course
2594   * @param int $userid
2595   * @param string $shortname
2596   * @return string
2597   */
2598  function data_preset_path($course, $userid, $shortname) {
2599      global $USER, $CFG;
2600  
2601      $context = context_course::instance($course->id);
2602  
2603      $userid = (int)$userid;
2604  
2605      $path = null;
2606      if ($userid > 0 && ($userid == $USER->id || has_capability('mod/data:viewalluserpresets', $context))) {
2607          $path = $CFG->dataroot.'/data/preset/'.$userid.'/'.$shortname;
2608      } else if ($userid == 0) {
2609          $path = $CFG->dirroot.'/mod/data/preset/'.$shortname;
2610      } else if ($userid < 0) {
2611          $path = $CFG->tempdir.'/data/'.-$userid.'/'.$shortname;
2612      }
2613  
2614      return $path;
2615  }
2616  
2617  /**
2618   * Implementation of the function for printing the form elements that control
2619   * whether the course reset functionality affects the data.
2620   *
2621   * @param $mform form passed by reference
2622   */
2623  function data_reset_course_form_definition(&$mform) {
2624      $mform->addElement('header', 'dataheader', get_string('modulenameplural', 'data'));
2625      $mform->addElement('checkbox', 'reset_data', get_string('deleteallentries','data'));
2626  
2627      $mform->addElement('checkbox', 'reset_data_notenrolled', get_string('deletenotenrolled', 'data'));
2628      $mform->disabledIf('reset_data_notenrolled', 'reset_data', 'checked');
2629  
2630      $mform->addElement('checkbox', 'reset_data_ratings', get_string('deleteallratings'));
2631      $mform->disabledIf('reset_data_ratings', 'reset_data', 'checked');
2632  
2633      $mform->addElement('checkbox', 'reset_data_comments', get_string('deleteallcomments'));
2634      $mform->disabledIf('reset_data_comments', 'reset_data', 'checked');
2635  }
2636  
2637  /**
2638   * Course reset form defaults.
2639   * @return array
2640   */
2641  function data_reset_course_form_defaults($course) {
2642      return array('reset_data'=>0, 'reset_data_ratings'=>1, 'reset_data_comments'=>1, 'reset_data_notenrolled'=>0);
2643  }
2644  
2645  /**
2646   * Removes all grades from gradebook
2647   *
2648   * @global object
2649   * @global object
2650   * @param int $courseid
2651   * @param string $type optional type
2652   */
2653  function data_reset_gradebook($courseid, $type='') {
2654      global $CFG, $DB;
2655  
2656      $sql = "SELECT d.*, cm.idnumber as cmidnumber, d.course as courseid
2657                FROM {data} d, {course_modules} cm, {modules} m
2658               WHERE m.name='data' AND m.id=cm.module AND cm.instance=d.id AND d.course=?";
2659  
2660      if ($datas = $DB->get_records_sql($sql, array($courseid))) {
2661          foreach ($datas as $data) {
2662              data_grade_item_update($data, 'reset');
2663          }
2664      }
2665  }
2666  
2667  /**
2668   * Actual implementation of the reset course functionality, delete all the
2669   * data responses for course $data->courseid.
2670   *
2671   * @global object
2672   * @global object
2673   * @param object $data the data submitted from the reset course.
2674   * @return array status array
2675   */
2676  function data_reset_userdata($data) {
2677      global $CFG, $DB;
2678      require_once($CFG->libdir.'/filelib.php');
2679      require_once($CFG->dirroot.'/rating/lib.php');
2680  
2681      $componentstr = get_string('modulenameplural', 'data');
2682      $status = array();
2683  
2684      $allrecordssql = "SELECT r.id
2685                          FROM {data_records} r
2686                               INNER JOIN {data} d ON r.dataid = d.id
2687                         WHERE d.course = ?";
2688  
2689      $alldatassql = "SELECT d.id
2690                        FROM {data} d
2691                       WHERE d.course=?";
2692  
2693      $rm = new rating_manager();
2694      $ratingdeloptions = new stdClass;
2695      $ratingdeloptions->component = 'mod_data';
2696      $ratingdeloptions->ratingarea = 'entry';
2697  
2698      // Set the file storage - may need it to remove files later.
2699      $fs = get_file_storage();
2700  
2701      // delete entries if requested
2702      if (!empty($data->reset_data)) {
2703          $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
2704          $DB->delete_records_select('data_content', "recordid IN ($allrecordssql)", array($data->courseid));
2705          $DB->delete_records_select('data_records', "dataid IN ($alldatassql)", array($data->courseid));
2706  
2707          if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
2708              foreach ($datas as $dataid=>$unused) {
2709                  if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
2710                      continue;
2711                  }
2712                  $datacontext = context_module::instance($cm->id);
2713  
2714                  // Delete any files that may exist.
2715                  $fs->delete_area_files($datacontext->id, 'mod_data', 'content');
2716  
2717                  $ratingdeloptions->contextid = $datacontext->id;
2718                  $rm->delete_ratings($ratingdeloptions);
2719              }
2720          }
2721  
2722          if (empty($data->reset_gradebook_grades)) {
2723              // remove all grades from gradebook
2724              data_reset_gradebook($data->courseid);
2725          }
2726          $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallentries', 'data'), 'error'=>false);
2727      }
2728  
2729      // remove entries by users not enrolled into course
2730      if (!empty($data->reset_data_notenrolled)) {
2731          $recordssql = "SELECT r.id, r.userid, r.dataid, u.id AS userexists, u.deleted AS userdeleted
2732                           FROM {data_records} r
2733                                JOIN {data} d ON r.dataid = d.id
2734                                LEFT JOIN {user} u ON r.userid = u.id
2735                          WHERE d.course = ? AND r.userid > 0";
2736  
2737          $course_context = context_course::instance($data->courseid);
2738          $notenrolled = array();
2739          $fields = array();
2740          $rs = $DB->get_recordset_sql($recordssql, array($data->courseid));
2741          foreach ($rs as $record) {
2742              if (array_key_exists($record->userid, $notenrolled) or !$record->userexists or $record->userdeleted
2743                or !is_enrolled($course_context, $record->userid)) {
2744                  //delete ratings
2745                  if (!$cm = get_coursemodule_from_instance('data', $record->dataid)) {
2746                      continue;
2747                  }
2748                  $datacontext = context_module::instance($cm->id);
2749                  $ratingdeloptions->contextid = $datacontext->id;
2750                  $ratingdeloptions->itemid = $record->id;
2751                  $rm->delete_ratings($ratingdeloptions);
2752  
2753                  // Delete any files that may exist.
2754                  if ($contents = $DB->get_records('data_content', array('recordid' => $record->id), '', 'id')) {
2755                      foreach ($contents as $content) {
2756                          $fs->delete_area_files($datacontext->id, 'mod_data', 'content', $content->id);
2757                      }
2758                  }
2759                  $notenrolled[$record->userid] = true;
2760  
2761                  $DB->delete_records('comments', array('itemid' => $record->id, 'commentarea' => 'database_entry'));
2762                  $DB->delete_records('data_content', array('recordid' => $record->id));
2763                  $DB->delete_records('data_records', array('id' => $record->id));
2764              }
2765          }
2766          $rs->close();
2767          $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'data'), 'error'=>false);
2768      }
2769  
2770      // remove all ratings
2771      if (!empty($data->reset_data_ratings)) {
2772          if ($datas = $DB->get_records_sql($alldatassql, array($data->courseid))) {
2773              foreach ($datas as $dataid=>$unused) {
2774                  if (!$cm = get_coursemodule_from_instance('data', $dataid)) {
2775                      continue;
2776                  }
2777                  $datacontext = context_module::instance($cm->id);
2778  
2779                  $ratingdeloptions->contextid = $datacontext->id;
2780                  $rm->delete_ratings($ratingdeloptions);
2781              }
2782          }
2783  
2784          if (empty($data->reset_gradebook_grades)) {
2785              // remove all grades from gradebook
2786              data_reset_gradebook($data->courseid);
2787          }
2788  
2789          $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false);
2790      }
2791  
2792      // remove all comments
2793      if (!empty($data->reset_data_comments)) {
2794          $DB->delete_records_select('comments', "itemid IN ($allrecordssql) AND commentarea='database_entry'", array($data->courseid));
2795          $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false);
2796      }
2797  
2798      // updating dates - shift may be negative too
2799      if ($data->timeshift) {
2800          shift_course_mod_dates('data', array('timeavailablefrom', 'timeavailableto', 'timeviewfrom', 'timeviewto'), $data->timeshift, $data->courseid);
2801          $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
2802      }
2803  
2804      return $status;
2805  }
2806  
2807  /**
2808   * Returns all other caps used in module
2809   *
2810   * @return array
2811   */
2812  function data_get_extra_capabilities() {
2813      return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate', 'moodle/comment:view', 'moodle/comment:post', 'moodle/comment:delete');
2814  }
2815  
2816  /**
2817   * @param string $feature FEATURE_xx constant for requested feature
2818   * @return mixed True if module supports feature, null if doesn't know
2819   */
2820  function data_supports($feature) {
2821      switch($feature) {
2822          case FEATURE_GROUPS:                  return true;
2823          case FEATURE_GROUPINGS:               return true;
2824          case FEATURE_MOD_INTRO:               return true;
2825          case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
2826          case FEATURE_GRADE_HAS_GRADE:         return true;
2827          case FEATURE_GRADE_OUTCOMES:          return true;
2828          case FEATURE_RATE:                    return true;
2829          case FEATURE_BACKUP_MOODLE2:          return true;
2830          case FEATURE_SHOW_DESCRIPTION:        return true;
2831  
2832          default: return null;
2833      }
2834  }
2835  /**
2836   * @global object
2837   * @param array $export
2838   * @param string $delimiter_name
2839   * @param object $database
2840   * @param int $count
2841   * @param bool $return
2842   * @return string|void
2843   */
2844  function data_export_csv($export, $delimiter_name, $database, $count, $return=false) {
2845      global $CFG;
2846      require_once($CFG->libdir . '/csvlib.class.php');
2847  
2848      $filename = $database . '-' . $count . '-record';
2849      if ($count > 1) {
2850          $filename .= 's';
2851      }
2852      if ($return) {
2853          return csv_export_writer::print_array($export, $delimiter_name, '"', true);
2854      } else {
2855          csv_export_writer::download_array($filename, $export, $delimiter_name);
2856      }
2857  }
2858  
2859  /**
2860   * @global object
2861   * @param array $export
2862   * @param string $dataname
2863   * @param int $count
2864   * @return string
2865   */
2866  function data_export_xls($export, $dataname, $count) {
2867      global $CFG;
2868      require_once("$CFG->libdir/excellib.class.php");
2869      $filename = clean_filename("{$dataname}-{$count}_record");
2870      if ($count > 1) {
2871          $filename .= 's';
2872      }
2873      $filename .= clean_filename('-' . gmdate("Ymd_Hi"));
2874      $filename .= '.xls';
2875  
2876      $filearg = '-';
2877      $workbook = new MoodleExcelWorkbook($filearg);
2878      $workbook->send($filename);
2879      $worksheet = array();
2880      $worksheet[0] = $workbook->add_worksheet('');
2881      $rowno = 0;
2882      foreach ($export as $row) {
2883          $colno = 0;
2884          foreach($row as $col) {
2885              $worksheet[0]->write($rowno, $colno, $col);
2886              $colno++;
2887          }
2888          $rowno++;
2889      }
2890      $workbook->close();
2891      return $filename;
2892  }
2893  
2894  /**
2895   * @global object
2896   * @param array $export
2897   * @param string $dataname
2898   * @param int $count
2899   * @param string
2900   */
2901  function data_export_ods($export, $dataname, $count) {
2902      global $CFG;
2903      require_once("$CFG->libdir/odslib.class.php");
2904      $filename = clean_filename("{$dataname}-{$count}_record");
2905      if ($count > 1) {
2906          $filename .= 's';
2907      }
2908      $filename .= clean_filename('-' . gmdate("Ymd_Hi"));
2909      $filename .= '.ods';
2910      $filearg = '-';
2911      $workbook = new MoodleODSWorkbook($filearg);
2912      $workbook->send($filename);
2913      $worksheet = array();
2914      $worksheet[0] = $workbook->add_worksheet('');
2915      $rowno = 0;
2916      foreach ($export as $row) {
2917          $colno = 0;
2918          foreach($row as $col) {
2919              $worksheet[0]->write($rowno, $colno, $col);
2920              $colno++;
2921          }
2922          $rowno++;
2923      }
2924      $workbook->close();
2925      return $filename;
2926  }
2927  
2928  /**
2929   * @global object
2930   * @param int $dataid
2931   * @param array $fields
2932   * @param array $selectedfields
2933   * @param int $currentgroup group ID of the current group. This is used for
2934   * exporting data while maintaining group divisions.
2935   * @param object $context the context in which the operation is performed (for capability checks)
2936   * @param bool $userdetails whether to include the details of the record author
2937   * @param bool $time whether to include time created/modified
2938   * @param bool $approval whether to include approval status
2939   * @return array
2940   */
2941  function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null,
2942                               $userdetails=false, $time=false, $approval=false) {
2943      global $DB;
2944  
2945      if (is_null($context)) {
2946          $context = context_system::instance();
2947      }
2948      // exporting user data needs special permission
2949      $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context);
2950  
2951      $exportdata = array();
2952  
2953      // populate the header in first row of export
2954      foreach($fields as $key => $field) {
2955          if (!in_array($field->field->id, $selectedfields)) {
2956              // ignore values we aren't exporting
2957              unset($fields[$key]);
2958          } else {
2959              $exportdata[0][] = $field->field->name;
2960          }
2961      }
2962      if ($userdetails) {
2963          $exportdata[0][] = get_string('user');
2964          $exportdata[0][] = get_string('username');
2965          $exportdata[0][] = get_string('email');
2966      }
2967      if ($time) {
2968          $exportdata[0][] = get_string('timeadded', 'data');
2969          $exportdata[0][] = get_string('timemodified', 'data');
2970      }
2971      if ($approval) {
2972          $exportdata[0][] = get_string('approved', 'data');
2973      }
2974  
2975      $datarecords = $DB->get_records('data_records', array('dataid'=>$dataid));
2976      ksort($datarecords);
2977      $line = 1;
2978      foreach($datarecords as $record) {
2979          // get content indexed by fieldid
2980          if ($currentgroup) {
2981              $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?';
2982              $where = array($record->id, $currentgroup);
2983          } else {
2984              $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?';
2985              $where = array($record->id);
2986          }
2987  
2988          if( $content = $DB->get_records_sql($select, $where) ) {
2989              foreach($fields as $field) {
2990                  $contents = '';
2991                  if(isset($content[$field->field->id])) {
2992                      $contents = $field->export_text_value($content[$field->field->id]);
2993                  }
2994                  $exportdata[$line][] = $contents;
2995              }
2996              if ($userdetails) { // Add user details to the export data
2997                  $userdata = get_complete_user_data('id', $record->userid);
2998                  $exportdata[$line][] = fullname($userdata);
2999                  $exportdata[$line][] = $userdata->username;
3000                  $exportdata[$line][] = $userdata->email;
3001              }
3002              if ($time) { // Add time added / modified
3003                  $exportdata[$line][] = userdate($record->timecreated);
3004                  $exportdata[$line][] = userdate($record->timemodified);
3005              }
3006              if ($approval) { // Add approval status
3007                  $exportdata[$line][] = (int) $record->approved;
3008              }
3009          }
3010          $line++;
3011      }
3012      $line--;
3013      return $exportdata;
3014  }
3015  
3016  ////////////////////////////////////////////////////////////////////////////////
3017  // File API                                                                   //
3018  ////////////////////////////////////////////////////////////////////////////////
3019  
3020  /**
3021   * Lists all browsable file areas
3022   *
3023   * @package  mod_data
3024   * @category files
3025   * @param stdClass $course course object
3026   * @param stdClass $cm course module object
3027   * @param stdClass $context context object
3028   * @return array
3029   */
3030  function data_get_file_areas($course, $cm, $context) {
3031      return array('content' => get_string('areacontent', 'mod_data'));
3032  }
3033  
3034  /**
3035   * File browsing support for data module.
3036   *
3037   * @param file_browser $browser
3038   * @param array $areas
3039   * @param stdClass $course
3040   * @param cm_info $cm
3041   * @param context $context
3042   * @param string $filearea
3043   * @param int $itemid
3044   * @param string $filepath
3045   * @param string $filename
3046   * @return file_info_stored file_info_stored instance or null if not found
3047   */
3048  function data_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
3049      global $CFG, $DB, $USER;
3050  
3051      if ($context->contextlevel != CONTEXT_MODULE) {
3052          return null;
3053      }
3054  
3055      if (!isset($areas[$filearea])) {
3056          return null;
3057      }
3058  
3059      if (is_null($itemid)) {
3060          require_once($CFG->dirroot.'/mod/data/locallib.php');
3061          return new data_file_info_container($browser, $course, $cm, $context, $areas, $filearea);
3062      }
3063  
3064      if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) {
3065          return null;
3066      }
3067  
3068      if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) {
3069          return null;
3070      }
3071  
3072      if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) {
3073          return null;
3074      }
3075  
3076      if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) {
3077          return null;
3078      }
3079  
3080      //check if approved
3081      if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
3082          return null;
3083      }
3084  
3085      // group access
3086      if ($record->groupid) {
3087          $groupmode = groups_get_activity_groupmode($cm, $course);
3088          if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
3089              if (!groups_is_member($record->groupid)) {
3090                  return null;
3091              }
3092          }
3093      }
3094  
3095      $fieldobj = data_get_field($field, $data, $cm);
3096  
3097      $filepath = is_null($filepath) ? '/' : $filepath;
3098      $filename = is_null($filename) ? '.' : $filename;
3099      if (!$fieldobj->file_ok($filepath.$filename)) {
3100          return null;
3101      }
3102  
3103      $fs = get_file_storage();
3104      if (!($storedfile = $fs->get_file($context->id, 'mod_data', $filearea, $itemid, $filepath, $filename))) {
3105          return null;
3106      }
3107  
3108      // Checks to see if the user can manage files or is the owner.
3109      // TODO MDL-33805 - Do not use userid here and move the capability check above.
3110      if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
3111          return null;
3112      }
3113  
3114      $urlbase = $CFG->wwwroot.'/pluginfile.php';
3115  
3116      return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
3117  }
3118  
3119  /**
3120   * Serves the data attachments. Implements needed access control ;-)
3121   *
3122   * @package  mod_data
3123   * @category files
3124   * @param stdClass $course course object
3125   * @param stdClass $cm course module object
3126   * @param stdClass $context context object
3127   * @param string $filearea file area
3128   * @param array $args extra arguments
3129   * @param bool $forcedownload whether or not force download
3130   * @param array $options additional options affecting the file serving
3131   * @return bool false if file not found, does not return if found - justsend the file
3132   */
3133  function data_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
3134      global $CFG, $DB;
3135  
3136      if ($context->contextlevel != CONTEXT_MODULE) {
3137          return false;
3138      }
3139  
3140      require_course_login($course, true, $cm);
3141  
3142      if ($filearea === 'content') {
3143          $contentid = (int)array_shift($args);
3144  
3145          if (!$content = $DB->get_record('data_content', array('id'=>$contentid))) {
3146              return false;
3147          }
3148  
3149          if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) {
3150              return false;
3151          }
3152  
3153          if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) {
3154              return false;
3155          }
3156  
3157          if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) {
3158              return false;
3159          }
3160  
3161          if ($data->id != $cm->instance) {
3162              // hacker attempt - context does not match the contentid
3163              return false;
3164          }
3165  
3166          //check if approved
3167          if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
3168              return false;
3169          }
3170  
3171          // group access
3172          if ($record->groupid) {
3173              $groupmode = groups_get_activity_groupmode($cm, $course);
3174              if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
3175                  if (!groups_is_member($record->groupid)) {
3176                      return false;
3177                  }
3178              }
3179          }
3180  
3181          $fieldobj = data_get_field($field, $data, $cm);
3182  
3183          $relativepath = implode('/', $args);
3184          $fullpath = "/$context->id/mod_data/content/$content->id/$relativepath";
3185  
3186          if (!$fieldobj->file_ok($relativepath)) {
3187              return false;
3188          }
3189  
3190          $fs = get_file_storage();
3191          if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
3192              return false;
3193          }
3194  
3195          // finally send the file
3196          send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
3197      }
3198  
3199      return false;
3200  }
3201  
3202  
3203  function data_extend_navigation($navigation, $course, $module, $cm) {
3204      global $CFG, $OUTPUT, $USER, $DB;
3205  
3206      $rid = optional_param('rid', 0, PARAM_INT);
3207  
3208      $data = $DB->get_record('data', array('id'=>$cm->instance));
3209      $currentgroup = groups_get_activity_group($cm);
3210      $groupmode = groups_get_activity_groupmode($cm);
3211  
3212       $numentries = data_numentries($data);
3213      /// Check the number of entries required against the number of entries already made (doesn't apply to teachers)
3214      if ($data->requiredentries > 0 && $numentries < $data->requiredentries && !has_capability('mod/data:manageentries', context_module::instance($cm->id))) {
3215          $data->entriesleft = $data->requiredentries - $numentries;
3216          $entriesnode = $navigation->add(get_string('entrieslefttoadd', 'data', $data));
3217          $entriesnode->add_class('note');
3218      }
3219  
3220      $navigation->add(get_string('list', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance)));
3221      if (!empty($rid)) {
3222          $navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'rid'=>$rid)));
3223      } else {
3224          $navigation->add(get_string('single', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'single')));
3225      }
3226      $navigation->add(get_string('search', 'data'), new moodle_url('/mod/data/view.php', array('d'=>$cm->instance, 'mode'=>'asearch')));
3227  }
3228  
3229  /**
3230   * Adds module specific settings to the settings block
3231   *
3232   * @param settings_navigation $settings The settings navigation object
3233   * @param navigation_node $datanode The node to add module settings to
3234   */
3235  function data_extend_settings_navigation(settings_navigation $settings, navigation_node $datanode) {
3236      global $PAGE, $DB, $CFG, $USER;
3237  
3238      $data = $DB->get_record('data', array("id" => $PAGE->cm->instance));
3239  
3240      $currentgroup = groups_get_activity_group($PAGE->cm);
3241      $groupmode = groups_get_activity_groupmode($PAGE->cm);
3242  
3243      if (data_user_can_add_entry($data, $currentgroup, $groupmode, $PAGE->cm->context)) { // took out participation list here!
3244          if (empty($editentry)) { //TODO: undefined
3245              $addstring = get_string('add', 'data');
3246          } else {
3247              $addstring = get_string('editentry', 'data');
3248          }
3249          $datanode->add($addstring, new moodle_url('/mod/data/edit.php', array('d'=>$PAGE->cm->instance)));
3250      }
3251  
3252      if (has_capability(DATA_CAP_EXPORT, $PAGE->cm->context)) {
3253          // The capability required to Export database records is centrally defined in 'lib.php'
3254          // and should be weaker than those required to edit Templates, Fields and Presets.
3255          $datanode->add(get_string('exportentries', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id)));
3256      }
3257      if (has_capability('mod/data:manageentries', $PAGE->cm->context)) {
3258          $datanode->add(get_string('importentries', 'data'), new moodle_url('/mod/data/import.php', array('d'=>$data->id)));
3259      }
3260  
3261      if (has_capability('mod/data:managetemplates', $PAGE->cm->context)) {
3262          $currenttab = '';
3263          if ($currenttab == 'list') {
3264              $defaultemplate = 'listtemplate';
3265          } else if ($currenttab == 'add') {
3266              $defaultemplate = 'addtemplate';
3267          } else if ($currenttab == 'asearch') {
3268              $defaultemplate = 'asearchtemplate';
3269          } else {
3270              $defaultemplate = 'singletemplate';
3271          }
3272  
3273          $templates = $datanode->add(get_string('templates', 'data'));
3274  
3275          $templatelist = array ('listtemplate', 'singletemplate', 'asearchtemplate', 'addtemplate', 'rsstemplate', 'csstemplate', 'jstemplate');
3276          foreach ($templatelist as $template) {
3277              $templates->add(get_string($template, 'data'), new moodle_url('/mod/data/templates.php', array('d'=>$data->id,'mode'=>$template)));
3278          }
3279  
3280          $datanode->add(get_string('fields', 'data'), new moodle_url('/mod/data/field.php', array('d'=>$data->id)));
3281          $datanode->add(get_string('presets', 'data'), new moodle_url('/mod/data/preset.php', array('d'=>$data->id)));
3282      }
3283  
3284      if (!empty($CFG->enablerssfeeds) && !empty($CFG->data_enablerssfeeds) && $data->rssarticles > 0) {
3285          require_once("$CFG->libdir/rsslib.php");
3286  
3287          $string = get_string('rsstype','forum');
3288  
3289          $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $USER->id, 'mod_data', $data->id));
3290          $datanode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
3291      }
3292  }
3293  
3294  /**
3295   * Save the database configuration as a preset.
3296   *
3297   * @param stdClass $course The course the database module belongs to.
3298   * @param stdClass $cm The course module record
3299   * @param stdClass $data The database record
3300   * @param string $path
3301   * @return bool
3302   */
3303  function data_presets_save($course, $cm, $data, $path) {
3304      global $USER;
3305      $fs = get_file_storage();
3306      $filerecord = new stdClass;
3307      $filerecord->contextid = DATA_PRESET_CONTEXT;
3308      $filerecord->component = DATA_PRESET_COMPONENT;
3309      $filerecord->filearea = DATA_PRESET_FILEAREA;
3310      $filerecord->itemid = 0;
3311      $filerecord->filepath = '/'.$path.'/';
3312      $filerecord->userid = $USER->id;
3313  
3314      $filerecord->filename = 'preset.xml';
3315      $fs->create_file_from_string($filerecord, data_presets_generate_xml($course, $cm, $data));
3316  
3317      $filerecord->filename = 'singletemplate.html';
3318      $fs->create_file_from_string($filerecord, $data->singletemplate);
3319  
3320      $filerecord->filename = 'listtemplateheader.html';
3321      $fs->create_file_from_string($filerecord, $data->listtemplateheader);
3322  
3323      $filerecord->filename = 'listtemplate.html';
3324      $fs->create_file_from_string($filerecord, $data->listtemplate);
3325  
3326      $filerecord->filename = 'listtemplatefooter.html';
3327      $fs->create_file_from_string($filerecord, $data->listtemplatefooter);
3328  
3329      $filerecord->filename = 'addtemplate.html';
3330      $fs->create_file_from_string($filerecord, $data->addtemplate);
3331  
3332      $filerecord->filename = 'rsstemplate.html';
3333      $fs->create_file_from_string($filerecord, $data->rsstemplate);
3334  
3335      $filerecord->filename = 'rsstitletemplate.html';
3336      $fs->create_file_from_string($filerecord, $data->rsstitletemplate);
3337  
3338      $filerecord->filename = 'csstemplate.css';
3339      $fs->create_file_from_string($filerecord, $data->csstemplate);
3340  
3341      $filerecord->filename = 'jstemplate.js';
3342      $fs->create_file_from_string($filerecord, $data->jstemplate);
3343  
3344      $filerecord->filename = 'asearchtemplate.html';
3345      $fs->create_file_from_string($filerecord, $data->asearchtemplate);
3346  
3347      return true;
3348  }
3349  
3350  /**
3351   * Generates the XML for the database module provided
3352   *
3353   * @global moodle_database $DB
3354   * @param stdClass $course The course the database module belongs to.
3355   * @param stdClass $cm The course module record
3356   * @param stdClass $data The database record
3357   * @return string The XML for the preset
3358   */
3359  function data_presets_generate_xml($course, $cm, $data) {
3360      global $DB;
3361  
3362      // Assemble "preset.xml":
3363      $presetxmldata = "<preset>\n\n";
3364  
3365      // Raw settings are not preprocessed during saving of presets
3366      $raw_settings = array(
3367          'intro',
3368          'comments',
3369          'requiredentries',
3370          'requiredentriestoview',
3371          'maxentries',
3372          'rssarticles',
3373          'approval',
3374          'manageapproved',
3375          'defaultsortdir'
3376      );
3377  
3378      $presetxmldata .= "<settings>\n";
3379      // First, settings that do not require any conversion
3380      foreach ($raw_settings as $setting) {
3381          $presetxmldata .= "<$setting>" . htmlspecialchars($data->$setting) . "</$setting>\n";
3382      }
3383  
3384      // Now specific settings
3385      if ($data->defaultsort > 0 && $sortfield = data_get_field_from_id($data->defaultsort, $data)) {
3386          $presetxmldata .= '<defaultsort>' . htmlspecialchars($sortfield->field->name) . "</defaultsort>\n";
3387      } else {
3388          $presetxmldata .= "<defaultsort>0</defaultsort>\n";
3389      }
3390      $presetxmldata .= "</settings>\n\n";
3391      // Now for the fields. Grab all that are non-empty
3392      $fields = $DB->get_records('data_fields', array('dataid'=>$data->id));
3393      ksort($fields);
3394      if (!empty($fields)) {
3395          foreach ($fields as $field) {
3396              $presetxmldata .= "<field>\n";
3397              foreach ($field as $key => $value) {
3398                  if ($value != '' && $key != 'id' && $key != 'dataid') {
3399                      $presetxmldata .= "<$key>" . htmlspecialchars($value) . "</$key>\n";
3400                  }
3401              }
3402              $presetxmldata .= "</field>\n\n";
3403          }
3404      }
3405      $presetxmldata .= '</preset>';
3406      return $presetxmldata;
3407  }
3408  
3409  function data_presets_export($course, $cm, $data, $tostorage=false) {
3410      global $CFG, $DB;
3411  
3412      $presetname = clean_filename($data->name) . '-preset-' . gmdate("Ymd_Hi");
3413      $exportsubdir = "mod_data/presetexport/$presetname";
3414      make_temp_directory($exportsubdir);
3415      $exportdir = "$CFG->tempdir/$exportsubdir";
3416  
3417      // Assemble "preset.xml":
3418      $presetxmldata = data_presets_generate_xml($course, $cm, $data);
3419  
3420      // After opening a file in write mode, close it asap
3421      $presetxmlfile = fopen($exportdir . '/preset.xml', 'w');
3422      fwrite($presetxmlfile, $presetxmldata);
3423      fclose($presetxmlfile);
3424  
3425      // Now write the template files
3426      $singletemplate = fopen($exportdir . '/singletemplate.html', 'w');
3427      fwrite($singletemplate, $data->singletemplate);
3428      fclose($singletemplate);
3429  
3430      $listtemplateheader = fopen($exportdir . '/listtemplateheader.html', 'w');
3431      fwrite($listtemplateheader, $data->listtemplateheader);
3432      fclose($listtemplateheader);
3433  
3434      $listtemplate = fopen($exportdir . '/listtemplate.html', 'w');
3435      fwrite($listtemplate, $data->listtemplate);
3436      fclose($listtemplate);
3437  
3438      $listtemplatefooter = fopen($exportdir . '/listtemplatefooter.html', 'w');
3439      fwrite($listtemplatefooter, $data->listtemplatefooter);
3440      fclose($listtemplatefooter);
3441  
3442      $addtemplate = fopen($exportdir . '/addtemplate.html', 'w');
3443      fwrite($addtemplate, $data->addtemplate);
3444      fclose($addtemplate);
3445  
3446      $rsstemplate = fopen($exportdir . '/rsstemplate.html', 'w');
3447      fwrite($rsstemplate, $data->rsstemplate);
3448      fclose($rsstemplate);
3449  
3450      $rsstitletemplate = fopen($exportdir . '/rsstitletemplate.html', 'w');
3451      fwrite($rsstitletemplate, $data->rsstitletemplate);
3452      fclose($rsstitletemplate);
3453  
3454      $csstemplate = fopen($exportdir . '/csstemplate.css', 'w');
3455      fwrite($csstemplate, $data->csstemplate);
3456      fclose($csstemplate);
3457  
3458      $jstemplate = fopen($exportdir . '/jstemplate.js', 'w');
3459      fwrite($jstemplate, $data->jstemplate);
3460      fclose($jstemplate);
3461  
3462      $asearchtemplate = fopen($exportdir . '/asearchtemplate.html', 'w');
3463      fwrite($asearchtemplate, $data->asearchtemplate);
3464      fclose($asearchtemplate);
3465  
3466      // Check if all files have been generated
3467      if (! is_directory_a_preset($exportdir)) {
3468          print_error('generateerror', 'data');
3469      }
3470  
3471      $filenames = array(
3472          'preset.xml',
3473          'singletemplate.html',
3474          'listtemplateheader.html',
3475          'listtemplate.html',
3476          'listtemplatefooter.html',
3477          'addtemplate.html',
3478          'rsstemplate.html',
3479          'rsstitletemplate.html',
3480          'csstemplate.css',
3481          'jstemplate.js',
3482          'asearchtemplate.html'
3483      );
3484  
3485      $filelist = array();
3486      foreach ($filenames as $filename) {
3487          $filelist[$filename] = $exportdir . '/' . $filename;
3488      }
3489  
3490      $exportfile = $exportdir.'.zip';
3491      file_exists($exportfile) && unlink($exportfile);
3492  
3493      $fp = get_file_packer('application/zip');
3494      $fp->archive_to_pathname($filelist, $exportfile);
3495  
3496      foreach ($filelist as $file) {
3497          unlink($file);
3498      }
3499      rmdir($exportdir);
3500  
3501      // Return the full path to the exported preset file:
3502      return $exportfile;
3503  }
3504  
3505  /**
3506   * Running addtional permission check on plugin, for example, plugins
3507   * may have switch to turn on/off comments option, this callback will
3508   * affect UI display, not like pluginname_comment_validate only throw
3509   * exceptions.
3510   * Capability check has been done in comment->check_permissions(), we
3511   * don't need to do it again here.
3512   *
3513   * @package  mod_data
3514   * @category comment
3515   *
3516   * @param stdClass $comment_param {
3517   *              context  => context the context object
3518   *              courseid => int course id
3519   *              cm       => stdClass course module object
3520   *              commentarea => string comment area
3521   *              itemid      => int itemid
3522   * }
3523   * @return array
3524   */
3525  function data_comment_permissions($comment_param) {
3526      global $CFG, $DB;
3527      if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) {
3528          throw new comment_exception('invalidcommentitemid');
3529      }
3530      if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) {
3531          throw new comment_exception('invalidid', 'data');
3532      }
3533      if ($data->comments) {
3534          return array('post'=>true, 'view'=>true);
3535      } else {
3536          return array('post'=>false, 'view'=>false);
3537      }
3538  }
3539  
3540  /**
3541   * Validate comment parameter before perform other comments actions
3542   *
3543   * @package  mod_data
3544   * @category comment
3545   *
3546   * @param stdClass $comment_param {
3547   *              context  => context the context object
3548   *              courseid => int course id
3549   *              cm       => stdClass course module object
3550   *              commentarea => string comment area
3551   *              itemid      => int itemid
3552   * }
3553   * @return boolean
3554   */
3555  function data_comment_validate($comment_param) {
3556      global $DB;
3557      // validate comment area
3558      if ($comment_param->commentarea != 'database_entry') {
3559          throw new comment_exception('invalidcommentarea');
3560      }
3561      // validate itemid
3562      if (!$record = $DB->get_record('data_records', array('id'=>$comment_param->itemid))) {
3563          throw new comment_exception('invalidcommentitemid');
3564      }
3565      if (!$data = $DB->get_record('data', array('id'=>$record->dataid))) {
3566          throw new comment_exception('invalidid', 'data');
3567      }
3568      if (!$course = $DB->get_record('course', array('id'=>$data->course))) {
3569          throw new comment_exception('coursemisconf');
3570      }
3571      if (!$cm = get_coursemodule_from_instance('data', $data->id, $course->id)) {
3572          throw new comment_exception('invalidcoursemodule');
3573      }
3574      if (!$data->comments) {
3575          throw new comment_exception('commentsoff', 'data');
3576      }
3577      $context = context_module::instance($cm->id);
3578  
3579      //check if approved
3580      if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) {
3581          throw new comment_exception('notapproved', 'data');
3582      }
3583  
3584      // group access
3585      if ($record->groupid) {
3586          $groupmode = groups_get_activity_groupmode($cm, $course);
3587          if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
3588              if (!groups_is_member($record->groupid)) {
3589                  throw new comment_exception('notmemberofgroup');
3590              }
3591          }
3592      }
3593      // validate context id
3594      if ($context->id != $comment_param->context->id) {
3595          throw new comment_exception('invalidcontext');
3596      }
3597      // validation for comment deletion
3598      if (!empty($comment_param->commentid)) {
3599          if ($comment = $DB->get_record('comments', array('id'=>$comment_param->commentid))) {
3600              if ($comment->commentarea != 'database_entry') {
3601                  throw new comment_exception('invalidcommentarea');
3602              }
3603              if ($comment->contextid != $comment_param->context->id) {
3604                  throw new comment_exception('invalidcontext');
3605              }
3606              if ($comment->itemid != $comment_param->itemid) {
3607                  throw new comment_exception('invalidcommentitemid');
3608              }
3609          } else {
3610              throw new comment_exception('invalidcommentid');
3611          }
3612      }
3613      return true;
3614  }
3615  
3616  /**
3617   * Return a list of page types
3618   * @param string $pagetype current page type
3619   * @param stdClass $parentcontext Block's parent context
3620   * @param stdClass $currentcontext Current context of block
3621   */
3622  function data_page_type_list($pagetype, $parentcontext, $currentcontext) {
3623      $module_pagetype = array('mod-data-*'=>get_string('page-mod-data-x', 'data'));
3624      return $module_pagetype;
3625  }
3626  
3627  /**
3628   * Get all of the record ids from a database activity.
3629   *
3630   * @param int    $dataid      The dataid of the database module.
3631   * @param object $selectdata  Contains an additional sql statement for the
3632   *                            where clause for group and approval fields.
3633   * @param array  $params      Parameters that coincide with the sql statement.
3634   * @return array $idarray     An array of record ids
3635   */
3636  function data_get_all_recordids($dataid, $selectdata = '', $params = null) {
3637      global $DB;
3638      $initsql = 'SELECT r.id
3639                    FROM {data_records} r
3640                   WHERE r.dataid = :dataid';
3641      if ($selectdata != '') {
3642          $initsql .= $selectdata;
3643          $params = array_merge(array('dataid' => $dataid), $params);
3644      } else {
3645          $params = array('dataid' => $dataid);
3646      }
3647      $initsql .= ' GROUP BY r.id';
3648      $initrecord = $DB->get_recordset_sql($initsql, $params);
3649      $idarray = array();
3650      foreach ($initrecord as $data) {
3651          $idarray[] = $data->id;
3652      }
3653      // Close the record set and free up resources.
3654      $initrecord->close();
3655      return $idarray;
3656  }
3657  
3658  /**
3659   * Get the ids of all the records that match that advanced search criteria
3660   * This goes and loops through each criterion one at a time until it either
3661   * runs out of records or returns a subset of records.
3662   *
3663   * @param array $recordids    An array of record ids.
3664   * @param array $searcharray  Contains information for the advanced search criteria
3665   * @param int $dataid         The data id of the database.
3666   * @return array $recordids   An array of record ids.
3667   */
3668  function data_get_advance_search_ids($recordids, $searcharray, $dataid) {
3669      // Check to see if we have any record IDs.
3670      if (empty($recordids)) {
3671          // Send back an empty search.
3672          return array();
3673      }
3674      $searchcriteria = array_keys($searcharray);
3675      // Loop through and reduce the IDs one search criteria at a time.
3676      foreach ($searchcriteria as $key) {
3677          $recordids = data_get_recordids($key, $searcharray, $dataid, $recordids);
3678          // If we don't have anymore IDs then stop.
3679          if (!$recordids) {
3680              break;
3681          }
3682      }
3683      return $recordids;
3684  }
3685  
3686  /**
3687   * Gets the record IDs given the search criteria
3688   *
3689   * @param string $alias       Record alias.
3690   * @param array $searcharray  Criteria for the search.
3691   * @param int $dataid         Data ID for the database
3692   * @param array $recordids    An array of record IDs.
3693   * @return array $nestarray   An arry of record IDs
3694   */
3695  function data_get_recordids($alias, $searcharray, $dataid, $recordids) {
3696      global $DB;
3697  
3698      $nestsearch = $searcharray[$alias];
3699      // searching for content outside of mdl_data_content
3700      if ($alias < 0) {
3701          $alias = '';
3702      }
3703      list($insql, $params) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED);
3704      $nestselect = 'SELECT c' . $alias . '.recordid
3705                       FROM {data_content} c' . $alias . ',
3706                            {data_fields} f,
3707                            {data_records} r,
3708                            {user} u ';
3709      $nestwhere = 'WHERE u.id = r.userid
3710                      AND f.id = c' . $alias . '.fieldid
3711                      AND r.id = c' . $alias . '.recordid
3712                      AND r.dataid = :dataid
3713                      AND c' . $alias .'.recordid ' . $insql . '
3714                      AND ';
3715  
3716      $params['dataid'] = $dataid;
3717      if (count($nestsearch->params) != 0) {
3718          $params = array_merge($params, $nestsearch->params);
3719          $nestsql = $nestselect . $nestwhere . $nestsearch->sql;
3720      } else {
3721          $thing = $DB->sql_like($nestsearch->field, ':search1', false);
3722          $nestsql = $nestselect . $nestwhere . $thing . ' GROUP BY c' . $alias . '.recordid';
3723          $params['search1'] = "%$nestsearch->data%";
3724      }
3725      $nestrecords = $DB->get_recordset_sql($nestsql, $params);
3726      $nestarray = array();
3727      foreach ($nestrecords as $data) {
3728          $nestarray[] = $data->recordid;
3729      }
3730      // Close the record set and free up resources.
3731      $nestrecords->close();
3732      return $nestarray;
3733  }
3734  
3735  /**
3736   * Returns an array with an sql string for advanced searches and the parameters that go with them.
3737   *
3738   * @param int $sort            DATA_*
3739   * @param stdClass $data       Data module object
3740   * @param array $recordids     An array of record IDs.
3741   * @param string $selectdata   Information for the where and select part of the sql statement.
3742   * @param string $sortorder    Additional sort parameters
3743   * @return array sqlselect     sqlselect['sql'] has the sql string, sqlselect['params'] contains an array of parameters.
3744   */
3745  function data_get_advanced_search_sql($sort, $data, $recordids, $selectdata, $sortorder) {
3746      global $DB;
3747  
3748      $namefields = user_picture::fields('u');
3749      // Remove the id from the string. This already exists in the sql statement.
3750      $namefields = str_replace('u.id,', '', $namefields);
3751  
3752      if ($sort == 0) {
3753          $nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . '
3754                          FROM {data_content} c,
3755                               {data_records} r,
3756                               {user} u ';
3757          $groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, ' . $namefields;
3758      } else {
3759          // Sorting through 'Other' criteria
3760          if ($sort <= 0) {
3761              switch ($sort) {
3762                  case DATA_LASTNAME:
3763                      $sortcontentfull = "u.lastname";
3764                      break;
3765                  case DATA_FIRSTNAME:
3766                      $sortcontentfull = "u.firstname";
3767                      break;
3768                  case DATA_APPROVED:
3769                      $sortcontentfull = "r.approved";
3770                      break;
3771                  case DATA_TIMEMODIFIED:
3772                      $sortcontentfull = "r.timemodified";
3773                      break;
3774                  case DATA_TIMEADDED:
3775                  default:
3776                      $sortcontentfull = "r.timecreated";
3777              }
3778          } else {
3779              $sortfield = data_get_field_from_id($sort, $data);
3780              $sortcontent = $DB->sql_compare_text('c.' . $sortfield->get_sort_field());
3781              $sortcontentfull = $sortfield->get_sort_sql($sortcontent);
3782          }
3783  
3784          $nestselectsql = 'SELECT r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ',
3785                                   ' . $sortcontentfull . '
3786                                AS sortorder
3787                              FROM {data_content} c,
3788                                   {data_records} r,
3789                                   {user} u ';
3790          $groupsql = ' GROUP BY r.id, r.approved, r.timecreated, r.timemodified, r.userid, ' . $namefields . ', ' .$sortcontentfull;
3791      }
3792  
3793      // Default to a standard Where statement if $selectdata is empty.
3794      if ($selectdata == '') {
3795          $selectdata = 'WHERE c.recordid = r.id
3796                           AND r.dataid = :dataid
3797                           AND r.userid = u.id ';
3798      }
3799  
3800      // Find the field we are sorting on
3801      if ($sort > 0 or data_get_field_from_id($sort, $data)) {
3802          $selectdata .= ' AND c.fieldid = :sort';
3803      }
3804  
3805      // If there are no record IDs then return an sql statment that will return no rows.
3806      if (count($recordids) != 0) {
3807          list($insql, $inparam) = $DB->get_in_or_equal($recordids, SQL_PARAMS_NAMED);
3808      } else {
3809          list($insql, $inparam) = $DB->get_in_or_equal(array('-1'), SQL_PARAMS_NAMED);
3810      }
3811      $nestfromsql = $selectdata . ' AND c.recordid ' . $insql . $groupsql;
3812      $sqlselect['sql'] = "$nestselectsql $nestfromsql $sortorder";
3813      $sqlselect['params'] = $inparam;
3814      return $sqlselect;
3815  }
3816  
3817  /**
3818   * Checks to see if the user has permission to delete the preset.
3819   * @param stdClass $context  Context object.
3820   * @param stdClass $preset  The preset object that we are checking for deletion.
3821   * @return bool  Returns true if the user can delete, otherwise false.
3822   */
3823  function data_user_can_delete_preset($context, $preset) {
3824      global $USER;
3825  
3826      if (has_capability('mod/data:manageuserpresets', $context)) {
3827          return true;
3828      } else {
3829          $candelete = false;
3830          if ($preset->userid == $USER->id) {
3831              $candelete = true;
3832          }
3833          return $candelete;
3834      }
3835  }
3836  
3837  /**
3838   * Delete a record entry.
3839   *
3840   * @param int $recordid The ID for the record to be deleted.
3841   * @param object $data The data object for this activity.
3842   * @param int $courseid ID for the current course (for logging).
3843   * @param int $cmid The course module ID.
3844   * @return bool True if the record deleted, false if not.
3845   */
3846  function data_delete_record($recordid, $data, $courseid, $cmid) {
3847      global $DB, $CFG;
3848  
3849      if ($deleterecord = $DB->get_record('data_records', array('id' => $recordid))) {
3850          if ($deleterecord->dataid == $data->id) {
3851              if ($contents = $DB->get_records('data_content', array('recordid' => $deleterecord->id))) {
3852                  foreach ($contents as $content) {
3853                      if ($field = data_get_field_from_id($content->fieldid, $data)) {
3854                          $field->delete_content($content->recordid);
3855                      }
3856                  }
3857                  $DB->delete_records('data_content', array('recordid'=>$deleterecord->id));
3858                  $DB->delete_records('data_records', array('id'=>$deleterecord->id));
3859  
3860                  // Delete cached RSS feeds.
3861                  if (!empty($CFG->enablerssfeeds)) {
3862                      require_once($CFG->dirroot.'/mod/data/rsslib.php');
3863                      data_rss_delete_file($data);
3864                  }
3865  
3866                  // Trigger an event for deleting this record.
3867                  $event = \mod_data\event\record_deleted::create(array(
3868                      'objectid' => $deleterecord->id,
3869                      'context' => context_module::instance($cmid),
3870                      'courseid' => $courseid,
3871                      'other' => array(
3872                          'dataid' => $deleterecord->dataid
3873                      )
3874                  ));
3875                  $event->add_record_snapshot('data_records', $deleterecord);
3876                  $event->trigger();
3877  
3878                  return true;
3879              }
3880          }
3881      }
3882      return false;
3883  }
3884  
3885  /**
3886   * Check for required fields, and build a list of fields to be updated in a
3887   * submission.
3888   *
3889   * @param $mod stdClass The current recordid - provided as an optimisation.
3890   * @param $fields array The field data
3891   * @param $datarecord stdClass The submitted data.
3892   * @return stdClass containing:
3893   * * string[] generalnotifications Notifications for the form as a whole.
3894   * * string[] fieldnotifications Notifications for a specific field.
3895   * * bool validated Whether the field was validated successfully.
3896   * * data_field_base[] fields The field objects to be update.
3897   */
3898  function data_process_submission(stdClass $mod, $fields, stdClass $datarecord) {
3899      $result = new stdClass();
3900  
3901      // Empty form checking - you can't submit an empty form.
3902      $emptyform = true;
3903      $requiredfieldsfilled = true;
3904      $fieldsvalidated = true;
3905  
3906      // Store the notifications.
3907      $result->generalnotifications = array();
3908      $result->fieldnotifications = array();
3909  
3910      // Store the instantiated classes as an optimisation when processing the result.
3911      // This prevents the fields being re-initialised when updating.
3912      $result->fields = array();
3913  
3914      $submitteddata = array();
3915      foreach ($datarecord as $fieldname => $fieldvalue) {
3916          if (strpos($fieldname, '_')) {
3917              $namearray = explode('_', $fieldname, 3);
3918              $fieldid = $namearray[1];
3919              if (!isset($submitteddata[$fieldid])) {
3920                  $submitteddata[$fieldid] = array();
3921              }
3922              if (count($namearray) === 2) {
3923                  $subfieldid = 0;
3924              } else {
3925                  $subfieldid = $namearray[2];
3926              }
3927  
3928              $fielddata = new stdClass();
3929              $fielddata->fieldname = $fieldname;
3930              $fielddata->value = $fieldvalue;
3931              $submitteddata[$fieldid][$subfieldid] = $fielddata;
3932          }
3933      }
3934  
3935      // Check all form fields which have the required are filled.
3936      foreach ($fields as $fieldrecord) {
3937          // Check whether the field has any data.
3938          $fieldhascontent = false;
3939  
3940          $field = data_get_field($fieldrecord, $mod);
3941          if (isset($submitteddata[$fieldrecord->id])) {
3942              // Field validation check.
3943              if (method_exists($field, 'field_validation')) {
3944                  $errormessage = $field->field_validation($submitteddata[$fieldrecord->id]);
3945                  if ($errormessage) {
3946                      $result->fieldnotifications[$field->field->name][] = $errormessage;
3947                      $fieldsvalidated = false;
3948                  }
3949              }
3950              foreach ($submitteddata[$fieldrecord->id] as $fieldname => $value) {
3951                  if ($field->notemptyfield($value->value, $value->fieldname)) {
3952                      // The field has content and the form is not empty.
3953                      $fieldhascontent = true;
3954                      $emptyform = false;
3955                  }
3956              }
3957          }
3958  
3959          // If the field is required, add a notification to that effect.
3960          if ($field->field->required && !$fieldhascontent) {
3961              if (!isset($result->fieldnotifications[$field->field->name])) {
3962                  $result->fieldnotifications[$field->field->name] = array();
3963              }
3964              $result->fieldnotifications[$field->field->name][] = get_string('errormustsupplyvalue', 'data');
3965              $requiredfieldsfilled = false;
3966          }
3967  
3968          // Update the field.
3969          if (isset($submitteddata[$fieldrecord->id])) {
3970              foreach ($submitteddata[$fieldrecord->id] as $value) {
3971                  $result->fields[$value->fieldname] = $field;
3972              }
3973          }
3974      }
3975  
3976      if ($emptyform) {
3977          // The form is empty.
3978          $result->generalnotifications[] = get_string('emptyaddform', 'data');
3979      }
3980  
3981      $result->validated = $requiredfieldsfilled && !$emptyform && $fieldsvalidated;
3982  
3983      return $result;
3984  }
3985  
3986  /**
3987   * This standard function will check all instances of this module
3988   * and make sure there are up-to-date events created for each of them.
3989   * If courseid = 0, then every data event in the site is checked, else
3990   * only data events belonging to the course specified are checked.
3991   * This function is used, in its new format, by restore_refresh_events()
3992   *
3993   * @param int $courseid
3994   * @return bool
3995   */
3996  function data_refresh_events($courseid = 0) {
3997      global $DB, $CFG;
3998      require_once($CFG->dirroot.'/mod/data/locallib.php');
3999  
4000      if ($courseid) {
4001          if (! $data = $DB->get_records("data", array("course" => $courseid))) {
4002              return true;
4003          }
4004      } else {
4005          if (! $data = $DB->get_records("data")) {
4006              return true;
4007          }
4008      }
4009  
4010      foreach ($data as $datum) {
4011          data_set_events($datum);
4012      }
4013      return true;
4014  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1