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 local_downloadcenter for 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
 * Download center plugin
19
 *
20
 * @package       local_downloadcenter
21
 * @author        Simeon Naydenov (moniNaydenov@gmail.com)
22
 * @copyright     2020 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
23
 * @license       http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
class local_downloadcenter_factory {
29
    /**
30
     * @var
31
     */
32
    private $course;
33
    /**
34
     * @var
35
     */
36
    private $user;
37
    /**
38
     * @var
39
     */
40
    private $sortedresources;
41
    /**
42
     * @var
43
     */
44
    private $filteredresources;
45
    private $_downloadoptions;
46
    /**
47
     * @var array
48
     */
49
    private $availableresources = [
50
        'resource',
51
        'folder',
52
        'publication',
53
        'page',
54
        'book',
55
        'lightboxgallery',
56
        'assign',
57
        'glossary',
58
        'etherpadlite',
59
    ];
60
    /**
61
     * @var array
62
     */
63
    private $jsnames = [];
64
    /**
65
     * @var
66
     */
67
    private $progress;
68
 
69
    /**
70
     * local_downloadcenter_factory constructor.
71
     * @param $course
72
     * @param $user
73
     */
74
    public function __construct($course, $user) {
75
        $this->course = $course;
76
        $this->user = $user;
77
        $this->_downloadoptions = [
78
            'filesrealnames' => false,
79
            'addnumbering' => false,
80
        ];
81
    }
82
 
83
    /**
84
     * @return array
85
     * @throws coding_exception
86
     * @throws dml_exception
87
     * @throws moodle_exception
88
     */
89
    public function get_resources_for_user() {
90
        global $DB, $CFG;
91
 
92
        // Only downloadable resources should be shown!
93
        if (!empty($this->sortedresources)) {
94
            return $this->sortedresources;
95
        }
96
 
97
        $modinfo = get_fast_modinfo($this->course);
98
        $usesections = course_format_uses_sections($this->course->format);
99
 
100
        $sorted = [];
101
        if ($usesections) {
102
            $sections = $DB->get_records('course_sections', array('course' => $this->course->id), 'section');
103
            // Thanks to https://github.com/marinaglancy for the fix!
104
            $max = course_get_format($this->course)->get_format_options()['numsections'] ?? count($sections);
105
            $unnamedsections = [];
106
            $namedsections = [];
107
            foreach ($sections as $section) {
108
                if (intval($section->section) > $max) {
109
                    break;
110
                }
111
                if (!isset($sorted[$section->section]) && $section->visible) {
112
                    $sorted[$section->section] = new stdClass;
113
                    $title = trim(get_section_name($this->course, $section->section));
114
                    $title = self::shorten_filename($title);
115
                    $sorted[$section->section]->title = $title;
116
                    if (empty($title)) {
117
                        $unnamedsections[] = $section->section;
118
                    } else {
119
                        $namedsections[$title] = true;
120
                    }
121
                    $sorted[$section->section]->res = []; // TODO: fix empty names here!!!
122
                }
123
            }
124
            foreach ($unnamedsections as $sectionid) {
125
                $untitled = get_string('untitled', 'local_downloadcenter');
126
                $title = $untitled;
127
                $i = 1;
128
                while (isset($namedsections[$title])) {
129
                    $title = $untitled . ' ' . strval($i);
130
                    $i++;
131
                }
132
                $namedsections[$title] = true;
133
                $sorted[$sectionid]->title = $title;
134
            }
135
        } else {
136
            $sorted['default'] = new stdClass;// TODO: fix here if needed!
137
            $sorted['default']->title = '0';
138
            $sorted['default']->res = [];
139
        }
140
        $cms = [];
141
        $resources = [];
142
        foreach ($modinfo->cms as $cm) {
143
            if (!in_array($cm->modname, $this->availableresources)) {
144
                continue;
145
            }
146
            if (!$cm->uservisible) {
147
                continue;
148
            }
149
            if (!$cm->has_view() && $cm->modname != 'folder') {
150
                // Exclude label and similar!
151
                continue;
152
            }
153
            $cms[$cm->id] = $cm;
154
            $resources[$cm->modname][] = $cm->instance;
155
        }
156
 
157
        // Preload instances!
158
        foreach ($resources as $modname => $instances) {
159
            $resources[$modname] = $DB->get_records_list($modname, 'id', $instances, 'id');
160
        }
161
        $availablesections = array_keys($sorted);
162
        $currentsection = '';
163
        foreach ($cms as $cm) {
164
            if (!isset($resources[$cm->modname][$cm->instance])) {
165
                continue;
166
            }
167
            $resource = $resources[$cm->modname][$cm->instance];
168
 
169
            if ($usesections) {
170
                if ($cm->sectionnum !== $currentsection) {
171
                    $currentsection = $cm->sectionnum;
172
                }
173
                if (!in_array($currentsection, $availablesections)) {
174
                    continue;
175
                }
176
            } else {
177
                $currentsection = 'default';
178
            }
179
 
180
            $cmcontext = context_module::instance($cm->id);
181
            if ($cm->modname == 'glossary') {
182
                if ( !has_capability('mod/glossary:manageentries', $cmcontext) && !$resource->allowprintview) {
183
                    continue;
184
                }
185
            }
186
 
187
            if (!isset($this->jsnames[$cm->modname])) {
188
                $this->jsnames[$cm->modname] = get_string('modulenameplural', 'mod_' . $cm->modname);
189
            }
190
 
191
            $icon = '<img src="'.$cm->get_icon_url().'" class="activityicon" alt="'.$cm->get_module_type_name().'" /> ';
192
            // TODO: $cm->visible..
193
            $res = new stdClass;
194
            $res->icon = $icon;
195
            $res->cmid = $cm->id;
196
            $res->name = $cm->get_formatted_name();
197
            $res->modname = $cm->modname;
198
            $res->instanceid = $cm->instance;
199
            $res->resource = $resource;
200
            $res->cm = $cm;
201
            $res->context = $cmcontext;
202
            $sorted[$currentsection]->res[] = $res;
203
        }
204
 
205
        $this->sortedresources = $sorted;
206
        return $sorted;
207
    }
208
 
209
    /**
210
     * @return array
211
     */
212
    public function get_js_modnames() {
213
        return array($this->jsnames);
214
    }
215
 
216
    /**
217
     * @return string
218
     * @throws coding_exception
219
     * @throws dml_exception
220
     */
221
    public function create_zip() {
222
        global $DB, $CFG, $USER, $OUTPUT, $PAGE, $SITE;
223
 
224
        if (file_exists($CFG->dirroot . '/mod/publication/locallib.php')) {
225
            require_once($CFG->dirroot . '/mod/publication/locallib.php');
226
        } else {
227
            define('PUBLICATION_MODE_UPLOAD', 0);
228
            define('PUBLICATION_MODE_IMPORT', 1);
229
        }
230
 
231
        $modbookmissing = true;
232
        if (file_exists($CFG->dirroot . '/mod/book/tool/print/locallib.php')) {
233
            require_once($CFG->dirroot . '/mod/book/tool/print/locallib.php');
234
            $modbookmissing = false;
235
        }
236
 
237
        $bookrenderer = $PAGE->get_renderer('booktool_print');
238
 
239
        // Zip files and sent them to a user.
240
        $fs = get_file_storage();
241
 
242
        $filelist = [];
243
        $filteredresources = $this->filteredresources;
244
 
245
        // Needed for mod_publication!
246
        $userfields = \core_user\fields::for_userpic();
247
        $ufields = $userfields->get_sql('u', false, '', 'id', false)->selects;
248
        $useridentityfields = $CFG->showuseridentity != '' ? 'u.'.str_replace(', ', ', u.', $CFG->showuseridentity) . ', ' : '';
249
 
250
        $topicprefixid = 1;
251
        $topicscount = count($filteredresources);
252
        $topicprefixformat = '%0' . strlen($topicscount) . 'd';
253
        $filesrealnames = $this->_downloadoptions['filesrealnames'];
254
        $addnumbering = $this->_downloadoptions['addnumbering'];
255
        foreach ($filteredresources as $topicid => $info) {
256
            $info->title = html_entity_decode($info->title);
257
            $basedir = clean_filename($info->title);
258
            if ($addnumbering) {
259
                $basedir = sprintf($topicprefixformat, $topicprefixid) . '_' . $basedir;
260
            }
261
            $topicprefixid++;
262
            $basedir = self::shorten_filename($basedir);
263
            $filelist[$basedir] = null;
264
            $resprefixid = 1;
265
            $rescount = count($info->res);
266
            $resprefixformat = '%0' . strlen($rescount) . 'd';
267
            foreach ($info->res as $res) {
268
                $res->name = html_entity_decode($res->name);
269
                if ($addnumbering) {
270
                    $res->name = sprintf($resprefixformat, $resprefixid) . '_' . $res->name;
271
                }
272
                $resdir = $basedir . '/' . self::shorten_filename(clean_filename($res->name));
273
                $filelist[$resdir] = null;
274
                $context = $res->context;
275
                if ($res->modname == 'resource') {
276
                    $files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
277
                    $file = array_shift($files); // Get only the first file - such are the requirements!
278
 
279
                    if ($filesrealnames) {
280
                        $realfilename = $file->get_filename();
281
                        if ($addnumbering) {
282
                            $realfilename = sprintf($resprefixformat, $resprefixid) . '_' . $realfilename;
283
                        }
284
                        $filename = $basedir . '/' . self::shorten_filename($realfilename);
285
                    } else {
286
                        $filename = $basedir . '/' . self::shorten_filename(clean_filename($res->name));
287
                    }
288
                    unset($filelist[$resdir]);
289
 
290
                    $extension = mimeinfo_from_type('extension', $file->get_mimetype());
291
 
292
                    $currentextension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
293
                    if (!empty($currentextension)) {
294
                        $filename = mb_substr($filename, 0, -mb_strlen($currentextension) - 1);
295
                    }
296
                    $fullfilename = $filename . $extension;
297
                    $filei = 1;
298
                    while (isset($filelist[$fullfilename]) && $filei < 200) {
299
                        $fullfilename = $filename . '_' . $filei . $extension;
300
                        $filei++;
301
                    }
302
                    $filelist[$fullfilename] = $file;
303
                } else if ($res->modname == 'folder') {
304
                    $folder = $fs->get_area_tree($context->id, 'mod_folder', 'content', 0);
305
                    $this->add_folder_contents($filelist, $folder, $resdir);
306
                } else if ($res->modname == 'publication') {
307
 
308
                    $cm = $res->cm;
309
 
310
                    $conditions = [];
311
                    $conditions['publication'] = $res->instanceid;
312
 
313
                    $filesforzipping = [];
314
                    $filearea = 'attachment';
315
                    // Find out current groups mode.
316
                    $groupmode = groups_get_activity_groupmode($cm);
317
                    $currentgroup = groups_get_activity_group($cm, true);
318
 
319
                    // Get group name for filename.
320
                    $groupname = '';
321
 
322
                    // Get all ppl that are allowed to submit assignments.
323
                    list($esql, $params) = get_enrolled_sql($context, 'mod/publication:view', $currentgroup);
324
                    $showall = false;
325
 
326
                    if (has_capability('mod/publication:approve', $context) ||
327
                        has_capability('mod/publication:grantextension', $context)) {
328
                        $showall = true;
329
                    }
330
 
331
                    if ($showall) {
332
                        $sql = 'SELECT u.id FROM {user} u '.
333
                            'LEFT JOIN ('.$esql.') eu ON eu.id=u.id '.
334
                            'WHERE u.deleted = 0 AND eu.id=u.id';
335
                    } else {
336
                        $sql = 'SELECT u.id FROM {user} u '.
337
                            'LEFT JOIN ('.$esql.') eu ON eu.id=u.id '.
338
                            'LEFT JOIN {publication_file} files ON (u.id = files.userid) '.
339
                            'WHERE u.deleted = 0 AND eu.id=u.id '.
340
                            'AND files.publication = '. $res->instanceid . ' ';
341
 
342
                        if ($res->resource->mode == PUBLICATION_MODE_UPLOAD) {
343
                            // Mode upload.
344
                            // SN 11.07.2016 - feature #2738:
345
                            // in mod/publication/locallib : line 81, publication::__construct() { ...
346
                            // .....$this->instance->obtainteacherapproval = !$this->obtainteacherapproval ...
347
                            // ..} ...
348
                            // So flag has to be actually inverted!
349
                            if (!$res->resource->obtainteacherapproval) {
350
                                // Need teacher approval.
351
 
352
                                $where = 'files.teacherapproval = 1';
353
                            } else {
354
                                // No need for teacher approval.
355
                                // Teacher only hasnt rejected.
356
                                $where = '(files.teacherapproval = 1 OR files.teacherapproval IS NULL)';
357
                            }
358
                        } else {
359
                            // Mode import.
360
                            if (!$res->resource->obtainstudentapproval) {
361
                                // No need to ask student and teacher has approved.
362
                                $where = 'files.teacherapproval = 1';
363
                            } else {
364
                                // Student and teacher have approved.
365
                                $where = 'files.teacherapproval = 1 AND files.studentapproval = 1';
366
                            }
367
                        }
368
 
369
                        $sql .= 'AND ' . $where . ' ';
370
                        $sql .= 'GROUP BY u.id';
371
                    }
372
 
373
                    $users = $DB->get_records_sql($sql, $params);
374
 
375
                    if (!empty($users)) {
376
                        $users = array_keys($users);
377
                    }
378
 
379
                    // If groupmembersonly used, remove users who are not in any group.
380
                    if ($users && !empty($CFG->enablegroupmembersonly) && $cm->groupmembersonly) {
381
                        if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) {
382
                            $users = array_intersect($users, array_keys($groupingusers));
383
                        }
384
                    }
385
 
386
                    $userfields = [];
387
                    foreach (\core_user\fields::get_name_fields() as $field) {
388
                        $userfields[$field] = $field;
389
                    }
390
                    $userfields['id'] = 'id';
391
                    $userfields['username'] = 'username';
392
                    $userfields = implode(', ', $userfields);
393
 
394
                    $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
395
 
396
                    // Get all files from each user.
397
                    foreach ($users as $uploader) {
398
                        $auserid = $uploader;
399
 
400
                        $conditions['userid'] = $uploader;
401
                        $records = $DB->get_records('publication_file', $conditions);
402
 
403
                        // Get user firstname/lastname.
404
                        $auser = $DB->get_record('user', array('id' => $auserid), $userfields);
405
 
406
                        foreach ($records as $record) {
407
 
408
                            $haspermission = false;
409
 
410
                            if ($res->resource->mode == PUBLICATION_MODE_UPLOAD) {
411
                                // Mode upload.
412
                                // SN 11.07.2016 - feature #2738 - check comment above!
413
                                if (!$res->resource->obtainteacherapproval) {
414
                                    // Need teacher approval.
415
                                    if ($record->teacherapproval == 1) {
416
                                        // Teacher has approved.
417
                                        $haspermission = true;
418
                                    }
419
                                } else {
420
                                    // No need for teacher approval.
421
                                    if (is_null($record->teacherapproval) || $record->teacherapproval == 1) {
422
                                        // Teacher only hasnt rejected.
423
                                        $haspermission = true;
424
                                    }
425
                                }
426
                            } else {
427
                                // Mode import.
428
                                if (!$res->resource->obtainstudentapproval && $record->teacherapproval == 1) {
429
                                    // No need to ask student and teacher has approved.
430
                                    $haspermission = true;
431
                                } else if ($res->resource->obtainstudentapproval &&
432
                                    $record->teacherapproval == 1 &&
433
                                    $record->studentapproval == 1) {
434
                                    // Student and teacher have approved.
435
                                    $haspermission = true;
436
                                }
437
                            }
438
 
439
                            if (has_capability('mod/publication:approve', $context) || $haspermission) {
440
                                // Is teacher or file is public.
441
 
442
                                $file = $fs->get_file_by_id($record->fileid);
443
 
444
                                // Get files new name.
445
                                $fileext = strstr($file->get_filename(), '.');
446
                                $fileoriginal = str_replace($fileext, '', $file->get_filename());
447
                                $fileforzipname = clean_filename(($viewfullnames ? (fullname($auser) . '_') : '') .
448
                                    $fileoriginal.'_' . $auserid . $fileext);
449
                                $fileforzipname = $resdir . '/' . self::shorten_filename($fileforzipname);
450
                                // Save file name to array for zipping.
451
                                $filelist[$fileforzipname] = $file;
452
                            }
453
                        }
454
                    } // End of foreach.
455
                } else if ($res->modname == 'page') {
456
                    $fsfiles = $fs->get_area_files($context->id,
457
                        'mod_page',
458
                        'content');
459
                    if (count($fsfiles) > 0) {
460
                        foreach ($fsfiles as $file) {
461
                            if ($file->get_filesize() == 0) {
462
                                continue;
463
                            }
464
                            $filename = $resdir . '/data' . $file->get_filepath() . self::shorten_filename($file->get_filename());
465
                            $filelist[$filename] = $file;
466
                        }
467
                    }
468
                    if (count($fsfiles) == 0) {
469
                        unset($filelist[$resdir]);
470
                        $filename = $basedir . '/' . self::shorten_filename($res->name . '.html');
471
                    } else {
472
                        $filename = $resdir . '/' . self::shorten_filename($res->name . '.html');
473
                    }
474
                    $content = str_replace('@@PLUGINFILE@@', 'data', $res->resource->content);
475
                    $content = self::convert_content_to_html_doc($res->name, $content);
476
                    $filelist[$filename] = array($content); // Needs to be array to be saved as file.
477
 
478
                } else if ($res->modname == 'book' && !$modbookmissing) {
479
                    $book = $res->resource;
480
                    $cm = $res->cm;
481
                    $chapters = book_preload_chapters($book);
482
 
483
                    $fsfiles = $fs->get_area_files($context->id,
484
                        'mod_book',
485
                        'chapter');
486
                    if (count($fsfiles) > 0) {
487
                        foreach ($fsfiles as $file) {
488
                            if ($file->get_filesize() == 0) {
489
                                continue;
490
                            }
491
                            $filename = $resdir . '/data' . $file->get_filepath() . self::shorten_filename($file->get_filename());
492
                            $filelist[$filename] = $file;
493
                        }
494
                    }
495
                    if (count($fsfiles) == 0) {
496
                        unset($filelist[$resdir]);
497
                        $filename = $basedir . '/' . self::shorten_filename($res->name . '.html');
498
                    } else {
499
                        $filename = $resdir . '/' . self::shorten_filename($res->name . '.html');
500
                    }
501
 
502
                    // Taken from mod/book/tool/print/index.php!
503
                    $allchapters = $DB->get_records('book_chapters', array('bookid' => $book->id), 'pagenum');
504
 
505
                    $book->intro = str_replace('@@PLUGINFILE@@', 'data', $book->intro);
506
                    $content = '<a name="top"></a>';
507
                    $content .= $OUTPUT->heading(format_string($book->name, true, array('context' => $context)), 1);
508
                    $content .= '<p class="book_summary">' .
509
                        format_text($book->intro, $book->introformat, array('noclean' => true, 'context' => $context))  .
510
                        '</p>';
511
 
512
                    $toc = $bookrenderer->render_print_book_toc($chapters, $book, $cm);
513
                    $content .= $toc;
514
                    // Chapters!
515
                    $link1 = $CFG->wwwroot.'/mod/book/view.php?id='.$this->course->id.'&chapterid=';
516
                    $link2 = $CFG->wwwroot.'/mod/book/view.php?id='.$this->course->id;
517
                    foreach ($chapters as $ch) {
518
                        $chapter = $allchapters[$ch->id];
519
                        if ($chapter->hidden) {
520
                            continue;
521
                        }
522
                        $content .= '<div class="book_chapter"><a name="ch'.$ch->id.'"></a>';
523
                        $title = book_get_chapter_title($chapter->id, $chapters, $book, $context);
524
                        if (!$book->customtitles) {
525
                            if (!$chapter->subchapter) {
526
                                $content .= $OUTPUT->heading($title);
527
                            } else {
528
                                $content .= $OUTPUT->heading($title, 3);
529
                            }
530
                        }
531
                        $chaptercontent = str_replace($link1, '#ch', $chapter->content);
532
                        $chaptercontent = str_replace($link2, '#top', $chaptercontent);
533
 
534
                        $chaptercontent = str_replace('@@PLUGINFILE@@', 'data', $chaptercontent);
535
                        $content .= format_text($chaptercontent,
536
                            $chapter->contentformat,
537
                            array('noclean' => true, 'context' => $context));
538
                        $content .= '</div>';
539
                        $content .= '<a href="#toc">&uarr; ' . get_string('top', 'mod_book') . '</a>';
540
                    }
541
                    $content = self::convert_content_to_html_doc($res->name, $content);
542
                    $filelist[$filename] = array($content); // Needs to be array to be saved as file.
543
                } else if ($res->modname == 'lightboxgallery') {
544
 
545
                    $fs = get_file_storage();
546
                    $files = $fs->get_area_files($context->id, 'mod_lightboxgallery', 'gallery_images');
547
 
548
                    foreach ($files as $storedfile) {
549
                        if (!$storedfile->is_valid_image()) {
550
                            continue;
551
                        }
552
 
553
                        $filename = $resdir . '/' . self::shorten_filename($storedfile->get_filename());
554
                        $filelist[$filename] = $storedfile;
555
                    }
556
                } else if ($res->modname == 'assign') {
557
                    require_once($CFG->dirroot . '/mod/assign/locallib.php');
558
                    require_once($CFG->dirroot . '/mod/assign/externallib.php');
559
 
560
                    if ($res->resource->allowsubmissionsfromdate < time() || $res->resource->alwaysshowdescription) {
561
                        $fsfiles = $fs->get_area_files($context->id, 'mod_assign', 'introattachment', 0, 'id', false);
562
                        foreach ($fsfiles as $file) {
563
                            if ($file->get_filesize() == 0) {
564
                                continue;
565
                            }
566
                            $filename = $resdir . '/intro' . $file->get_filepath() . self::shorten_filename($file->get_filename());
567
                            $filelist[$filename] = $file;
568
                        }
569
 
570
                        $fsfiles = $fs->get_area_files($context->id, 'mod_assign', 'intro', 0, 'id', false);
571
                        foreach ($fsfiles as $file) {
572
                            if ($file->get_filesize() == 0) {
573
                                continue;
574
                            }
575
                            $filename = $resdir . '/intro/files' . $file->get_filepath() . self::shorten_filename($file->get_filename());
576
                            $filelist[$filename] = $file;
577
                        }
578
 
579
                        $introtitle = get_string('description') . ' ' . $res->name;
580
 
581
                        $introcontent = str_replace('@@PLUGINFILE@@', 'files', $res->resource->intro);
582
                        $introcontent = self::convert_content_to_html_doc($introtitle, $introcontent);
583
                        $filelist[$resdir . '/intro/intro.html'] = [$introcontent];
584
                    }
585
 
586
                    $submissionsstr = get_string('gradeitem:submissions', 'assign');
587
                    $assign = new assign($context, null, null);
588
                    $assignplugins = $assign->get_submission_plugins();
589
                    $feedbackplugins = $assign->get_feedback_plugins();
590
 
591
                    $params = ['assignment' => $res->instanceid];
592
                    $isstudent = !has_capability('mod/assign:viewgrades', $context);
593
                    if ($isstudent) {
594
                        // When student, fetch only own submissions!
595
                        $submissions = $assign->get_all_submissions($USER->id);
596
                    } else {
597
                        $submissions = $DB->get_records('assign_submission', $params, 'attemptnumber ASC');
598
                    }
599
                    foreach ($submissions as $submission) {
600
                        $user = null;
601
                        $group = null;
602
                        if ($submission->userid != 0) {
603
                            $user = $DB->get_record('user', ['id' => $submission->userid]);
604
                            $fullname = $resdir.  '/' . $submissionsstr . '/' . self::shorten_filename(fullname($user));
605
                        } else if ($submission->groupid != 0) {
606
                            $group = $DB->get_record('groups', ['id' => $submission->groupid]);
607
                            $groupname = get_string('group', 'group') . ': ' . $group->name;
608
                            $fullname = $resdir.  '/' . $submissionsstr . '/' . self::shorten_filename($groupname);
609
                        } else {
610
                            $groupname = get_string('group', 'group') . ': ' . get_string('defaultteam', 'assign');
611
                            $fullname = $resdir.  '/' . $submissionsstr . '/' . self::shorten_filename($groupname);
612
                        }
613
 
614
                        // Submission!
615
                        foreach ($assignplugins as $assignplugin) {
616
                            if (!$assignplugin->is_enabled() || !$assignplugin->is_visible()) {
617
                                continue;
618
                            }
619
 
620
                            // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
621
                            $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type();
622
                            $fileareas = $assignplugin->get_file_areas();
623
                            foreach ($fileareas as $filearea => $name) {
624
                                if ($areafiles = $fs->get_area_files($context->id, $component, $filearea, $submission->id, 'itemid, filepath, filename', false)) {
625
                                    foreach ($areafiles as $file) {
626
                                        $filename = $fullname . $file->get_filepath() . self::shorten_filename($file->get_filename());
627
                                        $filelist[$filename] = $file;
628
                                    }
629
                                }
630
                            }
631
                            if ($assignplugin->get_type() == 'onlinetext') {
632
                                $onlinetext = $assignplugin->get_editor_text('onlinetext', $submission->id);
633
                                $onlinetext = str_replace('@@PLUGINFILE@@/', '', $onlinetext);
634
                                if (mb_strlen(trim($onlinetext)) > 0) {
635
                                    $onlinetext = self::convert_content_to_html_doc($assignplugin->get_name(), $onlinetext);
636
                                    $filename = $fullname . '/' . self::shorten_filename($assignplugin->get_name() . '.html');
637
                                    $filelist[$filename] = [$onlinetext];
638
                                }
639
                            }
640
                        }
641
 
642
                        // Feedback!
643
                        if (empty($user)) {
644
                            if ($isstudent) {
645
                                $user = $USER; // Applicable with group submissions!
646
                            } else {
647
                                continue; // There is no feedback per group AFAIK.
648
                            }
649
                        }
650
                        $feedback = $assign->get_assign_feedback_status_renderable($user);
651
                        // The feedback for our latest submission.
652
                        if ($feedback && $feedback->grade) {
653
                            $fullname .= '/' . get_string('feedback', 'grades');
654
 
655
                            foreach ($feedbackplugins as $feedbackplugin) {
656
                                if (!$feedbackplugin->is_enabled() || !$feedbackplugin->is_visible()) {
657
                                    continue;
658
                                }
659
                                $component = $feedbackplugin->get_subtype().'_'.$feedbackplugin->get_type();
660
                                $fileareas = $feedbackplugin->get_file_areas();
661
                                foreach ($fileareas as $filearea => $name) {
662
 
663
                                    if ($areafiles = $fs->get_area_files($context->id, $component, $filearea, $feedback->grade->id, 'itemid, filepath, filename', false)) {
664
                                        foreach ($areafiles as $file) {
665
 
666
                                            $filename = $fullname . $file->get_filepath() . self::shorten_filename($file->get_filename());
667
                                            $filelist[$filename] = $file;
668
                                        }
669
                                    }
670
                                }
671
 
672
                                if ($feedbackplugin->get_type() == 'comments') {
673
                                    $comments = $feedbackplugin->get_editor_text('comments', $feedback->grade->id);
674
                                    $comments = str_replace('@@PLUGINFILE@@/', '', $comments);
675
                                    if (mb_strlen(trim($comments)) > 0) {
676
                                        $comments = self::convert_content_to_html_doc($feedbackplugin->get_name(), $comments);
677
                                        $filename = $fullname . '/' . self::shorten_filename($feedbackplugin->get_name() . '.html');
678
                                        $filelist[$filename] = [$comments];
679
                                    }
680
                                }
681
                            }
682
                        }
683
                    }
684
                } else if ($res->modname == 'glossary') {
685
                    $hook = 'ALL'; // Setting up default values as taken from mod/glossary/print.php!
686
                    $pivotkey = 'concept';
687
                    $fullpivot = false;
688
                    $currentpivot = '';
689
                    $mode = '';
690
                    $fmtoptions = ['context' => $context];
691
                    $glossary = $res->resource;
692
                    $displayformat = $glossary->displayformat;
693
                    $course = $this->course;
694
                    $cm = $res->cm;
695
                    $content = '';
696
                    ob_start();
697
                    $sitename = get_string("site") . ': <span class="strong">' . format_string($SITE->fullname) . '</span>';
698
                    echo html_writer::tag('div', $sitename, array('class' => 'sitename'));
699
 
700
                    $coursename = get_string("course") . ': <span class="strong">' . format_string($course->fullname) . ' ('. format_string($course->shortname) . ')</span>';
701
                    echo html_writer::tag('div', $coursename, array('class' => 'coursename'));
702
 
703
                    $modname = get_string("modulename", "glossary") . ': <span class="strong">' . format_string($glossary->name, true) . '</span>';
704
                    echo html_writer::tag('div', $modname, array('class' => 'modname'));
705
 
706
                    list($allentries, $count) = glossary_get_entries_by_letter($glossary, $context, 'ALL', 0, 0);
707
                    if ( $allentries ) {
708
                        foreach ($allentries as $entry) {
709
                            $pivot = $entry->{$pivotkey};
710
                            $upperpivot = core_text::strtoupper($pivot);
711
                            $pivottoshow = core_text::strtoupper(format_string($pivot, true, $fmtoptions));
712
 
713
                            // Reduce pivot to 1cc if necessary.
714
                            if (!$fullpivot) {
715
                                $upperpivot = core_text::substr($upperpivot, 0, 1);
716
                                $pivottoshow = core_text::substr($pivottoshow, 0, 1);
717
                            }
718
 
719
                            // If there's a group break.
720
                            if ($currentpivot != $upperpivot) {
721
                                $currentpivot = $upperpivot;
722
                                echo html_writer::tag('div', clean_text($pivottoshow), array('class' => 'mdl-align strong'));
723
                            }
724
                            glossary_print_entry($course, $cm, $glossary, $entry, $mode, $hook, 1, $displayformat, true);
725
                        }
726
                        // The all entries value may be a recordset or an array.
727
                        if ($allentries instanceof moodle_recordset) {
728
                            $allentries->close();
729
                        }
730
                    }
731
                    $content .= ob_get_contents();
732
                    ob_end_clean();
733
 
734
                    $fileurl = $CFG->wwwroot . '/pluginfile.php/' . $context->id . '/mod_glossary/';
735
                    $content = str_replace($fileurl, 'data/', $content);
736
                    $filename = $resdir . '/' . self::shorten_filename($res->name . '.html');
737
                    $linkrel = '<link href="css/styles.css" rel="stylesheet">';
738
                    $linkrel .= '<style> .img-fluid { max-width: 100%; height: auto;}</style>';
739
                    $content = '<div class="path-mod-glossary" id="#page-mod-glossary-print">' . $content . '</div>';
740
                    $content = self::convert_content_to_html_doc($res->name, $content, $linkrel);
741
                    $filelist[$filename] = [$content];
742
                    $filelist[$resdir . '/css/styles.css'] = $CFG->dirroot . '/mod/glossary/styles.css';
743
 
744
                    // Handle attachments.
745
                    $fsfiles = $fs->get_area_files($context->id,
746
                        'mod_glossary',
747
                        'attachment');
748
                    if (count($fsfiles) > 0) {
749
                        foreach ($fsfiles as $file) {
750
                            if ($file->get_filesize() == 0) {
751
                                continue;
752
                            }
753
                            $filename = $resdir . '/data/attachment/' . $file->get_itemid() . '/' . $file->get_filename();
754
                            $filelist[$filename] = $file;
755
                        }
756
                    }
757
                    // Handle entries.
758
                    $fsfiles = $fs->get_area_files($context->id,
759
                        'mod_glossary',
760
                        'entry');
761
                    if (count($fsfiles) > 0) {
762
                        foreach ($fsfiles as $file) {
763
                            if ($file->get_filesize() == 0) {
764
                                continue;
765
                            }
766
                            $filename = $resdir . '/data/entry/' . $file->get_itemid() . '/' . $file->get_filename();
767
                            $filelist[$filename] = $file;
768
                        }
769
                    }
770
                } else if ($res->modname == 'etherpadlite') {
771
                    require_once($CFG->dirroot . '/mod/etherpadlite/lib.php');
772
                    $etherpadconfig = get_config('etherpadlite');
773
                    $domain = $etherpadconfig->url;
774
                    $padid = $res->resource->uri;
775
                    $etherpadclient = \mod_etherpadlite\api\client::get_instance($etherpadconfig->apikey, $domain); // If not working, try $domain.'api' instead.
776
                    // Handle groups here.
777
                    $groupmode = groups_get_activity_groupmode($res->cm);
778
                    if ($groupmode) {
779
                        if ($groupmode == VISIBLEGROUPS || has_capability('moodle/course:managegroups', $res->context)) {
780
                            $htmlcontent = $etherpadclient->get_html($padid);
781
                            if (!empty($htmlcontent)) {
782
                                $htmlcontent = self::append_etherpadlite_css($htmlcontent->html);
783
                                $filename = $resdir . '/' . self::shorten_filename($res->name . '_' . get_string('allparticipants') . '.html');
784
                                $filelist[$filename] = array($htmlcontent); // Needs to be array to be saved as file.
785
                            }
786
                        }
787
                        $allgroups = groups_get_activity_allowed_groups($res->cm);
788
                        foreach ($allgroups as $group) {
789
                            $htmlcontent = $etherpadclient->get_html($padid . $group->id);
790
                            if (!empty($htmlcontent)) {
791
                                $htmlcontent = self::append_etherpadlite_css($htmlcontent->html);
792
                                $filename = $resdir . '/' . self::shorten_filename($res->name . '_' . $group->name . '.html');
793
                                $filelist[$filename] = array($htmlcontent); // Needs to be array to be saved as file.
794
                            }
795
                        }
796
                    } else {
797
                        $htmlcontent = $etherpadclient->get_html($padid);
798
                        if (!empty($htmlcontent)) {
799
                            $htmlcontent = self::append_etherpadlite_css($htmlcontent->html);
800
                            $filename = $resdir . '/' . self::shorten_filename($res->name . '.html');
801
                            $filelist[$filename] = array($htmlcontent); // Needs to be array to be saved as file.
802
                        }
803
                    }
804
                }
805
 
806
                $resprefixid++;
807
            }
808
        }
809
 
810
        \core\session\manager::write_close();
811
 
812
        $filename = sprintf('%s_%s.zip', $this->course->shortname, userdate(time(), '%Y%m%d_%H%M'));
813
 
814
        $zipwriter = \core_files\archive_writer::get_stream_writer($filename, \core_files\archive_writer::ZIP_WRITER);
815
 
816
        // Stream the files into the zip.
817
        foreach ($filelist as $pathinzip => $file) {
818
            if ($file instanceof \stored_file) {
819
                // Most of cases are \stored_file.
820
                $zipwriter->add_file_from_stored_file($pathinzip, $file);
821
            } else if (is_array($file)) {
822
                // Save $file as contents, from onlinetext subplugin.
823
                $content = reset($file);
824
                $zipwriter->add_file_from_string($pathinzip, $content);
825
            } else if (is_string($file)) {
826
                $zipwriter->add_file_from_filepath($pathinzip, $file);
827
            }
828
        }
829
 
830
        // Finish the archive.
831
        $zipwriter->finish();
832
        die;
833
    }
