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
 * Provides the {@link mod_workshop_portfolio_caller} class.
19
 *
20
 * @package   mod_workshop
21
 * @category  portfolio
22
 * @copyright Loc Nguyen <ndloc1905@gmail.com>
23
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
require_once($CFG->libdir . '/portfolio/caller.php');
28
 
29
/**
30
 * Workshop portfolio caller class to integrate with portfolio API.
31
 *
32
 * @package   mod_workshop
33
 * @copyright Loc Nguyen <ndloc1905@gmail.com>
34
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
36
class mod_workshop_portfolio_caller extends portfolio_module_caller_base {
37
 
38
    /** @var workshop The workshop instance where the export is happening. */
39
    protected $workshop;
40
 
41
    /** @var int ID if the exported submission, set via the constructor. */
42
    protected $submissionid;
43
 
44
    /** @var object The submission being exported. */
45
    protected $submission;
46
 
47
    /** @var array of objects List of assessments of the exported submission. */
48
    protected $assessments = [];
49
 
50
    /**
51
     * Explicit constructor to set the properties declared by the parent class.
52
     *
53
     * Firstly we call the parent's constructor to set the $this->id property
54
     * from the passed argument. Then we populate the $this->cm so that the
55
     * default parent class methods work well.
56
     *
57
     * @param array $callbackargs
58
     */
59
    public function __construct($callbackargs) {
60
 
61
        // Let the parent class set the $this->id property.
62
        parent::__construct($callbackargs);
63
        // Populate the $this->cm property.
64
        $this->cm = get_coursemodule_from_id('workshop', $this->id, 0, false, MUST_EXIST);
65
    }
66
 
67
    /**
68
     * Return array of expected callback arguments and whether they are required or not.
69
     *
70
     * The 'id' argument is supposed to be our course module id (cmid) - see
71
     * the parent class' properties.
72
     *
73
     * @return array of (string)callbackname => (bool)required
74
     */
75
    public static function expected_callbackargs() {
76
        return [
77
            'id' => true,
78
            'submissionid' => true,
79
        ];
80
    }
81
 
82
    /**
83
     * Load data required for the export.
84
     */
85
    public function load_data() {
86
        global $DB, $USER;
87
 
88
        // Note that require_login() is normally called later as a part of
89
        // portfolio_export_pagesetup() in the portfolio/add.php file. But we
90
        // load various data depending of capabilities so it makes sense to
91
        // call it explicitly here, too.
92
        require_login($this->get('course'), false, $this->cm, false, true);
93
 
94
        if (isguestuser()) {
95
            throw new portfolio_caller_exception('guestsarenotallowed', 'core_error');
96
        }
97
 
98
        $workshoprecord = $DB->get_record('workshop', ['id' => $this->cm->instance], '*', MUST_EXIST);
99
        $this->workshop = new workshop($workshoprecord, $this->cm, $this->get('course'));
100
 
101
        $this->submission = $this->workshop->get_submission_by_id($this->submissionid);
102
 
103
        // Is the user exporting her/his own submission?
104
        $ownsubmission = $this->submission->authorid == $USER->id;
105
 
106
        // Does the user have permission to see all submissions (aka is it a teacher)?
107
        $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $this->workshop->context);
108
        $canviewallsubmissions = $canviewallsubmissions && $this->workshop->check_group_membership($this->submission->authorid);
109
 
110
        // Is the user exporting a submission that she/he has peer-assessed?
111
        $userassessment = $this->workshop->get_assessment_of_submission_by_user($this->submission->id, $USER->id);
112
        if ($userassessment) {
113
            $this->assessments[$userassessment->id] = $userassessment;
114
            $isreviewer = true;
115
        }
116
 
117
        if (!$ownsubmission and !$canviewallsubmissions and !$isreviewer) {
118
            throw new portfolio_caller_exception('nopermissions', 'core_error');
119
        }
120
 
121
        // Does the user have permission to see all assessments (aka is it a teacher)?
