Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • /mod/data/ -> lib.php (source)

    Differences Between: [Versions 28 and 29] [Versions 28 and 30] [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

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