834
 
835
    /**
836
     * @param $filelist
837
     * @param $folder
838
     * @param $path
839
     */
840
    private function add_folder_contents(&$filelist, $folder, $path) {
841
        if (!empty($folder['subdirs'])) {
842
            foreach ($folder['subdirs'] as $foldername => $subfolder) {
843
                $foldername = self::shorten_filename($foldername);
844
                $this->add_folder_contents($filelist, $subfolder, $path . '/' . $foldername);
845
            }
846
        }
847
        foreach ($folder['files'] as $filename => $file) {
848
            $filelist[$path . '/' . self::shorten_filename($filename)] = $file;
849
        }
850
    }
851
 
852
    /**
853
     * @param $data
854
     * @throws coding_exception
855
     * @throws dml_exception
856
     * @throws moodle_exception
857
     */
858
    public function parse_form_data($data) {
859
        $data = (array)$data;
860
        $filtered = [];
861
 
862
        $sortedresources = $this->get_resources_for_user();
863
        $excludeempty = get_config('local_downloadcenter', 'exclude_empty_topics');
864
 
865
        foreach ($sortedresources as $sectionid => $info) {
866
            if (!isset($data['item_topic_' . $sectionid])) {
867
                continue;
868
            }
869
            $filtered[$sectionid] = new stdClass;
870
            $filtered[$sectionid]->title = $info->title;
871
            $filtered[$sectionid]->res = [];
872
            foreach ($info->res as $res) {
873
                $name = 'item_' . $res->modname . '_' . $res->instanceid;
874
                if (!isset($data[$name])) {
875
                    continue;
876
                }
877
                $filtered[$sectionid]->res[] = $res;
878
            }
879
            if ($excludeempty && empty($filtered[$sectionid]->res)) {
880
                unset($filtered[$sectionid]);
881
            }
882
        }
883
 
884
        $this->filteredresources = $filtered;
885
        $this->_downloadoptions['filesrealnames'] = isset($data['filesrealnames']);
886
        $this->_downloadoptions['addnumbering'] = isset($data['addnumbering']);
887
    }
