Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
use \assignfeedback_editpdf\document_services;
29
use \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
 */
38
class assign_feedback_editpdf extends assign_feedback_plugin {
39
 
40
    /** @var boolean|null $enabledcache Cached lookup of the is_enabled function */
41
    private $enabledcache = null;
42
 
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;
61
        if ($grade && isset($grade->attemptnumber)) {
62
            $attempt = $grade->attemptnumber;
63
        } else {
64
            $grade = $this->assignment->get_user_grade($userid, true);
65
        }
66
 
67
        $feedbackfile = document_services::get_feedback_document(
68
            $this->assignment->get_instance()->id,
69
            $userid,
70
            $attempt
71
        );
72
 
73
        $stampfiles = array();
74
        $fs = get_file_storage();
75
        $syscontext = context_system::instance();
76
        $asscontext = $this->assignment->get_context();
77
 
78
        // Three file areas are used for stamps.
79
        // Current stamps are those configured as a site administration setting to be available for new uses.
80
        // When a stamp is removed from this filearea it is no longer available for new grade items.
81
        $currentstamps = $fs->get_area_files($syscontext->id, 'assignfeedback_editpdf', 'stamps', 0, 'filename', false);
82
 
83
        // Grade stamps are those which have been assigned for a specific grade item.
84
        // The stamps associated with a grade item are always used for that grade item, even if the stamp is removed
85
        // from the list of current stamps.
86
        $gradestamps = $fs->get_area_files($asscontext->id, 'assignfeedback_editpdf', 'stamps', $grade->id, 'filename', false);
87
 
88
        // The system stamps are perpetual and always exist.
89
        // They allow Moodle to serve a common URL for all users for any possible combination of stamps.
90
        // Files in the perpetual stamp filearea are within the system context, in itemid 0, and use the original stamps
91
        // contenthash as a folder name. This ensures that the combination of stamp filename, and stamp file content is
92
        // unique.
93
        $systemstamps = $fs->get_area_files($syscontext->id, 'assignfeedback_editpdf', 'systemstamps', 0, 'filename', false);
94
 
95
        // First check that all current stamps are listed in the grade stamps.
96
        foreach ($currentstamps as $stamp) {
97
            // Ensure that the current stamp is in the list of perpetual stamps.
98
            $systempathnamehash = $this->get_system_stamp_path($stamp);
99
            if (!array_key_exists($systempathnamehash, $systemstamps)) {
100
                $filerecord = (object) [
101
                    'filearea' => 'systemstamps',
102
                    'filepath' => '/' . $stamp->get_contenthash() . '/',
103
                ];
104
                $newstamp = $fs->create_file_from_storedfile($filerecord, $stamp);
105
                $systemstamps[$newstamp->get_pathnamehash()] = $newstamp;
106
            }
107
 
108
            // Ensure that the current stamp is in the list of stamps for the current grade item.
109
            $gradeitempathhash = $this->get_assignment_stamp_path($stamp, $grade->id);
110
            if (!array_key_exists($gradeitempathhash, $gradestamps)) {
111
                $filerecord = (object) [
112
                    'contextid' => $asscontext->id,
113
                    'filearea' => 'stamps',
114
                    'itemid' => $grade->id,
115
                ];
116
                $newstamp = $fs->create_file_from_storedfile($filerecord, $stamp);
117
                $gradestamps[$newstamp->get_pathnamehash()] = $newstamp;
118
            }
119
        }
120
 
121
        foreach ($gradestamps as $stamp) {
122
            // All gradestamps should be available in the systemstamps filearea, but some legacy stamps may not be.
123
            // These need to be copied over.
124
            // Note: This should ideally be performed as an upgrade step, but there may be other cases that these do not
125
            // exist, for example restored backups.
126
            // In any case this is a cheap operation as it is solely performing an array lookup.
127
            $systempathnamehash = $this->get_system_stamp_path($stamp);
128
            if (!array_key_exists($systempathnamehash, $systemstamps)) {
129
                $filerecord = (object) [
130
                    'contextid' => $syscontext->id,
131
                    'itemid' => 0,
132
                    'filearea' => 'systemstamps',
133
                    'filepath' => '/' . $stamp->get_contenthash() . '/',
134
                ];
135
                $systemstamp = $fs->create_file_from_storedfile($filerecord, $stamp);
136
                $systemstamps[$systemstamp->get_pathnamehash()] = $systemstamp;
137
            }
138
 
139
            // Always serve the perpetual system stamp.
140
            // This ensures that the stamp is highly cached and reduces the hit on the application server.
141
            $gradestamp = $systemstamps[$systempathnamehash];
142
            $url = moodle_url::make_pluginfile_url(
143
                $gradestamp->get_contextid(),
144
                $gradestamp->get_component(),
145
                $gradestamp->get_filearea(),
146
                null,
147
                $gradestamp->get_filepath(),
148
                $gradestamp->get_filename(),
149
                false
150
            );
151
            array_push($stampfiles, $url->out());
152
        }
153
 
154
        $url = false;
155
        $filename = '';
156
        if ($feedbackfile) {
157
            $url = moodle_url::make_pluginfile_url(
158
                $this->assignment->get_context()->id,
159
                'assignfeedback_editpdf',
160
                document_services::FINAL_PDF_FILEAREA,
161
                $grade->id,
162
                '/',
163
                $feedbackfile->get_filename(),
164
                false
165
            );
166
           $filename = $feedbackfile->get_filename();
167
        }
168
 
169
        $widget = new assignfeedback_editpdf_widget(
170
            $this->assignment->get_instance()->id,
171
            $userid,
172
            $attempt,
173
            $url,
174
            $filename,
175
            $stampfiles,
176
            $readonly
177
        );
178
        return $widget;
179
    }