122
        $canviewallassessments = has_capability('mod/workshop:viewallassessments', $this->workshop->context);
123
 
124
        // Load other assessments eventually if the user can see them.
125
        if ($canviewallassessments or ($ownsubmission and $this->workshop->assessments_available())) {
126
            foreach ($this->workshop->get_assessments_of_submission($this->submission->id) as $assessment) {
127
                if ($assessment->reviewerid == $USER->id) {
128
                    // User's own assessment is already loaded.
129
                    continue;
130
                }
131
                if (is_null($assessment->grade) and !$canviewallassessments) {
132
                    // Students do not see peer-assessment that are not graded.
133
                    continue;
134
                }
135
                $this->assessments[$assessment->id] = $assessment;
136
            }
137
        }
138
 
139
        // Prepare embedded and attached files for the export.
140
        $this->multifiles = [];
141
 
142
        $this->add_area_files('submission_content', $this->submission->id);
143
        $this->add_area_files('submission_attachment', $this->submission->id);
144
 
145
        foreach ($this->assessments as $assessment) {
146
            $this->add_area_files('overallfeedback_content', $assessment->id);
147
            $this->add_area_files('overallfeedback_attachment', $assessment->id);
148
        }
149
 
150
        $this->add_area_files('instructauthors', 0);
151
 
152
        // If there are no files to be exported, we can offer plain HTML file export.
153
        if (empty($this->multifiles)) {
154
            $this->add_format(PORTFOLIO_FORMAT_PLAINHTML);
155
        }
156
    }
157
 
158
    /**
159
     * Prepare the package ready to be passed off to the portfolio plugin.
160
     */
161
    public function prepare_package() {
162
 
163
        $canviewauthornames = has_capability('mod/workshop:viewauthornames', $this->workshop->context, $this->get('user'));
164
 
165
        // Prepare the submission record for rendering.
166
        $workshopsubmission = $this->workshop->prepare_submission($this->submission, $canviewauthornames);
167
 
168
        // Set up the LEAP2A writer if we need it.
169
        $writingleap = false;
170
 
171
        if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
172
            $leapwriter = $this->exporter->get('format')->leap2a_writer();
173
            $writingleap = true;
174
        }
175
 
176
        // If writing to HTML file, accumulate the exported hypertext here.
177
        $html = '';
178
 
179
        // If writing LEAP2A, keep track of all entry ids so we can add a selection element.
180
        $leapids = [];
181
 
182
        $html .= $this->export_header($workshopsubmission);
183
        $content = $this->export_content($workshopsubmission);
184
        // Get rid of the JS relics left by moodleforms.
185
        $content = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content);
186
        $html .= $content;
187
 
188
        if ($writingleap) {
189
            $leapids[] = $this->export_content_leap2a($leapwriter, $workshopsubmission, $content);
190
        }
191
 
192
        // Export the files.
193
        foreach ($this->multifiles as $file) {
194
            $this->exporter->copy_existing_file($file);
195
        }
196
 
197
        if ($writingleap) {
198
            // Add an extra LEAP2A selection entry. In Mahara, this maps to a journal.
199
            $selection = new portfolio_format_leap2a_entry('workshop'.$this->workshop->id,
200
                get_string('pluginname', 'mod_workshop').': '.s($this->workshop->name), 'selection');
201
            $leapwriter->add_entry($selection);
202
            $leapwriter->make_selection($selection, $leapids, 'Grouping');
203
            $leapxml = $leapwriter->to_xml();
204
            $name = $this->exporter->get('format')->manifest_name();
205
            $this->exporter->write_new_file($leapxml, $name, true);
206
 
207
        } else {
208
            $this->exporter->write_new_file($html, 'submission.html', true);
209
        }
210
    }
211
 
212
    /**
213
     * Helper method to add all files from the given location to $this->multifiles
214
     *
215
     * @param string $filearea
216
     * @param int $itemid
217
     */