888
 
889
    /**
890
     * @param $filename
891
     * @param int $maxlength
892
     * @return string
893
     */
894
    public static function shorten_filename($filename, $maxlength = 64) {
895
        $filename = (string)$filename;
896
        $filename = str_replace('/', '_', $filename);
897
        if (strlen($filename) <= $maxlength) {
898
            return $filename;
899
        }
900
        $limit = round($maxlength / 2) - 1;
901
        return substr($filename, 0, $limit) . '___' . substr($filename, (1 - $limit));
902
    }
903
 
904
    public static function convert_content_to_html_doc($title, $content, $additionalhead = '') {
905
        return <<<HTML
906
<!doctype html>
907
<html>
908
<head>
909
    <title>$title</title>
910
    <meta charset="utf-8">
911
    $additionalhead
912
</head>
913
<body>
914
$content
915
</body>
916
</html>
917
HTML;
918
    }
919
 
920
    public static function append_etherpadlite_css($htmlcontent) {
921
        $csscontent = <<<CSS
922
<style>
923
ol {
924
  counter-reset: item;
925
}
926
 
927
ol > li {
928
  counter-increment: item;
929
}
930
 
931
ol ol > li {
932
  display: block;
933
}
934
 
935
ol > li {
936
  display: block;
937
}
938
 
939
ol > li:before {
940
  content: counters(item, ".") ". ";
941
}
942
 
943
ol ol > li:before {
944
  content: counters(item, ".") ". ";
945
  margin-left: -20px;
946
}
947
 
948
ul.indent {
949
  list-style-type: none;
950
}
951
 
952
 
953
</style>
954
</body>
955
CSS;
956
        return str_replace('</body>', $csscontent, $htmlcontent);
957
 
958
    }
959
 
960
}