180
 
181
    /**
182
     * Get the pathnamehash for the specified stamp if in the system stamps.
183
     *
184
     * @param   stored_file $file
185
     * @return  string
186
     */
187
    protected function get_system_stamp_path(stored_file $stamp): string {
188
        $systemcontext = context_system::instance();
189
 
190
        return file_storage::get_pathname_hash(
191
            $systemcontext->id,
192
            'assignfeedback_editpdf',
193
            'systemstamps',
194
            0,
195
            '/' . $stamp->get_contenthash() . '/',
196
            $stamp->get_filename()
197
        );
198
    }
199
 
200
    /**
201
     * Get the pathnamehash for the specified stamp if in the current assignment stamps.
202
     *
203
     * @param   stored_file $file
204
     * @param   int $gradeid
205
     * @return  string
206
     */
207
    protected function get_assignment_stamp_path(stored_file $stamp, int $gradeid): string {
208
        return file_storage::get_pathname_hash(
209
            $this->assignment->get_context()->id,
210
            'assignfeedback_editpdf',
211
            'stamps',
212
            $gradeid,
213
            $stamp->get_filepath(),
214
            $stamp->get_filename()
215
        );
216
    }
217
 
218
    /**
219
     * Get form elements for grading form
220
     *
221
     * @param stdClass $grade
222
     * @param MoodleQuickForm $mform
223
     * @param stdClass $data
224
     * @param int $userid
225
     * @return bool true if elements were added to the form
226
     */
227
    public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
228
        global $PAGE;
229
 
230
        $attempt = -1;
231
        if ($grade) {
232
            $attempt = $grade->attemptnumber;
233
        }
234
 
235
        $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
236
 
237
        // Links to download the generated pdf...
238
        if ($attempt > -1 && page_editor::has_annotations_or_comments($grade->id, false)) {
239
            $html = $this->assignment->render_area_files('assignfeedback_editpdf',
240
                                                         document_services::FINAL_PDF_FILEAREA,
241
                                                         $grade->id);
242
            $mform->addElement('static', 'editpdf_files', get_string('downloadfeedback', 'assignfeedback_editpdf'), $html);
243
        }
244
 
245
        $widget = $this->get_widget($userid, $grade, false);
246
 
247
        $html = $renderer->render($widget);
248
        $mform->addElement('static', 'editpdf', get_string('editpdf', 'assignfeedback_editpdf'), $html);
249
        $mform->addHelpButton('editpdf', 'editpdf', 'assignfeedback_editpdf');
250
        $mform->addElement('hidden', 'editpdf_source_userid', $userid);
251
        $mform->setType('editpdf_source_userid', PARAM_INT);
252
        $mform->setConstant('editpdf_source_userid', $userid);
253
    }
254
 
255
    /**
256
     * Check to see if the grade feedback for the pdf has been modified.
257
     *
258
     * @param stdClass $grade Grade object.
259
     * @param stdClass $data Data from the form submission (not used).
260
     * @return boolean True if the pdf has been modified, else false.
261
     */