218
    protected function add_area_files($filearea, $itemid) {
219
 
220
        $fs = get_file_storage();
221
        $areafiles = $fs->get_area_files($this->workshop->context->id, 'mod_workshop', $filearea, $itemid, null, false);
222
        if ($areafiles) {
223
            $this->multifiles = array_merge($this->multifiles, array_values($areafiles));
224
        }
225
    }
226
 
227
    /**
228
     * Render the header of the exported content.
229
     *
230
     * This is mainly used for the HTML output format. In case of LEAP2A
231
     * export, this is not used as the information is stored in metadata and
232
     * displayed as a part of the journal and entry title in Mahara.
233
     *
234
     * @param workshop_submission $workshopsubmission
235
     * @return string HTML
236
     */
237
    protected function export_header(workshop_submission $workshopsubmission) {
238
 
239
        $output = '';
240
        $output .= html_writer::tag('h2', get_string('pluginname', 'mod_workshop').': '.s($this->workshop->name));
241
        $output .= html_writer::tag('h3', s($workshopsubmission->title));
242
 
243
        $created = get_string('userdatecreated', 'workshop', userdate($workshopsubmission->timecreated));
244
        $created = html_writer::tag('span', $created);
245
 
246
        if ($workshopsubmission->timemodified > $workshopsubmission->timecreated) {
247
            $modified = get_string('userdatemodified', 'workshop', userdate($workshopsubmission->timemodified));
248
            $modified = ' | ' . html_writer::tag('span', $modified);
249
        } else {
250
            $modified = '';
251
        }
252
 
253
        $output .= html_writer::div($created.$modified);
254
        $output .= html_writer::empty_tag('br');
255
 
256
        return $output;
257
    }
258
 
259
    /**
260
     * Render the content of the submission.
261
     *
262
     * @param workshop_submission $workshopsubmission
263
     * @return string
264
     */
265
    protected function export_content(workshop_submission $workshopsubmission) {
266
 
267
        $output = '';
268
 
269
        if (!$workshopsubmission->is_anonymous()) {
270
            $author = username_load_fields_from_object((object)[], $workshopsubmission, 'author');
271
            $output .= html_writer::div(get_string('byfullnamewithoutlink', 'mod_workshop', fullname($author)));
272
        }
273
 
274
        $content = $this->format_exported_text($workshopsubmission->content, $workshopsubmission->contentformat);
275
        $content = portfolio_rewrite_pluginfile_urls($content, $this->workshop->context->id, 'mod_workshop',
276
            'submission_content', $workshopsubmission->id, $this->exporter->get('format'));
277
        $output .= html_writer::div($content);
278
 
279
        $output .= $this->export_files_list('submission_attachment');
280
 
281
        $strategy = $this->workshop->grading_strategy_instance();
282
 
283
        $canviewauthornames = has_capability('mod/workshop:viewauthornames', $this->workshop->context, $this->get('user'));
284
        $canviewreviewernames = has_capability('mod/workshop:viewreviewernames', $this->workshop->context, $this->get('user'));
285
 
286
        foreach ($this->assessments as $assessment) {
287
            $mform = $strategy->get_assessment_form(null, 'assessment', $assessment, false);
288
            $options = [
289
                'showreviewer' => $canviewreviewernames,
290
                'showauthor' => $canviewauthornames,
291
                'showform' => true,
292
                'showweight' => true,
293
            ];
294
            if ($assessment->reviewerid == $this->get('user')->id) {
295
                $options['showreviewer'] = true;
296
            }
297
 
298
            $workshopassessment = $this->workshop->prepare_assessment($assessment, $mform, $options);
299
 
300
            if ($assessment->reviewerid == $this->get('user')->id) {
301
                $workshopassessment->title = get_string('assessmentbyyourself', 'mod_workshop');
302
            } else {
303
                $workshopassessment->title = get_string('assessment', 'mod_workshop');
304
            }
305
 
306
            $output .= html_writer::empty_tag('hr');
307
            $output .= $this->export_assessment($workshopassessment);
308
        }
309
 
310
        if (trim($this->workshop->instructauthors)) {
311
            $output .= html_writer::tag('h3', get_string('instructauthors', 'mod_workshop'));
312
            $content = $this->format_exported_text($this->workshop->instructauthors, $this->workshop->instructauthorsformat);
313
            $content = portfolio_rewrite_pluginfile_urls($content, $this->workshop->context->id, 'mod_workshop',
314
                'instructauthors', 0, $this->exporter->get('format'));
315
            $output .= $content;
316
        }
317
 
318
        return html_writer::div($output);
319
    }
