Merge branch 'MDL-70248-310' of https://github.com/HuongNV13/moodle into MOODLE_310_S...
[moodle.git] / question / type / ddmarker / edit_ddmarker_form.php
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/>.
17 /**
18  * Defines the editing form for the drag-and-drop images onto images question type.
19  *
20  * @package   qtype_ddmarker
21  * @copyright 2012 The Open University
22  * @author    Jamie Pratt <me@jamiep.org>
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->dirroot.'/question/type/ddimageortext/edit_ddtoimage_form_base.php');
30 require_once($CFG->dirroot.'/question/type/ddmarker/shapes.php');
32 define('QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER', '<br><i><em><b><strong><sup><sub><u><span>');
35 /**
36  * Drag-and-drop images onto images  editing form definition.
37  *
38  * @copyright 2009 The Open University
39  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base {
42     public function qtype() {
43         return 'ddmarker';
44     }
46     protected function definition_inner($mform) {
47         $mform->addElement('advcheckbox', 'showmisplaced', ' ',
48                                                 get_string('showmisplaced', 'qtype_ddmarker'));
49         parent::definition_inner($mform);
51         $mform->addHelpButton('drops[0]', 'dropzones', 'qtype_ddmarker');
52     }
54     public function js_call() {
55         global $PAGE;
56         $PAGE->requires->js_call_amd('qtype_ddmarker/form', 'init');
57     }
60     protected function definition_draggable_items($mform, $itemrepeatsatstart) {
61         $mform->addElement('header', 'draggableitemheader',
62                                 get_string('markers', 'qtype_ddmarker'));
63         $mform->addElement('advcheckbox', 'shuffleanswers', ' ',
64                                         get_string('shuffleimages', 'qtype_'.$this->qtype()));
65         $mform->setDefault('shuffleanswers', 0);
66         $this->repeat_elements($this->draggable_item($mform), $itemrepeatsatstart,
67                 $this->draggable_items_repeated_options(),
68                 'noitems', 'additems', self::ADD_NUM_ITEMS,
69                 get_string('addmoreitems', 'qtype_ddmarker'), true);
70     }
72     protected function draggable_item($mform) {
73         $draggableimageitem = array();
75         $grouparray = array();
76         $grouparray[] = $mform->createElement('text', 'label', '',
77                 array('size' => 30, 'class' => 'tweakcss'));
78         $mform->setType('text', PARAM_RAW_TRIMMED);
80         $noofdragoptions = array(0 => get_string('infinite', 'qtype_ddmarker'));
81         foreach (range(1, 6) as $option) {
82             $noofdragoptions[$option] = $option;
83         }
84         $grouparray[] = $mform->createElement('select', 'noofdrags', get_string('noofdrags', 'qtype_ddmarker'), $noofdragoptions);
86         $draggableimageitem[] = $mform->createElement('group', 'drags',
87                                             get_string('marker_n', 'qtype_ddmarker'), $grouparray);
88         return $draggableimageitem;
89     }
91     protected function draggable_items_repeated_options() {
92         $repeatedoptions = array();
93         $repeatedoptions['drags[label]']['type'] = PARAM_RAW;
94         return $repeatedoptions;
95     }
97     protected function drop_zone($mform, $imagerepeats) {
98         $grouparray = array();
99         $shapearray = qtype_ddmarker_shape::shape_options();
100         $grouparray[] = $mform->createElement('select', 'shape',
101                                     get_string('shape', 'qtype_ddmarker'), $shapearray);
102         $markernos = array();
103         $markernos[0] = '';
104         for ($i = 1; $i <= $imagerepeats; $i += 1) {
105             $markernos[$i] = $i;
106         }
107         $grouparray[] = $mform->createElement('select', 'choice',
108                                     get_string('marker', 'qtype_ddmarker'), $markernos);
109         $grouparray[] = $mform->createElement('text', 'coords',
110                 get_string('coords', 'qtype_ddmarker'),
111                 array('size' => 30, 'class' => 'tweakcss'));
112         $mform->setType('coords', PARAM_RAW); // These are validated manually.
113         $dropzone = $mform->createElement('group', 'drops',
114                 get_string('dropzone', 'qtype_ddmarker', '{no}'), $grouparray);
115         return array($dropzone);
116     }
118     protected function drop_zones_repeated_options() {
119         $repeatedoptions = array();
120         $repeatedoptions['drops[coords]']['type'] = PARAM_RAW;
121         return $repeatedoptions;
122     }
124     protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) {
125         $mform = $this->_form;
127         $repeated = array();
128         $repeated[] = $mform->createElement('editor', 'hint', get_string('hintn', 'question'),
129                 array('rows' => 5), $this->editoroptions);
130         $repeatedoptions['hint']['type'] = PARAM_RAW;
132         $repeated[] = $mform->createElement('checkbox', 'hintshownumcorrect',
133                         get_string('options', 'question'),
134                         get_string('shownumpartscorrect', 'question'));
135         $repeated[] = $mform->createElement('checkbox', 'hintoptions',
136                         '',
137                         get_string('stateincorrectlyplaced', 'qtype_ddmarker'));
138         $repeated[] = $mform->createElement('checkbox', 'hintclearwrong',
139                         '',
140                         get_string('clearwrongparts', 'qtype_ddmarker'));
142         return array($repeated, $repeatedoptions);
143     }
145     public function data_preprocessing($question) {
147         $question = parent::data_preprocessing($question);
148         $question = $this->data_preprocessing_combined_feedback($question, true);
149         $question = $this->data_preprocessing_hints($question, true, true);
151         $dragids = array(); // Drag no -> dragid.
152         if (!empty($question->options)) {
153             $question->shuffleanswers = $question->options->shuffleanswers;
154             $question->showmisplaced = $question->options->showmisplaced;
155             $question->drags = array();
156             foreach ($question->options->drags as $drag) {
157                 $dragindex = $drag->no - 1;
158                 $question->drags[$dragindex] = array();
159                 $question->drags[$dragindex]['label'] = $drag->label;
160                 if ($drag->infinite == 1) {
161                     $question->drags[$dragindex]['noofdrags'] = 0;
162                 } else {
163                     $question->drags[$dragindex]['noofdrags'] = $drag->noofdrags;
164                 }
165                 $dragids[$dragindex] = $drag->id;
166             }
167             $question->drops = array();
168             foreach ($question->options->drops as $drop) {
169                 $droparray = (array)$drop;
170                 unset($droparray['id']);
171                 unset($droparray['no']);
172                 unset($droparray['questionid']);
173                 $question->drops[$drop->no - 1] = $droparray;
174             }
175         }
176         // Initialise file picker for bgimage.
177         $draftitemid = file_get_submitted_draft_itemid('bgimage');
179         file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker',
180                                 'bgimage', !empty($question->id) ? (int) $question->id : null,
181                                 self::file_picker_options());
182         $question->bgimage = $draftitemid;
184         $this->js_call();
186         return $question;
187     }
189     /**
190      * Perform the necessary preprocessing for the hint fields.
191      *
192      * @param object $question The data being passed to the form.
193      * @param bool $withclearwrong Clear wrong hints.
194      * @param bool $withshownumpartscorrect Show number correct.
195      * @return object The modified data.
196      */
197     protected function data_preprocessing_hints($question, $withclearwrong = false,
198                                                 $withshownumpartscorrect = false) {
199         if (empty($question->hints)) {
200             return $question;
201         }
202         parent::data_preprocessing_hints($question, $withclearwrong, $withshownumpartscorrect);
204         $question->hintoptions = array();
205         foreach ($question->hints as $hint) {
206             $question->hintoptions[] = $hint->options;
207         }
209         return $question;
210     }
212     public function validation($data, $files) {
213         $errors = parent::validation($data, $files);
214         $bgimagesize = $this->get_image_size_in_draft_area($data['bgimage']);
215         if ($bgimagesize === null) {
216             $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_ddmarker');
217         }
219         for ($i = 0; $i < $data['nodropzone']; $i++) {
220             $choice = $data['drops'][$i]['choice'];
221             $choicepresent = ($choice !== '0');
223             if ($choicepresent) {
224                 // Test coords here.
225                 if ($bgimagesize !== null) {
226                     $shape = $data['drops'][$i]['shape'];
227                     $coordsstring = $data['drops'][$i]['coords'];
228                     $shapeobj = qtype_ddmarker_shape::create($shape, $coordsstring);
229                     $interpretererror = $shapeobj->get_coords_interpreter_error();
230                     if ($interpretererror !== false) {
231                         $errors["drops[{$i}]"] = $interpretererror;
232                     } else if (!$shapeobj->inside_width_height($bgimagesize)) {
233                         $errorcode = 'shapeoutsideboundsofbgimage';
234                         $errors["drops[{$i}]"] =
235                                             get_string('formerror_'.$errorcode, 'qtype_ddmarker');
236                     }
237                 }
238             } else {
239                 if (trim($data['drops'][$i]['coords']) !== '') {
240                     $errorcode = 'noitemselected';
241                     $errors["drops[{$i}]"] = get_string('formerror_'.$errorcode, 'qtype_ddmarker');
242                 }
243             }
245         }
246         for ($dragindex = 0; $dragindex < $data['noitems']; $dragindex++) {
247             $label = $data['drags'][$dragindex]['label'];
248             if ($label != strip_tags($label, QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER)) {
249                 $errors["drags[{$dragindex}]"]
250                     = get_string('formerror_onlysometagsallowed', 'qtype_ddmarker',
251                                   s(QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER));
252             }
253         }
254         return $errors;
255     }
257     /**
258      * Gets the width and height of a draft image.
259      *
260      * @param int $draftitemid ID of the draft image
261      * @return array Return array of the width and height of the draft image.
262      */
263     public function get_image_size_in_draft_area($draftitemid) {
264         global $USER;
265         $usercontext = context_user::instance($USER->id);
266         $fs = get_file_storage();
267         $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id');
268         if ($draftfiles) {
269             foreach ($draftfiles as $file) {
270                 if ($file->is_directory()) {
271                     continue;
272                 }
273                 // Just return the data for the first good file, there should only be one.
274                 $imageinfo = $file->get_imageinfo();
275                 $width    = $imageinfo['width'];
276                 $height   = $imageinfo['height'];
277                 return array($width, $height);
278             }
279         }
280         return null;
281     }