262
    public function is_feedback_modified(stdClass $grade, stdClass $data) {
263
        // We only need to know if the source user's PDF has changed. If so then all
264
        // following users will have the same status. If it's only an individual annotation
265
        // then only one user will come through this method.
266
        // Source user id is only added to the form if there was a pdf.
267
        if (!empty($data->editpdf_source_userid)) {
268
            $sourceuserid = $data->editpdf_source_userid;
269
            // Retrieve the grade information for the source user.
270
            $sourcegrade = $this->assignment->get_user_grade($sourceuserid, true, $grade->attemptnumber);
271
            $pagenumbercount = document_services::page_number_for_attempt($this->assignment, $sourceuserid, $sourcegrade->attemptnumber);
272
            for ($i = 0; $i < $pagenumbercount; $i++) {
273
                // Select all annotations.
274
                $draftannotations = page_editor::get_annotations($sourcegrade->id, $i, true);
275
                $nondraftannotations = page_editor::get_annotations($grade->id, $i, false);
276
                // Check to see if the count is the same.
277
                if (count($draftannotations) != count($nondraftannotations)) {
278
                    // The count is different so we have a modification.
279
                    return true;
280
                } else {
281
                    $matches = 0;
282
                    // Have a closer look and see if the draft files match all the non draft files.
283
                    foreach ($nondraftannotations as $ndannotation) {
284
                        foreach ($draftannotations as $dannotation) {
285
                            foreach ($ndannotation as $key => $value) {
286
                                // As the $draft was included in the class annotation,
287
                                // it is necessary to omit it in the condition below as well,
288
                                // otherwise, an error would be raised.
289
                                if ($key != 'id' && $key != 'draft' && $value != $dannotation->{$key}) {
290
                                    continue 2;
291
                                }
292
                            }
293
                            $matches++;
294
                        }
295
                    }
296
                    if ($matches !== count($nondraftannotations)) {
297
                        return true;
298
                    }
299
                }
300
                // Select all comments.
301
                $draftcomments = page_editor::get_comments($sourcegrade->id, $i, true);
302
                $nondraftcomments = page_editor::get_comments($grade->id, $i, false);
303
                if (count($draftcomments) != count($nondraftcomments)) {
304
                    return true;
305
                } else {
306
                    // Go for a closer inspection.
307
                    $matches = 0;
308
                    foreach ($nondraftcomments as $ndcomment) {
309
                        foreach ($draftcomments as $dcomment) {
310
                            foreach ($ndcomment as $key => $value) {
311
                                // As the $draft was included in the class comment,
312
                                // it is necessary to omit it in the condition below as well,
313
                                // otherwise, an error would be raised.
314
                                if ($key != 'id' && $key != 'draft' && $value != $dcomment->{$key}) {
315
                                    continue 2;
316
                                }
317
                            }
318
                            $matches++;
319
                        }
320
                    }
321
                    if ($matches !== count($nondraftcomments)) {
322
                        return true;
323
                    }
324
                }
325
            }
326
        }
327
        return false;
328
    }
329
 
330
    /**
331
     * Generate the pdf.
332
     *
333
     * @param stdClass $grade
334
     * @param stdClass $data
335
     * @return bool
336
     */
337
    public function save(stdClass $grade, stdClass $data) {
338
        // Source user id is only added to the form if there was a pdf.
339
        if (!empty($data->editpdf_source_userid)) {
340
            $sourceuserid = $data->editpdf_source_userid;
341
            // Copy drafts annotations and comments if current user is different to sourceuserid.
342
            if ($sourceuserid != $grade->userid) {
343
                page_editor::copy_drafts_from_to($this->assignment, $grade, $sourceuserid);
344
            }
345
        }
346
        if (page_editor::has_annotations_or_comments($grade->id, true)) {
347
            document_services::generate_feedback_document($this->assignment, $grade->userid, $grade->attemptnumber);
348
        }
349
 
350
        return true;
351
    }
352
 
353
    /**
354
     * Display the list of files in the feedback status table.
355
     *
356
     * @param stdClass $grade
357
     * @param bool $showviewlink (Always set to false).
358
     * @return string
359
     */
360
    public function view_summary(stdClass $grade, & $showviewlink) {
361
        $showviewlink = false;
362
        return $this->view($grade);
363
    }
364
 
365
    /**
366
     * Display the list of files in the feedback status table.
367
     *
368
     * @param stdClass $grade
369
     * @return string
370
     */