320
 
321
    /**
322
     * Render the content of an assessment.
323
     *
324
     * @param workshop_assessment $assessment
325
     * @return string HTML
326
     */
327
    protected function export_assessment(workshop_assessment $assessment) {
328
 
329
        $output = '';
330
 
331
        if (empty($assessment->title)) {
332
            $title = get_string('assessment', 'workshop');
333
        } else {
334
            $title = s($assessment->title);
335
        }
336
 
337
        $output .= html_writer::tag('h3', $title);
338
 
339
        if ($assessment->reviewer) {
340
            $output .= html_writer::div(get_string('byfullnamewithoutlink', 'mod_workshop', fullname($assessment->reviewer)));
341
            $output .= html_writer::empty_tag('br');
342
        }
343
 
344
        if ($this->workshop->overallfeedbackmode) {
345
            if ($assessment->feedbackauthorattachment || trim($assessment->feedbackauthor ?? '') !== '') {
346
                $output .= html_writer::tag('h3', get_string('overallfeedback', 'mod_workshop'));
347
                $content = $this->format_exported_text($assessment->feedbackauthor, $assessment->feedbackauthorformat);
348
                $content = portfolio_rewrite_pluginfile_urls($content, $this->workshop->context->id, 'mod_workshop',
349
                    'overallfeedback_content', $assessment->id , $this->exporter->get('format'));
350
                $output .= $content;
351
 
352
                $output .= $this->export_files_list('overallfeedback_attachment');
353
            }
354
        }
355
 
356
        if ($assessment->form) {
357
            $output .= $assessment->form->render();
358
        }
359
 
360
        return $output;
361
    }
362
 
363
    /**
364
     * Export the files in the given file area in a list.
365
     *
366
     * @param string $filearea
367
     * @return string HTML
368
     */
369
    protected function export_files_list($filearea) {
370
 
371
        $output = '';
372
        $files = [];
373
 
374
        foreach ($this->multifiles as $file) {
375
            if ($file->is_directory()) {
376
                continue;
377
            }
378
            if ($file->get_filearea() !== $filearea) {
379
                continue;
380
            }
381
            if ($file->is_valid_image()) {
382
                // Not optimal but looks better than original images.
383
                $files[] = html_writer::tag('li', $this->exporter->get('format')->file_output($file,
384
                    ['attributes' => ['style' => 'max-height:24px; max-width:24px']]).' '.s($file->get_filename()));
385
            } else {
386
                $files[] = html_writer::tag('li', $this->exporter->get('format')->file_output($file));
387
            }
388
        }
389
 
390
        if ($files) {
391
            $output .= html_writer::tag('ul', implode('', $files));
392
        }
393
 
394
        return $output;
395
    }
396
 
397
    /**
398
     * Helper function to call {@link format_text()} on exported text.
399
     *
400
     * We need to call {@link format_text()} to convert the text into HTML, but
401
     * we have to keep the original @@PLUGINFILE@@ placeholder there without a
402
     * warning so that {@link portfolio_rewrite_pluginfile_urls()} can do its work.
403
     *
404
     * @param string $text
405
     * @param int $format
406
     * @return string HTML
407
     */
408
    protected function format_exported_text($text, $format) {
409
 
410
        $text = str_replace('@@PLUGINFILE@@', '@@ORIGINALPLUGINFILE@@', $text);
411
        $html = format_text($text, $format, portfolio_format_text_options());
412
        $html = str_replace('@@ORIGINALPLUGINFILE@@', '@@PLUGINFILE@@', $html);
413
 
414
        return $html;
415
    }
