Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  
  18  /**
  19   * Defines the editing form for the drag-and-drop images onto images question type.
  20   *
  21   * @package   qtype_ddimageortext
  22   * @copyright 2009 The Open University
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once($CFG->dirroot . '/question/type/ddimageortext/edit_ddtoimage_form_base.php');
  29  
  30  
  31  /**
  32   * Drag-and-drop images onto images  editing form definition.
  33   *
  34   * @copyright 2009 The Open University
  35   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class qtype_ddimageortext_edit_form extends qtype_ddtoimage_edit_form_base {
  38      public function qtype() {
  39          return 'ddimageortext';
  40      }
  41  
  42      public function data_preprocessing($question) {
  43          $question = parent::data_preprocessing($question);
  44          $question = $this->data_preprocessing_combined_feedback($question, true);
  45          $question = $this->data_preprocessing_hints($question, true, true);
  46  
  47          $dragids = array(); // Drag no -> dragid.
  48          if (!empty($question->options)) {
  49              $question->shuffleanswers = $question->options->shuffleanswers;
  50              $question->drags = array();
  51              foreach ($question->options->drags as $drag) {
  52                  $dragindex = $drag->no - 1;
  53                  $question->drags[$dragindex] = array();
  54                  $question->draglabel[$dragindex] = $drag->label;
  55                  $question->drags[$dragindex]['infinite'] = $drag->infinite;
  56                  $question->drags[$dragindex]['draggroup'] = $drag->draggroup;
  57                  $dragids[$dragindex] = $drag->id;
  58              }
  59              $question->drops = array();
  60              foreach ($question->options->drops as $drop) {
  61                  $question->drops[$drop->no - 1] = array();
  62                  $question->drops[$drop->no - 1]['choice'] = $drop->choice;
  63                  $question->drops[$drop->no - 1]['droplabel'] = $drop->label;
  64                  $question->drops[$drop->no - 1]['xleft'] = $drop->xleft;
  65                  $question->drops[$drop->no - 1]['ytop'] = $drop->ytop;
  66              }
  67          }
  68          // Initialise file picker for bgimage.
  69          $draftitemid = file_get_submitted_draft_itemid('bgimage');
  70  
  71          file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddimageortext',
  72                                  'bgimage', !empty($question->id) ? (int) $question->id : null,
  73                                  self::file_picker_options());
  74          $question->bgimage = $draftitemid;
  75  
  76          // Initialise file picker for dragimages.
  77          list(, $imagerepeats) = $this->get_drag_item_repeats();
  78          $draftitemids = optional_param_array('dragitem', array(), PARAM_INT);
  79          for ($imageindex = 0; $imageindex < $imagerepeats; $imageindex++) {
  80              $draftitemid = isset($draftitemids[$imageindex]) ? $draftitemids[$imageindex] : 0;
  81              // Numbers not allowed in filearea name.
  82              $itemid = isset($dragids[$imageindex]) ? $dragids[$imageindex] : null;
  83              file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddimageortext',
  84                                  'dragimage', $itemid, self::file_picker_options());
  85              $question->dragitem[$imageindex] = $draftitemid;
  86          }
  87          if (!empty($question->options)) {
  88              foreach ($question->options->drags as $drag) {
  89                  $dragindex = $drag->no - 1;
  90                  if (!isset($question->dragitem[$dragindex])) {
  91                      $fileexists = false;
  92                  } else {
  93                      $fileexists = self::file_uploaded($question->dragitem[$dragindex]);
  94                  }
  95                  $labelexists = (trim($question->draglabel[$dragindex]) != '');
  96                  if ($labelexists && !$fileexists) {
  97                      $question->drags[$dragindex]['dragitemtype'] = 'word';
  98                  } else {
  99                      $question->drags[$dragindex]['dragitemtype'] = 'image';
 100                  }
 101              }
 102          }
 103          $this->js_call();
 104  
 105          return $question;
 106      }
 107  
 108  
 109      public function js_call() {
 110          global $PAGE;
 111          $PAGE->requires->js_call_amd('qtype_ddimageortext/form', 'init');
 112      }
 113  
 114      // Drag items.
 115  
 116      protected function definition_draggable_items($mform, $itemrepeatsatstart) {
 117          $mform->addElement('header', 'draggableitemheader',
 118                                  get_string('draggableitems', 'qtype_ddimageortext'));
 119          $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleimages', 'qtype_'.$this->qtype()));
 120          $mform->setDefault('shuffleanswers', 0);
 121          $this->repeat_elements($this->draggable_item($mform), $itemrepeatsatstart,
 122                  $this->draggable_items_repeated_options(),
 123                  'noitems', 'additems', self::ADD_NUM_ITEMS,
 124                  get_string('addmoreimages', 'qtype_ddimageortext'), true);
 125      }
 126  
 127      protected function draggable_item($mform) {
 128          $draggableimageitem = array();
 129  
 130          $grouparray = array();
 131          $dragitemtypes = array('image' => get_string('draggableimage', 'qtype_ddimageortext'),
 132                                  'word' => get_string('draggableword', 'qtype_ddimageortext'));
 133          $grouparray[] = $mform->createElement('select', 'dragitemtype',
 134                                              get_string('draggableitemtype', 'qtype_ddimageortext'),
 135                                              $dragitemtypes,
 136                                              array('class' => 'dragitemtype'));
 137          $options = array();
 138          for ($i = 1; $i <= self::MAX_GROUPS; $i += 1) {
 139              $options[$i] = question_utils::int_to_letter($i);
 140          }
 141          $grouparray[] = $mform->createElement('select', 'draggroup',
 142                                                  get_string('group', 'qtype_gapselect'),
 143                                                  $options,
 144                                                  array('class' => 'draggroup'));
 145          $grouparray[] = $mform->createElement('advcheckbox', 'infinite', get_string('infinite', 'qtype_ddimageortext'));
 146          $draggableimageitem[] = $mform->createElement('group', 'drags',
 147                  get_string('draggableitemheader', 'qtype_ddimageortext', '{no}'), $grouparray);
 148  
 149          $draggableimageitem[] = $mform->createElement('filepicker', 'dragitem', '', null,
 150                                      self::file_picker_options());
 151  
 152          $draggableimageitem[] = $mform->createElement('text', 'draglabel',
 153                                                  get_string('label', 'qtype_ddimageortext'),
 154                                                  array('size' => 30, 'class' => 'tweakcss draglabel'));
 155          $mform->setType('draglabel', PARAM_RAW); // These are validated manually.
 156          return $draggableimageitem;
 157      }
 158  
 159      protected function draggable_items_repeated_options() {
 160          $repeatedoptions = array();
 161          $repeatedoptions['draggroup']['default'] = '1';
 162          return $repeatedoptions;
 163      }
 164  
 165      // Drop zones.
 166  
 167      protected function drop_zone($mform, $imagerepeats) {
 168          $dropzoneitem = array();
 169  
 170          $grouparray = array();
 171          $grouparray[] = $mform->createElement('text', 'xleft',
 172                                                  get_string('xleft', 'qtype_ddimageortext'),
 173                                                  array('size' => 5, 'class' => 'tweakcss'));
 174          $grouparray[] = $mform->createElement('text', 'ytop',
 175                                                  get_string('ytop', 'qtype_ddimageortext'),
 176                                                  array('size' => 5, 'class' => 'tweakcss'));
 177          $options = array();
 178  
 179          $options[0] = '';
 180          for ($i = 1; $i <= $imagerepeats; $i += 1) {
 181              $options[$i] = $i;
 182          }
 183          $grouparray[] = $mform->createElement('select', 'choice',
 184                                      get_string('draggableitem', 'qtype_ddimageortext'), $options);
 185          $grouparray[] = $mform->createElement('text', 'droplabel',
 186                                                  get_string('label', 'qtype_ddimageortext'),
 187                                                  array('size' => 10, 'class' => 'tweakcss'));
 188          $mform->setType('droplabel', PARAM_NOTAGS);
 189          $dropzone = $mform->createElement('group', 'drops',
 190                  get_string('dropzone', 'qtype_ddimageortext', '{no}'), $grouparray);
 191          return array($dropzone);
 192      }
 193  
 194      protected function drop_zones_repeated_options() {
 195          $repeatedoptions = array();
 196          // The next two are PARAM_RAW becuase we need to distinguish 0 and ''.
 197          // We do the necessary validation in the validation method.
 198          $repeatedoptions['drops[xleft]']['type']     = PARAM_RAW;
 199          $repeatedoptions['drops[ytop]']['type']      = PARAM_RAW;
 200          $repeatedoptions['drops[droplabel]']['type'] = PARAM_RAW;
 201          $repeatedoptions['choice']['default'] = '0';
 202          return $repeatedoptions;
 203      }
 204  
 205      public function validation($data, $files) {
 206          $errors = parent::validation($data, $files);
 207          if (!self::file_uploaded($data['bgimage'])) {
 208              $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_'.$this->qtype());
 209          }
 210  
 211          $allchoices = array();
 212          for ($i = 0; $i < $data['nodropzone']; $i++) {
 213              $ytoppresent = (trim($data['drops'][$i]['ytop']) !== '');
 214              $xleftpresent = (trim($data['drops'][$i]['xleft']) !== '');
 215              $ytopisint = (string) clean_param($data['drops'][$i]['ytop'], PARAM_INT) === trim($data['drops'][$i]['ytop']);
 216              $xleftisint = (string) clean_param($data['drops'][$i]['xleft'], PARAM_INT) === trim($data['drops'][$i]['xleft']);
 217              $labelpresent = (trim($data['drops'][$i]['droplabel']) !== '');
 218              $choice = $data['drops'][$i]['choice'];
 219              $imagechoicepresent = ($choice !== '0');
 220  
 221              if ($imagechoicepresent) {
 222                  if (!$ytoppresent) {
 223                      $errors["drops[$i]"] = get_string('formerror_noytop', 'qtype_ddimageortext');
 224                  } else if (!$ytopisint) {
 225                      $errors["drops[$i]"] = get_string('formerror_notintytop', 'qtype_ddimageortext');
 226                  }
 227                  if (!$xleftpresent) {
 228                      $errors["drops[$i]"] = get_string('formerror_noxleft', 'qtype_ddimageortext');
 229                  } else if (!$xleftisint) {
 230                      $errors["drops[$i]"] = get_string('formerror_notintxleft', 'qtype_ddimageortext');
 231                  }
 232  
 233                  if ($data['drags'][$choice - 1]['dragitemtype'] != 'word' &&
 234                                          !self::file_uploaded($data['dragitem'][$choice - 1])) {
 235                      $errors['dragitem['.($choice - 1).']'] =
 236                                      get_string('formerror_nofile', 'qtype_ddimageortext', $i);
 237                  }
 238  
 239                  if (isset($allchoices[$choice]) && !$data['drags'][$choice - 1]['infinite']) {
 240                      $errors["drops[$i]"] =
 241                              get_string('formerror_multipledraginstance', 'qtype_ddimageortext', $choice);
 242                      $errors['drops['.($allchoices[$choice]).']'] =
 243                              get_string('formerror_multipledraginstance', 'qtype_ddimageortext', $choice);
 244                      $errors['drags['.($choice - 1).']'] =
 245                              get_string('formerror_multipledraginstance2', 'qtype_ddimageortext', $choice);
 246                  }
 247                  $allchoices[$choice] = $i;
 248              } else {
 249                  if ($ytoppresent || $xleftpresent || $labelpresent) {
 250                      $errors["drops[$i]"] =
 251                              get_string('formerror_noimageselected', 'qtype_ddimageortext');
 252                  }
 253              }
 254          }
 255          for ($dragindex = 0; $dragindex < $data['noitems']; $dragindex++) {
 256              $label = $data['draglabel'][$dragindex];
 257              if ($data['drags'][$dragindex]['dragitemtype'] == 'word') {
 258                  $allowedtags = '<br><sub><sup><b><i><strong><em><span>';
 259                  $errormessage = get_string('formerror_disallowedtags', 'qtype_ddimageortext', s($allowedtags));
 260              } else {
 261                  $allowedtags = '';
 262                  $errormessage = get_string('formerror_noallowedtags', 'qtype_ddimageortext');
 263              }
 264              if ($label != strip_tags($label, $allowedtags)) {
 265                  $errors["drags[{$dragindex}]"] = $errormessage;
 266              }
 267  
 268          }
 269          return $errors;
 270      }
 271  }