371
    public function view(stdClass $grade) {
372
        global $PAGE;
373
        $html = '';
374
        // Show a link to download the pdf.
375
        if (page_editor::has_annotations_or_comments($grade->id, false)) {
376
            $html = $this->assignment->render_area_files('assignfeedback_editpdf',
377
                                                         document_services::FINAL_PDF_FILEAREA,
378
                                                         $grade->id);
379
 
380
            // Also show the link to the read-only interface.
381
            $renderer = $PAGE->get_renderer('assignfeedback_editpdf');
382
            $widget = $this->get_widget($grade->userid, $grade, true);
383
 
384
            $html .= $renderer->render($widget);
385
        }
386
        return $html;
387
    }
388
 
389
    /**
390
     * Return true if there are no released comments/annotations.
391
     *
392
     * @param stdClass $grade
393
     */
394
    public function is_empty(stdClass $grade) {
395
        global $DB;
396
 
397
        $comments = $DB->count_records('assignfeedback_editpdf_cmnt', array('gradeid'=>$grade->id, 'draft'=>0));
398
        $annotations = $DB->count_records('assignfeedback_editpdf_annot', array('gradeid'=>$grade->id, 'draft'=>0));
399
        return $comments == 0 && $annotations == 0;
400
    }
401
 
402
    /**
403
     * The assignment has been deleted - remove the plugin specific data
404
     *
405
     * @return bool
406
     */
407
    public function delete_instance() {
408
        global $DB;
409
        $grades = $DB->get_records('assign_grades', array('assignment'=>$this->assignment->get_instance()->id), '', 'id');
410
        if ($grades) {
411
            list($gradeids, $params) = $DB->get_in_or_equal(array_keys($grades), SQL_PARAMS_NAMED);
412
            $DB->delete_records_select('assignfeedback_editpdf_annot', 'gradeid ' . $gradeids, $params);
413
            $DB->delete_records_select('assignfeedback_editpdf_cmnt', 'gradeid ' . $gradeids, $params);
414
            $DB->delete_records_select('assignfeedback_editpdf_rot', 'gradeid ' . $gradeids, $params);
415
        }
416
        return true;
417
    }
418
 
419
    /**
420
     * Determine if ghostscript is available and working.
421
     *
422
     * @return bool
423
     */
424
    public function is_available() {
425
        if ($this->enabledcache === null) {
426
            $testpath = assignfeedback_editpdf\pdf::test_gs_path(false);
427
            $this->enabledcache = ($testpath->status == assignfeedback_editpdf\pdf::GSPATH_OK);
428
        }
429
        return $this->enabledcache;
430
    }
431
    /**
432
     * Prevent enabling this plugin if ghostscript is not available.
433
     *
434
     * @return bool false
435
     */
436
    public function is_configurable() {
437
        return $this->is_available();
438
    }
439
 
440
    /**
441
     * Get file areas returns a list of areas this plugin stores files.
442
     *
443
     * @return array - An array of fileareas (keys) and descriptions (values)
444
     */
445
    public function get_file_areas() {
446
        return [
447
            document_services::FINAL_PDF_FILEAREA => $this->get_name(),
448
            document_services::COMBINED_PDF_FILEAREA => $this->get_name(),
449
            document_services::PARTIAL_PDF_FILEAREA => $this->get_name(),
450
            document_services::IMPORT_HTML_FILEAREA => $this->get_name(),
451
            document_services::PAGE_IMAGE_FILEAREA => $this->get_name(),
452
            document_services::PAGE_IMAGE_READONLY_FILEAREA => $this->get_name(),
453
            document_services::STAMPS_FILEAREA => $this->get_name(),
454
            document_services::TMP_JPG_TO_PDF_FILEAREA => $this->get_name(),
455
            document_services::TMP_ROTATED_JPG_FILEAREA => $this->get_name()
456
        ];
457
    }
458
 
459
    /**
460
     * Get all file areas for user data related to this plugin.
461
     *
462
     * @return array - An array of user data fileareas (keys) and descriptions (values)
463
     */
464
    public function get_user_data_file_areas(): array {
465
        return [
466
            document_services::FINAL_PDF_FILEAREA => $this->get_name(),
467
        ];
468
    }
469
 
470
    /**
471
     * This plugin will inject content into the review panel with javascript.
472
     * @return bool true
473
     */
474
    public function supports_review_panel() {
475
        return true;
476
    }
477
 
478
    /**
479
     * Return the plugin configs for external functions.
480
     *
481
     * @return array the list of settings
482
     * @since Moodle 3.2
483
     */
484
    public function get_config_for_external() {
485
        return (array) $this->get_config();
486
    }
487
}