416
 
417
    /**
418
     * Add a LEAP2A entry element that corresponds to a submission including attachments.
419
     *
420
     * @param portfolio_format_leap2a_writer $leapwriter Writer object to add entries to.
421
     * @param workshop_submission $workshopsubmission
422
     * @param string $html The exported HTML content of the submission
423
     * @return int id of new entry
424
     */
425
    protected function export_content_leap2a(portfolio_format_leap2a_writer $leapwriter,
426
            workshop_submission $workshopsubmission, $html) {
427
 
428
        $entry = new portfolio_format_leap2a_entry('workshopsubmission'.$workshopsubmission->id,  s($workshopsubmission->title),
429
            'resource', $html);
430
        $entry->published = $workshopsubmission->timecreated;
431
        $entry->updated = $workshopsubmission->timemodified;
432
        $entry->author = (object)[
433
            'id' => $workshopsubmission->authorid,
434
            'email' => $workshopsubmission->authoremail
435
        ];
436
        username_load_fields_from_object($entry->author, $workshopsubmission);
437
 
438
        $leapwriter->link_files($entry, $this->multifiles);
439
        $entry->add_category('web', 'resource_type');
440
        $leapwriter->add_entry($entry);
441
 
442
        return $entry->id;
443
    }
444
 
445
    /**
446
     * Return URL for redirecting the user back to where the export started.
447
     *
448
     * @return string
449
     */
450
    public function get_return_url() {
451
 
452
        $returnurl = new moodle_url('/mod/workshop/submission.php', ['cmid' => $this->cm->id, 'id' => $this->submissionid]);
453
        return $returnurl->out();
454
    }
455
 
456
    /**
457
     * Get navigation that logically follows from the place the user was before.
458
     *
459
     * @return array
460
     */
461
    public function get_navigation() {
462
 
463
        $navlinks = [
464
            ['name' => s($this->submission->title)],
465
        ];
466
 
467
        return [$navlinks, $this->cm];
468
    }
469
 
470
    /**
471
     * How long might we expect this export to take.
472
     *
473
     * @return string such as PORTFOLIO_TIME_LOW
474
     */
475
    public function expected_time() {
476
        return $this->expected_time_file();
477
    }
478
 
479
    /**
480
     * Make sure that the current user is allowed to do the export.
481
     *
482
     * @return boolean
483
     */
484
    public function check_permissions() {
485
        return has_capability('mod/workshop:exportsubmissions', context_module::instance($this->cm->id));
486
    }
487
 
488
    /**
489
     * Return the SHA1 hash of the exported content.
490
     *
491
     * @return string
492
     */
493
    public function get_sha1() {
494
 
495
        $identifier = 'submission:'.$this->submission->id.'@'.$this->submission->timemodified;
496
 
497
        if ($this->assessments) {
498
            $ids = array_keys($this->assessments);
499
            sort($ids);
500
            $identifier .= '/assessments:'.implode(',', $ids);
501
        }
502
 
503
        if ($this->multifiles) {
504
            $identifier .= '/files:'.$this->get_sha1_file();
505
        }
506
 
507
        return sha1($identifier);
508
    }
509
 
510
    /**
511
     * Return a nice name to be displayed about this export location.
512
     *
513
     * @return string
514
     */
515
    public static function display_name() {
516
        return get_string('pluginname', 'mod_workshop');
517
    }
518
 
519
    /**
520
     * What export formats the workshop generally supports.
521
     *
522
     * If there are no files embedded/attached, the plain HTML format is added
523
     * in {@link self::load_data()}.
524
     *
525
     * @return array
526
     */
527
    public static function base_supported_formats() {
528
        return [
529
            PORTFOLIO_FORMAT_RICHHTML,
530
            PORTFOLIO_FORMAT_LEAP2A,
531
        ];
532
    }
533
}