99d9d875f90a27ced9f50ad74bb4937dd10f8966
[moodle.git] / mod / scorm / mod_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 if (!defined('MOODLE_INTERNAL')) {
18     die('Direct access to this script is forbidden.');    //  It must be included from a Moodle page.
19 }
21 require_once($CFG->dirroot.'/course/moodleform_mod.php');
22 require_once($CFG->dirroot.'/mod/scorm/locallib.php');
24 class mod_scorm_mod_form extends moodleform_mod {
26     public function definition() {
27         global $CFG, $COURSE, $OUTPUT;
28         $cfgscorm = get_config('scorm');
30         $mform = $this->_form;
32         if (!$CFG->slasharguments) {
33             $mform->addElement('static', '', '', $OUTPUT->notification(get_string('slashargs', 'scorm'), 'notifyproblem'));
34         }
36         $mform->addElement('header', 'general', get_string('general', 'form'));
38         // Name.
39         $mform->addElement('text', 'name', get_string('name'));
40         if (!empty($CFG->formatstringstriptags)) {
41             $mform->setType('name', PARAM_TEXT);
42         } else {
43             $mform->setType('name', PARAM_CLEANHTML);
44         }
45         $mform->addRule('name', null, 'required', null, 'client');
46         $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
48         // Summary.
49         $this->add_intro_editor(true);
51         // Package.
52         $mform->addElement('header', 'packagehdr', get_string('packagehdr', 'scorm'));
53         $mform->setExpanded('packagehdr', true);
55         // Scorm types.
56         $scormtypes = array(SCORM_TYPE_LOCAL => get_string('typelocal', 'scorm'));
58         if ($cfgscorm->allowtypeexternal) {
59             $scormtypes[SCORM_TYPE_EXTERNAL] = get_string('typeexternal', 'scorm');
60         }
62         if ($cfgscorm->allowtypelocalsync) {
63             $scormtypes[SCORM_TYPE_LOCALSYNC] = get_string('typelocalsync', 'scorm');
64         }
66         if ($cfgscorm->allowtypeexternalaicc) {
67             $scormtypes[SCORM_TYPE_AICCURL] = get_string('typeaiccurl', 'scorm');
68         }
70         // Reference.
71         if (count($scormtypes) > 1) {
72             $mform->addElement('select', 'scormtype', get_string('scormtype', 'scorm'), $scormtypes);
73             $mform->setType('scormtype', PARAM_ALPHA);
74             $mform->addHelpButton('scormtype', 'scormtype', 'scorm');
75             $mform->addElement('text', 'packageurl', get_string('packageurl', 'scorm'), array('size'=>60));
76             $mform->setType('packageurl', PARAM_RAW);
77             $mform->addHelpButton('packageurl', 'packageurl', 'scorm');
78             $mform->disabledIf('packageurl', 'scormtype', 'eq', SCORM_TYPE_LOCAL);
79         } else {
80             $mform->addElement('hidden', 'scormtype', SCORM_TYPE_LOCAL);
81             $mform->setType('scormtype', PARAM_ALPHA);
82         }
84         // Update packages timing.
85         $mform->addElement('select', 'updatefreq', get_string('updatefreq', 'scorm'), scorm_get_updatefreq_array());
86         $mform->setType('updatefreq', PARAM_INT);
87         $mform->setDefault('updatefreq', $cfgscorm->updatefreq);
88         $mform->addHelpButton('updatefreq', 'updatefreq', 'scorm');
90         // New local package upload.
91         $filemanageroptions = array();
92         $filemanageroptions['accepted_types'] = array('.zip');
93         $filemanageroptions['maxbytes'] = 0;
94         $filemanageroptions['maxfiles'] = 1;
95         $filemanageroptions['subdirs'] = 0;
97         $mform->addElement('filemanager', 'packagefile', get_string('package', 'scorm'), null, $filemanageroptions);
98         $mform->addHelpButton('packagefile', 'package', 'scorm');
99         $mform->disabledIf('packagefile', 'scormtype', 'noteq', SCORM_TYPE_LOCAL);
101         // Display Settings.
102         $mform->addElement('header', 'displaysettings', get_string('appearance'));
104         // Framed / Popup Window.
105         $mform->addElement('select', 'popup', get_string('display', 'scorm'), scorm_get_popup_display_array());
106         $mform->setDefault('popup', $cfgscorm->popup);
107         $mform->setAdvanced('popup', $cfgscorm->popup_adv);
109         // Width.
110         $mform->addElement('text', 'width', get_string('width', 'scorm'), 'maxlength="5" size="5"');
111         $mform->setDefault('width', $cfgscorm->framewidth);
112         $mform->setType('width', PARAM_INT);
113         $mform->setAdvanced('width', $cfgscorm->framewidth_adv);
114         $mform->disabledIf('width', 'popup', 'eq', 0);
116         // Height.
117         $mform->addElement('text', 'height', get_string('height', 'scorm'), 'maxlength="5" size="5"');
118         $mform->setDefault('height', $cfgscorm->frameheight);
119         $mform->setType('height', PARAM_INT);
120         $mform->setAdvanced('height', $cfgscorm->frameheight_adv);
121         $mform->disabledIf('height', 'popup', 'eq', 0);
123         // Window Options.
124         $winoptgrp = array();
125         foreach (scorm_get_popup_options_array() as $key => $value) {
126             $winoptgrp[] = &$mform->createElement('checkbox', $key, '', get_string($key, 'scorm'));
127             $mform->setDefault($key, $value);
128         }
129         $mform->addGroup($winoptgrp, 'winoptgrp', get_string('options', 'scorm'), '<br />', false);
130         $mform->disabledIf('winoptgrp', 'popup', 'eq', 0);
131         $mform->setAdvanced('winoptgrp', $cfgscorm->winoptgrp_adv);
133         // Skip view page.
134         $skipviewoptions = scorm_get_skip_view_array();
135         if ($COURSE->format == 'singleactivity') { // Remove option that would cause a constant redirect.
136             unset($skipviewoptions[SCORM_SKIPVIEW_ALWAYS]);
137             if ($cfgscorm->skipview == SCORM_SKIPVIEW_ALWAYS) {
138                 $cfgscorm->skipview = SCORM_SKIPVIEW_FIRST;
139             }
140         }
141         $mform->addElement('select', 'skipview', get_string('skipview', 'scorm'), $skipviewoptions);
142         $mform->addHelpButton('skipview', 'skipview', 'scorm');
143         $mform->setDefault('skipview', $cfgscorm->skipview);
144         $mform->setAdvanced('skipview', $cfgscorm->skipview_adv);
146         // Hide Browse.
147         $mform->addElement('selectyesno', 'hidebrowse', get_string('hidebrowse', 'scorm'));
148         $mform->addHelpButton('hidebrowse', 'hidebrowse', 'scorm');
149         $mform->setDefault('hidebrowse', $cfgscorm->hidebrowse);
150         $mform->setAdvanced('hidebrowse', $cfgscorm->hidebrowse_adv);
152         // Display course structure.
153         $mform->addElement('selectyesno', 'displaycoursestructure', get_string('displaycoursestructure', 'scorm'));
154         $mform->addHelpButton('displaycoursestructure', 'displaycoursestructure', 'scorm');
155         $mform->setDefault('displaycoursestructure', $cfgscorm->displaycoursestructure);
156         $mform->setAdvanced('displaycoursestructure', $cfgscorm->displaycoursestructure_adv);
158         // Toc display.
159         $mform->addElement('select', 'hidetoc', get_string('hidetoc', 'scorm'), scorm_get_hidetoc_array());
160         $mform->addHelpButton('hidetoc', 'hidetoc', 'scorm');
161         $mform->setDefault('hidetoc', $cfgscorm->hidetoc);
162         $mform->setAdvanced('hidetoc', $cfgscorm->hidetoc_adv);
163         $mform->disabledIf('hidetoc', 'scormtype', 'eq', SCORM_TYPE_AICCURL);
165         // Hide Navigation panel.
166         $mform->addElement('selectyesno', 'hidenav', get_string('hidenav', 'scorm'));
167         $mform->setDefault('hidenav', $cfgscorm->hidenav);
168         $mform->setAdvanced('hidenav', $cfgscorm->hidenav_adv);
169         $mform->disabledIf('hidenav', 'hidetoc', 'noteq', 0);
171         // Display attempt status.
172         $mform->addElement('select', 'displayattemptstatus', get_string('displayattemptstatus', 'scorm'),
173                            scorm_get_attemptstatus_array());
174         $mform->addHelpButton('displayattemptstatus', 'displayattemptstatus', 'scorm');
175         $mform->setDefault('displayattemptstatus', $cfgscorm->displayattemptstatus);
176         $mform->setAdvanced('displayattemptstatus', $cfgscorm->displayattemptstatus_adv);
178         // Availability.
179         $mform->addElement('header', 'availability', get_string('availability'));
181         $mform->addElement('date_time_selector', 'timeopen', get_string("scormopen", "scorm"), array('optional' => true));
182         $mform->addElement('date_time_selector', 'timeclose', get_string("scormclose", "scorm"), array('optional' => true));
184         // Grade Settings.
185         $mform->addElement('header', 'gradesettings', get_string('grade'));
187         // Grade Method.
188         $mform->addElement('select', 'grademethod', get_string('grademethod', 'scorm'), scorm_get_grade_method_array());
189         $mform->addHelpButton('grademethod', 'grademethod', 'scorm');
190         $mform->setDefault('grademethod', $cfgscorm->grademethod);
192         // Maximum Grade.
193         for ($i=0; $i<=100; $i++) {
194             $grades[$i] = "$i";
195         }
196         $mform->addElement('select', 'maxgrade', get_string('maximumgrade'), $grades);
197         $mform->setDefault('maxgrade', $cfgscorm->maxgrade);
198         $mform->disabledIf('maxgrade', 'grademethod', 'eq', GRADESCOES);
200         // Attempts management.
201         $mform->addElement('header', 'attemptsmanagementhdr', get_string('attemptsmanagement', 'scorm'));
203         // Max Attempts.
204         $mform->addElement('select', 'maxattempt', get_string('maximumattempts', 'scorm'), scorm_get_attempts_array());
205         $mform->addHelpButton('maxattempt', 'maximumattempts', 'scorm');
206         $mform->setDefault('maxattempt', $cfgscorm->maxattempt);
208         // What Grade.
209         $mform->addElement('select', 'whatgrade', get_string('whatgrade', 'scorm'),  scorm_get_what_grade_array());
210         $mform->disabledIf('whatgrade', 'maxattempt', 'eq', 1);
211         $mform->addHelpButton('whatgrade', 'whatgrade', 'scorm');
212         $mform->setDefault('whatgrade', $cfgscorm->whatgrade);
214         // Force new attempt.
215         $mform->addElement('selectyesno', 'forcenewattempt', get_string('forcenewattempt', 'scorm'));
216         $mform->addHelpButton('forcenewattempt', 'forcenewattempt', 'scorm');
217         $mform->setDefault('forcenewattempt', $cfgscorm->forcenewattempt);
219         // Last attempt lock - lock the enter button after the last available attempt has been made.
220         $mform->addElement('selectyesno', 'lastattemptlock', get_string('lastattemptlock', 'scorm'));
221         $mform->addHelpButton('lastattemptlock', 'lastattemptlock', 'scorm');
222         $mform->setDefault('lastattemptlock', $cfgscorm->lastattemptlock);
224         // Compatibility settings.
225         $mform->addElement('header', 'compatibilitysettingshdr', get_string('compatibilitysettings', 'scorm'));
227         // Force completed.
228         $mform->addElement('selectyesno', 'forcecompleted', get_string('forcecompleted', 'scorm'));
229         $mform->addHelpButton('forcecompleted', 'forcecompleted', 'scorm');
230         $mform->setDefault('forcecompleted', $cfgscorm->forcecompleted);
232         // Autocontinue.
233         $mform->addElement('selectyesno', 'auto', get_string('autocontinue', 'scorm'));
234         $mform->addHelpButton('auto', 'autocontinue', 'scorm');
235         $mform->setDefault('auto', $cfgscorm->auto);
237         // Hidden Settings.
238         $mform->addElement('hidden', 'datadir', null);
239         $mform->setType('datadir', PARAM_RAW);
240         $mform->addElement('hidden', 'pkgtype', null);
241         $mform->setType('pkgtype', PARAM_RAW);
242         $mform->addElement('hidden', 'launch', null);
243         $mform->setType('launch', PARAM_RAW);
244         $mform->addElement('hidden', 'redirect', null);
245         $mform->setType('redirect', PARAM_RAW);
246         $mform->addElement('hidden', 'redirecturl', null);
247         $mform->setType('redirecturl', PARAM_RAW);
249         $this->standard_coursemodule_elements();
251         // Buttons.
252         $this->add_action_buttons();
253     }
255     public function data_preprocessing(&$defaultvalues) {
256         global $COURSE;
258         if (isset($defaultvalues['popup']) && ($defaultvalues['popup'] == 1) && isset($defaultvalues['options'])) {
259             if (!empty($defaultvalues['options'])) {
260                 $options = explode(',', $defaultvalues['options']);
261                 foreach ($options as $option) {
262                     list($element, $value) = explode('=', $option);
263                     $element = trim($element);
264                     $defaultvalues[$element] = trim($value);
265                 }
266             }
267         }
268         if (isset($defaultvalues['grademethod'])) {
269             $defaultvalues['grademethod'] = intval($defaultvalues['grademethod']);
270         }
271         if (isset($defaultvalues['width']) && (strpos($defaultvalues['width'], '%') === false)
272                                            && ($defaultvalues['width'] <= 100)) {
273             $defaultvalues['width'] .= '%';
274         }
275         if (isset($defaultvalues['width']) && (strpos($defaultvalues['height'], '%') === false)
276                                            && ($defaultvalues['height'] <= 100)) {
277             $defaultvalues['height'] .= '%';
278         }
279         $scorms = get_all_instances_in_course('scorm', $COURSE);
280         $coursescorm = current($scorms);
282         $draftitemid = file_get_submitted_draft_itemid('packagefile');
283         file_prepare_draft_area($draftitemid, $this->context->id, 'mod_scorm', 'package', 0,
284             array('subdirs' => 0, 'maxfiles' => 1));
285         $defaultvalues['packagefile'] = $draftitemid;
287         if (($COURSE->format == 'singleactivity') && ((count($scorms) == 0) || ($defaultvalues['instance'] == $coursescorm->id))) {
288             $defaultvalues['redirect'] = 'yes';
289             $defaultvalues['redirecturl'] = '../course/view.php?id='.$defaultvalues['course'];
290         } else {
291             $defaultvalues['redirect'] = 'no';
292             $defaultvalues['redirecturl'] = '../mod/scorm/view.php?id='.$defaultvalues['coursemodule'];
293         }
294         if (isset($defaultvalues['version'])) {
295             $defaultvalues['pkgtype'] = (substr($defaultvalues['version'], 0, 5) == 'SCORM') ? 'scorm':'aicc';
296         }
297         if (isset($defaultvalues['instance'])) {
298             $defaultvalues['datadir'] = $defaultvalues['instance'];
299         }
300         if (empty($defaultvalues['timeopen'])) {
301             $defaultvalues['timeopen'] = 0;
302         }
303         if (empty($defaultvalues['timeclose'])) {
304             $defaultvalues['timeclose'] = 0;
305         }
307         // Set some completion default data.
308         if (!empty($defaultvalues['completionstatusrequired']) && !is_array($defaultvalues['completionstatusrequired'])) {
309             // Unpack values.
310             $cvalues = array();
311             foreach (scorm_status_options() as $key => $value) {
312                 if (($defaultvalues['completionstatusrequired'] & $key) == $key) {
313                     $cvalues[$key] = 1;
314                 }
315             }
317             $defaultvalues['completionstatusrequired'] = $cvalues;
318         }
320         if (!isset($defaultvalues['completionscorerequired']) || !strlen($defaultvalues['completionscorerequired'])) {
321             $defaultvalues['completionscoredisabled'] = 1;
322         }
324     }
326     public function validation($data, $files) {
327         global $CFG, $USER;
328         $errors = parent::validation($data, $files);
330         $type = $data['scormtype'];
332         if ($type === SCORM_TYPE_LOCAL) {
333             if (empty($data['packagefile'])) {
334                 $errors['packagefile'] = get_string('required');
336             } else {
337                 $draftitemid = file_get_submitted_draft_itemid('packagefile');
339                 file_prepare_draft_area($draftitemid, $this->context->id, 'mod_scorm', 'packagefilecheck', null,
340                     array('subdirs' => 0, 'maxfiles' => 1));
342                 // Get file from users draft area.
343                 $usercontext = context_user::instance($USER->id);
344                 $fs = get_file_storage();
345                 $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id', false);
347                 if (count($files)<1) {
348                     $errors['packagefile'] = get_string('required');
349                     return $errors;
350                 }
351                 $file = reset($files);
352                 if (!$file->is_external_file() && !empty($data['updatefreq'])) {
353                     // Make sure updatefreq is not set if using normal local file.
354                     $errors['updatefreq'] = get_string('updatefreq_error', 'mod_scorm');
355                 }
356                 $errors = array_merge($errors, scorm_validate_package($file));
357             }
359         } else if ($type === SCORM_TYPE_EXTERNAL) {
360             $reference = $data['packageurl'];
361             // Syntax check.
362             if (!preg_match('/(http:\/\/|https:\/\/|www).*\/imsmanifest.xml$/i', $reference)) {
363                 $errors['packageurl'] = get_string('invalidurl', 'scorm');
364             } else {
365                 // Availability check.
366                 $result = scorm_check_url($reference);
367                 if (is_string($result)) {
368                     $errors['packageurl'] = $result;
369                 }
370             }
372         } else if ($type === 'packageurl') {
373             $reference = $data['reference'];
374             // Syntax check.
375             if (!preg_match('/(http:\/\/|https:\/\/|www).*(\.zip|\.pif)$/i', $reference)) {
376                 $errors['packageurl'] = get_string('invalidurl', 'scorm');
377             } else {
378                 // Availability check.
379                 $result = scorm_check_url($reference);
380                 if (is_string($result)) {
381                     $errors['packageurl'] = $result;
382                 }
383             }
385         } else if ($type === SCORM_TYPE_AICCURL) {
386             $reference = $data['packageurl'];
387             // Syntax check.
388             if (!preg_match('/(http:\/\/|https:\/\/|www).*/', $reference)) {
389                 $errors['packageurl'] = get_string('invalidurl', 'scorm');
390             } else {
391                 // Availability check.
392                 $result = scorm_check_url($reference);
393                 if (is_string($result)) {
394                     $errors['packageurl'] = $result;
395                 }
396             }
398         }
400         return $errors;
401     }
403     // Need to translate the "options" and "reference" field.
404     public function set_data($defaultvalues) {
405         $defaultvalues = (array)$defaultvalues;
407         if (isset($defaultvalues['scormtype']) and isset($defaultvalues['reference'])) {
408             switch ($defaultvalues['scormtype']) {
409                 case SCORM_TYPE_LOCALSYNC :
410                 case SCORM_TYPE_EXTERNAL:
411                 case SCORM_TYPE_AICCURL:
412                     $defaultvalues['packageurl'] = $defaultvalues['reference'];
413             }
414         }
415         unset($defaultvalues['reference']);
417         if (!empty($defaultvalues['options'])) {
418             $options = explode(',', $defaultvalues['options']);
419             foreach ($options as $option) {
420                 $opt = explode('=', $option);
421                 if (isset($opt[1])) {
422                     $defaultvalues[$opt[0]] = $opt[1];
423                 }
424             }
425         }
427         $this->data_preprocessing($defaultvalues);
428         parent::set_data($defaultvalues);
429     }
431     public function add_completion_rules() {
432         $mform =& $this->_form;
433         $items = array();
435         // Require score.
436         $group = array();
437         $group[] =& $mform->createElement('text', 'completionscorerequired', '', array('size' => 5));
438         $group[] =& $mform->createElement('checkbox', 'completionscoredisabled', null, get_string('disable'));
439         $mform->setType('completionscorerequired', PARAM_INT);
440         $mform->addGroup($group, 'completionscoregroup', get_string('completionscorerequired', 'scorm'), '', false);
441         $mform->addHelpButton('completionscoregroup', 'completionscorerequired', 'scorm');
442         $mform->disabledIf('completionscorerequired', 'completionscoredisabled', 'checked');
443         $mform->setDefault('completionscorerequired', 0);
445         $items[] = 'completionscoregroup';
448         // Require status.
449         $first = true;
450         $firstkey = null;
451         foreach (scorm_status_options(true) as $key => $value) {
452             $name = null;
453             $key = 'completionstatusrequired['.$key.']';
454             if ($first) {
455                 $name = get_string('completionstatusrequired', 'scorm');
456                 $first = false;
457                 $firstkey = $key;
458             }
459             $mform->addElement('checkbox', $key, $name, $value);
460             $mform->setType($key, PARAM_BOOL);
461             $items[] = $key;
462         }
463         $mform->addHelpButton($firstkey, 'completionstatusrequired', 'scorm');
465         return $items;
466     }
468     function completion_rule_enabled($data) {
469         $status = !empty($data['completionstatusrequired']);
470         $score = empty($data['completionscoredisabled']) && strlen($data['completionscorerequired']);
472         return $status || $score;
473     }
475     function get_data($slashed = true) {
476         $data = parent::get_data($slashed);
478         if (!$data) {
479             return false;
480         }
482         // Convert completionstatusrequired to a proper integer, if any.
483         $total = 0;
484         if (isset($data->completionstatusrequired) && is_array($data->completionstatusrequired)) {
485             foreach (array_keys($data->completionstatusrequired) as $state) {
486                 $total |= $state;
487             }
488             $data->completionstatusrequired = $total;
489         }
491         if (!empty($data->completionunlocked)) {
492             // Turn off completion settings if the checkboxes aren't ticked.
493             $autocompletion = isset($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;
495             if (isset($data->completionstatusrequired) && $autocompletion) {
496                 // Do nothing: completionstatusrequired has been already converted
497                 //             into a correct integer representation.
498             } else {
499                 $data->completionstatusrequired = null;
500             }
502             if (!empty($data->completionscoredisabled) || !$autocompletion) {
503                 $data->completionscorerequired = null;
504             }
505         }
507         return $data;
508     }