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 editor class for the assignfeedback_editpdf plugin
19 *
20 * @package   assignfeedback_editpdf
21 * @copyright 2012 Davo Smith
22 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace assignfeedback_editpdf;
26
27/**
28 * This class performs crud operations on comments and annotations from a page of a response.
29 *
30 * No capability checks are done - they should be done by the calling class.
31 *
32 * @package   assignfeedback_editpdf
33 * @copyright 2012 Davo Smith
34 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36class page_editor {
37
38    /**
39     * Get all comments for a page.
40     * @param int $gradeid
41     * @param int $pageno
42     * @param bool $draft
43     * @return comment[]
44     */
45    public static function get_comments($gradeid, $pageno, $draft) {
46        global $DB;
47
48        $comments = array();
49        $params = array('gradeid'=>$gradeid, 'pageno'=>$pageno, 'draft'=>1);
50        if (!$draft) {
51            $params['draft'] = 0;
52        }
53        // Fetch comments ordered by position on the page.
54        $records = $DB->get_records('assignfeedback_editpdf_cmnt', $params, 'y, x');
55        foreach ($records as $record) {
56            array_push($comments, new comment($record));
57        }
58
59        return $comments;
60    }
61
62    /**
63     * Set all comments for a page.
64     * @param int $gradeid
65     * @param int $pageno
66     * @param comment[] $comments
67     * @return int - the number of comments.
68     */
69    public static function set_comments($gradeid, $pageno, $comments) {
70        global $DB;
71
72        $DB->delete_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'pageno'=>$pageno, 'draft'=>1));
73
74        $added = 0;
75        foreach ($comments as $record) {
76            // Force these.
77            if (!($record instanceof comment)) {
78                $comment = new comment($record);
79            } else {
80                $comment = $record;
81            }
82            if (trim($comment->rawtext) === '') {
83                continue;
84            }
85            $comment->gradeid = $gradeid;
86            $comment->pageno = $pageno;
87            $comment->draft = 1;
88            if (self::add_comment($comment)) {
89                $added++;
90            }
91        }
92
93        return $added;
94    }
95
96    /**
97     * Get a single comment by id.
98     * @param int $commentid
99     * @return comment or false
100     */
101    public static function get_comment($commentid) {
102        $record = $DB->get_record('assignfeedback_editpdf_cmnt', array('id'=>$commentid), '*', IGNORE_MISSING);
103        if ($record) {
104            return new comment($record);
105        }
106        return false;
107    }
108
109    /**
110     * Add a comment to a page.
111     * @param comment $comment
112     * @return bool
113     */
114    public static function add_comment(comment $comment) {
115        global $DB;
116        $comment->id = null;
117        return $DB->insert_record('assignfeedback_editpdf_cmnt', $comment);
118    }
119
120    /**
121     * Remove a comment from a page.
122     * @param int $commentid
123     * @return bool
124     */
125    public static function remove_comment($commentid) {
126        global $DB;
127        return $DB->delete_records('assignfeedback_editpdf_cmnt', array('id'=>$commentid));
128    }
129
130    /**
131     * Get all annotations for a page.
132     * @param int $gradeid
133     * @param int $pageno
134     * @param bool $draft
135     * @return annotation[]
136     */
137    public static function get_annotations($gradeid, $pageno, $draft) {
138        global $DB;
139
140        $params = array('gradeid'=>$gradeid, 'pageno'=>$pageno, 'draft'=>1);
141        if (!$draft) {
142            $params['draft'] = 0;
143        }
144        $annotations = array();
145        $records = $DB->get_records('assignfeedback_editpdf_annot', $params);
146        foreach ($records as $record) {
147            array_push($annotations, new annotation($record));
148        }
149
150        return $annotations;
151    }
152
153    /**
154     * Set all annotations for a page.
155     * @param int $gradeid
156     * @param int $pageno
157     * @param annotation[] $annotations
158     * @return int - the number of annotations.
159     */
160    public static function set_annotations($gradeid, $pageno, $annotations) {
161        global $DB;
162
163        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid' => $gradeid, 'pageno' => $pageno, 'draft' => 1));
164        $added = 0;
165        foreach ($annotations as $record) {
166            // Force these.
167            if (!($record instanceof annotation)) {
168                $annotation = new annotation($record);
169            } else {
170                $annotation = $record;
171            }
172            $annotation->gradeid = $gradeid;
173            $annotation->pageno = $pageno;
174            $annotation->draft = 1;
175            if (self::add_annotation($annotation)) {
176                $added++;
177            }
178        }
179
180        return $added;
181    }
182
183    /**
184     * Get a single annotation by id.
185     * @param int $annotationid
186     * @return annotation or false
187     */
188    public static function get_annotation($annotationid) {
189        global $DB;
190
191        $record = $DB->get_record('assignfeedback_editpdf_annot', array('id'=>$annotationid), '*', IGNORE_MISSING);
192        if ($record) {
193            return new annotation($record);
194        }
195        return false;
196    }
197
198    /**
199     * Unrelease drafts
200     * @param int $gradeid
201     * @return bool
202     */
203    public static function unrelease_drafts($gradeid) {
204        global $DB;
205
206        // Delete the non-draft annotations and comments.
207        $result = $DB->delete_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'draft'=>0));
208        $result = $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'draft'=>0)) && $result;
209        return $result;
210    }
211
212    /**
213     * Release the draft comments and annotations to students.
214     * @param int $gradeid
215     * @return bool
216     */
217    public static function release_drafts($gradeid) {
218        global $DB;
219
220        // Delete the previous non-draft annotations and comments.
221        $DB->delete_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'draft'=>0));
222        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'draft'=>0));
223
224        // Copy all the draft annotations and comments to non-drafts.
225        $records = $DB->get_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'draft'=>1));
226        foreach ($records as $record) {
227            unset($record->id);
228            $record->draft = 0;
229            $DB->insert_record('assignfeedback_editpdf_annot', $record);
230        }
231        $records = $DB->get_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'draft'=>1));
232        foreach ($records as $record) {
233            unset($record->id);
234            $record->draft = 0;
235            $DB->insert_record('assignfeedback_editpdf_cmnt', $record);
236        }
237
238        return true;
239    }
240
241    /**
242     * Has annotations or comments.
243     * @param int $gradeid
244     * @return bool
245     */
246    public static function has_annotations_or_comments($gradeid, $includedraft) {
247        global $DB;
248        $params = array('gradeid'=>$gradeid);
249        if (!$includedraft) {
250            $params['draft'] = 0;
251        }
252        if ($DB->count_records('assignfeedback_editpdf_cmnt', $params)) {
253            return true;
254        }
255        if ($DB->count_records('assignfeedback_editpdf_annot', $params)) {
256            return true;
257        }
258        return false;
259    }
260
261    /**
262     * Aborts all draft annotations and reverts to the last version released to students.
263     * @param int $gradeid
264     * @return bool
265     */
266    public static function revert_drafts($gradeid) {
267        global $DB;
268
269        // Delete the previous non-draft annotations and comments.
270        $DB->delete_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'draft'=>1));
271        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'draft'=>1));
272
273        // Copy all the draft annotations and comments to non-drafts.
274        $records = $DB->get_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'draft'=>0));
275        foreach ($records as $record) {
276            unset($record->id);
277            $record->draft = 0;
278            $DB->insert_record('assignfeedback_editpdf_annot', $record);
279        }
280        $records = $DB->get_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$gradeid, 'draft'=>0));
281        foreach ($records as $record) {
282            unset($record->id);
283            $record->draft = 0;
284            $DB->insert_record('assignfeedback_editpdf_annot', $record);
285        }
286
287        return true;
288    }
289
290    /**
291     * Add a annotation to a page.
292     * @param annotation $annotation
293     * @return bool
294     */
295    public static function add_annotation(annotation $annotation) {
296        global $DB;
297
298        $annotation->id = null;
299        return $DB->insert_record('assignfeedback_editpdf_annot', $annotation);
300    }
301
302    /**
303     * Remove a annotation from a page.
304     * @param int $annotationid
305     * @return bool
306     */
307    public static function remove_annotation($annotationid) {
308        global $DB;
309
310        return $DB->delete_records('assignfeedback_editpdf_annot', array('id'=>$annotationid));
311    }
312
313    /**
314     * Copy annotations, comments, pages, and other required content from the source user to the current group member
315     * being procssed when using applytoall.
316     *
317     * @param int|\assign $assignment
318     * @param stdClass $grade
319     * @param int $sourceuserid
320     * @return bool
321     */
322    public static function copy_drafts_from_to($assignment, $grade, $sourceuserid) {
323        global $DB;
324
325        // Delete any existing annotations and comments from current user.
326        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid' => $grade->id));
327        $DB->delete_records('assignfeedback_editpdf_cmnt', array('gradeid' => $grade->id));
328        // Get gradeid, annotations and comments from sourceuserid.
329        $sourceusergrade = $assignment->get_user_grade($sourceuserid, true, $grade->attemptnumber);
330        $annotations = $DB->get_records('assignfeedback_editpdf_annot', array('gradeid' => $sourceusergrade->id, 'draft' => 1));
331        $comments = $DB->get_records('assignfeedback_editpdf_cmnt', array('gradeid' => $sourceusergrade->id, 'draft' => 1));
332        $contextid = $assignment->get_context()->id;
333        $sourceitemid = $sourceusergrade->id;
334
335        // Add annotations and comments to current user to generate feedback file.
336        foreach ($annotations as $annotation) {
337            $annotation->gradeid = $grade->id;
338            $DB->insert_record('assignfeedback_editpdf_annot', $annotation);
339        }
340        foreach ($comments as $comment) {
341            $comment->gradeid = $grade->id;
342            $DB->insert_record('assignfeedback_editpdf_cmnt', $comment);
343        }
344
345        $fs = get_file_storage();
346
347        // Copy the stamp files.
348        self::replace_files_from_to($fs, $contextid, $sourceitemid, $grade->id, document_services::STAMPS_FILEAREA, true);
349
350        // Copy the PAGE_IMAGE_FILEAREA files.
351        self::replace_files_from_to($fs, $contextid, $sourceitemid, $grade->id, document_services::PAGE_IMAGE_FILEAREA);
352
353        return true;
354    }
355
356    /**
357     * Replace the area files in the specified area with those in the source item id.
358     *
359     * @param \file_storage $fs The file storage class
360     * @param int $contextid The ID of the context for the assignment.
361     * @param int $sourceitemid The itemid to copy from - typically the source grade id.
362     * @param int $itemid The itemid to copy to - typically the target grade id.
363     * @param string $area The file storage area.
364     * @param bool $includesubdirs Whether to copy the content of sub-directories too.
365     */
366    public static function replace_files_from_to($fs, $contextid, $sourceitemid, $itemid, $area, $includesubdirs = false) {
367        $component = 'assignfeedback_editpdf';
368        // Remove the existing files within this area.
369        $fs->delete_area_files($contextid, $component, $area, $itemid);
370
371        // Copy the files from the source area.
372        if ($files = $fs->get_area_files($contextid, $component, $area, $sourceitemid,
373                                         "filename", $includesubdirs)) {
374            foreach ($files as $file) {
375                $newrecord = new \stdClass();
376                $newrecord->contextid = $contextid;
377                $newrecord->itemid = $itemid;
378                $fs->create_file_from_storedfile($newrecord, $file);
379            }
380        }
381    }
382
383    /**
384     * Delete the draft annotations and comments.
385     *
386     * This is intended to be used when the version of the PDF has changed and the annotations
387     * might not be relevant any more, therefore we should delete them.
388     *
389     * @param int $gradeid The grade ID.
390     * @return bool
391     */
392    public static function delete_draft_content($gradeid) {
393        global $DB;
394        $conditions = array('gradeid' => $gradeid, 'draft' => 1);
395        $result = $DB->delete_records('assignfeedback_editpdf_annot', $conditions);
396        $result = $result && $DB->delete_records('assignfeedback_editpdf_cmnt', $conditions);
397        return $result;
398    }
399
400    /**
401     * Set page rotation value.
402     * @param int $gradeid grade id.
403     * @param int $pageno page number.
404     * @param bool $isrotated whether the page is rotated or not.
405     * @param string $pathnamehash path name hash
406     * @param int $degree rotation degree.
407     * @throws \dml_exception
408     */
409    public static function set_page_rotation($gradeid, $pageno, $isrotated, $pathnamehash, $degree = 0) {
410        global $DB;
411        $oldrecord = self::get_page_rotation($gradeid, $pageno);
412        if ($oldrecord == null) {
413            $record = new \stdClass();
414            $record->gradeid = $gradeid;
415            $record->pageno = $pageno;
416            $record->isrotated = $isrotated;
417            $record->pathnamehash = $pathnamehash;
418            $record->degree = $degree;
419            $DB->insert_record('assignfeedback_editpdf_rot', $record, false);
420        } else {
421            $oldrecord->isrotated = $isrotated;
422            $oldrecord->pathnamehash = $pathnamehash;
423            $oldrecord->degree = $degree;
424            $DB->update_record('assignfeedback_editpdf_rot', $oldrecord, false);
425        }
426    }
427
428    /**
429     * Get Page Rotation Value.
430     * @param int $gradeid grade id.
431     * @param int $pageno page number.
432     * @return mixed
433     * @throws \dml_exception
434     */
435    public static function get_page_rotation($gradeid, $pageno) {
436        global $DB;
437        $result = $DB->get_record('assignfeedback_editpdf_rot', array('gradeid' => $gradeid, 'pageno' => $pageno));
438        return $result;
439    }
440
441}
442