2f039b20152160673a14642e766a8b8f1d7b8001
[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         $systemfiles = array();
73         $fs = get_file_storage();
74         $syscontext = context_system::instance();
76         // Copy any new stamps to this instance.
77         if ($files = $fs->get_area_files($syscontext->id,
78                                          'assignfeedback_editpdf',
79                                          'stamps',
80                                          0,
81                                          "filename",
82                                          false)) {
83             foreach ($files as $file) {
84                 $filename = $file->get_filename();
85                 if ($filename !== '.') {
86                     $systemfiles[] = $filename;
88                     $existingfile = $fs->file_exists(
89                         $this->assignment->get_context()->id,
90                         'assignfeedback_editpdf',
91                         'stamps',
92                         $grade->id,
93                         '/',
94                         $file->get_filename()
95                     );
97                     if (!$existingfile) {
98                         $newrecord = new stdClass();
99                         $newrecord->contextid = $this->assignment->get_context()->id;
100                         $newrecord->itemid = $grade->id;
101                         $fs->create_file_from_storedfile($newrecord, $file);
102                     }
103                 }
104             }
105         }
107         // Now get the full list of stamp files for this instance.
108         if ($files = $fs->get_area_files($this->assignment->get_context()->id,
109                                          'assignfeedback_editpdf',
110                                          'stamps',
111                                          $grade->id,
112                                          "filename",
113                                          false)) {
114             foreach ($files as $file) {
115                 $filename = $file->get_filename();
116                 if ($filename !== '.') {
118                     // Check to see if the file exists in system context.
119                     $insystemfiles = in_array($filename, $systemfiles);
121                     // If stamp is available in the system context, use that copy.
122                     // If not then fall back to file saved in the files table.
123                     $context = $insystemfiles ? $syscontext->id : $this->assignment->get_context()->id;
124                     $itemid = $insystemfiles ? 0 : $grade->id;
126                     $url = moodle_url::make_pluginfile_url(
127                         $context,
128                         'assignfeedback_editpdf',
129                         'stamps',
130                         $itemid,
131                         '/',
132                         $file->get_filename(),
133                         false
134                     );
135                     array_push($stampfiles, $url->out());
136                 }
137             }
138         }
140         $url = false;
141         $filename = '';
142         if ($feedbackfile) {
143             $url = moodle_url::make_pluginfile_url($this->assignment->get_context()->id,
144                                                    'assignfeedback_editpdf',
145                                                    document_services::FINAL_PDF_FILEAREA,
146                                                    $grade->id,
147                                                    '/',
148                                                    $feedbackfile->get_filename(),
149                                                    false);
150            $filename = $feedbackfile->get_filename();
151         }
153         $widget = new assignfeedback_editpdf_widget($this->assignment->get_instance()->id,
154                                                     $userid,
155                                                     $attempt,
156                                                     $url,
157                                                     $filename,
158                                                     $stampfiles,
159                                                     $readonly
160                                                 );
161         return $widget;
162     }
164     /**
165      * Get form elements for grading form
166      *
167      * @param stdClass $grade
168      * @param MoodleQuickForm $mform
169      * @param stdClass $data
170      * @param int $userid
171      * @return bool true if elements were added to the form
172      */
173     public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
174         global $PAGE;
176         $attempt = -1;
177         if ($grade) {
178             $attempt = $grade->attemptnumber;
179         }
181         $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
183         // Links to download the generated pdf...
184         if ($attempt > -1 && page_editor::has_annotations_or_comments($grade->id, false)) {
185             $html = $this->assignment->render_area_files('assignfeedback_editpdf',
186                                                          document_services::FINAL_PDF_FILEAREA,
187                                                          $grade->id);
188             $mform->addElement('static', 'editpdf_files', get_string('downloadfeedback', 'assignfeedback_editpdf'), $html);
189         }
191         $widget = $this->get_widget($userid, $grade, false);
193         $html = $renderer->render($widget);
194         $mform->addElement('static', 'editpdf', get_string('editpdf', 'assignfeedback_editpdf'), $html);
195         $mform->addHelpButton('editpdf', 'editpdf', 'assignfeedback_editpdf');
196         $mform->addElement('hidden', 'editpdf_source_userid', $userid);
197         $mform->setType('editpdf_source_userid', PARAM_INT);
198         $mform->setConstant('editpdf_source_userid', $userid);
199     }
201     /**
202      * Check to see if the grade feedback for the pdf has been modified.
203      *
204      * @param stdClass $grade Grade object.
205      * @param stdClass $data Data from the form submission (not used).
206      * @return boolean True if the pdf has been modified, else false.
207      */
208     public function is_feedback_modified(stdClass $grade, stdClass $data) {
209         // We only need to know if the source user's PDF has changed. If so then all
210         // following users will have the same status. If it's only an individual annotation
211         // then only one user will come through this method.
212         // Source user id is only added to the form if there was a pdf.
213         if (!empty($data->editpdf_source_userid)) {
214             $sourceuserid = $data->editpdf_source_userid;
215             // Retrieve the grade information for the source user.
216             $sourcegrade = $this->assignment->get_user_grade($sourceuserid, true, $grade->attemptnumber);
217             $pagenumbercount = document_services::page_number_for_attempt($this->assignment, $sourceuserid, $sourcegrade->attemptnumber);
218             for ($i = 0; $i < $pagenumbercount; $i++) {
219                 // Select all annotations.
220                 $draftannotations = page_editor::get_annotations($sourcegrade->id, $i, true);
221                 $nondraftannotations = page_editor::get_annotations($grade->id, $i, false);
222                 // Check to see if the count is the same.
223                 if (count($draftannotations) != count($nondraftannotations)) {
224                     // The count is different so we have a modification.
225                     return true;
226                 } else {
227                     $matches = 0;
228                     // Have a closer look and see if the draft files match all the non draft files.
229                     foreach ($nondraftannotations as $ndannotation) {
230                         foreach ($draftannotations as $dannotation) {
231                             foreach ($ndannotation as $key => $value) {
232                                 if ($key != 'id' && $value != $dannotation->{$key}) {
233                                     continue 2;
234                                 }
235                             }
236                             $matches++;
237                         }
238                     }
239                     if ($matches !== count($nondraftannotations)) {
240                         return true;
241                     }
242                 }
243                 // Select all comments.
244                 $draftcomments = page_editor::get_comments($sourcegrade->id, $i, true);
245                 $nondraftcomments = page_editor::get_comments($grade->id, $i, false);
246                 if (count($draftcomments) != count($nondraftcomments)) {
247                     return true;
248                 } else {
249                     // Go for a closer inspection.
250                     $matches = 0;
251                     foreach ($nondraftcomments as $ndcomment) {
252                         foreach ($draftcomments as $dcomment) {
253                             foreach ($ndcomment as $key => $value) {
254                                 if ($key != 'id' && $value != $dcomment->{$key}) {
255                                     continue 2;
256                                 }
257                             }
258                             $matches++;
259                         }
260                     }
261                     if ($matches !== count($nondraftcomments)) {
262                         return true;
263                     }
264                 }
265             }
266         }
267         return false;
268     }
270     /**
271      * Generate the pdf.
272      *
273      * @param stdClass $grade
274      * @param stdClass $data
275      * @return bool
276      */
277     public function save(stdClass $grade, stdClass $data) {
278         // Source user id is only added to the form if there was a pdf.
279         if (!empty($data->editpdf_source_userid)) {
280             $sourceuserid = $data->editpdf_source_userid;
281             // Copy drafts annotations and comments if current user is different to sourceuserid.
282             if ($sourceuserid != $grade->userid) {
283                 page_editor::copy_drafts_from_to($this->assignment, $grade, $sourceuserid);
284             }
285         }
286         if (page_editor::has_annotations_or_comments($grade->id, true)) {
287             document_services::generate_feedback_document($this->assignment, $grade->userid, $grade->attemptnumber);
288         }
290         return true;
291     }
293     /**
294      * Display the list of files in the feedback status table.
295      *
296      * @param stdClass $grade
297      * @param bool $showviewlink (Always set to false).
298      * @return string
299      */
300     public function view_summary(stdClass $grade, & $showviewlink) {
301         $showviewlink = false;
302         return $this->view($grade);
303     }
305     /**
306      * Display the list of files in the feedback status table.
307      *
308      * @param stdClass $grade
309      * @return string
310      */
311     public function view(stdClass $grade) {
312         global $PAGE;
313         $html = '';
314         // Show a link to download the pdf.
315         if (page_editor::has_annotations_or_comments($grade->id, false)) {
316             $html = $this->assignment->render_area_files('assignfeedback_editpdf',
317                                                          document_services::FINAL_PDF_FILEAREA,
318                                                          $grade->id);
320             // Also show the link to the read-only interface.
321             $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
322             $widget = $this->get_widget($grade->userid, $grade, true);
324             $html .= $renderer->render($widget);
325         }
326         return $html;
327     }
329     /**
330      * Return true if there are no released comments/annotations.
331      *
332      * @param stdClass $grade
333      */
334     public function is_empty(stdClass $grade) {
335         global $DB;
337         $comments = $DB->count_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$grade->id, 'draft'=>0));
338         $annotations = $DB->count_records('assignfeedback_editpdf_annot', array('gradeid'=>$grade->id, 'draft'=>0));
339         return $comments == 0 && $annotations == 0;
340     }
342     /**
343      * The assignment has been deleted - remove the plugin specific data
344      *
345      * @return bool
346      */
347     public function delete_instance() {
348         global $DB;
349         $grades = $DB->get_records('assign_grades', array('assignment'=>$this->assignment->get_instance()->id), '', 'id');
350         if ($grades) {
351             list($gradeids, $params) = $DB->get_in_or_equal(array_keys($grades), SQL_PARAMS_NAMED);
352             $DB->delete_records_select('assignfeedback_editpdf_annot', 'gradeid ' . $gradeids, $params);
353             $DB->delete_records_select('assignfeedback_editpdf_cmnt', 'gradeid ' . $gradeids, $params);
354         }
355         return true;
356     }
358     /**
359      * Determine if ghostscript is available and working.
360      *
361      * @return bool
362      */
363     public function is_available() {
364         if ($this->enabledcache === null) {
365             $testpath = assignfeedback_editpdf\pdf::test_gs_path(false);
366             $this->enabledcache = ($testpath->status == assignfeedback_editpdf\pdf::GSPATH_OK);
367         }
368         return $this->enabledcache;
369     }
370     /**
371      * Prevent enabling this plugin if ghostscript is not available.
372      *
373      * @return bool false
374      */
375     public function is_configurable() {
376         return $this->is_available();
377     }
379     /**
380      * Get file areas returns a list of areas this plugin stores files.
381      *
382      * @return array - An array of fileareas (keys) and descriptions (values)
383      */
384     public function get_file_areas() {
385         return array(document_services::FINAL_PDF_FILEAREA => $this->get_name());
386     }
388     /**
389      * This plugin will inject content into the review panel with javascript.
390      * @return bool true
391      */
392     public function supports_review_panel() {
393         return true;
394     }
396     /**
397      * Return the plugin configs for external functions.
398      *
399      * @return array the list of settings
400      * @since Moodle 3.2
401      */
402     public function get_config_for_external() {
403         return (array) $this->get_config();
404     }