weekly release 3.2dev
[moodle.git] / mod / assign / submission / onlinetext / locallib.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  * This file contains the definition for the library class for onlinetext submission plugin
19  *
20  * This class provides all the functionality for the new assign module.
21  *
22  * @package assignsubmission_onlinetext
23  * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
24  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 defined('MOODLE_INTERNAL') || die();
28 // File area for online text submission assignment.
29 define('ASSIGNSUBMISSION_ONLINETEXT_FILEAREA', 'submissions_onlinetext');
31 /**
32  * library class for onlinetext submission plugin extending submission plugin base class
33  *
34  * @package assignsubmission_onlinetext
35  * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
36  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class assign_submission_onlinetext extends assign_submission_plugin {
40     /**
41      * Get the name of the online text submission plugin
42      * @return string
43      */
44     public function get_name() {
45         return get_string('onlinetext', 'assignsubmission_onlinetext');
46     }
49     /**
50      * Get onlinetext submission information from the database
51      *
52      * @param  int $submissionid
53      * @return mixed
54      */
55     private function get_onlinetext_submission($submissionid) {
56         global $DB;
58         return $DB->get_record('assignsubmission_onlinetext', array('submission'=>$submissionid));
59     }
61     /**
62      * Get the settings for onlinetext submission plugin
63      *
64      * @param MoodleQuickForm $mform The form to add elements to
65      * @return void
66      */
67     public function get_settings(MoodleQuickForm $mform) {
68         global $CFG, $COURSE;
70         $defaultwordlimit = $this->get_config('wordlimit') == 0 ? '' : $this->get_config('wordlimit');
71         $defaultwordlimitenabled = $this->get_config('wordlimitenabled');
73         $options = array('size' => '6', 'maxlength' => '6');
74         $name = get_string('wordlimit', 'assignsubmission_onlinetext');
76         // Create a text box that can be enabled/disabled for onlinetext word limit.
77         $wordlimitgrp = array();
78         $wordlimitgrp[] = $mform->createElement('text', 'assignsubmission_onlinetext_wordlimit', '', $options);
79         $wordlimitgrp[] = $mform->createElement('checkbox', 'assignsubmission_onlinetext_wordlimit_enabled',
80                 '', get_string('enable'));
81         $mform->addGroup($wordlimitgrp, 'assignsubmission_onlinetext_wordlimit_group', $name, ' ', false);
82         $mform->addHelpButton('assignsubmission_onlinetext_wordlimit_group',
83                               'wordlimit',
84                               'assignsubmission_onlinetext');
85         $mform->disabledIf('assignsubmission_onlinetext_wordlimit',
86                            'assignsubmission_onlinetext_wordlimit_enabled',
87                            'notchecked');
89         // Add numeric rule to text field.
90         $wordlimitgrprules = array();
91         $wordlimitgrprules['assignsubmission_onlinetext_wordlimit'][] = array(null, 'numeric', null, 'client');
92         $mform->addGroupRule('assignsubmission_onlinetext_wordlimit_group', $wordlimitgrprules);
94         // Rest of group setup.
95         $mform->setDefault('assignsubmission_onlinetext_wordlimit', $defaultwordlimit);
96         $mform->setDefault('assignsubmission_onlinetext_wordlimit_enabled', $defaultwordlimitenabled);
97         $mform->setType('assignsubmission_onlinetext_wordlimit', PARAM_INT);
98         $mform->disabledIf('assignsubmission_onlinetext_wordlimit_group',
99                            'assignsubmission_onlinetext_enabled',
100                            'notchecked');
101     }
103     /**
104      * Save the settings for onlinetext submission plugin
105      *
106      * @param stdClass $data
107      * @return bool
108      */
109     public function save_settings(stdClass $data) {
110         if (empty($data->assignsubmission_onlinetext_wordlimit) || empty($data->assignsubmission_onlinetext_wordlimit_enabled)) {
111             $wordlimit = 0;
112             $wordlimitenabled = 0;
113         } else {
114             $wordlimit = $data->assignsubmission_onlinetext_wordlimit;
115             $wordlimitenabled = 1;
116         }
118         $this->set_config('wordlimit', $wordlimit);
119         $this->set_config('wordlimitenabled', $wordlimitenabled);
121         return true;
122     }
124     /**
125      * Add form elements for settings
126      *
127      * @param mixed $submission can be null
128      * @param MoodleQuickForm $mform
129      * @param stdClass $data
130      * @return true if elements were added to the form
131      */
132     public function get_form_elements($submission, MoodleQuickForm $mform, stdClass $data) {
133         $elements = array();
135         $editoroptions = $this->get_edit_options();
136         $submissionid = $submission ? $submission->id : 0;
138         if (!isset($data->onlinetext)) {
139             $data->onlinetext = '';
140         }
141         if (!isset($data->onlinetextformat)) {
142             $data->onlinetextformat = editors_get_preferred_format();
143         }
145         if ($submission) {
146             $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
147             if ($onlinetextsubmission) {
148                 $data->onlinetext = $onlinetextsubmission->onlinetext;
149                 $data->onlinetextformat = $onlinetextsubmission->onlineformat;
150             }
152         }
154         $data = file_prepare_standard_editor($data,
155                                              'onlinetext',
156                                              $editoroptions,
157                                              $this->assignment->get_context(),
158                                              'assignsubmission_onlinetext',
159                                              ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
160                                              $submissionid);
161         $mform->addElement('editor', 'onlinetext_editor', $this->get_name(), null, $editoroptions);
163         return true;
164     }
166     /**
167      * Editor format options
168      *
169      * @return array
170      */
171     private function get_edit_options() {
172          $editoroptions = array(
173            'noclean' => false,
174            'maxfiles' => EDITOR_UNLIMITED_FILES,
175            'maxbytes' => $this->assignment->get_course()->maxbytes,
176            'context' => $this->assignment->get_context(),
177            'return_types' => FILE_INTERNAL | FILE_EXTERNAL
178         );
179         return $editoroptions;
180     }
182     /**
183      * Save data to the database and trigger plagiarism plugin,
184      * if enabled, to scan the uploaded content via events trigger
185      *
186      * @param stdClass $submission
187      * @param stdClass $data
188      * @return bool
189      */
190     public function save(stdClass $submission, stdClass $data) {
191         global $USER, $DB;
193         $editoroptions = $this->get_edit_options();
195         $data = file_postupdate_standard_editor($data,
196                                                 'onlinetext',
197                                                 $editoroptions,
198                                                 $this->assignment->get_context(),
199                                                 'assignsubmission_onlinetext',
200                                                 ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
201                                                 $submission->id);
203         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
205         $fs = get_file_storage();
207         $files = $fs->get_area_files($this->assignment->get_context()->id,
208                                      'assignsubmission_onlinetext',
209                                      ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
210                                      $submission->id,
211                                      'id',
212                                      false);
214         // Check word count before submitting anything.
215         $exceeded = $this->check_word_count(trim($data->onlinetext));
216         if ($exceeded) {
217             $this->set_error($exceeded);
218             return false;
219         }
221         $params = array(
222             'context' => context_module::instance($this->assignment->get_course_module()->id),
223             'courseid' => $this->assignment->get_course()->id,
224             'objectid' => $submission->id,
225             'other' => array(
226                 'pathnamehashes' => array_keys($files),
227                 'content' => trim($data->onlinetext),
228                 'format' => $data->onlinetext_editor['format']
229             )
230         );
231         if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
232             $params['relateduserid'] = $submission->userid;
233         }
234         $event = \assignsubmission_onlinetext\event\assessable_uploaded::create($params);
235         $event->trigger();
237         $groupname = null;
238         $groupid = 0;
239         // Get the group name as other fields are not transcribed in the logs and this information is important.
240         if (empty($submission->userid) && !empty($submission->groupid)) {
241             $groupname = $DB->get_field('groups', 'name', array('id' => $submission->groupid), '*', MUST_EXIST);
242             $groupid = $submission->groupid;
243         } else {
244             $params['relateduserid'] = $submission->userid;
245         }
247         $count = count_words($data->onlinetext);
249         // Unset the objectid and other field from params for use in submission events.
250         unset($params['objectid']);
251         unset($params['other']);
252         $params['other'] = array(
253             'submissionid' => $submission->id,
254             'submissionattempt' => $submission->attemptnumber,
255             'submissionstatus' => $submission->status,
256             'onlinetextwordcount' => $count,
257             'groupid' => $groupid,
258             'groupname' => $groupname
259         );
261         if ($onlinetextsubmission) {
263             $onlinetextsubmission->onlinetext = $data->onlinetext;
264             $onlinetextsubmission->onlineformat = $data->onlinetext_editor['format'];
265             $params['objectid'] = $onlinetextsubmission->id;
266             $updatestatus = $DB->update_record('assignsubmission_onlinetext', $onlinetextsubmission);
267             $event = \assignsubmission_onlinetext\event\submission_updated::create($params);
268             $event->set_assign($this->assignment);
269             $event->trigger();
270             return $updatestatus;
271         } else {
273             $onlinetextsubmission = new stdClass();
274             $onlinetextsubmission->onlinetext = $data->onlinetext;
275             $onlinetextsubmission->onlineformat = $data->onlinetext_editor['format'];
277             $onlinetextsubmission->submission = $submission->id;
278             $onlinetextsubmission->assignment = $this->assignment->get_instance()->id;
279             $onlinetextsubmission->id = $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
280             $params['objectid'] = $onlinetextsubmission->id;
281             $event = \assignsubmission_onlinetext\event\submission_created::create($params);
282             $event->set_assign($this->assignment);
283             $event->trigger();
284             return $onlinetextsubmission->id > 0;
285         }
286     }
288     /**
289      * Return a list of the text fields that can be imported/exported by this plugin
290      *
291      * @return array An array of field names and descriptions. (name=>description, ...)
292      */
293     public function get_editor_fields() {
294         return array('onlinetext' => get_string('pluginname', 'assignsubmission_comments'));
295     }
297     /**
298      * Get the saved text content from the editor
299      *
300      * @param string $name
301      * @param int $submissionid
302      * @return string
303      */
304     public function get_editor_text($name, $submissionid) {
305         if ($name == 'onlinetext') {
306             $onlinetextsubmission = $this->get_onlinetext_submission($submissionid);
307             if ($onlinetextsubmission) {
308                 return $onlinetextsubmission->onlinetext;
309             }
310         }
312         return '';
313     }
315     /**
316      * Get the content format for the editor
317      *
318      * @param string $name
319      * @param int $submissionid
320      * @return int
321      */
322     public function get_editor_format($name, $submissionid) {
323         if ($name == 'onlinetext') {
324             $onlinetextsubmission = $this->get_onlinetext_submission($submissionid);
325             if ($onlinetextsubmission) {
326                 return $onlinetextsubmission->onlineformat;
327             }
328         }
330         return 0;
331     }
334      /**
335       * Display onlinetext word count in the submission status table
336       *
337       * @param stdClass $submission
338       * @param bool $showviewlink - If the summary has been truncated set this to true
339       * @return string
340       */
341     public function view_summary(stdClass $submission, & $showviewlink) {
342         global $CFG;
344         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
345         // Always show the view link.
346         $showviewlink = true;
348         if ($onlinetextsubmission) {
349             $text = $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
350                                                              $onlinetextsubmission->submission,
351                                                              $this->get_type(),
352                                                              'onlinetext',
353                                                              'assignsubmission_onlinetext');
355             $shorttext = shorten_text($text, 140);
356             $plagiarismlinks = '';
358             if (!empty($CFG->enableplagiarism)) {
359                 require_once($CFG->libdir . '/plagiarismlib.php');
361                 $plagiarismlinks .= plagiarism_get_links(array('userid' => $submission->userid,
362                     'content' => trim($text),
363                     'cmid' => $this->assignment->get_course_module()->id,
364                     'course' => $this->assignment->get_course()->id,
365                     'assignment' => $submission->assignment));
366             }
367             if ($text != $shorttext) {
368                 $wordcount = get_string('numwords', 'assignsubmission_onlinetext', count_words($text));
370                 return $plagiarismlinks . $wordcount . $shorttext;
371             } else {
372                 return $plagiarismlinks . $shorttext;
373             }
374         }
375         return '';
376     }
378     /**
379      * Produce a list of files suitable for export that represent this submission.
380      *
381      * @param stdClass $submission - For this is the submission data
382      * @param stdClass $user - This is the user record for this submission
383      * @return array - return an array of files indexed by filename
384      */
385     public function get_files(stdClass $submission, stdClass $user) {
386         global $DB;
388         $files = array();
389         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
391         if ($onlinetextsubmission) {
392             $finaltext = $this->assignment->download_rewrite_pluginfile_urls($onlinetextsubmission->onlinetext, $user, $this);
393             $formattedtext = format_text($finaltext,
394                                          $onlinetextsubmission->onlineformat,
395                                          array('context'=>$this->assignment->get_context()));
396             $head = '<head><meta charset="UTF-8"></head>';
397             $submissioncontent = '<!DOCTYPE html><html>' . $head . '<body>'. $formattedtext . '</body></html>';
399             $filename = get_string('onlinetextfilename', 'assignsubmission_onlinetext');
400             $files[$filename] = array($submissioncontent);
402             $fs = get_file_storage();
404             $fsfiles = $fs->get_area_files($this->assignment->get_context()->id,
405                                            'assignsubmission_onlinetext',
406                                            ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
407                                            $submission->id,
408                                            'timemodified',
409                                            false);
411             foreach ($fsfiles as $file) {
412                 $files[$file->get_filename()] = $file;
413             }
414         }
416         return $files;
417     }
419     /**
420      * Display the saved text content from the editor in the view table
421      *
422      * @param stdClass $submission
423      * @return string
424      */
425     public function view(stdClass $submission) {
426         global $CFG;
427         $result = '';
429         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
431         if ($onlinetextsubmission) {
433             // Render for portfolio API.
434             $result .= $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
435                                                                 $onlinetextsubmission->submission,
436                                                                 $this->get_type(),
437                                                                 'onlinetext',
438                                                                 'assignsubmission_onlinetext');
440             $plagiarismlinks = '';
442             if (!empty($CFG->enableplagiarism)) {
443                 require_once($CFG->libdir . '/plagiarismlib.php');
445                 $plagiarismlinks .= plagiarism_get_links(array('userid' => $submission->userid,
446                     'content' => trim($result),
447                     'cmid' => $this->assignment->get_course_module()->id,
448                     'course' => $this->assignment->get_course()->id,
449                     'assignment' => $submission->assignment));
450             }
451         }
453         return $plagiarismlinks . $result;
454     }
456     /**
457      * Return true if this plugin can upgrade an old Moodle 2.2 assignment of this type and version.
458      *
459      * @param string $type old assignment subtype
460      * @param int $version old assignment version
461      * @return bool True if upgrade is possible
462      */
463     public function can_upgrade($type, $version) {
464         if ($type == 'online' && $version >= 2011112900) {
465             return true;
466         }
467         return false;
468     }
471     /**
472      * Upgrade the settings from the old assignment to the new plugin based one
473      *
474      * @param context $oldcontext - the database for the old assignment context
475      * @param stdClass $oldassignment - the database for the old assignment instance
476      * @param string $log record log events here
477      * @return bool Was it a success?
478      */
479     public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {
480         // No settings to upgrade.
481         return true;
482     }
484     /**
485      * Upgrade the submission from the old assignment to the new one
486      *
487      * @param context $oldcontext - the database for the old assignment context
488      * @param stdClass $oldassignment The data record for the old assignment
489      * @param stdClass $oldsubmission The data record for the old submission
490      * @param stdClass $submission The data record for the new submission
491      * @param string $log Record upgrade messages in the log
492      * @return bool true or false - false will trigger a rollback
493      */
494     public function upgrade(context $oldcontext,
495                             stdClass $oldassignment,
496                             stdClass $oldsubmission,
497                             stdClass $submission,
498                             & $log) {
499         global $DB;
501         $onlinetextsubmission = new stdClass();
502         $onlinetextsubmission->onlinetext = $oldsubmission->data1;
503         $onlinetextsubmission->onlineformat = $oldsubmission->data2;
505         $onlinetextsubmission->submission = $submission->id;
506         $onlinetextsubmission->assignment = $this->assignment->get_instance()->id;
508         if ($onlinetextsubmission->onlinetext === null) {
509             $onlinetextsubmission->onlinetext = '';
510         }
512         if ($onlinetextsubmission->onlineformat === null) {
513             $onlinetextsubmission->onlineformat = editors_get_preferred_format();
514         }
516         if (!$DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission) > 0) {
517             $log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);
518             return false;
519         }
521         // Now copy the area files.
522         $this->assignment->copy_area_files_for_upgrade($oldcontext->id,
523                                                         'mod_assignment',
524                                                         'submission',
525                                                         $oldsubmission->id,
526                                                         $this->assignment->get_context()->id,
527                                                         'assignsubmission_onlinetext',
528                                                         ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
529                                                         $submission->id);
530         return true;
531     }
533     /**
534      * Formatting for log info
535      *
536      * @param stdClass $submission The new submission
537      * @return string
538      */
539     public function format_for_log(stdClass $submission) {
540         // Format the info for each submission plugin (will be logged).
541         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
542         $onlinetextloginfo = '';
543         $onlinetextloginfo .= get_string('numwordsforlog',
544                                          'assignsubmission_onlinetext',
545                                          count_words($onlinetextsubmission->onlinetext));
547         return $onlinetextloginfo;
548     }
550     /**
551      * The assignment has been deleted - cleanup
552      *
553      * @return bool
554      */
555     public function delete_instance() {
556         global $DB;
557         $DB->delete_records('assignsubmission_onlinetext',
558                             array('assignment'=>$this->assignment->get_instance()->id));
560         return true;
561     }
563     /**
564      * No text is set for this plugin
565      *
566      * @param stdClass $submission
567      * @return bool
568      */
569     public function is_empty(stdClass $submission) {
570         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
572         return empty($onlinetextsubmission->onlinetext);
573     }
575     /**
576      * Get file areas returns a list of areas this plugin stores files
577      * @return array - An array of fileareas (keys) and descriptions (values)
578      */
579     public function get_file_areas() {
580         return array(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA=>$this->get_name());
581     }
583     /**
584      * Copy the student's submission from a previous submission. Used when a student opts to base their resubmission
585      * on the last submission.
586      * @param stdClass $sourcesubmission
587      * @param stdClass $destsubmission
588      */
589     public function copy_submission(stdClass $sourcesubmission, stdClass $destsubmission) {
590         global $DB;
592         // Copy the files across (attached via the text editor).
593         $contextid = $this->assignment->get_context()->id;
594         $fs = get_file_storage();
595         $files = $fs->get_area_files($contextid, 'assignsubmission_onlinetext',
596                                      ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $sourcesubmission->id, 'id', false);
597         foreach ($files as $file) {
598             $fieldupdates = array('itemid' => $destsubmission->id);
599             $fs->create_file_from_storedfile($fieldupdates, $file);
600         }
602         // Copy the assignsubmission_onlinetext record.
603         $onlinetextsubmission = $this->get_onlinetext_submission($sourcesubmission->id);
604         if ($onlinetextsubmission) {
605             unset($onlinetextsubmission->id);
606             $onlinetextsubmission->submission = $destsubmission->id;
607             $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
608         }
609         return true;
610     }
612     /**
613      * Return a description of external params suitable for uploading an onlinetext submission from a webservice.
614      *
615      * @return external_description|null
616      */
617     public function get_external_parameters() {
618         $editorparams = array('text' => new external_value(PARAM_TEXT, 'The text for this submission.'),
619                               'format' => new external_value(PARAM_INT, 'The format for this submission'),
620                               'itemid' => new external_value(PARAM_INT, 'The draft area id for files attached to the submission'));
621         $editorstructure = new external_single_structure($editorparams, 'Editor structure', VALUE_OPTIONAL);
622         return array('onlinetext_editor' => $editorstructure);
623     }
625     /**
626      * Compare word count of onlinetext submission to word limit, and return result.
627      *
628      * @param string $submissiontext Onlinetext submission text from editor
629      * @return string Error message if limit is enabled and exceeded, otherwise null
630      */
631     public function check_word_count($submissiontext) {
632         global $OUTPUT;
634         $wordlimitenabled = $this->get_config('wordlimitenabled');
635         $wordlimit = $this->get_config('wordlimit');
637         if ($wordlimitenabled == 0) {
638             return null;
639         }
641         // Count words and compare to limit.
642         $wordcount = count_words($submissiontext);
643         if ($wordcount <= $wordlimit) {
644             return null;
645         } else {
646             $errormsg = get_string('wordlimitexceeded', 'assignsubmission_onlinetext',
647                     array('limit' => $wordlimit, 'count' => $wordcount));
648             return $OUTPUT->error_text($errormsg);
649         }
650     }