03f9f266fd4b701bc1a2d3d9a6a5a86ed1dd30ea
[moodle.git] / mod / assign / feedback / editpdf / 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 PDF feedback plugin
19  *
20  *
21  * @package   assignfeedback_editpdf
22  * @copyright 2012 Davo Smith
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 use \assignfeedback_editpdf\document_services;
29 use \assignfeedback_editpdf\page_editor;
31 /**
32  * library class for editpdf feedback plugin extending feedback plugin base class
33  *
34  * @package   assignfeedback_editpdf
35  * @copyright 2012 Davo Smith
36  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class assign_feedback_editpdf extends assign_feedback_plugin {
40     /** @var boolean|null $enabledcache Cached lookup of the is_enabled function */
41     private $enabledcache = null;
43     /**
44      * Get the name of the file feedback plugin
45      * @return string
46      */
47     public function get_name() {
48         return get_string('pluginname', 'assignfeedback_editpdf');
49     }
51     /**
52      * Create a widget for rendering the editor.
53      *
54      * @param int $userid
55      * @param stdClass $grade
56      * @param bool $readonly
57      * @return assignfeedback_editpdf_widget
58      */
59     public function get_widget($userid, $grade, $readonly) {
60         $attempt = -1;
61         if ($grade && isset($grade->attemptnumber)) {
62             $attempt = $grade->attemptnumber;
63         } else {
64             $grade = $this->assignment->get_user_grade($userid, true);
65         }
67         $feedbackfile = document_services::get_feedback_document($this->assignment->get_instance()->id,
68                                                                  $userid,
69                                                                  $attempt);
71         $stampfiles = array();
72         $fs = get_file_storage();
73         $syscontext = context_system::instance();
75         // Copy any new stamps to this instance.
76         if ($files = $fs->get_area_files($syscontext->id,
77                                          'assignfeedback_editpdf',
78                                          'stamps',
79                                          0,
80                                          "filename",
81                                          false)) {
82             foreach ($files as $file) {
83                 $filename = $file->get_filename();
84                 if ($filename !== '.') {
86                     $existingfile = $fs->get_file($this->assignment->get_context()->id,
87                                                   'assignfeedback_editpdf',
88                                                   'stamps',
89                                                   $grade->id,
90                                                   '/',
91                                                   $file->get_filename());
92                     if (!$existingfile) {
93                         $newrecord = new stdClass();
94                         $newrecord->contextid = $this->assignment->get_context()->id;
95                         $newrecord->itemid = $grade->id;
96                         $fs->create_file_from_storedfile($newrecord, $file);
97                     }
98                 }
99             }
100         }
102         // Now get the full list of stamp files for this instance.
103         if ($files = $fs->get_area_files($this->assignment->get_context()->id,
104                                          'assignfeedback_editpdf',
105                                          'stamps',
106                                          $grade->id,
107                                          "filename",
108                                          false)) {
109             foreach ($files as $file) {
110                 $filename = $file->get_filename();
111                 if ($filename !== '.') {
112                     $url = moodle_url::make_pluginfile_url($this->assignment->get_context()->id,
113                                                    'assignfeedback_editpdf',
114                                                    'stamps',
115                                                    $grade->id,
116                                                    '/',
117                                                    $file->get_filename(),
118                                                    false);
119                     array_push($stampfiles, $url->out());
120                 }
121             }
122         }
124         $url = false;
125         $filename = '';
126         if ($feedbackfile) {
127             $url = moodle_url::make_pluginfile_url($this->assignment->get_context()->id,
128                                                    'assignfeedback_editpdf',
129                                                    document_services::FINAL_PDF_FILEAREA,
130                                                    $grade->id,
131                                                    '/',
132                                                    $feedbackfile->get_filename(),
133                                                    false);
134            $filename = $feedbackfile->get_filename();
135         }
137         $widget = new assignfeedback_editpdf_widget($this->assignment->get_instance()->id,
138                                                     $userid,
139                                                     $attempt,
140                                                     $url,
141                                                     $filename,
142                                                     $stampfiles,
143                                                     $readonly
144                                                 );
145         return $widget;
146     }
148     /**
149      * Get form elements for grading form
150      *
151      * @param stdClass $grade
152      * @param MoodleQuickForm $mform
153      * @param stdClass $data
154      * @param int $userid
155      * @return bool true if elements were added to the form
156      */
157     public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
158         global $PAGE;
160         $attempt = -1;
161         if ($grade) {
162             $attempt = $grade->attemptnumber;
163         }
165         $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
167         // Links to download the generated pdf...
168         if ($attempt > -1 && page_editor::has_annotations_or_comments($grade->id, false)) {
169             $html = $this->assignment->render_area_files('assignfeedback_editpdf',
170                                                          document_services::FINAL_PDF_FILEAREA,
171                                                          $grade->id);
172             $mform->addElement('static', 'editpdf_files', get_string('downloadfeedback', 'assignfeedback_editpdf'), $html);
173         }
175         $widget = $this->get_widget($userid, $grade, false);
177         $html = $renderer->render($widget);
178         $mform->addElement('static', 'editpdf', get_string('editpdf', 'assignfeedback_editpdf'), $html);
179         $mform->addHelpButton('editpdf', 'editpdf', 'assignfeedback_editpdf');
180         $mform->addElement('hidden', 'editpdf_source_userid', $userid);
181         $mform->setType('editpdf_source_userid', PARAM_INT);
182         $mform->setConstant('editpdf_source_userid', $userid);
183     }
185     /**
186      * Check to see if the grade feedback for the pdf has been modified.
187      *
188      * @param stdClass $grade Grade object.
189      * @param stdClass $data Data from the form submission (not used).
190      * @return boolean True if the pdf has been modified, else false.
191      */
192     public function is_feedback_modified(stdClass $grade, stdClass $data) {
193         // We only need to know if the source user's PDF has changed. If so then all
194         // following users will have the same status. If it's only an individual annotation
195         // then only one user will come through this method.
196         // Source user id is only added to the form if there was a pdf.
197         if (!empty($data->editpdf_source_userid)) {
198             $sourceuserid = $data->editpdf_source_userid;
199             // Retrieve the grade information for the source user.
200             $sourcegrade = $this->assignment->get_user_grade($sourceuserid, true, $grade->attemptnumber);
201             $pagenumbercount = document_services::page_number_for_attempt($this->assignment, $sourceuserid, $sourcegrade->attemptnumber);
202             for ($i = 0; $i < $pagenumbercount; $i++) {
203                 // Select all annotations.
204                 $draftannotations = page_editor::get_annotations($sourcegrade->id, $i, true);
205                 $nondraftannotations = page_editor::get_annotations($grade->id, $i, false);
206                 // Check to see if the count is the same.
207                 if (count($draftannotations) != count($nondraftannotations)) {
208                     // The count is different so we have a modification.
209                     return true;
210                 } else {
211                     $matches = 0;
212                     // Have a closer look and see if the draft files match all the non draft files.
213                     foreach ($nondraftannotations as $ndannotation) {
214                         foreach ($draftannotations as $dannotation) {
215                             foreach ($ndannotation as $key => $value) {
216                                 if ($key != 'id' && $value != $dannotation->{$key}) {
217                                     continue 2;
218                                 }
219                             }
220                             $matches++;
221                         }
222                     }
223                     if ($matches !== count($nondraftannotations)) {
224                         return true;
225                     }
226                 }
227                 // Select all comments.
228                 $draftcomments = page_editor::get_comments($sourcegrade->id, $i, true);
229                 $nondraftcomments = page_editor::get_comments($grade->id, $i, false);
230                 if (count($draftcomments) != count($nondraftcomments)) {
231                     return true;
232                 } else {
233                     // Go for a closer inspection.
234                     $matches = 0;
235                     foreach ($nondraftcomments as $ndcomment) {
236                         foreach ($draftcomments as $dcomment) {
237                             foreach ($ndcomment as $key => $value) {
238                                 if ($key != 'id' && $value != $dcomment->{$key}) {
239                                     continue 2;
240                                 }
241                             }
242                             $matches++;
243                         }
244                     }
245                     if ($matches !== count($nondraftcomments)) {
246                         return true;
247                     }
248                 }
249             }
250         }
251         return false;
252     }
254     /**
255      * Generate the pdf.
256      *
257      * @param stdClass $grade
258      * @param stdClass $data
259      * @return bool
260      */
261     public function save(stdClass $grade, stdClass $data) {
262         // Source user id is only added to the form if there was a pdf.
263         if (!empty($data->editpdf_source_userid)) {
264             $sourceuserid = $data->editpdf_source_userid;
265             // Copy drafts annotations and comments if current user is different to sourceuserid.
266             if ($sourceuserid != $grade->userid) {
267                 page_editor::copy_drafts_from_to($this->assignment, $grade, $sourceuserid);
268             }
269         }
270         if (page_editor::has_annotations_or_comments($grade->id, true)) {
271             document_services::generate_feedback_document($this->assignment, $grade->userid, $grade->attemptnumber);
272         }
274         return true;
275     }
277     /**
278      * Display the list of files in the feedback status table.
279      *
280      * @param stdClass $grade
281      * @param bool $showviewlink (Always set to false).
282      * @return string
283      */
284     public function view_summary(stdClass $grade, & $showviewlink) {
285         $showviewlink = false;
286         return $this->view($grade);
287     }
289     /**
290      * Display the list of files in the feedback status table.
291      *
292      * @param stdClass $grade
293      * @return string
294      */
295     public function view(stdClass $grade) {
296         global $PAGE;
297         $html = '';
298         // Show a link to download the pdf.
299         if (page_editor::has_annotations_or_comments($grade->id, false)) {
300             $html = $this->assignment->render_area_files('assignfeedback_editpdf',
301                                                          document_services::FINAL_PDF_FILEAREA,
302                                                          $grade->id);
304             // Also show the link to the read-only interface.
305             $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
306             $widget = $this->get_widget($grade->userid, $grade, true);
308             $html .= $renderer->render($widget);
309         }
310         return $html;
311     }
313     /**
314      * Return true if there are no released comments/annotations.
315      *
316      * @param stdClass $grade
317      */
318     public function is_empty(stdClass $grade) {
319         global $DB;
321         $comments = $DB->count_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$grade->id, 'draft'=>0));
322         $annotations = $DB->count_records('assignfeedback_editpdf_annot', array('gradeid'=>$grade->id, 'draft'=>0));
323         return $comments == 0 && $annotations == 0;
324     }
326     /**
327      * The assignment has been deleted - remove the plugin specific data
328      *
329      * @return bool
330      */
331     public function delete_instance() {
332         global $DB;
333         $grades = $DB->get_records('assign_grades', array('assignment'=>$this->assignment->get_instance()->id), '', 'id');
334         if ($grades) {
335             list($gradeids, $params) = $DB->get_in_or_equal(array_keys($grades), SQL_PARAMS_NAMED);
336             $DB->delete_records_select('assignfeedback_editpdf_annot', 'gradeid ' . $gradeids, $params);
337             $DB->delete_records_select('assignfeedback_editpdf_cmnt', 'gradeid ' . $gradeids, $params);
338         }
339         return true;
340     }
342     /**
343      * Determine if ghostscript is available and working.
344      *
345      * @return bool
346      */
347     public function is_available() {
348         if ($this->enabledcache === null) {
349             $testpath = assignfeedback_editpdf\pdf::test_gs_path(false);
350             $this->enabledcache = ($testpath->status == assignfeedback_editpdf\pdf::GSPATH_OK);
351         }
352         return $this->enabledcache;
353     }
354     /**
355      * Prevent enabling this plugin if ghostscript is not available.
356      *
357      * @return bool false
358      */
359     public function is_configurable() {
360         return $this->is_available();
361     }
363     /**
364      * Get file areas returns a list of areas this plugin stores files.
365      *
366      * @return array - An array of fileareas (keys) and descriptions (values)
367      */
368     public function get_file_areas() {
369         return array(document_services::FINAL_PDF_FILEAREA => $this->get_name());
370     }
372     /**
373      * This plugin will inject content into the review panel with javascript.
374      * @return bool true
375      */
376     public function supports_review_panel() {
377         return true;
378     }
380     /**
381      * Return the plugin configs for external functions.
382      *
383      * @return array the list of settings
384      * @since Moodle 3.2
385      */
386     public function get_config_for_external() {
387         return (array) $this->get_config();
388     }