MDL-68665 assignfeedback_editpdf: Improve caching on stamps
[moodle.git] / mod / assign / feedback / editpdf / locallib.php
CommitLineData
5c386472
DW
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/>.
16
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 */
25
26defined('MOODLE_INTERNAL') || die();
27
28use \assignfeedback_editpdf\document_services;
29use \assignfeedback_editpdf\page_editor;
30
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 */
38class assign_feedback_editpdf extends assign_feedback_plugin {
39
c70de965
DW
40 /** @var boolean|null $enabledcache Cached lookup of the is_enabled function */
41 private $enabledcache = null;
42
5c386472
DW
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 }
50
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;
fc9bc658 61 if ($grade && isset($grade->attemptnumber)) {
5c386472
DW
62 $attempt = $grade->attemptnumber;
63 } else {
64 $grade = $this->assignment->get_user_grade($userid, true);
65 }
66
67 $feedbackfile = document_services::get_feedback_document($this->assignment->get_instance()->id,
68 $userid,
69 $attempt);
70
71 $stampfiles = array();
1343d84c 72 $systemfiles = array();
5c386472
DW
73 $fs = get_file_storage();
74 $syscontext = context_system::instance();
75
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 !== '.') {
1343d84c
JS
86 $systemfiles[] = $filename;
87
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 );
5c386472 96
5c386472
DW
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 }
106
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 !== '.') {
1343d84c
JS
117
118 // Check to see if the file exists in system context.
119 $insystemfiles = in_array($filename, $systemfiles);
120
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;
125
126 $url = moodle_url::make_pluginfile_url(
127 $context,
128 'assignfeedback_editpdf',
129 'stamps',
130 $itemid,
131 '/',
132 $file->get_filename(),
133 false
134 );
5c386472
DW
135 array_push($stampfiles, $url->out());
136 }
137 }
138 }
139
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 }
152
5c386472
DW
153 $widget = new assignfeedback_editpdf_widget($this->assignment->get_instance()->id,
154 $userid,
155 $attempt,
156 $url,
157 $filename,
158 $stampfiles,
f7a9f1dd
AN
159 $readonly
160 );
5c386472
DW
161 return $widget;
162 }
163
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;
175
176 $attempt = -1;
177 if ($grade) {
178 $attempt = $grade->attemptnumber;
179 }
180
bb690849 181 $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
5c386472 182
574b2f1f
DW
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 }
190
bb690849 191 $widget = $this->get_widget($userid, $grade, false);
5c386472 192
bb690849
DW
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);
5c386472
DW
199 }
200
238c05c2
AG
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) {
181c20bc
AG
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 }
4edd8e77 235 }
181c20bc 236 $matches++;
238c05c2 237 }
181c20bc
AG
238 }
239 if ($matches !== count($nondraftannotations)) {
240 return true;
238c05c2
AG
241 }
242 }
181c20bc
AG
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)) {
4edd8e77 247 return true;
181c20bc
AG
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 }
4edd8e77 257 }
181c20bc 258 $matches++;
238c05c2
AG
259 }
260 }
181c20bc
AG
261 if ($matches !== count($nondraftcomments)) {
262 return true;
263 }
4edd8e77 264 }
238c05c2
AG
265 }
266 }
267 return false;
268 }
269
5c386472
DW
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) {
177cc379
DW
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 }
028c9d20 285 }
5c386472
DW
286 if (page_editor::has_annotations_or_comments($grade->id, true)) {
287 document_services::generate_feedback_document($this->assignment, $grade->userid, $grade->attemptnumber);
288 }
289
290 return true;
291 }
292
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 }
304
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);
319
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);
323
324 $html .= $renderer->render($widget);
325 }
326 return $html;
327 }
328
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;
336
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 }
341
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;
9f5193a4
DW
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;
5c386472
DW
356 }
357
f159ad73 358 /**
9a73d11c 359 * Determine if ghostscript is available and working.
f159ad73 360 *
361 * @return bool
362 */
9a73d11c 363 public function is_available() {
c70de965
DW
364 if ($this->enabledcache === null) {
365 $testpath = assignfeedback_editpdf\pdf::test_gs_path(false);
366 $this->enabledcache = ($testpath->status == assignfeedback_editpdf\pdf::GSPATH_OK);
f159ad73 367 }
c70de965 368 return $this->enabledcache;
f159ad73 369 }
370 /**
9a73d11c 371 * Prevent enabling this plugin if ghostscript is not available.
f159ad73 372 *
373 * @return bool false
374 */
375 public function is_configurable() {
9a73d11c 376 return $this->is_available();
f159ad73 377 }
2fa6c632
JL
378
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 }
bb690849
DW
387
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 }
30cdddb0
JL
395
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 }
5c386472 405}