Passed
Push — master ( 6f8b91...26ffea )
by Julito
10:53
created

DocumentManager::addFileToDocument()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 25
nc 9
nop 5
dl 0
loc 40
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ResourceFile;
6
use Chamilo\CoreBundle\Entity\ResourceLink;
7
use Chamilo\CoreBundle\Entity\ResourceNode;
8
use Chamilo\CoreBundle\Entity\User;
9
use Chamilo\CoreBundle\Framework\Container;
10
use Chamilo\CourseBundle\Entity\CDocument;
11
use Chamilo\CourseBundle\Entity\CGroup;
12
use ChamiloSession as Session;
13
use Symfony\Component\HttpFoundation\File\UploadedFile;
14
15
/**
16
 *  Class DocumentManager
17
 *  This is the document library for Chamilo.
18
 *  It is / will be used to provide a service layer to all document-using tools.
19
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
20
 *  Include/require it in your code to use its functionality.
21
 */
22
class DocumentManager
23
{
24
    /**
25
     * @param string $course_code
26
     *
27
     * @return int the document folder quota for the current course in bytes
28
     *             or the default quota
29
     */
30
    public static function get_course_quota($course_code = null)
31
    {
32
        if (empty($course_code)) {
33
            $course_info = api_get_course_info();
34
        } else {
35
            $course_info = api_get_course_info($course_code);
36
        }
37
38
        $course_quota = null;
39
        if (empty($course_info)) {
40
            return DEFAULT_DOCUMENT_QUOTA;
41
        } else {
42
            $course_quota = $course_info['disk_quota'];
43
        }
44
        if (is_null($course_quota) || empty($course_quota)) {
45
            // Course table entry for quota was null, then use default value
46
            $course_quota = DEFAULT_DOCUMENT_QUOTA;
47
        }
48
49
        return $course_quota;
50
    }
51
52
    /**
53
     * Get the content type of a file by checking the extension
54
     * We could use mime_content_type() with php-versions > 4.3,
55
     * but this doesn't work as it should on Windows installations.
56
     *
57
     * @param string $filename or boolean TRUE to return complete array
58
     *
59
     * @author ? first version
60
     * @author Bert Vanderkimpen
61
     *
62
     * @return string
63
     */
64
    public static function file_get_mime_type($filename)
65
    {
66
        // All MIME types in an array (from 1.6, this is the authorative source)
67
        // Please, keep this alphabetical if you add something to this list!
68
        $mimeTypes = [
69
            'ai' => 'application/postscript',
70
            'aif' => 'audio/x-aiff',
71
            'aifc' => 'audio/x-aiff',
72
            'aiff' => 'audio/x-aiff',
73
            'asf' => 'video/x-ms-asf',
74
            'asc' => 'text/plain',
75
            'au' => 'audio/basic',
76
            'avi' => 'video/x-msvideo',
77
            'bcpio' => 'application/x-bcpio',
78
            'bin' => 'application/octet-stream',
79
            'bmp' => 'image/bmp',
80
            'cdf' => 'application/x-netcdf',
81
            'class' => 'application/octet-stream',
82
            'cpio' => 'application/x-cpio',
83
            'cpt' => 'application/mac-compactpro',
84
            'csh' => 'application/x-csh',
85
            'css' => 'text/css',
86
            'dcr' => 'application/x-director',
87
            'dir' => 'application/x-director',
88
            'djv' => 'image/vnd.djvu',
89
            'djvu' => 'image/vnd.djvu',
90
            'dll' => 'application/octet-stream',
91
            'dmg' => 'application/x-diskcopy',
92
            'dms' => 'application/octet-stream',
93
            'doc' => 'application/msword',
94
            'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
95
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
96
            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
97
            'dvi' => 'application/x-dvi',
98
            'dwg' => 'application/vnd.dwg',
99
            'dwf' => 'application/vnd.dwf',
100
            'dxf' => 'application/vnd.dxf',
101
            'dxr' => 'application/x-director',
102
            'eps' => 'application/postscript',
103
            'epub' => 'application/epub+zip',
104
            'etx' => 'text/x-setext',
105
            'exe' => 'application/octet-stream',
106
            'ez' => 'application/andrew-inset',
107
            'flv' => 'video/flv',
108
            'gif' => 'image/gif',
109
            'gtar' => 'application/x-gtar',
110
            'gz' => 'application/x-gzip',
111
            'hdf' => 'application/x-hdf',
112
            'hqx' => 'application/mac-binhex40',
113
            'htm' => 'text/html',
114
            'html' => 'text/html',
115
            'ice' => 'x-conference-xcooltalk',
116
            'ief' => 'image/ief',
117
            'iges' => 'model/iges',
118
            'igs' => 'model/iges',
119
            'jar' => 'application/java-archiver',
120
            'jpe' => 'image/jpeg',
121
            'jpeg' => 'image/jpeg',
122
            'jpg' => 'image/jpeg',
123
            'js' => 'application/x-javascript',
124
            'kar' => 'audio/midi',
125
            'lam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
126
            'latex' => 'application/x-latex',
127
            'lha' => 'application/octet-stream',
128
            'log' => 'text/plain',
129
            'lzh' => 'application/octet-stream',
130
            'm1a' => 'audio/mpeg',
131
            'm2a' => 'audio/mpeg',
132
            'm3u' => 'audio/x-mpegurl',
133
            'man' => 'application/x-troff-man',
134
            'me' => 'application/x-troff-me',
135
            'mesh' => 'model/mesh',
136
            'mid' => 'audio/midi',
137
            'midi' => 'audio/midi',
138
            'mov' => 'video/quicktime',
139
            'movie' => 'video/x-sgi-movie',
140
            'mp2' => 'audio/mpeg',
141
            'mp3' => 'audio/mpeg',
142
            'mp4' => 'video/mp4',
143
            'mpa' => 'audio/mpeg',
144
            'mpe' => 'video/mpeg',
145
            'mpeg' => 'video/mpeg',
146
            'mpg' => 'video/mpeg',
147
            'mpga' => 'audio/mpeg',
148
            'ms' => 'application/x-troff-ms',
149
            'msh' => 'model/mesh',
150
            'mxu' => 'video/vnd.mpegurl',
151
            'nc' => 'application/x-netcdf',
152
            'oda' => 'application/oda',
153
            'oga' => 'audio/ogg',
154
            'ogg' => 'application/ogg',
155
            'ogx' => 'application/ogg',
156
            'ogv' => 'video/ogg',
157
            'pbm' => 'image/x-portable-bitmap',
158
            'pct' => 'image/pict',
159
            'pdb' => 'chemical/x-pdb',
160
            'pdf' => 'application/pdf',
161
            'pgm' => 'image/x-portable-graymap',
162
            'pgn' => 'application/x-chess-pgn',
163
            'pict' => 'image/pict',
164
            'png' => 'image/png',
165
            'pnm' => 'image/x-portable-anymap',
166
            'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
167
            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
168
            'pps' => 'application/vnd.ms-powerpoint',
169
            'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
170
            'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
171
            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
172
            'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
173
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
174
            'ppm' => 'image/x-portable-pixmap',
175
            'ppt' => 'application/vnd.ms-powerpoint',
176
            'ps' => 'application/postscript',
177
            'qt' => 'video/quicktime',
178
            'ra' => 'audio/x-realaudio',
179
            'ram' => 'audio/x-pn-realaudio',
180
            'rar' => 'image/x-rar-compressed',
181
            'ras' => 'image/x-cmu-raster',
182
            'rgb' => 'image/x-rgb',
183
            'rm' => 'audio/x-pn-realaudio',
184
            'roff' => 'application/x-troff',
185
            'rpm' => 'audio/x-pn-realaudio-plugin',
186
            'rtf' => 'text/rtf',
187
            'rtx' => 'text/richtext',
188
            'sgm' => 'text/sgml',
189
            'sgml' => 'text/sgml',
190
            'sh' => 'application/x-sh',
191
            'shar' => 'application/x-shar',
192
            'silo' => 'model/mesh',
193
            'sib' => 'application/X-Sibelius-Score',
194
            'sit' => 'application/x-stuffit',
195
            'skd' => 'application/x-koan',
196
            'skm' => 'application/x-koan',
197
            'skp' => 'application/x-koan',
198
            'skt' => 'application/x-koan',
199
            'smi' => 'application/smil',
200
            'smil' => 'application/smil',
201
            'snd' => 'audio/basic',
202
            'so' => 'application/octet-stream',
203
            'spl' => 'application/x-futuresplash',
204
            'src' => 'application/x-wais-source',
205
            'sv4cpio' => 'application/x-sv4cpio',
206
            'sv4crc' => 'application/x-sv4crc',
207
            'svf' => 'application/vnd.svf',
208
            'svg' => 'image/svg+xml',
209
            //'svgz' => 'image/svg+xml',
210
            'swf' => 'application/x-shockwave-flash',
211
            'sxc' => 'application/vnd.sun.xml.calc',
212
            'sxi' => 'application/vnd.sun.xml.impress',
213
            'sxw' => 'application/vnd.sun.xml.writer',
214
            't' => 'application/x-troff',
215
            'tar' => 'application/x-tar',
216
            'tcl' => 'application/x-tcl',
217
            'tex' => 'application/x-tex',
218
            'texi' => 'application/x-texinfo',
219
            'texinfo' => 'application/x-texinfo',
220
            'tga' => 'image/x-targa',
221
            'tif' => 'image/tif',
222
            'tiff' => 'image/tiff',
223
            'tr' => 'application/x-troff',
224
            'tsv' => 'text/tab-seperated-values',
225
            'txt' => 'text/plain',
226
            'ustar' => 'application/x-ustar',
227
            'vcd' => 'application/x-cdlink',
228
            'vrml' => 'model/vrml',
229
            'wav' => 'audio/x-wav',
230
            'wbmp' => 'image/vnd.wap.wbmp',
231
            'wbxml' => 'application/vnd.wap.wbxml',
232
            'webp' => 'image/webp',
233
            'wml' => 'text/vnd.wap.wml',
234
            'wmlc' => 'application/vnd.wap.wmlc',
235
            'wmls' => 'text/vnd.wap.wmlscript',
236
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
237
            'wma' => 'audio/x-ms-wma',
238
            'wmv' => 'video/x-ms-wmv',
239
            'wrl' => 'model/vrml',
240
            'xbm' => 'image/x-xbitmap',
241
            'xht' => 'application/xhtml+xml',
242
            'xhtml' => 'application/xhtml+xml',
243
            'xls' => 'application/vnd.ms-excel',
244
            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
245
            'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
246
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
247
            'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
248
            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
249
            'xml' => 'text/xml',
250
            'xpm' => 'image/x-xpixmap',
251
            'xsl' => 'text/xml',
252
            'xwd' => 'image/x-windowdump',
253
            'xyz' => 'chemical/x-xyz',
254
            'zip' => 'application/zip',
255
        ];
256
257
        if (true === $filename) {
258
            return $mimeTypes;
259
        }
260
261
        // Get the extension of the file
262
        $extension = explode('.', $filename);
263
264
        // $filename will be an array if a . was found
265
        if (is_array($extension)) {
266
            $extension = strtolower($extension[count($extension) - 1]);
267
        } else {
268
            //file without extension
269
            $extension = 'empty';
270
        }
271
272
        //if the extension is found, return the content type
273
        if (isset($mimeTypes[$extension])) {
274
            return $mimeTypes[$extension];
275
        }
276
277
        return 'application/octet-stream';
278
    }
279
280
    /**
281
     * This function smart streams a file to the client using HTTP headers.
282
     *
283
     * @param string $fullFilename The full path of the file to be sent
284
     * @param string $filename     The name of the file as shown to the client
285
     * @param string $contentType  The MIME type of the file
286
     *
287
     * @return bool false if file doesn't exist, true if stream succeeded
288
     */
289
    public static function smartReadFile($fullFilename, $filename, $contentType = 'application/octet-stream')
290
    {
291
        if (!file_exists($fullFilename)) {
292
            header("HTTP/1.1 404 Not Found");
293
294
            return false;
295
        }
296
297
        $size = filesize($fullFilename);
298
        $time = date('r', filemtime($fullFilename));
299
300
        $fm = @fopen($fullFilename, 'rb');
301
        if (!$fm) {
0 ignored issues
show
introduced by
$fm is of type false|resource, thus it always evaluated to false.
Loading history...
302
            header("HTTP/1.1 505 Internal server error");
303
304
            return false;
305
        }
306
307
        $begin = 0;
308
        $end = $size - 1;
309
310
        if (isset($_SERVER['HTTP_RANGE'])) {
311
            if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
312
                $begin = intval($matches[1]);
313
                if (!empty($matches[2])) {
314
                    $end = intval($matches[2]);
315
                }
316
            }
317
        }
318
319
        if (isset($_SERVER['HTTP_RANGE'])) {
320
            header('HTTP/1.1 206 Partial Content');
321
        } else {
322
            header('HTTP/1.1 200 OK');
323
        }
324
325
        header("Content-Type: $contentType");
326
        header('Cache-Control: public, must-revalidate, max-age=0');
327
        header('Pragma: no-cache');
328
        header('Accept-Ranges: bytes');
329
        header('Content-Length:'.(($end - $begin) + 1));
330
        if (isset($_SERVER['HTTP_RANGE'])) {
331
            header("Content-Range: bytes $begin-$end/$size");
332
        }
333
        header("Content-Disposition: inline; filename=$filename");
334
        header("Content-Transfer-Encoding: binary");
335
        header("Last-Modified: $time");
336
337
        $cur = $begin;
338
        fseek($fm, $begin, 0);
339
340
        while (!feof($fm) && $cur <= $end && (0 == connection_status())) {
341
            echo fread($fm, min(1024 * 16, ($end - $cur) + 1));
342
            $cur += 1024 * 16;
343
        }
344
    }
345
346
    /**
347
     * This function streams a file to the client.
348
     *
349
     * @param string $full_file_name
350
     * @param bool   $forced              Whether to force the browser to download the file
351
     * @param string $name
352
     * @param bool   $fixLinksHttpToHttps change file content from http to https
353
     * @param array  $extraHeaders        Additional headers to be sent
354
     *
355
     * @return false if file doesn't exist, true if stream succeeded
356
     */
357
    public static function file_send_for_download(
358
        $full_file_name,
359
        $forced = false,
360
        $name = '',
361
        $fixLinksHttpToHttps = false,
362
        $extraHeaders = []
363
    ) {
364
        session_write_close(); //we do not need write access to session anymore
365
        if (!is_file($full_file_name)) {
366
            return false;
367
        }
368
        $filename = '' == $name ? basename($full_file_name) : api_replace_dangerous_char($name);
369
        $len = filesize($full_file_name);
370
        // Fixing error when file name contains a ","
371
        $filename = str_replace(',', '', $filename);
372
        $sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
373
374
        // Allows chrome to make videos and audios seekable
375
        header('Accept-Ranges: bytes');
376
        if (!empty($extraHeaders)) {
377
            foreach ($extraHeaders as $name => $value) {
378
                //TODO: add restrictions to allowed headers?
379
                header($name.': '.$value);
380
            }
381
        }
382
383
        if ($forced) {
384
            // Force the browser to save the file instead of opening it
385
            if (isset($sendFileHeaders) &&
386
                !empty($sendFileHeaders)) {
387
                header("X-Sendfile: $filename");
388
            }
389
390
            header('Content-type: application/octet-stream');
391
            header('Content-length: '.$len);
392
            if (preg_match("/MSIE 5.5/", $_SERVER['HTTP_USER_AGENT'])) {
393
                header('Content-Disposition: filename= '.$filename);
394
            } else {
395
                header('Content-Disposition: attachment; filename= '.$filename);
396
            }
397
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
398
                header('Pragma: ');
399
                header('Cache-Control: ');
400
                header('Cache-Control: public'); // IE cannot download from sessions without a cache
401
            }
402
            header('Content-Description: '.$filename);
403
            header('Content-Transfer-Encoding: binary');
404
405
            if (function_exists('ob_end_clean') && ob_get_length()) {
406
                // Use ob_end_clean() to avoid weird buffering situations
407
                // where file is sent broken/incomplete for download
408
                ob_end_clean();
409
            }
410
411
            $res = fopen($full_file_name, 'r');
412
            fpassthru($res);
413
414
            return true;
415
        } else {
416
            // no forced download, just let the browser decide what to do according to the mimetype
417
            $lpFixedEncoding = 'true' === api_get_setting('lp.fixed_encoding');
418
419
            // Commented to let courses content to be cached in order to improve performance:
420
            //header('Expires: Wed, 01 Jan 1990 00:00:00 GMT');
421
            //header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
422
423
            // Commented to avoid double caching declaration when playing with IE and HTTPS
424
            //header('Cache-Control: no-cache, must-revalidate');
425
            //header('Pragma: no-cache');
426
427
            $contentType = self::file_get_mime_type($filename);
428
429
            switch ($contentType) {
430
                case 'text/html':
431
                    if (isset($lpFixedEncoding) && 'true' === $lpFixedEncoding) {
432
                        $contentType .= '; charset=UTF-8';
433
                    } else {
434
                        $encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
435
                        if (!empty($encoding)) {
436
                            $contentType .= '; charset='.$encoding;
437
                        }
438
                    }
439
                    break;
440
                case 'text/plain':
441
                    if (isset($lpFixedEncoding) && 'true' === $lpFixedEncoding) {
442
                        $contentType .= '; charset=UTF-8';
443
                    } else {
444
                        $encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
445
                        if (!empty($encoding)) {
446
                            $contentType .= '; charset='.$encoding;
447
                        }
448
                    }
449
                    break;
450
                case 'video/mp4':
451
                case 'audio/mpeg':
452
                case 'audio/mp4':
453
                case 'audio/ogg':
454
                case 'audio/webm':
455
                case 'audio/wav':
456
                case 'video/ogg':
457
                case 'video/webm':
458
                    self::smartReadFile($full_file_name, $filename, $contentType);
459
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
460
                case 'application/vnd.dwg':
461
                case 'application/vnd.dwf':
462
                    header('Content-type: application/octet-stream');
463
                    break;
464
            }
465
466
            header('Content-type: '.$contentType);
467
            header('Content-Length: '.$len);
468
            $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
469
470
            if (strpos($userAgent, 'msie')) {
471
                header('Content-Disposition: ; filename= '.$filename);
472
            } else {
473
                //header('Content-Disposition: inline');
474
                header('Content-Disposition: inline;');
475
            }
476
477
            if ($fixLinksHttpToHttps) {
478
                $content = file_get_contents($full_file_name);
479
                $content = str_replace(
480
                    ['http%3A%2F%2F', 'http://'],
481
                    ['https%3A%2F%2F', 'https://'],
482
                    $content
483
                );
484
                echo $content;
485
            } else {
486
                if (function_exists('ob_end_clean') && ob_get_length()) {
487
                    // Use ob_end_clean() to avoid weird buffering situations
488
                    // where file is sent broken/incomplete for download
489
                    ob_end_clean();
490
                }
491
492
                readfile($full_file_name);
493
            }
494
495
            return true;
496
        }
497
    }
498
499
    /**
500
     * Session folder filters.
501
     *
502
     * @param string $path
503
     * @param int    $sessionId
504
     *
505
     * @return string|null
506
     */
507
    public static function getSessionFolderFilters($path, $sessionId)
508
    {
509
        $sessionId = (int) $sessionId;
510
        $condition = null;
511
512
        if (!empty($sessionId)) {
513
            // Chat folder filter
514
            if ('/chat_files' == $path) {
515
                $condition .= " AND (docs.session_id = '$sessionId') ";
516
            }
517
            // share_folder filter
518
            $condition .= " AND docs.path != '/shared_folder' ";
519
        }
520
521
        return $condition;
522
    }
523
524
    /**
525
     * Fetches all document data for the given user/group.
526
     *
527
     * @param array  $courseInfo
528
     * @param string $path
529
     * @param int    $toGroupId       iid
530
     * @param int    $toUserId
531
     * @param bool   $canSeeInvisible
532
     * @param bool   $search
533
     * @param int    $sessionId
534
     *
535
     * @return array with all document data
536
     */
537
    public static function getAllDocumentData(
538
        $courseInfo,
539
        $path = '/',
540
        $toGroupId = 0,
541
        $toUserId = null,
542
        $canSeeInvisible = false,
543
        $search = false,
544
        $sessionId = 0,
545
        User $currentUser = null
546
    ) {
547
        if (empty($courseInfo)) {
548
            return [];
549
        }
550
551
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
552
        $currentUser = $currentUser ?: api_get_current_user();
553
554
        // Escape underscores in the path so they don't act as a wildcard
555
        $originalPath = $path;
556
        $path = str_replace('_', '\_', $path);
557
558
        // The given path will not end with a slash, unless it's the root '/'
559
        // so no root -> add slash
560
        $addedSlash = '/' == $path ? '' : '/';
561
562
        $sharedCondition = null;
563
        if ('/shared_folder' == $originalPath) {
564
            $students = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
565
            if (!empty($students)) {
566
                $conditionList = [];
567
                foreach ($students as $studentInfo) {
568
                    $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
569
                }
570
                $sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
571
            }
572
        }
573
574
        $sql = "SELECT
575
                    docs.iid,
576
                    docs.filetype,
577
                    docs.path,
578
                    docs.title,
579
                    docs.comment,
580
                    docs.size,
581
                    docs.readonly,
582
                    docs.session_id,
583
                    creator_id,
584
                    visibility,
585
                    n.updated_at,
586
                    n.created_at,
587
                    n.creator_id
588
                FROM resource_node AS n
589
                INNER JOIN $tblDocument AS docs
590
                ON (docs.resource_node_id = n.id)
591
                INNER JOIN resource_link l
592
                ON (l.resource_node_id = n.id)
593
                WHERE
594
                    l.c_id = {$courseInfo['real_id']} AND
595
                    n.path LIKE '".Database::escape_string($path.$addedSlash.'%')."' AND
596
                    n.path NOT LIKE '".Database::escape_string($path.$addedSlash.'%/%')."' AND
597
                    l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
598
                    $sharedCondition
599
                ";
600
        //$userGroupFilter AND
601
        //$conditionSession
602
        $result = Database::query($sql);
603
604
        $documentData = [];
605
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
606
        $isCoach = api_is_coach();
607
        if (false !== $result && 0 != Database::num_rows($result)) {
608
            $rows = [];
609
            $hideInvisibleDocuments = api_get_configuration_value('hide_invisible_course_documents_in_sessions');
610
            while ($row = Database::fetch_array($result, 'ASSOC')) {
611
                if (isset($rows[$row['id']])) {
612
                    continue;
613
                }
614
615
                // If we are in session and hide_invisible_course_documents_in_sessions is enabled
616
                // Then we avoid the documents that have visibility in session but that they come from a base course
617
                /*if ($hideInvisibleDocuments && $sessionId) {
618
                    if ($row['item_property_session_id'] == $sessionId && empty($row['session_id'])) {
619
                        continue;
620
                    }
621
                }*/
622
623
                if (self::isBasicCourseFolder($row['path'], $sessionId)) {
624
                    $basicCourseDocumentsContent = self::getAllDocumentData(
625
                        $courseInfo,
626
                        $row['path']
627
                    );
628
629
                    if (empty($basicCourseDocumentsContent)) {
630
                        continue;
631
                    }
632
                }
633
634
                $rows[$row['id']] = $row;
635
            }
636
637
            // If we are in session and hide_invisible_course_documents_in_sessions is enabled
638
            // Or if we are students
639
            // Then don't list the invisible or deleted documents
640
            if (($sessionId && $hideInvisibleDocuments) || (!$isCoach && !$isAllowedToEdit)) {
641
                $rows = array_filter($rows, function ($row) {
642
                    if (in_array(
643
                        $row['visibility'],
644
                        [
645
                            ResourceLink::VISIBILITY_DELETED,
646
                            ResourceLink::VISIBILITY_DRAFT,
647
                        ]
648
                    )) {
649
                        return false;
650
                    }
651
652
                    return true;
653
                });
654
            }
655
656
            foreach ($rows as $row) {
657
                if ('file' == $row['filetype'] &&
658
                    'html' == pathinfo($row['path'], PATHINFO_EXTENSION)
659
                ) {
660
                    // Templates management
661
                    $tblTemplate = Database::get_main_table(TABLE_MAIN_TEMPLATES);
662
                    $sql = "SELECT id FROM $tblTemplate
663
                            WHERE
664
                                c_id = '".$courseInfo['real_id']."' AND
665
                                user_id = '".$currentUser->getId()."' AND
666
                                ref_doc = '".$row['id']."'";
667
                    $templateResult = Database::query($sql);
668
                    $row['is_template'] = (Database::num_rows($templateResult) > 0) ? 1 : 0;
669
                }
670
                $row['basename'] = basename($row['path']);
671
                // Just filling $document_data.
672
                $documentData[$row['id']] = $row;
673
            }
674
675
            // Only for the student we filter the results see BT#1652
676
            if (!$isCoach && !$isAllowedToEdit) {
677
                // Checking parents visibility.
678
                $finalDocumentData = [];
679
                foreach ($documentData as $row) {
680
                    $isVisible = self::check_visibility_tree(
681
                        $row['id'],
682
                        $courseInfo,
683
                        $sessionId,
684
                        $currentUser->getId(),
685
                        $toGroupId
686
                    );
687
                    if ($isVisible) {
688
                        $finalDocumentData[$row['id']] = $row;
689
                    }
690
                }
691
            } else {
692
                $finalDocumentData = $documentData;
693
            }
694
695
            return $finalDocumentData;
696
        }
697
698
        return [];
699
    }
700
701
    /**
702
     * Gets the paths of all folders in a course
703
     * can show all folders (except for the deleted ones) or only visible ones.
704
     *
705
     * @param array  $courseInfo
706
     * @param int    $groupIid          iid
707
     * @param bool   $can_see_invisible
708
     * @param bool   $getInvisibleList
709
     * @param string $path              current path
710
     *
711
     * @return array with paths
712
     */
713
    public static function get_all_document_folders(
714
        $courseInfo,
715
        $groupIid = 0,
716
        $can_see_invisible = false,
717
        $getInvisibleList = false,
718
        $path = ''
719
    ) {
720
        if (empty($courseInfo)) {
721
            return [];
722
        }
723
724
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
725
        $groupIid = (int) $groupIid;
726
        $courseId = $courseInfo['real_id'];
727
        $sessionId = api_get_session_id();
728
729
        $folders = [];
730
        $students = CourseManager::get_user_list_from_course_code(
731
            $courseInfo['code'],
732
            api_get_session_id()
733
        );
734
735
        $conditionList = [];
736
        if (!empty($students)) {
737
            foreach ($students as $studentId => $studentInfo) {
738
                $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
739
            }
740
        }
741
742
        $groupCondition = " l.group_id = $groupIid";
743
        if (empty($groupIid)) {
744
            $groupCondition = ' (l.group_id = 0 OR l.group_id IS NULL)';
745
        }
746
747
        $show_users_condition = '';
748
        if ('false' === api_get_setting('show_users_folders')) {
749
            $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
750
        }
751
752
        if ($can_see_invisible) {
753
            $sessionId = $sessionId ?: api_get_session_id();
754
            $condition_session = " AND (l.session_id = '$sessionId' OR (l.session_id = '0' OR l.session_id IS NULL) )";
755
            $condition_session .= self::getSessionFolderFilters($path, $sessionId);
756
757
            $sql = "SELECT DISTINCT docs.iid, n.path
758
                    FROM resource_node AS n
759
                    INNER JOIN $TABLE_DOCUMENT AS docs
760
                    ON (docs.resource_node_id = n.id)
761
                    INNER JOIN resource_link l
762
                    ON (l.resource_node_id = n.id)
763
                    WHERE
764
                        l.c_id = $courseId AND
765
                        docs.filetype = 'folder' AND
766
                        $groupCondition AND
767
                        n.path NOT LIKE '%shared_folder%' AND
768
                        l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
769
                        $condition_session ";
770
771
            if (0 != $groupIid) {
772
                $sql .= " AND n.path NOT LIKE '%shared_folder%' ";
773
            } else {
774
                $sql .= $show_users_condition;
775
            }
776
777
            $result = Database::query($sql);
778
            if ($result && 0 != Database::num_rows($result)) {
779
                while ($row = Database::fetch_array($result, 'ASSOC')) {
780
                    if (self::is_folder_to_avoid($row['path'])) {
781
                        continue;
782
                    }
783
784
                    if (false !== strpos($row['path'], '/shared_folder/')) {
785
                        if (!in_array($row['path'], $conditionList)) {
786
                            continue;
787
                        }
788
                    }
789
790
                    $folders[$row['iid']] = $row['path'];
791
                }
792
793
                if (!empty($folders)) {
794
                    natsort($folders);
795
                }
796
797
                return $folders;
798
            }
799
800
            return false;
801
        } else {
802
            // No invisible folders
803
            // Condition for the session
804
            $condition_session = api_get_session_condition(
805
                $sessionId,
806
                true,
807
                false,
808
                'docs.session_id'
809
            );
810
811
            $visibilityCondition = 'l.visibility = 1';
812
            $fileType = "docs.filetype = 'folder' AND";
813
            if ($getInvisibleList) {
814
                $visibilityCondition = 'l.visibility = 0';
815
                $fileType = '';
816
            }
817
818
            //get visible folders
819
            $sql = "SELECT DISTINCT docs.id, docs.path
820
                    FROM resource_node AS n
821
                    INNER JOIN $TABLE_DOCUMENT  AS docs
822
                    ON (docs.resource_node_id = n.id)
823
                    INNER JOIN resource_link l
824
                    ON (l.resource_node_id = n.id)
825
                    WHERE
826
                        $fileType
827
                        $groupCondition AND
828
                        $visibilityCondition
829
                        $show_users_condition
830
                        $condition_session AND
831
                        l.c_id = $courseId ";
832
            $result = Database::query($sql);
833
            $visibleFolders = [];
834
            while ($row = Database::fetch_array($result, 'ASSOC')) {
835
                $visibleFolders[$row['id']] = $row['path'];
836
            }
837
838
            if ($getInvisibleList) {
839
                return $visibleFolders;
840
            }
841
842
            // get invisible folders
843
            $sql = "SELECT DISTINCT docs.iid, n.path
844
                    FROM resource_node AS n
845
                    INNER JOIN $TABLE_DOCUMENT  AS docs
846
                    ON (docs.resource_node_id = n.id)
847
                    INNER JOIN resource_link l
848
                    ON (l.resource_node_id = n.id)
849
                    WHERE
850
                        docs.filetype = 'folder' AND
851
                        $groupCondition AND
852
                        l.visibility IN ('".ResourceLink::VISIBILITY_PENDING."')
853
                        $condition_session AND
854
                        l.c_id = $courseId ";
855
            $result = Database::query($sql);
856
            $invisibleFolders = [];
857
            while ($row = Database::fetch_array($result, 'ASSOC')) {
858
                //get visible folders in the invisible ones -> they are invisible too
859
                $sql = "SELECT DISTINCT docs.iid, n.path
860
                        FROM resource_node AS n
861
                        INNER JOIN $TABLE_DOCUMENT  AS docs
862
                        ON (docs.resource_node_id = n.id)
863
                        INNER JOIN resource_link l
864
                        ON (l.resource_node_id = n.id)
865
                        WHERE
866
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
867
                            docs.filetype = 'folder' AND
868
                            $groupCondition AND
869
                            l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
870
                            $condition_session AND
871
                            l.c_id = $courseId ";
872
                $folder_in_invisible_result = Database::query($sql);
873
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
874
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
875
                }
876
            }
877
878
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
879
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
880
                $folders = array_diff($visibleFolders, $invisibleFolders);
881
                natsort($folders);
882
883
                return $folders;
884
            }
885
886
            if (is_array($visibleFolders)) {
887
                natsort($visibleFolders);
888
889
                return $visibleFolders;
890
            }
891
892
            // no visible folders found
893
            return false;
894
        }
895
    }
896
897
    /**
898
     * This check if a document has the readonly property checked, then see if the user
899
     * is the owner of this file, if all this is true then return true.
900
     *
901
     * @param array $_course
902
     * @param int   $user_id     id of the current user
903
     * @param int   $document_id in case you don't have the file path ,
904
     *                           insert the id of the file here and leave $file in blank ''
905
     * @param bool  $to_delete
906
     * @param int   $sessionId
907
     *
908
     * @return bool true/false
909
     * */
910
    public static function check_readonly(
911
        $_course,
912
        $user_id,
913
        $document_id = 0,
914
        $to_delete = false,
915
        $sessionId = null
916
    ) {
917
        $sessionId = (int) $sessionId;
918
        if (empty($sessionId)) {
919
            $sessionId = api_get_session_id();
920
        }
921
        $document_id = (int) $document_id;
922
923
        $repo = Container::getDocumentRepository();
924
        /** @var CDocument $document */
925
        $document = $repo->find($document_id);
926
927
        if (null === $document) {
928
            return false;
929
        }
930
931
        $readOnly = $document->getReadonly();
932
933
        return $readOnly;
934
    }
935
936
    /**
937
     * This check if a document is a folder or not.
938
     *
939
     * @param array $_course
940
     * @param int   $id      document id
941
     *
942
     * @return bool true/false
943
     * */
944
    public static function isFolder($_course, $id)
945
    {
946
        $table = Database::get_course_table(TABLE_DOCUMENT);
947
        if (empty($_course)) {
948
            return false;
949
        }
950
        $course_id = $_course['real_id'];
951
        $id = (int) $id;
952
        $sql = "SELECT filetype FROM $table
953
                WHERE c_id = $course_id AND id= $id";
954
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
955
956
        return 'folder' === $result['filetype'];
957
    }
958
959
    /**
960
     * @param int   $document_id
961
     * @param array $course_info
962
     * @param int   $session_id
963
     * @param bool  $remove_content_from_db
964
     */
965
    public static function deleteDocumentFromDb(
966
        $document_id,
967
        $course_info = [],
968
        $session_id = 0,
969
        $remove_content_from_db = false
970
    ) {
971
        // Deleting from the DB
972
        $user_id = api_get_user_id();
973
        $document_id = (int) $document_id;
974
975
        if (empty($course_info)) {
976
            $course_info = api_get_course_info();
977
        }
978
979
        if (empty($session_id)) {
980
            $session_id = api_get_session_id();
981
        }
982
983
        self::delete_document_from_search_engine($course_info['code'], $document_id);
984
        self::unsetDocumentAsTemplate($document_id, $course_info['real_id'], $user_id);
985
986
        // Hard DB delete
987
        if ($remove_content_from_db) {
988
            $repo = Container::getDocumentRepository();
989
            /** @var CDocument $document */
990
            $document = $repo->find($document_id);
991
            $repo->softDelete($document);
992
993
            return true;
994
        }
995
    }
996
997
    /**
998
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
999
     * Files/folders that are inside a deleted folder get visibility 2.
1000
     *
1001
     * @param array  $_course
1002
     * @param string $path          Path stored in the database
1003
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
1004
     * @param int    $sessionId     The ID of the session, if any
1005
     * @param int    $documentId    The document id, if available
1006
     * @param int    $groupId       iid
1007
     *
1008
     * @return bool true/false
1009
     *
1010
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1011
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1012
     */
1013
    public static function delete_document(
1014
        $_course,
1015
        $path = null,
1016
        $base_work_dir = null,
1017
        $sessionId = null,
1018
        $documentId = null,
1019
        $groupId = 0
1020
    ) {
1021
        $groupId = (int) $groupId;
1022
        if (empty($groupId)) {
1023
            $groupId = api_get_group_id();
1024
        }
1025
1026
        $sessionId = (int) $sessionId;
1027
        if (empty($sessionId)) {
1028
            $sessionId = api_get_session_id();
1029
        }
1030
1031
        $course_id = $_course['real_id'];
1032
1033
        if (empty($course_id)) {
1034
            return false;
1035
        }
1036
1037
        if (empty($documentId)) {
1038
            $documentId = self::get_document_id($_course, $path, $sessionId);
1039
            $docInfo = self::get_document_data_by_id(
1040
                $documentId,
1041
                $_course['code'],
1042
                false,
1043
                $sessionId
1044
            );
1045
            $path = $docInfo['path'];
1046
        } else {
1047
            $docInfo = self::get_document_data_by_id(
1048
                $documentId,
1049
                $_course['code'],
1050
                false,
1051
                $sessionId
1052
            );
1053
            if (empty($docInfo)) {
1054
                return false;
1055
            }
1056
            $path = $docInfo['path'];
1057
        }
1058
1059
        $documentId = (int) $documentId;
1060
1061
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1062
            return false;
1063
        }
1064
1065
        $repo = Container::getDocumentRepository();
1066
        $document = $repo->find($docInfo['iid']);
1067
        if ($document) {
1068
            $repo->hardDelete($document);
1069
1070
            return true;
1071
        }
1072
1073
        return false;
1074
    }
1075
1076
    /**
1077
     * Removes documents from search engine database.
1078
     *
1079
     * @param string $course_id   Course code
1080
     * @param int    $document_id Document id to delete
1081
     */
1082
    public static function delete_document_from_search_engine($course_id, $document_id)
1083
    {
1084
        // remove from search engine if enabled
1085
        if ('true' === api_get_setting('search_enabled')) {
1086
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1087
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1088
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1089
            $res = Database::query($sql);
1090
            if (Database::num_rows($res) > 0) {
1091
                $row2 = Database::fetch_array($res);
1092
                $di = new ChamiloIndexer();
1093
                $di->remove_document($row2['search_did']);
1094
            }
1095
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1096
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1097
            Database::query($sql);
1098
1099
            // remove terms from db
1100
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1101
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1102
        }
1103
    }
1104
1105
    /**
1106
     * Gets the id of a document with a given path.
1107
     *
1108
     * @param array  $courseInfo
1109
     * @param string $path
1110
     * @param int    $sessionId
1111
     *
1112
     * @return int id of document / false if no doc found
1113
     */
1114
    public static function get_document_id($courseInfo, $path, $sessionId = 0)
1115
    {
1116
        $table = Database::get_course_table(TABLE_DOCUMENT);
1117
        $courseId = $courseInfo['real_id'];
1118
1119
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
1120
        $sessionCondition = api_get_session_condition($sessionId, true);
1121
1122
        $path = Database::escape_string($path);
1123
        if (!empty($courseId) && !empty($path)) {
1124
            $sql = "SELECT iid FROM $table
1125
                    WHERE
1126
                        c_id = $courseId AND
1127
                        path LIKE BINARY '$path'
1128
                        $sessionCondition
1129
                    LIMIT 1";
1130
1131
            $result = Database::query($sql);
1132
            if (Database::num_rows($result)) {
1133
                $row = Database::fetch_array($result);
1134
1135
                return (int) $row['iid'];
1136
            }
1137
        }
1138
1139
        return false;
1140
    }
1141
1142
    /**
1143
     * Gets the document data with a given id.
1144
     *
1145
     * @param int    $id            Document Id (id field in c_document table)
1146
     * @param string $course_code   Course code
1147
     * @param bool   $load_parents  load folder parents
1148
     * @param int    $session_id    The session ID,
1149
     *                              0 if requires context *out of* session, and null to use global context
1150
     * @param bool   $ignoreDeleted
1151
     *
1152
     * @deprecated  use $repo->find()
1153
     *
1154
     * @return array document content
1155
     */
1156
    public static function get_document_data_by_id(
1157
        $id,
1158
        $course_code,
1159
        $load_parents = false,
1160
        $session_id = null,
1161
        $ignoreDeleted = false
1162
    ) {
1163
        $course_info = api_get_course_info($course_code);
1164
        $course_id = $course_info['real_id'];
1165
1166
        if (empty($course_info)) {
1167
            return false;
1168
        }
1169
1170
        $session_id = empty($session_id) ? api_get_session_id() : (int) $session_id;
1171
        $groupId = api_get_group_id();
1172
1173
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1174
        $id = (int) $id;
1175
        $sessionCondition = api_get_session_condition($session_id, true, true);
1176
1177
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1178
                WHERE iid = $id";
1179
1180
        if ($ignoreDeleted) {
1181
            $sql .= " AND path NOT LIKE '%_DELETED_%' ";
1182
        }
1183
1184
        $result = Database::query($sql);
1185
        $courseParam = '&cid='.$course_id.'&id='.$id.'&sid='.$session_id.'&gid='.$groupId;
1186
        if ($result && 1 == Database::num_rows($result)) {
1187
            $row = Database::fetch_array($result, 'ASSOC');
1188
            //@todo need to clarify the name of the URLs not nice right now
1189
            $url_path = urlencode($row['path']);
1190
            $path = str_replace('%2F', '/', $url_path);
1191
            $pathinfo = pathinfo($row['path']);
1192
1193
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.$courseParam;
1194
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?id='.$id.$courseParam;
1195
            //$row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1196
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1197
            //$row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1198
            //$row['direct_url'] = $www.$path;
1199
            $row['basename'] = basename($row['path']);
1200
1201
            if ('.' == dirname($row['path'])) {
1202
                $row['parent_id'] = '0';
1203
            } else {
1204
                $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), $session_id);
1205
                if (empty($row['parent_id'])) {
1206
                    // Try one more with session id = 0
1207
                    $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), 0);
1208
                }
1209
            }
1210
            $parents = [];
1211
1212
            //Use to generate parents (needed for the breadcrumb)
1213
            //@todo sorry but this for is here because there's not a parent_id in the document table so we parsed the path!!
1214
            if ($load_parents) {
1215
                $dir_array = explode('/', $row['path']);
1216
                $dir_array = array_filter($dir_array);
1217
                $array_len = count($dir_array) + 1;
1218
                $real_dir = '';
1219
1220
                for ($i = 1; $i < $array_len; $i++) {
1221
                    $real_dir .= '/'.(isset($dir_array[$i]) ? $dir_array[$i] : '');
1222
                    $parent_id = self::get_document_id($course_info, $real_dir);
1223
                    if (0 != $session_id && empty($parent_id)) {
1224
                        $parent_id = self::get_document_id($course_info, $real_dir, 0);
1225
                    }
1226
                    if (!empty($parent_id)) {
1227
                        $sub_document_data = self::get_document_data_by_id(
1228
                            $parent_id,
1229
                            $course_code,
1230
                            false,
1231
                            $session_id
1232
                        );
1233
                        if (0 != $session_id and !$sub_document_data) {
1234
                            $sub_document_data = self::get_document_data_by_id(
1235
                                $parent_id,
1236
                                $course_code,
1237
                                false,
1238
                                0
1239
                            );
1240
                        }
1241
                        //@todo add visibility here
1242
                        $parents[] = $sub_document_data;
1243
                    }
1244
                }
1245
            }
1246
            $row['parents'] = $parents;
1247
1248
            return $row;
1249
        }
1250
1251
        return false;
1252
    }
1253
1254
    /**
1255
     * Allow to set a specific document as a new template for CKeditor
1256
     * for a particular user in a particular course.
1257
     *
1258
     * @param string $title
1259
     * @param string $description
1260
     * @param int    $document_id_for_template the document id
1261
     * @param int    $courseId
1262
     * @param int    $user_id
1263
     * @param string $image
1264
     *
1265
     * @return bool
1266
     */
1267
    public static function setDocumentAsTemplate(
1268
        $title,
1269
        $description,
1270
        $document_id_for_template,
1271
        $courseId,
1272
        $user_id,
1273
        $image
1274
    ) {
1275
        // Database table definition
1276
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1277
        $params = [
1278
            'title' => $title,
1279
            'description' => $description,
1280
            'c_id' => $courseId,
1281
            'user_id' => $user_id,
1282
            'ref_doc' => $document_id_for_template,
1283
            'image' => $image,
1284
        ];
1285
        Database::insert($table_template, $params);
1286
1287
        return true;
1288
    }
1289
1290
    /**
1291
     * Unset a document as template.
1292
     *
1293
     * @param int $document_id
1294
     * @param int $courseId
1295
     * @param int $user_id
1296
     */
1297
    public static function unsetDocumentAsTemplate(
1298
        $document_id,
1299
        $courseId,
1300
        $user_id
1301
    ) {
1302
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1303
        $courseId = (int) $courseId;
1304
        $user_id = (int) $user_id;
1305
        $document_id = (int) $document_id;
1306
1307
        $sql = 'SELECT id FROM '.$table_template.'
1308
                WHERE
1309
                    c_id = "'.$courseId.'" AND
1310
                    user_id = "'.$user_id.'" AND
1311
                    ref_doc = "'.$document_id.'"';
1312
        $result = Database::query($sql);
1313
        $template_id = Database::result($result, 0, 0);
1314
1315
        my_delete(api_get_path(SYS_CODE_PATH).'upload/template_thumbnails/'.$template_id.'.jpg');
1316
1317
        $sql = 'DELETE FROM '.$table_template.'
1318
                WHERE
1319
                    c_id ="'.$courseId.'" AND
1320
                    user_id="'.$user_id.'" AND
1321
                    ref_doc="'.$document_id.'"';
1322
1323
        Database::query($sql);
1324
    }
1325
1326
    /**
1327
     * Check document visibility.
1328
     *
1329
     * @param string $doc_path the relative complete path of the document
1330
     * @param array  $course   the _course array info of the document's course
1331
     * @param int
1332
     * @param string
1333
     *
1334
     * @return bool
1335
     */
1336
    public static function is_visible(
1337
        $doc_path,
1338
        $course,
1339
        $session_id = 0,
1340
        $file_type = 'file'
1341
    ) {
1342
        $docTable = Database::get_course_table(TABLE_DOCUMENT);
1343
1344
        $course_id = $course['real_id'];
1345
        // note the extra / at the end of doc_path to match every path in
1346
        // the document table that is part of the document path
1347
        $session_id = (int) $session_id;
1348
        $condition = " AND d.session_id IN  ('$session_id', '0') ";
1349
        // The " d.filetype='file' " let the user see a file even if the folder is hidden see #2198
1350
1351
        /*
1352
          When using hotpotatoes files, a new html files are generated
1353
          in the hotpotatoes folder to display the test.
1354
          The genuine html file is copied to math4.htm(user_id).t.html
1355
          Images files are not copied, and keep same name.
1356
          To check the html file visibility, we don't have to check file math4.htm(user_id).t.html but file math4.htm
1357
          In this case, we have to remove (user_id).t.html to check the visibility of the file
1358
          For images, we just check the path of the image file.
1359
1360
          Exemple of hotpotatoes folder :
1361
          A.jpg
1362
          maths4-consigne.jpg
1363
          maths4.htm
1364
          maths4.htm1.t.html
1365
          maths4.htm52.t.html
1366
          maths4.htm654.t.html
1367
          omega.jpg
1368
          theta.jpg
1369
         */
1370
1371
        if (strpos($doc_path, 'HotPotatoes_files') && preg_match("/\.t\.html$/", $doc_path)) {
1372
            $doc_path = substr($doc_path, 0, strlen($doc_path) - 7 - strlen(api_get_user_id()));
1373
        }
1374
1375
        if (!in_array($file_type, ['file', 'folder'])) {
1376
            $file_type = 'file';
1377
        }
1378
        $doc_path = Database::escape_string($doc_path).'/';
1379
1380
        $sql = "SELECT iid
1381
                FROM $docTable d
1382
        		WHERE
1383
        		    d.c_id  = $course_id AND
1384
        		    $condition AND
1385
        			filetype = '$file_type' AND
1386
        			locate(concat(path,'/'), '$doc_path')=1
1387
                ";
1388
1389
        $result = Database::query($sql);
1390
        $is_visible = false;
1391
        if (Database::num_rows($result) > 0) {
1392
            $row = Database::fetch_array($result, 'ASSOC');
1393
1394
            $em = Database::getManager();
1395
1396
            $repo = $em->getRepository('ChamiloCourseBundle:CDocument');
1397
            /** @var \Chamilo\CourseBundle\Entity\CDocument $document */
1398
            $document = $repo->find($row['iid']);
1399
            if (ResourceLink::VISIBILITY_PUBLISHED === $document->getVisibility()) {
1400
                $is_visible = api_is_allowed_in_course() || api_is_platform_admin();
1401
            }
1402
        }
1403
1404
        /* improved protection of documents viewable directly through the url:
1405
            incorporates the same protections of the course at the url of
1406
            documents:
1407
            access allowed for the whole world Open, access allowed for
1408
            users registered on the platform Private access, document accessible
1409
            only to course members (see the Users list), Completely closed;
1410
            the document is only accessible to the course admin and
1411
            teaching assistants.*/
1412
        //return $_SESSION ['is_allowed_in_course'] || api_is_platform_admin();
1413
        return $is_visible;
1414
    }
1415
1416
    /**
1417
     * Return true if user can see a file.
1418
     *
1419
     * @param   int     document id
1420
     * @param   array   course info
1421
     * @param   int
1422
     * @param   int
1423
     * @param bool
1424
     *
1425
     * @return bool
1426
     */
1427
    public static function is_visible_by_id(
1428
        $doc_id,
1429
        $course_info,
1430
        $sessionId,
1431
        $user_id,
1432
        $admins_can_see_everything = true,
1433
        $userIsSubscribed = null
1434
    ) {
1435
        $user_in_course = false;
1436
1437
        //1. Checking the course array
1438
        if (empty($course_info)) {
1439
            $course_info = api_get_course_info();
1440
            if (empty($course_info)) {
1441
                return false;
1442
            }
1443
        }
1444
1445
        $doc_id = (int) $doc_id;
1446
        $sessionId = (int) $sessionId;
1447
        // 2. Course and Session visibility are handle in local.inc.php/global.inc.php
1448
        // 3. Checking if user exist in course/session
1449
        if (0 == $sessionId) {
1450
            if (is_null($userIsSubscribed)) {
1451
                $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
1452
                    $user_id,
1453
                    $course_info['code']
1454
                );
1455
            }
1456
1457
            if (true === $userIsSubscribed || api_is_platform_admin()) {
1458
                $user_in_course = true;
1459
            }
1460
1461
            // Check if course is open then we can consider that the student is registered to the course
1462
            if (isset($course_info) &&
1463
                in_array(
1464
                    $course_info['visibility'],
1465
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
1466
                )
1467
            ) {
1468
                $user_in_course = true;
1469
            }
1470
        } else {
1471
            $user_status = SessionManager::get_user_status_in_course_session(
1472
                $user_id,
1473
                $course_info['real_id'],
1474
                $sessionId
1475
            );
1476
1477
            if (in_array($user_status, ['0', '2', '6'])) {
1478
                //is true if is an student, course session teacher or coach
1479
                $user_in_course = true;
1480
            }
1481
1482
            if (api_is_platform_admin()) {
1483
                $user_in_course = true;
1484
            }
1485
        }
1486
1487
        $em = Database::getManager();
1488
1489
        // 4. Checking document visibility (i'm repeating the code in order to be more clear when reading ) - jm
1490
        if ($user_in_course) {
1491
            $repo = $em->getRepository('ChamiloCourseBundle:CDocument');
1492
            /** @var CDocument $document */
1493
            $document = $repo->find($doc_id);
1494
            $link = $document->getFirstResourceLinkFromCourseSession($course_info['entity']);
1495
            if ($link && ResourceLink::VISIBILITY_PUBLISHED == $link->getVisibility()) {
1496
                return true;
1497
            }
1498
1499
            return false;
1500
        } elseif ($admins_can_see_everything && api_is_platform_admin()) {
1501
            return true;
1502
        }
1503
1504
        return false;
1505
    }
1506
1507
    /**
1508
     * Allow attach a certificate to a course.
1509
     *
1510
     * @todo move to certificate.lib.php
1511
     *
1512
     * @param int $courseId
1513
     * @param int $document_id
1514
     * @param int $sessionId
1515
     */
1516
    public static function attach_gradebook_certificate($courseId, $document_id, $sessionId = 0)
1517
    {
1518
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1519
        $sessionId = intval($sessionId);
1520
        $courseId = (int) $courseId;
1521
        if (empty($sessionId)) {
1522
            $sessionId = api_get_session_id();
1523
        }
1524
1525
        if (empty($sessionId)) {
1526
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1527
        } elseif ($sessionId > 0) {
1528
            $sql_session = 'AND session_id='.$sessionId;
1529
        } else {
1530
            $sql_session = '';
1531
        }
1532
        $sql = 'UPDATE '.$tbl_category.' SET document_id="'.intval($document_id).'"
1533
                WHERE c_id ="'.$courseId.'" '.$sql_session;
1534
        Database::query($sql);
1535
    }
1536
1537
    /**
1538
     * get the document id of default certificate.
1539
     *
1540
     * @todo move to certificate.lib.php
1541
     *
1542
     * @param int $courseId
1543
     * @param int $session_id
1544
     *
1545
     * @return int The default certificate id
1546
     */
1547
    public static function get_default_certificate_id($courseId, $session_id = 0)
1548
    {
1549
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1550
        $session_id = (int) $session_id;
1551
        $courseId = (int) $courseId;
1552
        if (empty($session_id)) {
1553
            $session_id = api_get_session_id();
1554
        }
1555
1556
        if (empty($session_id)) {
1557
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1558
        } elseif ($session_id > 0) {
1559
            $sql_session = 'AND session_id='.$session_id;
1560
        } else {
1561
            $sql_session = '';
1562
        }
1563
1564
        $sql = 'SELECT document_id FROM '.$tbl_category.'
1565
                WHERE c_id ="'.$courseId.'" '.$sql_session;
1566
1567
        $rs = Database::query($sql);
1568
        $num = Database::num_rows($rs);
1569
        if (0 == $num) {
1570
            return null;
1571
        }
1572
        $row = Database::fetch_array($rs);
1573
1574
        return $row['document_id'];
1575
    }
1576
1577
    /**
1578
     * Allow replace user info in file html.
1579
     *
1580
     * @param int   $user_id
1581
     * @param array $courseInfo
1582
     * @param int   $sessionId
1583
     * @param bool  $is_preview
1584
     *
1585
     * @return array
1586
     */
1587
    public static function replace_user_info_into_html(
1588
        $user_id,
1589
        $courseInfo,
1590
        $sessionId,
1591
        $is_preview = false
1592
    ) {
1593
        $user_id = (int) $user_id;
1594
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1595
        $course_id = $courseInfo['real_id'];
1596
        $document_id = self::get_default_certificate_id($course_id, $sessionId);
1597
1598
        $my_content_html = null;
1599
        if ($document_id) {
1600
            $repo = Container::getDocumentRepository();
1601
            $doc = Container::getDocumentRepository()->find($document_id);
1602
            $new_content = '';
1603
            $all_user_info = [];
1604
            if ($doc) {
1605
                $my_content_html = $repo->getResourceFileContent($doc);
1606
                $all_user_info = self::get_all_info_to_certificate(
1607
                    $user_id,
1608
                    $courseInfo,
1609
                    $is_preview
1610
                );
1611
1612
                $info_to_be_replaced_in_content_html = $all_user_info[0];
1613
                $info_to_replace_in_content_html = $all_user_info[1];
1614
                $new_content = str_replace(
1615
                    $info_to_be_replaced_in_content_html,
1616
                    $info_to_replace_in_content_html,
1617
                    $my_content_html
1618
                );
1619
            }
1620
1621
            return [
1622
                'content' => $new_content,
1623
                'variables' => $all_user_info,
1624
            ];
1625
        }
1626
1627
        return [];
1628
    }
1629
1630
    /**
1631
     * Return all content to replace and all content to be replace.
1632
     *
1633
     * @param int  $user_id
1634
     * @param bool $is_preview
1635
     *
1636
     * @return array
1637
     */
1638
    public static function get_all_info_to_certificate($user_id, $course_info, $sessionId, $is_preview = false)
1639
    {
1640
        $info_list = [];
1641
        $user_id = (int) $user_id;
1642
        $sessionId = (int) $sessionId;
1643
        $courseCode = $course_info['code'];
1644
1645
        // Portal info
1646
        $organization_name = api_get_setting('Institution');
1647
        $portal_name = api_get_setting('siteName');
1648
1649
        // Extra user data information
1650
        $extra_user_info_data = UserManager::get_extra_user_data(
1651
            $user_id,
1652
            false,
1653
            false,
1654
            false,
1655
            true
1656
        );
1657
1658
        // get extra fields
1659
        $extraField = new ExtraField('user');
1660
        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
1661
1662
        // Student information
1663
        $user_info = api_get_user_info($user_id);
1664
        $first_name = $user_info['firstname'];
1665
        $last_name = $user_info['lastname'];
1666
        $username = $user_info['username'];
1667
        $official_code = $user_info['official_code'];
1668
1669
        // Teacher information
1670
        $info_teacher_id = UserManager::get_user_id_of_course_admin_or_session_admin($course_info);
1671
        $teacher_info = api_get_user_info($info_teacher_id);
1672
        $teacher_first_name = $teacher_info['firstname'];
1673
        $teacher_last_name = $teacher_info['lastname'];
1674
1675
        // info gradebook certificate
1676
        $info_grade_certificate = UserManager::get_info_gradebook_certificate($course_info, $sessionId, $user_id);
1677
        $date_long_certificate = '';
1678
        $date_certificate = '';
1679
        $url = '';
1680
        if ($info_grade_certificate) {
1681
            $date_certificate = $info_grade_certificate['created_at'];
1682
            $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$info_grade_certificate['id'];
1683
        }
1684
        $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1685
        if (!empty($date_certificate)) {
1686
            $date_long_certificate = api_convert_and_format_date($date_certificate);
1687
            $date_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_LONG_NO_DAY);
1688
        }
1689
1690
        if ($is_preview) {
1691
            $date_long_certificate = api_convert_and_format_date(api_get_utc_datetime());
1692
            $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1693
        }
1694
1695
        $externalStyleFile = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/certificate.css';
1696
        $externalStyle = '';
1697
        if (is_file($externalStyleFile)) {
1698
            $externalStyle = file_get_contents($externalStyleFile);
1699
        }
1700
        $timeInCourse = Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $sessionId);
1701
        $timeInCourse = api_time_to_hms($timeInCourse, ':', false, true);
1702
1703
        $timeInCourseInAllSessions = 0;
1704
        $sessions = SessionManager::get_session_by_course($course_info['real_id']);
1705
1706
        if (!empty($sessions)) {
1707
            foreach ($sessions as $session) {
1708
                $timeInCourseInAllSessions += Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $session['id']);
1709
            }
1710
        }
1711
        $timeInCourseInAllSessions = api_time_to_hms($timeInCourseInAllSessions, ':', false, true);
1712
1713
        $first = Tracking::get_first_connection_date_on_the_course($user_id, $course_info['real_id'], $sessionId, false);
1714
        $first = substr($first, 0, 10);
1715
        $last = Tracking::get_last_connection_date_on_the_course($user_id, $course_info, $sessionId, false);
1716
        $last = substr($last, 0, 10);
1717
1718
        if ($first === $last) {
1719
            $startDateAndEndDate = get_lang('From').' '.$first;
1720
        } else {
1721
            $startDateAndEndDate = sprintf(
1722
                get_lang('FromDateXToDateY'),
1723
                $first,
1724
                $last
1725
            );
1726
        }
1727
        $courseDescription = new CourseDescription();
1728
        $description = $courseDescription->get_data_by_description_type(2, $course_info['real_id'], $sessionId);
1729
        $courseObjectives = '';
1730
        if ($description) {
1731
            $courseObjectives = $description['description_content'];
1732
        }
1733
1734
        // Replace content
1735
        $info_to_replace_in_content_html = [
1736
            $first_name,
1737
            $last_name,
1738
            $username,
1739
            $organization_name,
1740
            $portal_name,
1741
            $teacher_first_name,
1742
            $teacher_last_name,
1743
            $official_code,
1744
            $date_long_certificate,
1745
            $date_no_time,
1746
            $course_info['code'],
1747
            $course_info['name'],
1748
            isset($info_grade_certificate['grade']) ? $info_grade_certificate['grade'] : '',
1749
            $url,
1750
            '<a href="'.$url.'" target="_blank">'.get_lang('Online link to certificate').'</a>',
1751
            '((certificate_barcode))',
1752
            $externalStyle,
1753
            $timeInCourse,
1754
            $timeInCourseInAllSessions,
1755
            $startDateAndEndDate,
1756
            $courseObjectives,
1757
        ];
1758
1759
        $tags = [
1760
            '((user_firstname))',
1761
            '((user_lastname))',
1762
            '((user_username))',
1763
            '((gradebook_institution))',
1764
            '((gradebook_sitename))',
1765
            '((teacher_firstname))',
1766
            '((teacher_lastname))',
1767
            '((official_code))',
1768
            '((date_certificate))',
1769
            '((date_certificate_no_time))',
1770
            '((course_code))',
1771
            '((course_title))',
1772
            '((gradebook_grade))',
1773
            '((certificate_link))',
1774
            '((certificate_link_html))',
1775
            '((certificate_barcode))',
1776
            '((external_style))',
1777
            '((time_in_course))',
1778
            '((time_in_course_in_all_sessions))',
1779
            '((start_date_and_end_date))',
1780
            '((course_objectives))',
1781
        ];
1782
1783
        if (!empty($extraFields)) {
1784
            foreach ($extraFields as $extraField) {
1785
                $valueExtra = isset($extra_user_info_data[$extraField['variable']]) ? $extra_user_info_data[$extraField['variable']] : '';
1786
                $tags[] = '(('.strtolower($extraField['variable']).'))';
1787
                $info_to_replace_in_content_html[] = $valueExtra;
1788
            }
1789
        }
1790
1791
        $info_list[] = $tags;
1792
        $info_list[] = $info_to_replace_in_content_html;
1793
1794
        return $info_list;
1795
    }
1796
1797
    /**
1798
     * Remove default certificate.
1799
     *
1800
     * @param int $course_id              The course code
1801
     * @param int $default_certificate_id The document id of the default certificate
1802
     */
1803
    public static function remove_attach_certificate($course_id, $default_certificate_id)
1804
    {
1805
        if (empty($default_certificate_id)) {
1806
            return false;
1807
        }
1808
1809
        $default_certificate = self::get_default_certificate_id($course_id);
1810
        if ((int) $default_certificate == (int) $default_certificate_id) {
1811
            $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1812
            $session_id = api_get_session_id();
1813
            if (0 == $session_id || is_null($session_id)) {
1814
                $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
1815
            } elseif ($session_id > 0) {
1816
                $sql_session = 'AND session_id='.intval($session_id);
1817
            } else {
1818
                $sql_session = '';
1819
            }
1820
1821
            $sql = 'UPDATE '.$tbl_category.' SET document_id = null
1822
                    WHERE
1823
                        c_id = "'.Database::escape_string($course_id).'" AND
1824
                        document_id="'.$default_certificate_id.'" '.$sql_session;
1825
            Database::query($sql);
1826
        }
1827
    }
1828
1829
    /**
1830
     * Create directory certificate.
1831
     *
1832
     * @param array $courseInfo
1833
     */
1834
    public static function create_directory_certificate_in_course($courseInfo)
1835
    {
1836
        if (!empty($courseInfo)) {
1837
            $dir_name = '/certificates';
1838
            $post_dir_name = get_lang('Certificates');
1839
            $id = self::get_document_id_of_directory_certificate();
1840
            if (empty($id)) {
1841
                create_unexisting_directory(
1842
                    $courseInfo,
1843
                    api_get_user_id(),
1844
                    api_get_session_id(),
1845
                    0,
1846
                    0,
1847
                    '',
1848
                    $dir_name,
1849
                    $post_dir_name,
1850
                    null,
1851
                    false,
1852
                    false
1853
                );
1854
            }
1855
        }
1856
    }
1857
1858
    /**
1859
     * Get the document id of the directory certificate.
1860
     *
1861
     * @return int The document id of the directory certificate
1862
     *
1863
     * @todo move to certificate.lib.php
1864
     */
1865
    public static function get_document_id_of_directory_certificate()
1866
    {
1867
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1868
        $course_id = api_get_course_int_id();
1869
        $sql = "SELECT id FROM $tbl_document
1870
                WHERE c_id = $course_id AND path='/certificates' ";
1871
        $rs = Database::query($sql);
1872
        $row = Database::fetch_array($rs);
1873
1874
        return $row['id'];
1875
    }
1876
1877
    /**
1878
     * Check if a directory given is for certificate.
1879
     *
1880
     * @todo move to certificate.lib.php
1881
     *
1882
     * @param string $dir path of directory
1883
     *
1884
     * @return bool true if is a certificate or false otherwise
1885
     */
1886
    public static function is_certificate_mode($dir)
1887
    {
1888
        // I'm in the certification module?
1889
        $is_certificate_mode = false;
1890
        $is_certificate_array = explode('/', $dir);
1891
        array_shift($is_certificate_array);
1892
        if (isset($is_certificate_array[0]) && 'certificates' == $is_certificate_array[0]) {
1893
            $is_certificate_mode = true;
1894
        }
1895
1896
        return $is_certificate_mode || (isset($_GET['certificate']) && 'true' === $_GET['certificate']);
1897
    }
1898
1899
    /**
1900
     * Gets the list of included resources as a list of absolute or relative paths from a html file or string html
1901
     * This allows for a better SCORM export or replace urls inside content html from copy course
1902
     * The list will generally include pictures, flash objects, java applets, or any other
1903
     * stuff included in the source of the current item. The current item is expected
1904
     * to be an HTML file or string html. If it is not, then the function will return and empty list.
1905
     *
1906
     * @param    string  source html (content or path)
1907
     * @param    bool    is file or string html
1908
     * @param    string    type (one of the app tools) - optional (otherwise takes the current item's type)
1909
     * @param    int        level of recursivity we're in
1910
     *
1911
     * @return array List of file paths. An additional field containing 'local' or 'remote' helps determine
1912
     *               if the file should be copied into the zip or just linked
1913
     */
1914
    public static function get_resources_from_source_html(
1915
        $source_html,
1916
        $is_file = false,
1917
        $type = null,
1918
        $recursivity = 1
1919
    ) {
1920
        $max = 5;
1921
        $attributes = [];
1922
        $wanted_attributes = [
1923
            'src',
1924
            'url',
1925
            '@import',
1926
            'href',
1927
            'value',
1928
            'flashvars',
1929
            'poster',
1930
        ];
1931
        $explode_attributes = ['flashvars' => 'file'];
1932
        $abs_path = '';
1933
1934
        if ($recursivity > $max) {
1935
            return [];
1936
        }
1937
1938
        if (!isset($type)) {
1939
            $type = TOOL_DOCUMENT;
1940
        }
1941
1942
        if (!$is_file) {
1943
            $attributes = self::parse_HTML_attributes(
1944
                $source_html,
1945
                $wanted_attributes,
1946
                $explode_attributes
1947
            );
1948
        } else {
1949
            if (is_file($source_html)) {
1950
                $abs_path = $source_html;
1951
                //for now, read the whole file in one go (that's gonna be a problem when the file is too big)
1952
                $info = pathinfo($abs_path);
1953
                $ext = $info['extension'];
1954
                switch (strtolower($ext)) {
1955
                    case 'html':
1956
                    case 'htm':
1957
                    case 'shtml':
1958
                    case 'css':
1959
                        $file_content = file_get_contents($abs_path);
1960
                        // get an array of attributes from the HTML source
1961
                        $attributes = self::parse_HTML_attributes(
1962
                            $file_content,
1963
                            $wanted_attributes,
1964
                            $explode_attributes
1965
                        );
1966
                        break;
1967
                    default:
1968
                        break;
1969
                }
1970
            } else {
1971
                return [];
1972
            }
1973
        }
1974
1975
        $files_list = [];
1976
        switch ($type) {
1977
            case TOOL_DOCUMENT:
1978
            case TOOL_QUIZ:
1979
            case 'sco':
1980
                foreach ($wanted_attributes as $attr) {
1981
                    if (isset($attributes[$attr])) {
1982
                        //find which kind of path these are (local or remote)
1983
                        $sources = $attributes[$attr];
1984
                        foreach ($sources as $source) {
1985
                            //skip what is obviously not a resource
1986
                            if (strpos($source, '+this.')) {
1987
                                continue; //javascript code - will still work unaltered
1988
                            }
1989
                            if (false === strpos($source, '.')) {
1990
                                continue; //no dot, should not be an external file anyway
1991
                            }
1992
                            if (strpos($source, 'mailto:')) {
1993
                                continue; //mailto link
1994
                            }
1995
                            if (strpos($source, ';') && !strpos($source, '&amp;')) {
1996
                                continue; //avoid code - that should help
1997
                            }
1998
1999
                            if ('value' == $attr) {
2000
                                if (strpos($source, 'mp3file')) {
2001
                                    $files_list[] = [
2002
                                        substr($source, 0, strpos($source, '.swf') + 4),
2003
                                        'local',
2004
                                        'abs',
2005
                                    ];
2006
                                    $mp3file = substr($source, strpos($source, 'mp3file=') + 8);
2007
                                    if ('/' == substr($mp3file, 0, 1)) {
2008
                                        $files_list[] = [$mp3file, 'local', 'abs'];
2009
                                    } else {
2010
                                        $files_list[] = [$mp3file, 'local', 'rel'];
2011
                                    }
2012
                                } elseif (0 === strpos($source, 'flv=')) {
2013
                                    $source = substr($source, 4);
2014
                                    if (strpos($source, '&') > 0) {
2015
                                        $source = substr($source, 0, strpos($source, '&'));
2016
                                    }
2017
                                    if (strpos($source, '://') > 0) {
2018
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
2019
                                            //we found the current portal url
2020
                                            $files_list[] = [$source, 'local', 'url'];
2021
                                        } else {
2022
                                            //we didn't find any trace of current portal
2023
                                            $files_list[] = [$source, 'remote', 'url'];
2024
                                        }
2025
                                    } else {
2026
                                        $files_list[] = [$source, 'local', 'abs'];
2027
                                    }
2028
                                    /* skipping anything else to avoid two entries
2029
                                    (while the others can have sub-files in their url, flv's can't)*/
2030
                                    continue;
2031
                                }
2032
                            }
2033
                            if (strpos($source, '://') > 0) {
2034
                                //cut at '?' in a URL with params
2035
                                if (strpos($source, '?') > 0) {
2036
                                    $second_part = substr($source, strpos($source, '?'));
2037
                                    if (strpos($second_part, '://') > 0) {
2038
                                        //if the second part of the url contains a url too, treat the second one before cutting
2039
                                        $pos1 = strpos($second_part, '=');
2040
                                        $pos2 = strpos($second_part, '&');
2041
                                        $second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
2042
                                        if (false !== strpos($second_part, api_get_path(WEB_PATH))) {
2043
                                            //we found the current portal url
2044
                                            $files_list[] = [$second_part, 'local', 'url'];
2045
                                            $in_files_list[] = self::get_resources_from_source_html(
2046
                                                $second_part,
2047
                                                true,
2048
                                                TOOL_DOCUMENT,
2049
                                                $recursivity + 1
2050
                                            );
2051
                                            if (count($in_files_list) > 0) {
2052
                                                $files_list = array_merge($files_list, $in_files_list);
2053
                                            }
2054
                                        } else {
2055
                                            //we didn't find any trace of current portal
2056
                                            $files_list[] = [$second_part, 'remote', 'url'];
2057
                                        }
2058
                                    } elseif (strpos($second_part, '=') > 0) {
2059
                                        if ('/' === substr($second_part, 0, 1)) {
2060
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2061
                                            $files_list[] = [$second_part, 'local', 'abs'];
2062
                                            $in_files_list[] = self::get_resources_from_source_html(
2063
                                                $second_part,
2064
                                                true,
2065
                                                TOOL_DOCUMENT,
2066
                                                $recursivity + 1
2067
                                            );
2068
                                            if (count($in_files_list) > 0) {
2069
                                                $files_list = array_merge($files_list, $in_files_list);
2070
                                            }
2071
                                        } elseif (0 === strstr($second_part, '..')) {
2072
                                            //link is relative but going back in the hierarchy
2073
                                            $files_list[] = [$second_part, 'local', 'rel'];
2074
                                            //$dir = api_get_path(SYS_CODE_PATH);//dirname($abs_path);
2075
                                            //$new_abs_path = realpath($dir.'/'.$second_part);
2076
                                            $dir = '';
2077
                                            if (!empty($abs_path)) {
2078
                                                $dir = dirname($abs_path).'/';
2079
                                            }
2080
                                            $new_abs_path = realpath($dir.$second_part);
2081
                                            $in_files_list[] = self::get_resources_from_source_html(
2082
                                                $new_abs_path,
2083
                                                true,
2084
                                                TOOL_DOCUMENT,
2085
                                                $recursivity + 1
2086
                                            );
2087
                                            if (count($in_files_list) > 0) {
2088
                                                $files_list = array_merge($files_list, $in_files_list);
2089
                                            }
2090
                                        } else {
2091
                                            //no starting '/', making it relative to current document's path
2092
                                            if ('./' == substr($second_part, 0, 2)) {
2093
                                                $second_part = substr($second_part, 2);
2094
                                            }
2095
                                            $files_list[] = [$second_part, 'local', 'rel'];
2096
                                            $dir = '';
2097
                                            if (!empty($abs_path)) {
2098
                                                $dir = dirname($abs_path).'/';
2099
                                            }
2100
                                            $new_abs_path = realpath($dir.$second_part);
2101
                                            $in_files_list[] = self::get_resources_from_source_html(
2102
                                                $new_abs_path,
2103
                                                true,
2104
                                                TOOL_DOCUMENT,
2105
                                                $recursivity + 1
2106
                                            );
2107
                                            if (count($in_files_list) > 0) {
2108
                                                $files_list = array_merge($files_list, $in_files_list);
2109
                                            }
2110
                                        }
2111
                                    }
2112
                                    //leave that second part behind now
2113
                                    $source = substr($source, 0, strpos($source, '?'));
2114
                                    if (strpos($source, '://') > 0) {
2115
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
2116
                                            //we found the current portal url
2117
                                            $files_list[] = [$source, 'local', 'url'];
2118
                                            $in_files_list[] = self::get_resources_from_source_html(
2119
                                                $source,
2120
                                                true,
2121
                                                TOOL_DOCUMENT,
2122
                                                $recursivity + 1
2123
                                            );
2124
                                            if (count($in_files_list) > 0) {
2125
                                                $files_list = array_merge($files_list, $in_files_list);
2126
                                            }
2127
                                        } else {
2128
                                            //we didn't find any trace of current portal
2129
                                            $files_list[] = [$source, 'remote', 'url'];
2130
                                        }
2131
                                    } else {
2132
                                        //no protocol found, make link local
2133
                                        if ('/' === substr($source, 0, 1)) {
2134
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2135
                                            $files_list[] = [$source, 'local', 'abs'];
2136
                                            $in_files_list[] = self::get_resources_from_source_html(
2137
                                                $source,
2138
                                                true,
2139
                                                TOOL_DOCUMENT,
2140
                                                $recursivity + 1
2141
                                            );
2142
                                            if (count($in_files_list) > 0) {
2143
                                                $files_list = array_merge($files_list, $in_files_list);
2144
                                            }
2145
                                        } elseif (0 === strstr($source, '..')) {
2146
                                            //link is relative but going back in the hierarchy
2147
                                            $files_list[] = [$source, 'local', 'rel'];
2148
                                            $dir = '';
2149
                                            if (!empty($abs_path)) {
2150
                                                $dir = dirname($abs_path).'/';
2151
                                            }
2152
                                            $new_abs_path = realpath($dir.$source);
2153
                                            $in_files_list[] = self::get_resources_from_source_html(
2154
                                                $new_abs_path,
2155
                                                true,
2156
                                                TOOL_DOCUMENT,
2157
                                                $recursivity + 1
2158
                                            );
2159
                                            if (count($in_files_list) > 0) {
2160
                                                $files_list = array_merge($files_list, $in_files_list);
2161
                                            }
2162
                                        } else {
2163
                                            //no starting '/', making it relative to current document's path
2164
                                            if ('./' == substr($source, 0, 2)) {
2165
                                                $source = substr($source, 2);
2166
                                            }
2167
                                            $files_list[] = [$source, 'local', 'rel'];
2168
                                            $dir = '';
2169
                                            if (!empty($abs_path)) {
2170
                                                $dir = dirname($abs_path).'/';
2171
                                            }
2172
                                            $new_abs_path = realpath($dir.$source);
2173
                                            $in_files_list[] = self::get_resources_from_source_html(
2174
                                                $new_abs_path,
2175
                                                true,
2176
                                                TOOL_DOCUMENT,
2177
                                                $recursivity + 1
2178
                                            );
2179
                                            if (count($in_files_list) > 0) {
2180
                                                $files_list = array_merge($files_list, $in_files_list);
2181
                                            }
2182
                                        }
2183
                                    }
2184
                                }
2185
                                //found some protocol there
2186
                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
2187
                                    //we found the current portal url
2188
                                    $files_list[] = [$source, 'local', 'url'];
2189
                                    $in_files_list[] = self::get_resources_from_source_html(
2190
                                        $source,
2191
                                        true,
2192
                                        TOOL_DOCUMENT,
2193
                                        $recursivity + 1
2194
                                    );
2195
                                    if (count($in_files_list) > 0) {
2196
                                        $files_list = array_merge($files_list, $in_files_list);
2197
                                    }
2198
                                } else {
2199
                                    //we didn't find any trace of current portal
2200
                                    $files_list[] = [$source, 'remote', 'url'];
2201
                                }
2202
                            } else {
2203
                                //no protocol found, make link local
2204
                                if ('/' === substr($source, 0, 1)) {
2205
                                    //link starts with a /, making it absolute (relative to DocumentRoot)
2206
                                    $files_list[] = [$source, 'local', 'abs'];
2207
                                    $in_files_list[] = self::get_resources_from_source_html(
2208
                                        $source,
2209
                                        true,
2210
                                        TOOL_DOCUMENT,
2211
                                        $recursivity + 1
2212
                                    );
2213
                                    if (count($in_files_list) > 0) {
2214
                                        $files_list = array_merge($files_list, $in_files_list);
2215
                                    }
2216
                                } elseif (0 === strpos($source, '..')) {
2217
                                    //link is relative but going back in the hierarchy
2218
                                    $files_list[] = [$source, 'local', 'rel'];
2219
                                    $dir = '';
2220
                                    if (!empty($abs_path)) {
2221
                                        $dir = dirname($abs_path).'/';
2222
                                    }
2223
                                    $new_abs_path = realpath($dir.$source);
2224
                                    $in_files_list[] = self::get_resources_from_source_html(
2225
                                        $new_abs_path,
2226
                                        true,
2227
                                        TOOL_DOCUMENT,
2228
                                        $recursivity + 1
2229
                                    );
2230
                                    if (count($in_files_list) > 0) {
2231
                                        $files_list = array_merge($files_list, $in_files_list);
2232
                                    }
2233
                                } else {
2234
                                    //no starting '/', making it relative to current document's path
2235
                                    if ('./' == substr($source, 0, 2)) {
2236
                                        $source = substr($source, 2);
2237
                                    }
2238
                                    $files_list[] = [$source, 'local', 'rel'];
2239
                                    $dir = '';
2240
                                    if (!empty($abs_path)) {
2241
                                        $dir = dirname($abs_path).'/';
2242
                                    }
2243
                                    $new_abs_path = realpath($dir.$source);
2244
                                    $in_files_list[] = self::get_resources_from_source_html(
2245
                                        $new_abs_path,
2246
                                        true,
2247
                                        TOOL_DOCUMENT,
2248
                                        $recursivity + 1
2249
                                    );
2250
                                    if (count($in_files_list) > 0) {
2251
                                        $files_list = array_merge($files_list, $in_files_list);
2252
                                    }
2253
                                }
2254
                            }
2255
                        }
2256
                    }
2257
                }
2258
                break;
2259
            default: //ignore
2260
                break;
2261
        }
2262
2263
        $checked_files_list = [];
2264
        $checked_array_list = [];
2265
2266
        if (count($files_list) > 0) {
2267
            foreach ($files_list as $idx => $file) {
2268
                if (!empty($file[0])) {
2269
                    if (!in_array($file[0], $checked_files_list)) {
2270
                        $checked_files_list[] = $files_list[$idx][0];
2271
                        $checked_array_list[] = $files_list[$idx];
2272
                    }
2273
                }
2274
            }
2275
        }
2276
2277
        return $checked_array_list;
2278
    }
2279
2280
    /**
2281
     * Parses the HTML attributes given as string.
2282
     *
2283
     * @param string HTML attribute string
2284
     * @param array List of attributes that we want to get back
2285
     * @param array
2286
     *
2287
     * @return array An associative array of attributes
2288
     *
2289
     * @author Based on a function from the HTML_Common2 PEAR module     *
2290
     */
2291
    public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
2292
    {
2293
        $attributes = [];
2294
        $regs = [];
2295
        $reduced = false;
2296
        if (count($wanted) > 0) {
2297
            $reduced = true;
2298
        }
2299
        try {
2300
            //Find all occurences of something that looks like a URL
2301
            // The structure of this regexp is:
2302
            // (find protocol) then
2303
            // (optionally find some kind of space 1 or more times) then
2304
            // find (either an equal sign or a bracket) followed by an optional space
2305
            // followed by some text without quotes (between quotes itself or not)
2306
            // then possible closing brackets if we were in the opening bracket case
2307
            // OR something like @import()
2308
            $res = preg_match_all(
2309
                '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]*))'.
2310
                // '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]|[^\x00-\x7F])*)' . -> seems to be taking too much
2311
                // '/(((([A-Za-z_:])([^\x00-\x7F])*)' . -> takes only last letter of parameter name
2312
                '([ \n\t\r]+)?('.
2313
                // '(=([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+))' . -> doesn't restrict close enough to the url itself
2314
                '(=([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+))'.
2315
                '|'.
2316
                // '(\(([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)\))' . -> doesn't restrict close enough to the url itself
2317
                '(\(([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+)\))'.
2318
                '))'.
2319
                '|'.
2320
                // '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))?/', -> takes a lot (like 100's of thousands of empty possibilities)
2321
                '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))/',
2322
                $attrString,
2323
                $regs
2324
            );
2325
        } catch (Exception $e) {
2326
            error_log('Caught exception: '.$e->getMessage(), 0);
2327
        }
2328
        if ($res) {
2329
            for ($i = 0; $i < count($regs[1]); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2330
                $name = trim($regs[3][$i]);
2331
                $check = trim($regs[0][$i]);
2332
                $value = trim($regs[10][$i]);
2333
                if (empty($value) and !empty($regs[13][$i])) {
2334
                    $value = $regs[13][$i];
2335
                }
2336
                if (empty($name) && !empty($regs[16][$i])) {
2337
                    $name = '@import';
2338
                    $value = trim($regs[16][$i]);
2339
                }
2340
                if (!empty($name)) {
2341
                    if (!$reduced || in_array(strtolower($name), $wanted)) {
2342
                        if ($name == $check) {
2343
                            $attributes[strtolower($name)][] = strtolower($name);
2344
                        } else {
2345
                            if (!empty($value) && ('\'' == $value[0] || '"' == $value[0])) {
2346
                                $value = substr($value, 1, -1);
2347
                            }
2348
2349
                            if ('API.LMSGetValue(name' == $value) {
2350
                                $value = 'API.LMSGetValue(name)';
2351
                            }
2352
                            //Gets the xx.flv value from the string flashvars="width=320&height=240&autostart=false&file=xxx.flv&repeat=false"
2353
                            if (isset($explode_variables[$name])) {
2354
                                $value_modified = str_replace('&amp;', '&', $value);
2355
                                $value_array = explode('&', $value_modified);
2356
                                foreach ($value_array as $item) {
2357
                                    $itemParts = explode('=', $item);
2358
                                    $key = $itemParts[0];
2359
                                    $item_value = !empty($itemParts[1]) ? $itemParts[1] : '';
2360
                                    if ($key == $explode_variables[$name]) {
2361
                                        $attributes[strtolower($name)][] = $item_value;
2362
                                    }
2363
                                }
2364
                            }
2365
                            $attributes[strtolower($name)][] = $value;
2366
                        }
2367
                    }
2368
                }
2369
            }
2370
        }
2371
2372
        return $attributes;
2373
    }
2374
2375
    /**
2376
     * Replace urls inside content html from a copy course.
2377
     *
2378
     * @param string $content_html
2379
     * @param string $origin_course_code
2380
     * @param string $destination_course_directory
2381
     * @param string $origin_course_path_from_zip
2382
     * @param string $origin_course_info_path
2383
     *
2384
     * @return string new content html with replaced urls or return false if content is not a string
2385
     */
2386
    public static function replaceUrlWithNewCourseCode(
2387
        $content_html,
2388
        $origin_course_code,
2389
        $destination_course_directory,
2390
        $origin_course_path_from_zip = null,
2391
        $origin_course_info_path = null
2392
    ) {
2393
        if (empty($content_html)) {
2394
            return false;
2395
        }
2396
2397
        $orig_source_html = self::get_resources_from_source_html($content_html);
2398
        $orig_course_info = api_get_course_info($origin_course_code);
2399
2400
        // Course does not exist in the current DB probably this came from a zip file?
2401
        if (empty($orig_course_info)) {
2402
            if (!empty($origin_course_path_from_zip)) {
2403
                $orig_course_path = $origin_course_path_from_zip.'/';
2404
                $orig_course_info_path = $origin_course_info_path;
2405
            }
2406
        } else {
2407
            $orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
2408
            $orig_course_info_path = $orig_course_info['path'];
2409
        }
2410
2411
        $destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
2412
        $destination_course_info = api_get_course_info($destination_course_code);
2413
        $dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
2414
        $dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
2415
2416
        $user_id = api_get_user_id();
2417
2418
        if (!empty($orig_source_html)) {
2419
            foreach ($orig_source_html as $source) {
2420
                // Get information about source url
2421
                $real_orig_url = $source[0]; // url
2422
                $scope_url = $source[1]; // scope (local, remote)
2423
                $type_url = $source[2]; // type (rel, abs, url)
2424
2425
                // Get path and query from origin url
2426
                $orig_parse_url = parse_url($real_orig_url);
2427
                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
2428
                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
2429
2430
                // Replace origin course code by destination course code from origin url query
2431
                $dest_url_query = '';
2432
2433
                if (!empty($real_orig_query)) {
2434
                    $dest_url_query = '?'.$real_orig_query;
2435
                    if (false !== strpos($dest_url_query, $origin_course_code)) {
2436
                        $dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
2437
                    }
2438
                }
2439
2440
                if ('local' == $scope_url) {
2441
                    if ('abs' == $type_url || 'rel' == $type_url) {
2442
                        $document_file = strstr($real_orig_path, 'document');
2443
2444
                        if (false !== strpos($real_orig_path, $document_file)) {
2445
                            $origin_filepath = $orig_course_path.$document_file;
2446
                            $destination_filepath = $dest_course_path.$document_file;
2447
2448
                            // copy origin file inside destination course
2449
                            if (file_exists($origin_filepath)) {
2450
                                $filepath_dir = dirname($destination_filepath);
2451
2452
                                if (!is_dir($filepath_dir)) {
2453
                                    $perm = api_get_permissions_for_new_directories();
2454
                                    $result = @mkdir($filepath_dir, $perm, true);
2455
                                    if ($result) {
2456
                                        $filepath_to_add = str_replace(
2457
                                            [$dest_course_path, 'document'],
2458
                                            '',
2459
                                            $filepath_dir
2460
                                        );
2461
2462
                                        // Add to item properties to the new folder
2463
                                        self::addDocument(
2464
                                            $destination_course_info,
2465
                                            $filepath_to_add,
2466
                                            'folder',
2467
                                            0,
2468
                                            basename($filepath_to_add)
2469
                                        );
2470
                                    }
2471
                                }
2472
2473
                                if (!file_exists($destination_filepath)) {
2474
                                    $result = @copy($origin_filepath, $destination_filepath);
2475
                                    if ($result) {
2476
                                        $filepath_to_add = str_replace(
2477
                                            [$dest_course_path, 'document'],
2478
                                            '',
2479
                                            $destination_filepath
2480
                                        );
2481
                                        $size = filesize($destination_filepath);
2482
2483
                                        // Add to item properties to the file
2484
                                        self::addDocument(
2485
                                            $destination_course_info,
2486
                                            $filepath_to_add,
2487
                                            'file',
2488
                                            $size,
2489
                                            basename($filepath_to_add)
2490
                                        );
2491
                                    }
2492
                                }
2493
                            }
2494
2495
                            // Replace origin course path by destination course path.
2496
                            if (false !== strpos($content_html, $real_orig_url)) {
2497
                                $url_course_path = str_replace(
2498
                                    $orig_course_info_path.'/'.$document_file,
2499
                                    '',
2500
                                    $real_orig_path
2501
                                );
2502
                                // See BT#7780
2503
                                $destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
2504
                                // If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
2505
                                if (false === strpos($real_orig_path, $origin_course_code)) {
2506
                                    $url_course_path = $real_orig_path;
2507
                                    $destination_url = $real_orig_path;
2508
                                }
2509
                                $content_html = str_replace($real_orig_url, $destination_url, $content_html);
2510
                            }
2511
                        }
2512
2513
                        // replace origin course code by destination course code  from origin url
2514
                        if (0 === strpos($real_orig_url, '?')) {
2515
                            $dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
2516
                            $content_html = str_replace($real_orig_url, $dest_url, $content_html);
2517
                        }
2518
                    }
2519
                }
2520
            }
2521
        }
2522
2523
        return $content_html;
2524
    }
2525
2526
    /**
2527
     * Export document to PDF.
2528
     *
2529
     * @param int    $documentId
2530
     * @param string $courseCode
2531
     * @param string $orientation
2532
     * @param bool   $showHeaderAndFooter
2533
     */
2534
    public static function export_to_pdf(
2535
        $documentId,
2536
        $courseCode,
2537
        $orientation = 'landscape',
2538
        $showHeaderAndFooter = true
2539
    ) {
2540
        $repo = Container::getDocumentRepository();
2541
        $document = $repo->find($documentId);
2542
2543
        if (empty($document)) {
2544
            return false;
2545
        }
2546
2547
        $filePath = $repo->getDocumentPath($documentId);
2548
2549
        if (empty($filePath)) {
2550
            return false;
2551
        }
2552
2553
        $title = $document->getTitle();
2554
        //$filePath = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$document_data['path'];
2555
        $pageFormat = 'A4';
2556
        $pdfOrientation = 'P';
2557
        if ('landscape' === $orientation) {
2558
            $pageFormat = 'A4-L';
2559
            $pdfOrientation = 'L';
2560
        }
2561
2562
        $pdf = new PDF(
2563
            $pageFormat,
2564
            $pdfOrientation,
2565
            $showHeaderAndFooter ? [] : ['top' => 0, 'left' => 0, 'bottom' => 0, 'right' => 0]
2566
        );
2567
2568
        if (api_get_configuration_value('use_alternative_document_pdf_footer')) {
2569
            $view = new Template('', false, false, false, true, false, false);
2570
            $template = $view->get_template('export/alt_pdf_footer.tpl');
2571
2572
            $pdf->set_custom_footer([
2573
                'html' => $view->fetch($template),
2574
            ]);
2575
        }
2576
2577
        $pdf->html_to_pdf(
2578
            $filePath,
2579
            $title,
2580
            $courseCode,
2581
            false,
2582
            $showHeaderAndFooter
2583
        );
2584
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2585
    }
2586
2587
    /**
2588
     * Uploads a document.
2589
     *
2590
     * @param array  $files                   the $_FILES variable
2591
     * @param string $path
2592
     * @param string $title
2593
     * @param string $comment
2594
     * @param int    $unzip                   unzip or not the file
2595
     * @param string $ifExists                overwrite, rename or warn (default)
2596
     * @param bool   $index_document          index document (search xapian module)
2597
     * @param bool   $show_output             print html messages
2598
     * @param string $fileKey
2599
     * @param bool   $treat_spaces_as_hyphens
2600
     * @param int    $parentId
2601
     * @param string $content
2602
     *
2603
     * @return CDocument|false
2604
     */
2605
    public static function upload_document(
2606
        $files,
2607
        $path,
2608
        $title = '',
2609
        $comment = '',
2610
        $unzip = 0,
2611
        $ifExists = '',
2612
        $index_document = false,
2613
        $show_output = false,
2614
        $fileKey = 'file',
2615
        $treat_spaces_as_hyphens = true,
2616
        $parentId = 0,
2617
        $content = null
2618
    ) {
2619
        $course_info = api_get_course_info();
2620
        $sessionId = api_get_session_id();
2621
2622
        if (isset($files[$fileKey])) {
2623
            $uploadOk = process_uploaded_file($files[$fileKey], $show_output);
2624
2625
            if ($uploadOk) {
2626
                $document = handle_uploaded_document(
2627
                    $course_info,
2628
                    $files[$fileKey],
2629
                    null,
2630
                    $path,
2631
                    api_get_user_id(),
2632
                    api_get_group_id(),
2633
                    null,
2634
                    $unzip,
2635
                    $ifExists,
2636
                    $show_output,
2637
                    false,
2638
                    null,
2639
                    $sessionId,
2640
                    $treat_spaces_as_hyphens,
2641
                    $fileKey,
2642
                    $parentId,
2643
                    $content
2644
                );
2645
2646
                // Showing message when sending zip files
2647
                if ($document && 1 == $unzip) {
2648
                    if ($show_output) {
2649
                        echo Display::return_message(
2650
                            get_lang('File upload succeeded!').'<br />',
2651
                            'confirm',
2652
                            false
2653
                        );
2654
                    }
2655
2656
                    return $document;
2657
                }
2658
2659
                if ($document) {
2660
                    if ($index_document) {
2661
                        self::index_document(
2662
                            $document->getIid(),
2663
                            $course_info['code'],
2664
                            null,
2665
                            $_POST['language'] ?? '',
2666
                            $_REQUEST,
2667
                            $ifExists
2668
                        );
2669
                    }
2670
2671
                    return $document;
2672
                }
2673
            }
2674
        }
2675
2676
        return false;
2677
    }
2678
2679
    /**
2680
     * Obtains the text inside the file with the right parser.
2681
     */
2682
    public static function get_text_content($doc_path, $doc_mime)
2683
    {
2684
        // TODO: review w$ compatibility
2685
        // Use usual exec output lines array to store stdout instead of a temp file
2686
        // because we need to store it at RAM anyway before index on ChamiloIndexer object
2687
        $ret_val = null;
2688
        switch ($doc_mime) {
2689
            case 'text/plain':
2690
                $handle = fopen($doc_path, 'r');
2691
                $output = [fread($handle, filesize($doc_path))];
2692
                fclose($handle);
2693
                break;
2694
            case 'application/pdf':
2695
                exec("pdftotext $doc_path -", $output, $ret_val);
2696
                break;
2697
            case 'application/postscript':
2698
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
2699
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
2700
                if (0 !== $ret_val) { // shell fail, probably 127 (command not found)
2701
                    return false;
2702
                }
2703
                exec("pdftotext $temp_file -", $output, $ret_val);
2704
                unlink($temp_file);
2705
                break;
2706
            case 'application/msword':
2707
                exec("catdoc $doc_path", $output, $ret_val);
2708
                break;
2709
            case 'text/html':
2710
                exec("html2text $doc_path", $output, $ret_val);
2711
                break;
2712
            case 'text/rtf':
2713
                // Note: correct handling of code pages in unrtf
2714
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
2715
                exec("unrtf --text $doc_path", $output, $ret_val);
2716
                if (127 == $ret_val) { // command not found
2717
                    return false;
2718
                }
2719
                // Avoid index unrtf comments
2720
                if (is_array($output) && count($output) > 1) {
2721
                    $parsed_output = [];
2722
                    foreach ($output as &$line) {
2723
                        if (!preg_match('/^###/', $line, $matches)) {
2724
                            if (!empty($line)) {
2725
                                $parsed_output[] = $line;
2726
                            }
2727
                        }
2728
                    }
2729
                    $output = $parsed_output;
2730
                }
2731
                break;
2732
            case 'application/vnd.ms-powerpoint':
2733
                exec("catppt $doc_path", $output, $ret_val);
2734
                break;
2735
            case 'application/vnd.ms-excel':
2736
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
2737
                break;
2738
        }
2739
2740
        $content = '';
2741
        if (!is_null($ret_val)) {
2742
            if (0 !== $ret_val) { // shell fail, probably 127 (command not found)
2743
                return false;
2744
            }
2745
        }
2746
        if (isset($output)) {
2747
            foreach ($output as &$line) {
2748
                $content .= $line."\n";
2749
            }
2750
2751
            return $content;
2752
        } else {
2753
            return false;
2754
        }
2755
    }
2756
2757
    /**
2758
     * Display the document quota in a simple way.
2759
     *
2760
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
2761
     */
2762
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
2763
    {
2764
        $course_quota_m = round($course_quota / 1048576);
2765
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
2766
        $percentage = $already_consumed_space / $course_quota * 100;
2767
        $percentage = round($percentage, 1);
2768
        $message = get_lang('You are currently using %s MB (%s) of your %s MB.');
2769
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
2770
2771
        return Display::div($message, ['id' => 'document_quota', 'class' => 'card-quota']);
2772
    }
2773
2774
    /**
2775
     * Checks if there is enough place to add a file on a directory
2776
     * on the base of a maximum directory size allowed.
2777
     *
2778
     * @author Bert Vanderkimpen
2779
     *
2780
     * @param int $file_size     size of the file in byte
2781
     * @param int $max_dir_space maximum size
2782
     *
2783
     * @return bool true if there is enough space, false otherwise
2784
     */
2785
    public static function enough_space($file_size, $max_dir_space)
2786
    {
2787
        if ($max_dir_space) {
2788
            $courseEntity = api_get_course_entity();
2789
            $repo = Container::getDocumentRepository();
2790
            $total = $repo->getFolderSize($courseEntity->getResourceNode(), $courseEntity);
2791
2792
            if (($file_size + $total) > $max_dir_space) {
2793
                return false;
2794
            }
2795
        }
2796
2797
        return true;
2798
    }
2799
2800
    /**
2801
     * @param array $params count, url, extension
2802
     *
2803
     * @return string
2804
     */
2805
    public static function generateAudioJavascript($params = [])
2806
    {
2807
        $js = '
2808
            $(\'audio.audio_preview\').mediaelementplayer({
2809
                features: [\'playpause\'],
2810
                audioWidth: 30,
2811
                audioHeight: 30,
2812
                success: function(mediaElement, originalNode, instance) {
2813
                }
2814
            });';
2815
2816
        return $js;
2817
    }
2818
2819
    /**
2820
     * Shows a play icon next to the document title in the document list.
2821
     *
2822
     * @param string $documentWebPath
2823
     * @param array  $documentInfo
2824
     *
2825
     * @return string
2826
     */
2827
    public static function generateAudioPreview($documentWebPath, $documentInfo)
2828
    {
2829
        $filePath = $documentWebPath.$documentInfo['path'];
2830
        $extension = $documentInfo['file_extension'];
2831
        $html = '<span class="preview"> <audio class="audio_preview skip" src="'.$filePath.'" type="audio/'.$extension.'" > </audio></span>';
2832
2833
        return $html;
2834
    }
2835
2836
    /**
2837
     * @param string $file
2838
     * @param string $extension
2839
     *
2840
     * @return string
2841
     */
2842
    public static function generateMediaPreview($file, $extension)
2843
    {
2844
        $id = api_get_unique_id();
2845
        switch ($extension) {
2846
            case 'ogg':
2847
            case 'mp3':
2848
                $document_data['file_extension'] = $extension;
2849
                $html = '<div style="margin: 0; position: absolute; top: 50%; left: 35%;">';
2850
                $html .= '<audio id="'.$id.'" controls="controls" src="'.$file.'" type="audio/mp3" ></audio></div>';
2851
                break;
2852
            default:
2853
                $html = '<video id="'.$id.'" controls>';
2854
                $html .= '<source src="'.$file.'" >';
2855
                $html .= '</video>';
2856
                break;
2857
        }
2858
2859
        return $html;
2860
    }
2861
2862
    /**
2863
     * @param array  $course_info
2864
     * @param bool   $lp_id
2865
     * @param string $target
2866
     * @param int    $session_id
2867
     * @param bool   $add_move_button
2868
     * @param string $filter_by_folder
2869
     * @param string $overwrite_url
2870
     * @param bool   $showInvisibleFiles
2871
     * @param bool   $showOnlyFolders
2872
     * @param int    $folderId
2873
     * @param bool   $addCloseButton
2874
     * @param bool   $addAudioPreview
2875
     * @param array  $filterByExtension
2876
     *
2877
     * @return string
2878
     */
2879
    public static function get_document_preview(
2880
        $course_info,
2881
        $lp_id = false,
2882
        $target = '',
2883
        $session_id = 0,
2884
        $add_move_button = false,
2885
        $filter_by_folder = null,
2886
        $overwrite_url = '',
2887
        $showInvisibleFiles = false,
2888
        $showOnlyFolders = false,
2889
        $folderId = false,
2890
        $addCloseButton = true,
2891
        $addAudioPreview = false,
2892
        $filterByExtension = []
2893
    ) {
2894
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
2895
            return '';
2896
        }
2897
2898
        $repo = Container::getDocumentRepository();
2899
        $nodeRepository = $repo->getResourceNodeRepository();
2900
        $move = get_lang('Move');
2901
        $icon = Display::return_icon('move_everywhere.png', $move, null, ICON_SIZE_TINY);
2902
        $folderIcon = Display::return_icon('lp_folder.png');
2903
2904
        $options = [
2905
            'decorate' => true,
2906
            'rootOpen' => '<ul id="doc_list" class="list-group lp_resource">',
2907
            'rootClose' => '</ul>',
2908
            //'childOpen' => '<li class="doc_resource lp_resource_element ">',
2909
            'childOpen' => function($child) {
2910
                $id =  $child['id'];
2911
                $disableDrag = '';
2912
                if (!$child['resourceFile']) {
2913
                    $disableDrag = ' disable_drag ';
2914
                }
2915
2916
                return '<li
2917
                    id="'.$id.'"
2918
                    data-id="'.$id.'"
2919
                    class=" '.$disableDrag.' list-group-item nested-'.$child['level'].'"
2920
                >';
2921
            },
2922
            'childClose' => '</li>',
2923
            'nodeDecorator' => function ($node) use ($icon, $folderIcon) {
2924
                $disableDrag = '';
2925
                if (!$node['resourceFile']) {
2926
                    $disableDrag = ' disable_drag ';
2927
                }
2928
2929
                $link = '<div class="flex flex-row gap-1 h-4 item_data '.$disableDrag.' ">';
2930
                $file = $node['resourceFile'];
2931
                $extension = '';
2932
                if ($file) {
2933
                    $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
2934
                }
2935
2936
                $folder = $folderIcon;
2937
2938
                if ($node['resourceFile']) {
2939
                    $link .= '<a class="moved ui-sortable-handle" href="#">';
2940
                    $link .= $icon;
2941
                    $link .= '</a>';
2942
                    $folder = '';
2943
                }
2944
2945
                $link .= '<a
2946
                    data_id="'.$node['id'].'"
2947
                    data_type="document"
2948
                    class="moved ui-sortable-handle link_with_id"
2949
                >';
2950
                $link .= $folder.'&nbsp;';
2951
                $link .= '</a>';
2952
                $link .= cut(addslashes($node['title']), 30);
2953
                $link .= '</div>';
2954
2955
                return $link;
2956
            },
2957
        ];
2958
2959
        $type = $repo->getResourceType();
2960
        $em = Database::getManager();
2961
        $qb = $em
2962
            ->createQueryBuilder()
2963
            ->select('node')
2964
            ->from(ResourceNode::class, 'node')
2965
            ->innerJoin('node.resourceType', 'type')
2966
            ->innerJoin('node.resourceLinks', 'links')
2967
            ->leftJoin('node.resourceFile', 'file')
2968
            ->where('type = :type')
2969
            ->andWhere('links.course = :course')
2970
            ->setParameters(['type' => $type, 'course' => $course_info['entity']])
2971
            ->orderBy('node.parent', 'ASC')
2972
            ->addSelect('file')
2973
        ;
2974
2975
        if (!empty($filterByExtension)) {
2976
            $orX = $qb->expr()->orX();
2977
            foreach ($filterByExtension as $extension) {
2978
                $orX->add($qb->expr()->like('file.originalName', ':'.$extension));
2979
                $qb->setParameter($extension, '%'.$extension);
2980
            }
2981
            $qb->andWhere($orX);
2982
        }
2983
        $query = $qb->getQuery();
2984
2985
        return $nodeRepository->buildTree($query->getArrayResult(), $options);
2986
2987
        return $tree;
0 ignored issues
show
Unused Code introduced by
return $tree is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2988
        $nodeRepository->buildTree($query->getResult(), $options);
2989
2990
        $user_id = api_get_user_id();
2991
        $userInfo = api_get_user_info();
2992
        $user_in_course = api_is_platform_admin();
2993
        if (!$user_in_course) {
2994
            if (CourseManager::isCourseTeacher($user_id, $course_info['real_id'])) {
2995
                $user_in_course = true;
2996
            }
2997
        }
2998
2999
        // Condition for the session
3000
        $session_id = (int) $session_id;
3001
3002
        if (!$user_in_course) {
3003
            if (empty($session_id)) {
3004
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
3005
                    $user_in_course = true;
3006
                }
3007
                // Check if course is open then we can consider that the student is registered to the course
3008
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3009
                    $user_in_course = true;
3010
                }
3011
            } else {
3012
                $user_status = SessionManager::get_user_status_in_course_session(
3013
                    $user_id,
3014
                    $course_info['real_id'],
3015
                    $session_id
3016
                );
3017
                //is true if is an student, course session teacher or coach
3018
                if (in_array($user_status, ['0', '2', '6'])) {
3019
                    $user_in_course = true;
3020
                }
3021
            }
3022
        }
3023
3024
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3025
3026
        $add_folder_filter = null;
3027
        if (!empty($filter_by_folder)) {
3028
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3029
        }
3030
3031
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3032
        $lp_visibility_condition = null;
3033
        if ($lp_id) {
3034
            if ($showInvisibleFiles) {
3035
                $lp_visibility_condition .= ' OR l.visibility = 0';
3036
            }
3037
        }
3038
3039
        $folderCondition = " AND docs.path LIKE '/%' ";
3040
        if (!api_is_allowed_to_edit()) {
3041
            $protectedFolders = self::getProtectedFolderFromStudent();
3042
            foreach ($protectedFolders as $folder) {
3043
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3044
            }
3045
        }
3046
3047
        $parentData = [];
3048
        if (false !== $folderId) {
3049
            $parentData = self::get_document_data_by_id(
3050
                $folderId,
3051
                $course_info['code'],
3052
                false,
3053
                $session_id
3054
            );
3055
            if (!empty($parentData)) {
3056
                $cleanedPath = $parentData['path'];
3057
                $num = substr_count($cleanedPath, '/');
3058
3059
                $notLikeCondition = '';
3060
                for ($i = 1; $i <= $num; $i++) {
3061
                    $repeat = str_repeat('/%', $i + 1);
3062
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3063
                }
3064
3065
                $folderId = (int) $folderId;
3066
                $folderCondition = " AND
3067
                    docs.id <> $folderId AND
3068
                    docs.path LIKE '".$cleanedPath."/%'
3069
                    $notLikeCondition
3070
                ";
3071
            } else {
3072
                $folderCondition = " AND docs.filetype = 'file' ";
3073
            }
3074
        }
3075
3076
        $levelCondition = '';
3077
        if (false === $folderId) {
3078
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3079
        }
3080
3081
        $sql = "SELECT DISTINCT l.visibility, docs.*
3082
                FROM resource_node AS n
3083
                INNER JOIN $tbl_doc AS docs
3084
                ON (docs.resource_node_id = n.id)
3085
                INNER JOIN resource_link l
3086
                ON (l.resource_node_id = n.id)
3087
                WHERE
3088
                    l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
3089
                    $folderCondition
3090
                    $levelCondition
3091
                    $add_folder_filter
3092
                ORDER BY docs.filetype DESC, docs.title ASC";
3093
3094
        $res_doc = Database::query($sql);
3095
        $resources = Database::store_result($res_doc, 'ASSOC');
3096
3097
        $return = '';
3098
        if (false == $lp_id && $addCloseButton) {
3099
            if (false === $folderId) {
3100
                $return .= Display::div(
3101
                    Display::url(
3102
                        Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3103
                        ' javascript:void(0);',
3104
                        ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3105
                    ),
3106
                    ['style' => 'position:absolute;right:10px']
3107
                );
3108
            }
3109
        }
3110
3111
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3112
        $newResources = [];
3113
        if (!empty($resources) && $user_in_course) {
3114
            foreach ($resources as $resource) {
3115
                $is_visible = self::is_visible_by_id(
3116
                    $resource['id'],
3117
                    $course_info,
3118
                    $session_id,
3119
                    api_get_user_id()
3120
                );
3121
3122
                if (false === $showInvisibleFiles) {
3123
                    if (!$is_visible) {
3124
                        continue;
3125
                    }
3126
                }
3127
3128
                $newResources[] = $resource;
3129
            }
3130
        }
3131
3132
        $label = get_lang('Documents');
3133
3134
        $documents = [];
3135
        if (false === $folderId) {
3136
            $documents[$label] = [
3137
                'id' => 0,
3138
                'files' => $newResources,
3139
            ];
3140
        } else {
3141
            if (is_array($parentData)) {
3142
                $documents[$parentData['title']] = [
3143
                    'id' => (int) $folderId,
3144
                    'files' => $newResources,
3145
                ];
3146
            }
3147
        }
3148
3149
        $writeResult = self::write_resources_tree(
3150
            $userInfo,
3151
            $course_info,
3152
            $session_id,
3153
            $documents,
3154
            $lp_id,
3155
            $target,
3156
            $add_move_button,
3157
            $overwrite_url,
3158
            $folderId
3159
        );
3160
3161
        $return .= $writeResult;
3162
        $lpAjaxUrl = api_get_path(WEB_AJAX_PATH).'lp.ajax.php';
3163
        if (false === $lp_id) {
3164
            $url = $lpAjaxUrl.'?a=get_documents&lp_id=&cidReq='.$course_info['code'];
3165
            $return .= "<script>
3166
            $(function() {
3167
                $('.close_div').click(function() {
3168
                    var course_id = this.id.split('_')[2];
3169
                    var session_id = this.id.split('_')[3];
3170
                    $('#document_result_'+course_id+'_'+session_id).hide();
3171
                    $('.lp_resource').remove();
3172
                    $('.document_preview_container').html('');
3173
                });
3174
            });
3175
            </script>";
3176
        } else {
3177
            // For LPs
3178
            $url = $lpAjaxUrl.'?a=get_documents&lp_id='.$lp_id.'&'.api_get_cidreq();
3179
        }
3180
3181
        if (!empty($overwrite_url)) {
3182
            $url .= '&url='.Security::remove_XSS($overwrite_url);
3183
        }
3184
3185
        if ($add_move_button) {
3186
            $url .= '&add_move_button=1';
3187
        }
3188
3189
        $return .= "<script>
3190
            function testResources(id, img) {
3191
                var numericId = id.split('_')[1];
3192
                var parentId = 'doc_id_'+numericId;
3193
                var tempId = 'temp_'+numericId;
3194
                var image = $('#'+img);
3195
3196
                if (image.hasClass('open')) {
3197
                    image.removeClass('open');
3198
                    image.attr('src', '".Display::returnIconPath('nolines_plus.gif')."');
3199
                    $('#'+id).show();
3200
                    $('#'+tempId).hide();
3201
                } else {
3202
                    image.addClass('open');
3203
                    image.attr('src', '".Display::returnIconPath('nolines_minus.gif')."');
3204
                    $('#'+id).hide();
3205
                    $('#'+tempId).show();
3206
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3207
                    if (tempDiv.length == 0) {
3208
                        $.ajax({
3209
                            type: 'GET',
3210
                            async: false,
3211
                            url:  '".$url."',
3212
                            data: 'folder_id='+numericId,
3213
                            success: function(data) {
3214
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3215
                            }
3216
                        });
3217
                    }
3218
                }
3219
            }
3220
            </script>";
3221
3222
        if (!$user_in_course) {
3223
            $return = '';
3224
        }
3225
3226
        return $return;
3227
    }
3228
3229
    /**
3230
     * Generate and return an HTML list of resources based on a given array.
3231
     * This list is used to show the course creator a list of available resources to choose from
3232
     * when creating a learning path.
3233
     *
3234
     * @param array  $userInfo        current user info
3235
     * @param array  $course_info
3236
     * @param int    $session_id
3237
     * @param array  $documents
3238
     * @param bool   $lp_id
3239
     * @param string $target
3240
     * @param bool   $add_move_button
3241
     * @param string $overwrite_url
3242
     * @param int    $folderId
3243
     *
3244
     * @return string
3245
     */
3246
    public static function write_resources_tree(
3247
        $userInfo,
3248
        $course_info,
3249
        $session_id,
3250
        $documents,
3251
        $lp_id = false,
3252
        $target = '',
3253
        $add_move_button = false,
3254
        $overwrite_url = '',
3255
        $folderId = false,
3256
        $addAudioPreview = false
3257
    ) {
3258
        $return = '';
3259
        if (!empty($documents)) {
3260
            foreach ($documents as $key => $resource) {
3261
                if (isset($resource['id']) && is_int($resource['id'])) {
3262
                    $mainFolderResource = [
3263
                        'id' => $resource['id'],
3264
                        'title' => $key,
3265
                    ];
3266
3267
                    if (false === $folderId) {
3268
                        $return .= self::parseFolder($folderId, $mainFolderResource, $lp_id);
3269
                    }
3270
3271
                    if (isset($resource['files'])) {
3272
                        $return .= self::write_resources_tree(
3273
                            $userInfo,
3274
                            $course_info,
3275
                            $session_id,
3276
                            $resource['files'],
3277
                            $lp_id,
3278
                            $target,
3279
                            $add_move_button,
3280
                            $overwrite_url,
3281
                            null,
3282
                            $addAudioPreview
3283
                        );
3284
                    }
3285
                    $return .= '</div>';
3286
                    $return .= '</ul>';
3287
                } else {
3288
                    if ('folder' === $resource['filetype']) {
3289
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3290
                    } else {
3291
                        $return .= self::parseFile(
3292
                            $userInfo,
3293
                            $course_info,
3294
                            $session_id,
3295
                            $resource,
3296
                            $lp_id,
3297
                            $add_move_button,
3298
                            $target,
3299
                            $overwrite_url,
3300
                            $addAudioPreview
3301
                        );
3302
                    }
3303
                }
3304
            }
3305
        }
3306
3307
        return $return;
3308
    }
3309
3310
    /**
3311
     * @param int   $doc_id
3312
     * @param array $courseInfo
3313
     * @param int   $sessionId
3314
     * @param int   $user_id
3315
     * @param int   $groupId               iid
3316
     * @param bool  $checkParentVisibility
3317
     *
3318
     * @return bool
3319
     */
3320
    public static function check_visibility_tree(
3321
        $doc_id,
3322
        $courseInfo,
3323
        $sessionId,
3324
        $user_id,
3325
        $groupId = 0,
3326
        $checkParentVisibility = true
3327
    ) {
3328
        if (empty($courseInfo)) {
3329
            return false;
3330
        }
3331
3332
        $courseCode = $courseInfo['code'];
3333
3334
        if (empty($courseCode)) {
3335
            return false;
3336
        }
3337
3338
        $document_data = self::get_document_data_by_id(
3339
            $doc_id,
3340
            $courseCode,
3341
            null,
3342
            $sessionId
3343
        );
3344
3345
        if (0 != $sessionId && !$document_data) {
3346
            $document_data = self::get_document_data_by_id(
3347
                $doc_id,
3348
                $courseCode,
3349
                null,
3350
                0
3351
            );
3352
        }
3353
3354
        if (!empty($document_data)) {
3355
            // If admin or course teacher, allow anyway
3356
            if (api_is_platform_admin() || CourseManager::isCourseTeacher($user_id, $courseInfo['real_id'])) {
3357
                return true;
3358
            }
3359
3360
            if (false == $document_data['parent_id'] || empty($document_data['parent_id'])) {
3361
                if (!empty($groupId)) {
3362
                    return true;
3363
                }
3364
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3365
3366
                return $visible;
3367
            } else {
3368
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3369
3370
                if (!$visible) {
3371
                    return false;
3372
                } else {
3373
                    if ($checkParentVisibility) {
3374
                        return self::check_visibility_tree(
3375
                            $document_data['parent_id'],
3376
                            $courseInfo,
3377
                            $sessionId,
3378
                            $user_id,
3379
                            $groupId
3380
                        );
3381
                    }
3382
3383
                    return true;
3384
                }
3385
            }
3386
        } else {
3387
            return false;
3388
        }
3389
    }
3390
3391
    /**
3392
     * Index a given document.
3393
     *
3394
     * @param   int     Document ID inside its corresponding course
3395
     * @param   string  Course code
3396
     * @param   int     Session ID (not used yet)
3397
     * @param   string  Language of document's content (defaults to course language)
3398
     * @param   array   Array of specific fields (['code'=>'value',...])
3399
     * @param   string  What to do if the file already exists (default or overwrite)
3400
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3401
     *
3402
     * @return bool Returns true on presumed success, false on failure
3403
     */
3404
    public static function index_document(
3405
        $docid,
3406
        $course_code,
3407
        $session_id = 0,
3408
        $lang = 'english',
3409
        $specific_fields_values = [],
3410
        $if_exists = '',
3411
        $simulation = false
3412
    ) {
3413
        if ('true' !== api_get_setting('search_enabled')) {
3414
            return false;
3415
        }
3416
        if (empty($docid) or $docid != intval($docid)) {
3417
            return false;
3418
        }
3419
        if (empty($session_id)) {
3420
            $session_id = api_get_session_id();
3421
        }
3422
        $course_info = api_get_course_info($course_code);
3423
        $course_dir = $course_info['path'].'/document';
3424
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3425
        $base_work_dir = $sys_course_path.$course_dir;
3426
3427
        $course_id = $course_info['real_id'];
3428
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3429
3430
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3431
        $result = Database::query($qry);
3432
        if (1 == Database::num_rows($result)) {
3433
            $row = Database::fetch_array($result);
3434
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3435
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3436
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3437
            $doc_mime = mime_content_type($doc_path);
3438
            $allowed_mime_types = self::file_get_mime_type(true);
3439
3440
            // mime_content_type does not detect correctly some formats that
3441
            // are going to be supported for index, so an extensions array is used for the moment
3442
            if (empty($doc_mime)) {
3443
                $allowed_extensions = [
3444
                    'doc',
3445
                    'docx',
3446
                    'ppt',
3447
                    'pptx',
3448
                    'pps',
3449
                    'ppsx',
3450
                    'xls',
3451
                    'xlsx',
3452
                    'odt',
3453
                    'odp',
3454
                    'ods',
3455
                    'pdf',
3456
                    'txt',
3457
                    'rtf',
3458
                    'msg',
3459
                    'csv',
3460
                    'html',
3461
                    'htm',
3462
                ];
3463
                $extensions = preg_split("/[\/\\.]/", $doc_path);
3464
                $doc_ext = strtolower($extensions[count($extensions) - 1]);
3465
                if (in_array($doc_ext, $allowed_extensions)) {
3466
                    switch ($doc_ext) {
3467
                        case 'ppt':
3468
                        case 'pps':
3469
                            $doc_mime = 'application/vnd.ms-powerpoint';
3470
                            break;
3471
                        case 'xls':
3472
                            $doc_mime = 'application/vnd.ms-excel';
3473
                            break;
3474
                    }
3475
                }
3476
            }
3477
3478
            //@todo move this nightmare in a search controller or something like that!!! J.M
3479
3480
            if (in_array($doc_mime, $allowed_mime_types)) {
3481
                $file_title = $row['title'];
3482
                $file_content = self::get_text_content($doc_path, $doc_mime);
3483
                $course_code = Database::escape_string($course_code);
3484
                $ic_slide = new IndexableChunk();
3485
                $ic_slide->addValue('title', $file_title);
3486
                $ic_slide->addCourseId($course_code);
3487
                $ic_slide->addToolId(TOOL_DOCUMENT);
3488
                $xapian_data = [
3489
                    SE_COURSE_ID => $course_code,
3490
                    SE_TOOL_ID => TOOL_DOCUMENT,
3491
                    SE_DATA => ['doc_id' => $docid],
3492
                    SE_USER => api_get_user_id(),
3493
                ];
3494
3495
                $ic_slide->xapian_data = serialize($xapian_data);
3496
                $di = new ChamiloIndexer();
3497
                $return = $di->connectDb(null, null, $lang);
3498
3499
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
3500
                $specific_fields = get_specific_field_list();
3501
3502
                // process different depending on what to do if file exists
3503
                /**
3504
                 * @TODO Find a way to really verify if the file had been
3505
                 * overwriten. Now all work is done at
3506
                 * handle_uploaded_document() and it's difficult to verify it
3507
                 */
3508
                if (!empty($if_exists) && 'overwrite' == $if_exists) {
3509
                    // Overwrite the file on search engine
3510
                    // Actually, it consists on a delete of terms from db,
3511
                    // insert new ones, create a new search engine document,
3512
                    // and remove the old one
3513
                    // Get search_did
3514
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3515
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
3516
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
3517
3518
                    $res = Database::query($sql);
3519
3520
                    if (Database::num_rows($res) > 0) {
3521
                        $se_ref = Database::fetch_array($res);
3522
                        if (!$simulation) {
3523
                            $di->remove_document($se_ref['search_did']);
3524
                        }
3525
                        $all_specific_terms = '';
3526
                        foreach ($specific_fields as $specific_field) {
3527
                            if (!$simulation) {
3528
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
3529
                            }
3530
                            // Update search engine
3531
                            if (isset($specific_fields_values[$specific_field['code']])) {
3532
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
3533
                            } else { //if the specific field is not defined, force an empty one
3534
                                $sterms = '';
3535
                            }
3536
                            $all_specific_terms .= ' '.$sterms;
3537
                            $sterms = explode(',', $sterms);
3538
                            foreach ($sterms as $sterm) {
3539
                                $sterm = trim($sterm);
3540
                                if (!empty($sterm)) {
3541
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
3542
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
3543
                                    if (!$simulation) {
3544
                                        add_specific_field_value(
3545
                                            $specific_field['id'],
3546
                                            $course_code,
3547
                                            TOOL_DOCUMENT,
3548
                                            $docid,
3549
                                            $sterm
3550
                                        );
3551
                                    }
3552
                                }
3553
                            }
3554
                        }
3555
                        // Add terms also to content to make terms findable by probabilistic search
3556
                        $file_content = $all_specific_terms.' '.$file_content;
3557
3558
                        if (!$simulation) {
3559
                            $ic_slide->addValue('content', $file_content);
3560
                            $di->addChunk($ic_slide);
3561
                            // Index and return a new search engine document id
3562
                            $did = $di->index();
3563
3564
                            if ($did) {
3565
                                // update the search_did on db
3566
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3567
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
3568
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
3569
                                Database::query($sql);
3570
                            }
3571
                        }
3572
                    }
3573
                } else {
3574
                    // Add all terms
3575
                    $all_specific_terms = '';
3576
                    foreach ($specific_fields as $specific_field) {
3577
                        if (isset($specific_fields_values[$specific_field['code']])) {
3578
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
3579
                        } else { //if the specific field is not defined, force an empty one
3580
                            $sterms = '';
3581
                        }
3582
                        $all_specific_terms .= ' '.$sterms;
3583
                        if (!empty($sterms)) {
3584
                            $sterms = explode(',', $sterms);
3585
                            foreach ($sterms as $sterm) {
3586
                                if (!$simulation) {
3587
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
3588
                                    add_specific_field_value(
3589
                                        $specific_field['id'],
3590
                                        $course_code,
3591
                                        TOOL_DOCUMENT,
3592
                                        $docid,
3593
                                        $sterm
3594
                                    );
3595
                                }
3596
                            }
3597
                        }
3598
                    }
3599
                    // Add terms also to content to make terms findable by probabilistic search
3600
                    $file_content = $all_specific_terms.' '.$file_content;
3601
                    if (!$simulation) {
3602
                        $ic_slide->addValue('content', $file_content);
3603
                        $di->addChunk($ic_slide);
3604
                        // Index and return search engine document id
3605
                        $did = $di->index();
3606
                        if ($did) {
3607
                            // Save it to db
3608
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3609
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
3610
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
3611
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
3612
                            Database::query($sql);
3613
                        } else {
3614
                            return false;
3615
                        }
3616
                    }
3617
                }
3618
            } else {
3619
                return false;
3620
            }
3621
        }
3622
3623
        return true;
3624
    }
3625
3626
    /**
3627
     * @return array
3628
     */
3629
    public static function get_web_odf_extension_list()
3630
    {
3631
        return ['ods', 'odt', 'odp'];
3632
    }
3633
3634
    /**
3635
     * Set of extension allowed to use Jodconverter.
3636
     *
3637
     * @param $mode 'from'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'from' at position 0 could not be parsed: Unknown type name ''from'' at position 0 in 'from'.
Loading history...
3638
     *              'to'
3639
     *              'all'
3640
     * @param $format   'text'
3641
     *                  'spreadsheet'
3642
     *                  'presentation'
3643
     *                  'drawing'
3644
     *                  'all'
3645
     *
3646
     * @return array
3647
     */
3648
    public static function getJodconverterExtensionList($mode, $format)
3649
    {
3650
        $extensionList = [];
3651
        $extensionListFromText = [
3652
            'odt',
3653
            'sxw',
3654
            'rtf',
3655
            'doc',
3656
            'docx',
3657
            'wpd',
3658
            'txt',
3659
        ];
3660
        $extensionListToText = [
3661
            'pdf',
3662
            'odt',
3663
            'sxw',
3664
            'rtf',
3665
            'doc',
3666
            'docx',
3667
            'txt',
3668
        ];
3669
        $extensionListFromSpreadsheet = [
3670
            'ods',
3671
            'sxc',
3672
            'xls',
3673
            'xlsx',
3674
            'csv',
3675
            'tsv',
3676
        ];
3677
        $extensionListToSpreadsheet = [
3678
            'pdf',
3679
            'ods',
3680
            'sxc',
3681
            'xls',
3682
            'xlsx',
3683
            'csv',
3684
            'tsv',
3685
        ];
3686
        $extensionListFromPresentation = [
3687
            'odp',
3688
            'sxi',
3689
            'ppt',
3690
            'pptx',
3691
        ];
3692
        $extensionListToPresentation = [
3693
            'pdf',
3694
            'swf',
3695
            'odp',
3696
            'sxi',
3697
            'ppt',
3698
            'pptx',
3699
        ];
3700
        $extensionListFromDrawing = ['odg'];
3701
        $extensionListToDrawing = ['svg', 'swf'];
3702
3703
        if ('from' === $mode) {
3704
            if ('text' === $format) {
3705
                $extensionList = array_merge($extensionList, $extensionListFromText);
3706
            } elseif ('spreadsheet' === $format) {
3707
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3708
            } elseif ('presentation' === $format) {
3709
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3710
            } elseif ('drawing' === $format) {
3711
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3712
            } elseif ('all' === $format) {
3713
                $extensionList = array_merge($extensionList, $extensionListFromText);
3714
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3715
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3716
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3717
            }
3718
        } elseif ('to' === $mode) {
3719
            if ('text' === $format) {
3720
                $extensionList = array_merge($extensionList, $extensionListToText);
3721
            } elseif ('spreadsheet' === $format) {
3722
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3723
            } elseif ('presentation' === $format) {
3724
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3725
            } elseif ('drawing' === $format) {
3726
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3727
            } elseif ('all' === $format) {
3728
                $extensionList = array_merge($extensionList, $extensionListToText);
3729
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3730
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3731
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3732
            }
3733
        } elseif ('all' === $mode) {
3734
            if ('text' === $format) {
3735
                $extensionList = array_merge($extensionList, $extensionListFromText);
3736
                $extensionList = array_merge($extensionList, $extensionListToText);
3737
            } elseif ('spreadsheet' === $format) {
3738
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3739
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3740
            } elseif ('presentation' === $format) {
3741
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3742
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3743
            } elseif ('drawing' === $format) {
3744
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3745
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3746
            } elseif ('all' === $format) {
3747
                $extensionList = array_merge($extensionList, $extensionListFromText);
3748
                $extensionList = array_merge($extensionList, $extensionListToText);
3749
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3750
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3751
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3752
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3753
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3754
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3755
            }
3756
        }
3757
3758
        return $extensionList;
3759
    }
3760
3761
    /**
3762
     * Get Format type list by extension and mode.
3763
     *
3764
     * @param string $mode Mode to search format type list
3765
     *
3766
     * @example 'from'
3767
     * @example 'to'
3768
     *
3769
     * @param string $extension file extension to check file type
3770
     *
3771
     * @return array
3772
     */
3773
    public static function getFormatTypeListConvertor($mode = 'from', $extension)
3774
    {
3775
        $formatTypesList = [];
3776
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
3777
        foreach ($formatTypes as $formatType) {
3778
            if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
3779
                $formatTypesList[] = $formatType;
3780
            }
3781
        }
3782
3783
        return $formatTypesList;
3784
    }
3785
3786
    /**
3787
     * @param string $path
3788
     * @param bool   $is_certificate_mode
3789
     *
3790
     * @return bool
3791
     */
3792
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
3793
    {
3794
        $foldersToAvoid = [
3795
            '/HotPotatoes_files',
3796
            '/certificates',
3797
        ];
3798
        $systemFolder = api_get_course_setting('show_system_folders');
3799
3800
        if (1 == $systemFolder) {
3801
            $foldersToAvoid = [];
3802
        }
3803
3804
        if ('css' == basename($path)) {
3805
            return true;
3806
        }
3807
3808
        if (false == $is_certificate_mode) {
3809
            //Certificate results
3810
            if (strstr($path, 'certificates')) {
3811
                return true;
3812
            }
3813
        }
3814
3815
        // Admin setting for Hide/Show the folders of all users
3816
        if ('false' == api_get_setting('show_users_folders')) {
3817
            $foldersToAvoid[] = '/shared_folder';
3818
3819
            if (strstr($path, 'shared_folder_session_')) {
3820
                return true;
3821
            }
3822
        }
3823
3824
        // Admin setting for Hide/Show Default folders to all users
3825
        if ('false' == api_get_setting('show_default_folders')) {
3826
            $foldersToAvoid[] = '/images';
3827
            $foldersToAvoid[] = '/flash';
3828
            $foldersToAvoid[] = '/audio';
3829
            $foldersToAvoid[] = '/video';
3830
        }
3831
3832
        // Admin setting for Hide/Show chat history folder
3833
        if ('false' == api_get_setting('show_chat_folder')) {
3834
            $foldersToAvoid[] = '/chat_files';
3835
        }
3836
3837
        if (is_array($foldersToAvoid)) {
3838
            return in_array($path, $foldersToAvoid);
3839
        } else {
3840
            return false;
3841
        }
3842
    }
3843
3844
    /**
3845
     * @return array
3846
     */
3847
    public static function get_system_folders()
3848
    {
3849
        return [
3850
            '/certificates',
3851
            '/HotPotatoes_files',
3852
            '/chat_files',
3853
            '/images',
3854
            '/flash',
3855
            '/audio',
3856
            '/video',
3857
            '/shared_folder',
3858
            '/learning_path',
3859
        ];
3860
    }
3861
3862
    /**
3863
     * @return array
3864
     */
3865
    public static function getProtectedFolderFromStudent()
3866
    {
3867
        return [
3868
            '/certificates',
3869
            '/HotPotatoes_files',
3870
            '/chat_files',
3871
            '/shared_folder',
3872
            '/learning_path',
3873
        ];
3874
    }
3875
3876
    /**
3877
     * @param string $courseCode
3878
     *
3879
     * @return string 'visible' or 'invisible' string
3880
     */
3881
    public static function getDocumentDefaultVisibility($courseCode)
3882
    {
3883
        $settings = api_get_setting('tool_visible_by_default_at_creation');
3884
        $defaultVisibility = 'visible';
3885
3886
        if (isset($settings['documents'])) {
3887
            $portalDefaultVisibility = 'invisible';
3888
            if ('true' == $settings['documents']) {
3889
                $portalDefaultVisibility = 'visible';
3890
            }
3891
3892
            $defaultVisibility = $portalDefaultVisibility;
3893
        }
3894
3895
        if ('true' == api_get_setting('documents_default_visibility_defined_in_course')) {
3896
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseCode);
3897
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
3898
                $defaultVisibility = $courseVisibility;
3899
            }
3900
        }
3901
3902
        return $defaultVisibility;
3903
    }
3904
3905
    /**
3906
     * @param string $filePath
3907
     * @param string $path
3908
     * @param array  $courseInfo
3909
     * @param int    $sessionId
3910
     * @param string $whatIfFileExists overwrite|rename
3911
     * @param int    $userId
3912
     * @param int    $groupId
3913
     * @param int    $toUserId
3914
     * @param string $comment
3915
     *
3916
     * @return bool|path
3917
     */
3918
    public static function addFileToDocumentTool(
3919
        $filePath,
3920
        $path,
3921
        $courseInfo,
3922
        $sessionId,
3923
        $userId,
3924
        $whatIfFileExists = 'overwrite',
3925
        $groupId = null,
3926
        $toUserId = null,
3927
        $comment = null
3928
    ) {
3929
        if (!file_exists($filePath)) {
3930
            return false;
3931
        }
3932
3933
        $fileInfo = pathinfo($filePath);
3934
3935
        $file = [
3936
            'name' => $fileInfo['basename'],
3937
            'tmp_name' => $filePath,
3938
            'size' => filesize($filePath),
3939
            'from_file' => true,
3940
        ];
3941
3942
        $course_dir = $courseInfo['path'].'/document';
3943
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
3944
3945
        $filePath = handle_uploaded_document(
3946
            $courseInfo,
3947
            $file,
3948
            $baseWorkDir,
3949
            $path,
3950
            $userId,
3951
            $groupId,
3952
            $toUserId,
3953
            false,
3954
            $whatIfFileExists,
3955
            false,
3956
            false,
3957
            $comment,
3958
            $sessionId
3959
        );
3960
3961
        if ($filePath) {
3962
            return self::get_document_id(
3963
                $courseInfo,
3964
                $filePath,
3965
                $sessionId
3966
            );
3967
        }
3968
3969
        return false;
3970
    }
3971
3972
    /**
3973
     * Converts wav to mp3 file.
3974
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg.
3975
     *
3976
     * @param string $wavFile
3977
     * @param bool   $removeWavFileIfSuccess
3978
     *
3979
     * @return bool
3980
     */
3981
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
3982
    {
3983
        if (file_exists($wavFile)) {
3984
            try {
3985
                $ffmpeg = \FFMpeg\FFMpeg::create();
3986
                $video = $ffmpeg->open($wavFile);
3987
3988
                $mp3File = str_replace('wav', 'mp3', $wavFile);
3989
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
3990
                if ($result && $removeWavFileIfSuccess) {
3991
                    unlink($wavFile);
3992
                }
3993
3994
                if (file_exists($mp3File)) {
3995
                    return $mp3File;
3996
                }
3997
            } catch (Exception $e) {
3998
                error_log($e->getMessage());
3999
                error_log($e->getPrevious()->getMessage());
4000
            }
4001
        }
4002
4003
        return false;
4004
    }
4005
4006
    /**
4007
     * @param string $documentData     wav document information
4008
     * @param array  $courseInfo
4009
     * @param int    $sessionId
4010
     * @param int    $userId           user that adds the document
4011
     * @param string $whatIfFileExists
4012
     * @param bool   $deleteWavFile
4013
     *
4014
     * @return bool
4015
     */
4016
    public static function addAndConvertWavToMp3(
4017
        $documentData,
4018
        $courseInfo,
4019
        $sessionId,
4020
        $userId,
4021
        $whatIfFileExists = 'overwrite',
4022
        $deleteWavFile = false
4023
    ) {
4024
        if (empty($documentData)) {
4025
            return false;
4026
        }
4027
4028
        if (isset($documentData['absolute_path']) &&
4029
            file_exists($documentData['absolute_path'])
4030
        ) {
4031
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4032
4033
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4034
                $documentId = self::addFileToDocumentTool(
4035
                    $mp3FilePath,
4036
                    dirname($documentData['path']),
4037
                    $courseInfo,
4038
                    $sessionId,
4039
                    $userId,
4040
                    $whatIfFileExists,
4041
                    null,
4042
                    null,
4043
                    $documentData['comment']
4044
                );
4045
4046
                if (!empty($documentId)) {
4047
                    if ($deleteWavFile) {
4048
                        $coursePath = $courseInfo['directory'].'/document';
4049
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4050
                        self::delete_document(
4051
                            $courseInfo,
4052
                            null,
4053
                            $documentPath,
4054
                            $sessionId,
4055
                            $documentData['id']
4056
                        );
4057
                    }
4058
4059
                    return $documentId;
4060
                }
4061
            }
4062
        }
4063
4064
        return false;
4065
    }
4066
4067
    /**
4068
     * Sets.
4069
     *
4070
     * @param string $file         ($document_data['path'])
4071
     * @param string $file_url_sys
4072
     *
4073
     * @return string
4074
     */
4075
    public static function generateAudioTempFile($file, $file_url_sys)
4076
    {
4077
        //make temp audio
4078
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4079
        if (!file_exists($temp_folder)) {
4080
            @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);
4081
        }
4082
4083
        //make htaccess with allow from all, and file index.html into temp/audio
4084
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4085
        if (!file_exists($htaccess)) {
4086
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4087
            $fp = @fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4088
            if ($fp) {
4089
                fwrite($fp, $htaccess_content);
4090
                fclose($fp);
4091
            }
4092
        }
4093
4094
        //encript temp name file
4095
        $name_crip = sha1(uniqid()); //encript
4096
        $findext = explode(".", $file);
4097
        $extension = $findext[count($findext) - 1];
4098
        $file_crip = $name_crip.'.'.$extension;
4099
4100
        //copy file to temp/audio directory
4101
        $from_sys = $file_url_sys;
4102
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4103
4104
        if (file_exists($from_sys)) {
4105
            copy($from_sys, $to_sys);
4106
        }
4107
4108
        // get file from tmp directory
4109
        Session::write('temp_audio_nanogong', $to_sys);
4110
4111
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4112
    }
4113
4114
    /**
4115
     * Erase temp nanogong audio.
4116
     */
4117
    public static function removeGeneratedAudioTempFile()
4118
    {
4119
        $tempAudio = Session::read('temp_audio_nanogong');
4120
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4121
            unlink($tempAudio);
4122
            Session::erase('temp_audio_nanogong');
4123
        }
4124
    }
4125
4126
    /**
4127
     * Check if the path is used in this course.
4128
     *
4129
     * @deprecated
4130
     *
4131
     * @param array  $courseInfo
4132
     * @param string $path
4133
     *
4134
     * @return array
4135
     */
4136
    public static function getDocumentByPathInCourse($courseInfo, $path)
4137
    {
4138
        $table = Database::get_course_table(TABLE_DOCUMENT);
4139
        $path = Database::escape_string($path);
4140
        $courseId = $courseInfo['real_id'];
4141
        if (empty($courseId)) {
4142
            return false;
4143
        }
4144
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4145
        $result = Database::query($sql);
4146
4147
        return Database::store_result($result, 'ASSOC');
4148
    }
4149
4150
    /**
4151
     * @param array $_course
4152
     *
4153
     * @return CDocument
4154
     */
4155
    public static function createDefaultAudioFolder($_course)
4156
    {
4157
        if (!isset($_course['path'])) {
4158
            return false;
4159
        }
4160
4161
        return self::addDocument($_course, '/audio', 'folder', 0, 'Audio');
4162
    }
4163
4164
    /**
4165
     * Generate a default certificate for a courses.
4166
     *
4167
     * @todo move to certificate lib
4168
     *
4169
     * @global string $css CSS directory
4170
     * @global string $img_dir image directory
4171
     * @global string $default_course_dir Course directory
4172
     * @global string $js JS directory
4173
     *
4174
     * @param array $courseData     The course info
4175
     * @param bool  $fromBaseCourse
4176
     * @param int   $sessionId
4177
     */
4178
    public static function generateDefaultCertificate(
4179
        $courseData,
4180
        $fromBaseCourse = false,
4181
        $sessionId = 0
4182
    ) {
4183
        if (empty($courseData)) {
4184
            return false;
4185
        }
4186
4187
        global $css, $img_dir, $default_course_dir, $js;
4188
        $codePath = api_get_path(REL_CODE_PATH);
4189
        $dir = '/certificates';
4190
        $comment = null;
4191
        $title = get_lang('Default certificate');
4192
        $fileName = api_replace_dangerous_char($title);
4193
        //$filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4194
        /*if (!is_dir($filePath)) {
4195
            mkdir($filePath, api_get_permissions_for_new_directories());
4196
        }*/
4197
4198
        //$fileFullPath = "$filePath/$fileName.html";
4199
        $fileType = 'file';
4200
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4201
4202
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4203
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4204
4205
        $fileContent = str_replace($search, $replace, $templateContent);
4206
        $saveFilePath = "$dir/$fileName.html";
4207
4208
        if ($fromBaseCourse) {
4209
            $defaultCertificateId = self::get_default_certificate_id($courseData['real_id'], 0);
4210
            if (!empty($defaultCertificateId)) {
4211
                // We have a certificate from the course base
4212
                $documentData = self::get_document_data_by_id(
4213
                    $defaultCertificateId,
4214
                    $courseData['code'],
4215
                    false,
4216
                    0
4217
                );
4218
4219
                if ($documentData) {
4220
                    $fileContent = file_get_contents($documentData['absolute_path']);
4221
                }
4222
            }
4223
        }
4224
4225
        $document = self::addDocument(
4226
            $courseData,
4227
            $saveFilePath,
4228
            $fileType,
4229
            0,
4230
            $title,
4231
            $comment,
4232
            0, //$readonly = 0,
4233
            true, //$save_visibility = true,
4234
            null, //$group_id = null,
4235
            $sessionId,
4236
            0,
4237
            false,
4238
            $fileContent
4239
        );
4240
4241
        $defaultCertificateId = self::get_default_certificate_id($courseData['real_id'], $sessionId);
4242
4243
        if (!isset($defaultCertificateId)) {
4244
            self::attach_gradebook_certificate(
4245
                $courseData['real_id'],
4246
                $document->getIid(),
4247
                $sessionId
4248
            );
4249
        }
4250
    }
4251
4252
    /**
4253
     * Update the document name.
4254
     *
4255
     * @param int    $documentId The document id
4256
     * @param string $newName    The new name
4257
     */
4258
    public static function renameDocument($documentId, $newName)
4259
    {
4260
        $documentId = intval($documentId);
4261
        $newName = Database::escape_string($newName);
4262
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4263
4264
        $values = [
4265
            'title' => $newName,
4266
        ];
4267
4268
        $whereConditions = [
4269
            'id = ?' => $documentId,
4270
        ];
4271
4272
        Database::update($docuentTable, $values, $whereConditions);
4273
    }
4274
4275
    /**
4276
     * Get folder/file suffix.
4277
     *
4278
     * @param array $courseInfo
4279
     * @param int   $sessionId
4280
     * @param int   $groupId
4281
     *
4282
     * @return string
4283
     */
4284
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4285
    {
4286
        // If no session or group, then no suffix.
4287
        if (empty($sessionId) && empty($groupId)) {
4288
            return '';
4289
        }
4290
4291
        return '__'.(int) $sessionId.'__'.(int) $groupId;
4292
    }
4293
4294
    /**
4295
     * Fix a document name adding session id and group id
4296
     * Turns picture.jpg -> picture__1__2.jpg
4297
     * Where 1 = session id and 2 group id
4298
     * Of session id and group id are empty then the function returns:
4299
     * picture.jpg ->  picture.jpg.
4300
     *
4301
     * @param string $name       folder or file name
4302
     * @param string $type       'folder' or 'file'
4303
     * @param array  $courseInfo
4304
     * @param int    $sessionId
4305
     * @param int    $groupId
4306
     *
4307
     * @return string
4308
     */
4309
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4310
    {
4311
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4312
4313
        switch ($type) {
4314
            case 'folder':
4315
                $name = $name.$suffix;
4316
                break;
4317
            case 'file':
4318
                $name = self::addSuffixToFileName($name, $suffix);
4319
                break;
4320
        }
4321
4322
        return $name;
4323
    }
4324
4325
    /**
4326
     * Add a suffix to a file Example:
4327
     * /folder/picture.jpg => to /folder/picture_this.jpg
4328
     * where "_this" is the suffix.
4329
     *
4330
     * @param string $name
4331
     * @param string $suffix
4332
     *
4333
     * @return string
4334
     */
4335
    public static function addSuffixToFileName($name, $suffix)
4336
    {
4337
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4338
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4339
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4340
4341
        if ('.' == $dir) {
4342
            $dir = null;
4343
        }
4344
4345
        if (!empty($dir) && '/' != $dir) {
4346
            $dir = $dir.'/';
4347
        }
4348
4349
        $name = $dir.$fileName.$suffix.'.'.$extension;
4350
4351
        return $name;
4352
    }
4353
4354
    /**
4355
     * Check if folder exist in the course base or in the session course.
4356
     *
4357
     * @param string $folder     Example: /folder/folder2
4358
     * @param array  $courseInfo
4359
     * @param int    $sessionId
4360
     * @param int    $groupId    group.id
4361
     *
4362
     * @return bool
4363
     */
4364
    public static function folderExists(
4365
        $folder,
4366
        $courseInfo,
4367
        $sessionId,
4368
        $groupId
4369
    ) {
4370
        $courseId = $courseInfo['real_id'];
4371
4372
        if (empty($courseId)) {
4373
            return false;
4374
        }
4375
4376
        $sessionId = (int) $sessionId;
4377
        $folderWithSuffix = self::fixDocumentName(
4378
            $folder,
4379
            'folder',
4380
            $courseInfo,
4381
            $sessionId,
4382
            $groupId
4383
        );
4384
4385
        $folder = Database::escape_string($folder);
4386
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4387
4388
        // Check if pathname already exists inside document table
4389
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4390
        $sql = "SELECT iid, path FROM $tbl_document
4391
                WHERE
4392
                    filetype = 'folder' AND
4393
                    c_id = $courseId AND
4394
                    (path = '$folder' OR path = '$folderWithSuffix') AND
4395
                    (session_id = 0 OR session_id IS NULL OR session_id = $sessionId)
4396
        ";
4397
4398
        $rs = Database::query($sql);
4399
        if (Database::num_rows($rs)) {
4400
            return true;
4401
        }
4402
4403
        return false;
4404
    }
4405
4406
    /**
4407
     * Check if file exist in the course base or in the session course.
4408
     *
4409
     * @param string $fileName   Example: /folder/picture.jpg
4410
     * @param array  $courseInfo
4411
     * @param int    $sessionId
4412
     * @param int    $groupId
4413
     *
4414
     * @return bool
4415
     */
4416
    public static function documentExists(
4417
        $fileName,
4418
        $courseInfo,
4419
        $sessionId,
4420
        $groupId
4421
    ) {
4422
        $courseId = $courseInfo['real_id'];
4423
4424
        if (empty($courseId)) {
4425
            return false;
4426
        }
4427
4428
        $sessionId = (int) $sessionId;
4429
        $fileNameEscape = Database::escape_string($fileName);
4430
4431
        $fileNameWithSuffix = self::fixDocumentName(
4432
            $fileName,
4433
            'file',
4434
            $courseInfo,
4435
            $sessionId,
4436
            $groupId
4437
        );
4438
4439
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
4440
4441
        // Check if pathname already exists inside document table
4442
        $table = Database::get_course_table(TABLE_DOCUMENT);
4443
        $sql = "SELECT iid, title FROM $table
4444
                WHERE
4445
                    filetype = 'file' AND
4446
                    c_id = $courseId AND
4447
                    (
4448
                        title = '".$fileNameEscape."' OR
4449
                        title = '$fileNameWithSuffix'
4450
                    ) AND
4451
                    (session_id = 0 OR session_id = $sessionId)
4452
        ";
4453
        $rs = Database::query($sql);
4454
        if (Database::num_rows($rs)) {
4455
            return true;
4456
        }
4457
4458
        return false;
4459
    }
4460
4461
    /**
4462
     * Undo the suffix applied to a file example:
4463
     * turns picture__1__1.jpg to picture.jpg.
4464
     *
4465
     * @param string $name
4466
     * @param int    $courseId
4467
     * @param int    $sessionId
4468
     * @param int    $groupId
4469
     *
4470
     * @return string
4471
     */
4472
    public static function undoFixDocumentName(
4473
        $name,
4474
        $courseId,
4475
        $sessionId,
4476
        $groupId
4477
    ) {
4478
        if (empty($sessionId) && empty($groupId)) {
4479
            return $name;
4480
        }
4481
4482
        $suffix = self::getDocumentSuffix(
4483
            ['real_id' => $courseId],
4484
            $sessionId,
4485
            $groupId
4486
        );
4487
4488
        $name = str_replace($suffix, '', $name);
4489
4490
        return $name;
4491
    }
4492
4493
    /**
4494
     * @param string $path
4495
     * @param string $name
4496
     * @param array  $courseInfo
4497
     * @param int    $sessionId
4498
     * @param int    $groupId
4499
     *
4500
     * @return string
4501
     */
4502
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
4503
    {
4504
        $counter = 1;
4505
        $filePath = $path.$name;
4506
        $uniqueName = $name;
4507
        $baseName = pathinfo($name, PATHINFO_FILENAME);
4508
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4509
4510
        return uniqid($baseName.'-', true).'.'.$extension;
4511
4512
        while ($documentExists = self::documentExists(
0 ignored issues
show
Unused Code introduced by
WhileNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
4513
            $filePath,
4514
            $courseInfo,
4515
            $sessionId,
4516
            $groupId
4517
        )) {
4518
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
4519
            $filePath = $path.$uniqueName;
4520
            $counter++;
4521
        }
4522
4523
        return $uniqueName;
4524
    }
4525
4526
    /**
4527
     * Builds the form that enables the user to
4528
     * select a directory to browse/upload in.
4529
     *
4530
     * @param array    An array containing the folders we want to be able to select
4531
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
4532
     * @param string    Group directory, if empty, prevents documents to be uploaded
4533
     * (because group documents cannot be uploaded in root)
4534
     * @param bool    Whether to change the renderer (this will add a template <span>
4535
     * to the QuickForm object displaying the form)
4536
     *
4537
     * @return string html form
4538
     */
4539
    public static function build_directory_selector(
4540
        $folders,
4541
        $document_id,
4542
        $group_dir = '',
4543
        $change_renderer = false,
4544
        &$form = null,
4545
        $selectName = 'id'
4546
    ) {
4547
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
4548
        $course_id = api_get_course_int_id();
4549
        $folder_titles = [];
4550
4551
        if (is_array($folders)) {
4552
            $escaped_folders = [];
4553
            foreach ($folders as $key => &$val) {
4554
                $escaped_folders[$key] = Database::escape_string($val);
4555
            }
4556
            $folder_sql = implode("','", $escaped_folders);
4557
4558
            $sql = "SELECT DISTINCT docs.title, n.path
4559
                    FROM resource_node AS n
4560
                    INNER JOIN $doc_table AS docs
4561
                    ON (docs.resource_node_id = n.id)
4562
                    INNER JOIN resource_link l
4563
                    ON (l.resource_node_id = n.id)
4564
                    WHERE
4565
                        l.c_id = $course_id AND
4566
                        docs.filetype = 'folder' AND
4567
                        n.path IN ('".$folder_sql."') AND
4568
                        l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
4569
                         ";
4570
4571
            /*$sql = "SELECT path, title
4572
                    FROM $doc_table
4573
                    WHERE
4574
                        filetype = 'folder' AND
4575
                        c_id = $course_id AND
4576
                        path IN ('".$folder_sql."') ";*/
4577
            $res = Database::query($sql);
4578
            $folder_titles = [];
4579
            while ($obj = Database::fetch_object($res)) {
4580
                $folder_titles[$obj->path] = $obj->title;
4581
            }
4582
        }
4583
4584
        $attributes = [];
4585
        if (empty($form)) {
4586
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
4587
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
4588
        }
4589
        $form->addElement('hidden', 'cidReq', api_get_course_id());
4590
        $form->addElement('hidden', 'cid', api_get_course_int_id());
4591
        $form->addElement('hidden', 'sid', api_get_session_id());
4592
        $form->addElement('hidden', 'gid', api_get_group_id());
4593
4594
        $parent_select = $form->addSelect(
4595
            $selectName,
4596
            get_lang('Current folder'),
4597
            '',
4598
            $attributes
4599
        );
4600
4601
        // Group documents cannot be uploaded in the root
4602
        if (empty($group_dir)) {
4603
            $parent_select->addOption(get_lang('Documents'), '/');
4604
4605
            if (is_array($folders)) {
4606
                foreach ($folders as $folder_id => &$folder) {
4607
                    if (!isset($folder_titles[$folder])) {
4608
                        continue;
4609
                    }
4610
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4611
                    $path_parts = explode('/', $folder);
4612
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
4613
                    $counter = count($path_parts) - 2;
4614
                    if ($counter > 0) {
4615
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
4616
                    } else {
4617
                        $label = ' &mdash; '.$folder_titles[$folder];
4618
                    }
4619
                    $parent_select->addOption($label, $folder_id);
4620
                    if ('' != $selected) {
4621
                        $parent_select->setSelected($folder_id);
4622
                    }
4623
                }
4624
            }
4625
        } else {
4626
            if (!empty($folders)) {
4627
                foreach ($folders as $folder_id => &$folder) {
4628
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4629
                    $label = $folder_titles[$folder];
4630
                    if ($folder == $group_dir) {
4631
                        $label = get_lang('Documents');
4632
                    } else {
4633
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
4634
                        $label = cut($label, 80);
4635
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
4636
                    }
4637
                    $parent_select->addOption($label, $folder_id);
4638
                    if ('' != $selected) {
4639
                        $parent_select->setSelected($folder_id);
4640
                    }
4641
                }
4642
            }
4643
        }
4644
4645
        return $form->toHtml();
4646
    }
4647
4648
    /**
4649
     * Create a html hyperlink depending on if it's a folder or a file.
4650
     *
4651
     * @param string $documentWebPath
4652
     * @param array  $document_data
4653
     * @param bool   $show_as_icon      - if it is true, only a clickable icon will be shown
4654
     * @param int    $visibility        (1/0)
4655
     * @param int    $size
4656
     * @param bool   $isAllowedToEdit
4657
     * @param bool   $isCertificateMode
4658
     * @param bool   $addToEditor
4659
     * @param string $editorUrl
4660
     *
4661
     * @return string url
4662
     */
4663
    public static function create_document_link(
4664
        $documentWebPath,
4665
        $document_data,
4666
        $show_as_icon = false,
4667
        $visibility,
4668
        $size = 0,
4669
        $isAllowedToEdit = false,
4670
        $isCertificateMode = false,
4671
        $addToEditor = false,
4672
        $editorUrl = ''
4673
    ) {
4674
        global $dbl_click_id;
4675
4676
        $sessionId = api_get_session_id();
4677
        $courseParams = api_get_cidreq();
4678
        $courseCode = api_get_course_id();
4679
        $webODFList = self::get_web_odf_extension_list();
4680
4681
        // Get the title or the basename depending on what we're using
4682
        if ('' != $document_data['title']) {
4683
            $title = $document_data['title'];
4684
        } else {
4685
            $title = basename($document_data['path']);
4686
        }
4687
4688
        $isAdmin = api_is_platform_admin();
4689
4690
        $filetype = $document_data['filetype'];
4691
        $path = $document_data['path'];
4692
        $url_path = urlencode($document_data['path']);
4693
4694
        $basePageUrl = api_get_path(WEB_CODE_PATH).'document/';
4695
        $pageUrl = $basePageUrl.'document.php';
4696
4697
        // Add class="invisible" on invisible files
4698
        $classAddToEditor = '';
4699
        if ($addToEditor) {
4700
            $classAddToEditor = 'select_to_ckeditor';
4701
        }
4702
        $visibility_class = false === $visibility ? ' class="muted"' : ' class="'.$classAddToEditor.'" ';
4703
4704
        $forcedownload_link = '';
4705
        $forcedownload_icon = '';
4706
        $prevent_multiple_click = '';
4707
        $force_download_html = '';
4708
4709
        if (!$show_as_icon) {
4710
            // Build download link (icon)
4711
            $forcedownload_link = 'folder' === $filetype
4712
                ? $pageUrl.'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id']
4713
                : $pageUrl.'?'.$courseParams.'&action=download&id='.$document_data['id'];
4714
            // Folder download or file download?
4715
            $forcedownload_icon = 'folder' === $filetype ? 'save_pack.png' : 'save.png';
4716
            // Prevent multiple clicks on zipped folder download
4717
            $prevent_multiple_click = 'folder' === $filetype ? " onclick=\"javascript: if(typeof clic_$dbl_click_id == 'undefined' || !clic_$dbl_click_id) { clic_$dbl_click_id=true; window.setTimeout('clic_".($dbl_click_id++)."=false;',10000); } else { return false; }\"" : '';
4718
        }
4719
4720
        $target = '_self';
4721
        $is_browser_viewable_file = false;
4722
4723
        if ('file' === $filetype) {
4724
            // Check the extension
4725
            $ext = explode('.', $path);
4726
            $ext = strtolower($ext[count($ext) - 1]);
4727
4728
            // HTML-files an some other types are shown in a frameset by default.
4729
            $is_browser_viewable_file = self::isBrowserViewable($ext);
4730
            if ($is_browser_viewable_file) {
4731
                if ('pdf' == $ext || in_array($ext, $webODFList)) {
4732
                    $url = $pageUrl.'?'.$courseParams.'&action=download&amp;id='.$document_data['id'];
4733
                } else {
4734
                    $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4735
                }
4736
            } else {
4737
                $url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4738
            }
4739
        } else {
4740
            $url = $pageUrl.'?'.$courseParams.'&id='.$document_data['id'];
4741
        }
4742
4743
        if ($isCertificateMode) {
4744
            $url .= '&certificate=true&selectcat='.(isset($_GET['selectcat']) ? $_GET['selectcat'] : '');
4745
        }
4746
4747
        // The little download icon
4748
        $tooltip_title = $title;
4749
        $tooltip_title_alt = $tooltip_title;
4750
4751
        if ('link' == $filetype) {
4752
            $tooltip_title_alt = $title;
4753
            $url = $document_data['comment'].'" target="_blank';
4754
        }
4755
4756
        if ('/shared_folder' === $path) {
4757
            $tooltip_title_alt = get_lang('Folders of users');
4758
        } elseif (strstr($path, 'shared_folder_session_')) {
4759
            $tooltip_title_alt = get_lang('Folders of users').' ('.api_get_session_name($sessionId).')';
4760
        } elseif (strstr($tooltip_title, 'sf_user_')) {
4761
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
4762
            $tooltip_title_alt = get_lang('User folder').' '.$userinfo['complete_name'];
4763
        } elseif ('/chat_files' == $path) {
4764
            $tooltip_title_alt = get_lang('Chat conversations history');
4765
        } elseif ('/learning_path' == $path) {
4766
            $tooltip_title_alt = get_lang('Learning paths');
4767
        } elseif ('/video' == $path) {
4768
            $tooltip_title_alt = get_lang('Video');
4769
        } elseif ('/audio' == $path) {
4770
            $tooltip_title_alt = get_lang('Audio');
4771
        } elseif ('/flash' == $path) {
4772
            $tooltip_title_alt = get_lang('Flash');
4773
        } elseif ('/images' == $path) {
4774
            $tooltip_title_alt = get_lang('Images');
4775
        } elseif ('/images/gallery' == $path) {
4776
            $tooltip_title_alt = get_lang('Gallery');
4777
        }
4778
4779
        $copyToMyFiles = $open_in_new_window_link = '';
4780
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
4781
        $send_to = null;
4782
        $checkExtension = $path;
4783
        $extension = pathinfo($path, PATHINFO_EXTENSION);
4784
        $document_data['file_extension'] = $extension;
4785
4786
        if (!$show_as_icon) {
4787
            if ('folder' === $filetype) {
4788
                if ($isAllowedToEdit ||
4789
                    $isAdmin ||
4790
                    'true' == api_get_setting('students_download_folders')
4791
                ) {
4792
                    // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
4793
                    if (self::is_shared_folder($curdirpath, $sessionId)) {
4794
                        if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
4795
                            preg_match('/shared_folder_session_'.$sessionId.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
4796
                            $isAllowedToEdit || $isAdmin
4797
                        ) {
4798
                            $force_download_html = (0 == $size) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
4799
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4800
                        }
4801
                    } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
4802
                        $isAllowedToEdit ||
4803
                        $isAdmin
4804
                    ) {
4805
                        $force_download_html = (0 == $size) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
4806
                            Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4807
                    }
4808
                }
4809
            } else {
4810
                $force_download_html = 0 == $size ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
4811
                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4812
            }
4813
4814
            $pdf_icon = '';
4815
            if (!$isAllowedToEdit &&
4816
                'true' == api_get_setting('students_export2pdf') &&
4817
                'file' === $filetype &&
4818
                in_array($extension, ['html', 'htm'])
4819
            ) {
4820
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.$pageUrl.'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&curdirpath='.$curdirpath.'">'.
4821
                    Display::return_icon('pdf.png', get_lang('Export to PDF format'), [], ICON_SIZE_SMALL).'</a> ';
4822
            }
4823
4824
            if ($is_browser_viewable_file) {
4825
                $open_in_new_window_link = '<a href="'.$documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
4826
                    Display::return_icon('open_in_new_window.png', get_lang('Open in a new window'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
4827
            }
4828
4829
            if ($addToEditor) {
4830
                $force_download_html = '';
4831
                $open_in_new_window_link = '';
4832
                $send_to = '';
4833
                $pdf_icon = '';
4834
                if ('folder' === $filetype) {
4835
                    $url = $editorUrl.'/'.$document_data['id'].'/?'.api_get_cidreq();
4836
                } else {
4837
                    $url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4838
                }
4839
            }
4840
4841
            if ('file' === $filetype) {
4842
                // Sound preview
4843
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
4844
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
4845
                    preg_match('/ogg$/i', urldecode($checkExtension))
4846
                ) {
4847
                    return '<span style="float:left" '.$visibility_class.'>'.
4848
                    $title.
4849
                    '</span>'.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4850
                } elseif (
4851
                    // Show preview
4852
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
4853
                    preg_match('/png$/i', urldecode($checkExtension)) ||
4854
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
4855
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
4856
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
4857
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
4858
                    preg_match('/svg$/i', urldecode($checkExtension))
4859
                ) {
4860
                    // Simpler version of showinframesmin.php with no headers
4861
                    //$url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
4862
                    $class = 'ajax ';
4863
                    //$url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4864
                    $url_path = str_replace('%2F', '/', $url_path);
4865
                    $url = api_get_path(WEB_PUBLIC_PATH)."courses/$courseCode/document$url_path?type=show";
4866
                    if ($addToEditor) {
4867
                        $class = $classAddToEditor;
4868
                        $url = $documentWebPath.$url_path;
4869
                    }
4870
4871
                    if (false == $visibility) {
4872
                        $class = ' ajax text-muted ';
4873
                        if ($addToEditor) {
4874
                            $class = ' text-muted not_select_to_ckeditor';
4875
                        }
4876
                    }
4877
4878
                    return Display::url(
4879
                        $title,
4880
                        $url,
4881
                        [
4882
                            'class' => $class,
4883
                            'title' => $tooltip_title_alt,
4884
                            'data-title' => $title,
4885
                            'style' => 'float:left;',
4886
                        ]
4887
                    )
4888
                    .$force_download_html.$send_to.$copyToMyFiles
4889
                    .$open_in_new_window_link.$pdf_icon;
4890
                } else {
4891
                    // For a "PDF Download" of the file.
4892
                    $pdfPreview = null;
4893
                    if ('pdf' != $ext && !in_array($ext, $webODFList)) {
4894
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4895
                    } else {
4896
                        $pdfPreview = Display::url(
4897
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
4898
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
4899
                            ['style' => 'float:right']
4900
                        );
4901
                    }
4902
                    // No plugin just the old and good showinframes.php page
4903
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
4904
                    $pdfPreview.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4905
                }
4906
            } else {
4907
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'.
4908
                $force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4909
            }
4910
        } else {
4911
            $urlDecoded = urldecode($checkExtension);
4912
            // Icon column
4913
            if (preg_match('/shared_folder/', $urlDecoded) &&
4914
                false == preg_match('/shared_folder$/', $urlDecoded) &&
4915
                false == preg_match('/shared_folder_session_'.$sessionId.'$/', urldecode($url))
4916
            ) {
4917
                if ('file' === $filetype) {
4918
                    // Sound preview
4919
                    if (preg_match('/mp3$/i', $urlDecoded) ||
4920
                        preg_match('/wav$/i', $urlDecoded) ||
4921
                        preg_match('/ogg$/i', $urlDecoded)
4922
                    ) {
4923
                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
4924
4925
                        return $soundPreview;
4926
                    } elseif (
4927
                        // Show preview
4928
                        preg_match('/swf$/i', $urlDecoded) ||
4929
                        preg_match('/png$/i', $urlDecoded) ||
4930
                        preg_match('/gif$/i', $urlDecoded) ||
4931
                        preg_match('/jpg$/i', $urlDecoded) ||
4932
                        preg_match('/jpeg$/i', $urlDecoded) ||
4933
                        preg_match('/bmp$/i', $urlDecoded) ||
4934
                        preg_match('/svg$/i', $urlDecoded)
4935
                    ) {
4936
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4937
4938
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4939
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4940
                            Display::return_icon('shared.png', get_lang('Resource shared'), []).
4941
                        '</a>';
4942
                    } else {
4943
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4944
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4945
                            Display::return_icon('shared.png', get_lang('Resource shared'), []).
4946
                        '</a>';
4947
                    }
4948
                } else {
4949
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
4950
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4951
                        Display::return_icon('shared.png', get_lang('Resource shared'), []).
4952
                    '</a>';
4953
                }
4954
            } else {
4955
                if ('file' === $filetype) {
4956
                    // Sound preview with jplayer
4957
                    if (preg_match('/mp3$/i', $urlDecoded) ||
4958
                        (preg_match('/wav$/i', $urlDecoded)) ||
4959
                        preg_match('/ogg$/i', $urlDecoded)) {
4960
                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
4961
4962
                        return $soundPreview;
4963
                    } elseif (
4964
                        //Show preview
4965
                        preg_match('/html$/i', $urlDecoded) ||
4966
                        preg_match('/htm$/i', $urlDecoded) ||
4967
                        preg_match('/swf$/i', $urlDecoded) ||
4968
                        preg_match('/png$/i', $urlDecoded) ||
4969
                        preg_match('/gif$/i', $urlDecoded) ||
4970
                        preg_match('/jpg$/i', $urlDecoded) ||
4971
                        preg_match('/jpeg$/i', $urlDecoded) ||
4972
                        preg_match('/bmp$/i', $urlDecoded) ||
4973
                        preg_match('/svg$/i', $urlDecoded)
4974
                    ) {
4975
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
4976
4977
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4978
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4979
                        '</a>';
4980
                    } else {
4981
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4982
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4983
                        '</a>';
4984
                    }
4985
                } else {
4986
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
4987
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4988
                    '</a>';
4989
                }
4990
            }
4991
        }
4992
    }
4993
4994
    /**
4995
     * Builds an img html tag for the file type.
4996
     *
4997
     * @param string $type            (file/folder)
4998
     * @param string $path
4999
     * @param bool   $isAllowedToEdit
5000
     *
5001
     * @return string img html tag
5002
     */
5003
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
5004
    {
5005
        $basename = basename($path);
5006
        $sessionId = api_get_session_id();
5007
        if (is_null($isAllowedToEdit)) {
5008
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
5009
        }
5010
        $user_image = false;
5011
        if ('file' == $type) {
5012
            $icon = choose_image($basename);
5013
            $basename = substr(strrchr($basename, '.'), 1);
5014
        } elseif ('link' == $type) {
5015
            $icon = 'clouddoc.png';
5016
            $basename = get_lang('Cloud file link');
5017
        } else {
5018
            if ('/shared_folder' == $path) {
5019
                $icon = 'folder_users.png';
5020
                if ($isAllowedToEdit) {
5021
                    $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5022
The users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.');
5023
                } else {
5024
                    $basename = get_lang('Folders of users');
5025
                }
5026
            } elseif (strstr($basename, 'sf_user_')) {
5027
                $userInfo = api_get_user_info(substr($basename, 8));
5028
                $icon = $userInfo['avatar_small'];
5029
                $basename = get_lang('User folder').' '.$userInfo['complete_name'];
5030
                $user_image = true;
5031
            } elseif (strstr($path, 'shared_folder_session_')) {
5032
                $sessionName = api_get_session_name($sessionId);
5033
                if ($isAllowedToEdit) {
5034
                    $basename = '***('.$sessionName.')*** '.get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5035
The users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.');
5036
                } else {
5037
                    $basename = get_lang('Folders of users').' ('.$sessionName.')';
5038
                }
5039
                $icon = 'folder_users.png';
5040
            } else {
5041
                $icon = 'folder_document.png';
5042
5043
                if ('/audio' == $path) {
5044
                    $icon = 'folder_audio.png';
5045
                    if ($isAllowedToEdit) {
5046
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5047
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5048
                    } else {
5049
                        $basename = get_lang('Audio');
5050
                    }
5051
                } elseif ('/flash' == $path) {
5052
                    $icon = 'folder_flash.png';
5053
                    if ($isAllowedToEdit) {
5054
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5055
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5056
                    } else {
5057
                        $basename = get_lang('Flash');
5058
                    }
5059
                } elseif ('/images' == $path) {
5060
                    $icon = 'folder_images.png';
5061
                    if ($isAllowedToEdit) {
5062
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5063
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5064
                    } else {
5065
                        $basename = get_lang('Images');
5066
                    }
5067
                } elseif ('/video' == $path) {
5068
                    $icon = 'folder_video.png';
5069
                    if ($isAllowedToEdit) {
5070
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5071
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5072
                    } else {
5073
                        $basename = get_lang('Video');
5074
                    }
5075
                } elseif ('/images/gallery' == $path) {
5076
                    $icon = 'folder_gallery.png';
5077
                    if ($isAllowedToEdit) {
5078
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5079
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5080
                    } else {
5081
                        $basename = get_lang('Gallery');
5082
                    }
5083
                } elseif ('/chat_files' == $path) {
5084
                    $icon = 'folder_chat.png';
5085
                    if ($isAllowedToEdit) {
5086
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5087
This folder contains all sessions that have been opened in the chat. Although the chat sessions can often be trivial, others can be really interesting and worthy of being incorporated as an additional work document. To do this without changing the visibility of this folder, make the file visible and link it from where you deem appropriate. It is not recommended to make this folder visible to all.');
5088
                    } else {
5089
                        $basename = get_lang('Chat conversations history');
5090
                    }
5091
                } elseif ('/learning_path' == $path) {
5092
                    $icon = 'folder_learningpath.png';
5093
                    if ($isAllowedToEdit) {
5094
                        $basename = get_lang('HelpFolderLearning paths');
5095
                    } else {
5096
                        $basename = get_lang('Learning paths');
5097
                    }
5098
                }
5099
            }
5100
        }
5101
5102
        if ($user_image) {
5103
            return Display::img($icon, $basename, [], false);
5104
        }
5105
5106
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5107
    }
5108
5109
    /**
5110
     * Creates the row of edit icons for a file/folder.
5111
     *
5112
     * @param array $document_data
5113
     * @param int   $id
5114
     * @param bool  $is_template
5115
     * @param int   $visibility    (1/0)
5116
     *
5117
     * @return string html img tags with hyperlinks
5118
     */
5119
    public static function build_edit_icons($document_data, $id, $is_template, $visibility)
5120
    {
5121
        $sessionId = api_get_session_id();
5122
        $courseParams = api_get_cidreq();
5123
        $document_id = $document_data['id'];
5124
        $type = $document_data['filetype'];
5125
        $is_read_only = $document_data['readonly'];
5126
        $path = $document_data['path'];
5127
5128
        if ('link' == $type) {
5129
            $parent_id = self::get_document_id(
5130
                api_get_course_info(),
5131
                rtrim($path, '/'),
5132
                0
5133
            );
5134
        } else {
5135
            $parent_id = self::get_document_id(
5136
                api_get_course_info(),
5137
                dirname($path),
5138
                0
5139
            );
5140
        }
5141
5142
        if (empty($parent_id) && !empty($sessionId)) {
5143
            $parent_id = self::get_document_id(
5144
                api_get_course_info(),
5145
                dirname($path),
5146
                $sessionId
5147
            );
5148
        }
5149
5150
        $curdirpath = dirname($document_data['path']);
5151
        $is_certificate_mode = self::is_certificate_mode($path);
5152
        $curdirpath = urlencode($curdirpath);
5153
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5154
        //@todo Implement remote support for converter
5155
        $usePpt2lp = 'true' == api_get_setting('service_ppt2lp', 'active') && 'localhost' == api_get_setting('service_ppt2lp', 'host');
5156
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5157
        $formatType = current($formatTypeList);
5158
5159
        // If document is read only *or* we're in a session and the document
5160
        // is from a non-session context, hide the edition capabilities
5161
        $modify_icons = [];
5162
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5163
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5164
        $modify_icons[] = self::getButtonVisibility(
5165
            $is_read_only,
5166
            $visibility,
5167
            $document_data,
5168
            $is_certificate_mode,
5169
            $parent_id
5170
        );
5171
        $modify_icons[] = self::getButtonDelete(
5172
            $is_read_only,
5173
            $document_data,
5174
            $is_certificate_mode,
5175
            $curdirpath,
5176
            $parent_id
5177
        );
5178
5179
        if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
5180
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5181
            // @TODO: add prompt to select a format target
5182
            if (!in_array($path, self::get_system_folders())) {
5183
                if ($usePpt2lp && $formatType) {
5184
                    $modify_icons[] = Display::url(
5185
                        Display::return_icon('convert.png', get_lang('Convert')),
5186
                        '#',
5187
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5188
                    );
5189
                }
5190
            }
5191
        }
5192
5193
        if ('file' == $type && ('html' == $extension || 'htm' == $extension)) {
5194
            if (0 == $is_template) {
5195
                if ((isset($_GET['curdirpath']) && '/certificates' != $_GET['curdirpath']) || !isset($_GET['curdirpath'])) {
5196
                    $modify_icons[] = Display::url(
5197
                        Display::return_icon('wizard.png', get_lang('Add as a template')),
5198
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id"
5199
                    );
5200
                }
5201
                if ((isset($_GET['curdirpath']) && '/certificates' == $_GET['curdirpath']) || $is_certificate_mode) {//allow attach certificate to course
5202
                    $visibility_icon_certificate = 'nocertificate';
5203
                    if (self::get_default_certificate_id(api_get_course_int_id()) == $id) {
5204
                        $visibility_icon_certificate = 'certificate';
5205
                        $certificate = get_lang('Default certificate');
5206
                        $preview = get_lang('Preview certificate');
5207
                        $is_preview = true;
5208
                    } else {
5209
                        $is_preview = false;
5210
                        $certificate = get_lang('NoDefault certificate');
5211
                    }
5212
                    if (isset($_GET['selectcat'])) {
5213
                        $modify_icons[] = Display::url(
5214
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5215
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id"
5216
                        );
5217
                        if ($is_preview) {
5218
                            $modify_icons[] = Display::url(
5219
                                Display::return_icon('preview_view.png', $preview),
5220
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id"
5221
                            );
5222
                        }
5223
                    }
5224
                }
5225
            } else {
5226
                $modify_icons[] = Display::url(
5227
                    Display::return_icon('wizard_na.png', get_lang('Remove template')),
5228
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id"
5229
                );
5230
            }
5231
5232
            $modify_icons[] = Display::url(
5233
                Display::return_icon('pdf.png', get_lang('Export to PDF format')),
5234
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath"
5235
            );
5236
        }
5237
5238
        return implode(PHP_EOL, $modify_icons);
5239
    }
5240
5241
    /**
5242
     * @param $folders
5243
     * @param $curdirpath
5244
     * @param $move_file
5245
     * @param string $group_dir
5246
     *
5247
     * @return string
5248
     */
5249
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5250
    {
5251
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5252
5253
        // Form title
5254
        $form->addHidden('move_file', $move_file);
5255
5256
        $options = [];
5257
5258
        // Group documents cannot be uploaded in the root
5259
        if ('' == $group_dir) {
5260
            if ('/' != $curdirpath) {
5261
                $options['/'] = get_lang('Documents');
5262
            }
5263
5264
            if (is_array($folders)) {
5265
                foreach ($folders as &$folder) {
5266
                    // Hide some folders
5267
                    if ('/HotPotatoes_files' == $folder ||
5268
                        '/certificates' == $folder ||
5269
                        'css' == basename($folder)
5270
                    ) {
5271
                        continue;
5272
                    }
5273
                    // Admin setting for Hide/Show the folders of all users
5274
                    if ('false' == api_get_setting('show_users_folders') &&
5275
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5276
                    ) {
5277
                        continue;
5278
                    }
5279
5280
                    // Admin setting for Hide/Show Default folders to all users
5281
                    if ('false' == api_get_setting('show_default_folders') &&
5282
                        (
5283
                            '/images' == $folder ||
5284
                            '/flash' == $folder ||
5285
                            '/audio' == $folder ||
5286
                            '/video' == $folder ||
5287
                            strstr($folder, '/images/gallery') ||
5288
                            '/video/flv' == $folder
5289
                        )
5290
                    ) {
5291
                        continue;
5292
                    }
5293
5294
                    // Admin setting for Hide/Show chat history folder
5295
                    if ('false' == api_get_setting('show_chat_folder') &&
5296
                        '/chat_files' == $folder) {
5297
                        continue;
5298
                    }
5299
5300
                    // You cannot move a file to:
5301
                    // 1. current directory
5302
                    // 2. inside the folder you want to move
5303
                    // 3. inside a subfolder of the folder you want to move
5304
                    if (($curdirpath != $folder) &&
5305
                        ($folder != $move_file) &&
5306
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5307
                    ) {
5308
                        // If document title is used, we have to display titles instead of real paths...
5309
                        $path_displayed = self::get_titles_of_path($folder);
5310
                        if (empty($path_displayed)) {
5311
                            $path_displayed = get_lang('Untitled');
5312
                        }
5313
                        $options[$folder] = $path_displayed;
5314
                    }
5315
                }
5316
            }
5317
        } else {
5318
            foreach ($folders as $folder) {
5319
                if (($curdirpath != $folder) &&
5320
                    ($folder != $move_file) &&
5321
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5322
                ) {
5323
                    // Cannot copy dir into his own subdir
5324
                    $path_displayed = self::get_titles_of_path($folder);
5325
                    $display_folder = substr($path_displayed, strlen($group_dir));
5326
                    $display_folder = '' == $display_folder ? get_lang('Documents') : $display_folder;
5327
                    $options[$folder] = $display_folder;
5328
                }
5329
            }
5330
        }
5331
        $form->addElement('select', 'move_to', get_lang('Move to'), $options);
5332
        $form->addButtonNext(get_lang('Move element'), 'move_file_submit');
5333
5334
        return $form->returnForm();
5335
    }
5336
5337
    /**
5338
     * Gets the path translated with title of docs and folders.
5339
     *
5340
     * @param string $path the real path
5341
     *
5342
     * @return the path which should be displayed
5343
     */
5344
    public static function get_titles_of_path($path)
5345
    {
5346
        global $tmp_folders_titles;
5347
        $course_id = api_get_course_int_id();
5348
        $nb_slashes = substr_count($path, '/');
5349
        $current_slash_pos = 0;
5350
        $path_displayed = '';
5351
        for ($i = 0; $i < $nb_slashes; $i++) {
5352
            // For each folder of the path, retrieve title.
5353
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
5354
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
5355
5356
            if (empty($tmp_path)) {
5357
                // If empty, then we are in the final part of the path
5358
                $tmp_path = $path;
5359
            }
5360
5361
            if (!empty($tmp_folders_titles[$tmp_path])) {
5362
                // If this path has soon been stored here we don't need a new query
5363
                $path_displayed .= $tmp_folders_titles[$tmp_path];
5364
            } else {
5365
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
5366
                        WHERE c_id = '.$course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
5367
                $rs = Database::query($sql);
5368
                $tmp_title = '/'.Database::result($rs, 0, 0);
5369
                $path_displayed .= $tmp_title;
5370
                $tmp_folders_titles[$tmp_path] = $tmp_title;
5371
            }
5372
        }
5373
5374
        return $path_displayed;
5375
    }
5376
5377
    /**
5378
     * Creates form that asks for the directory name.
5379
     *
5380
     * @return string html-output text for the form
5381
     */
5382
    public static function create_dir_form($dirId)
5383
    {
5384
        global $document_id;
5385
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
5386
        $form->addElement('hidden', 'create_dir', 1);
5387
        $form->addElement('hidden', 'dir_id', intval($document_id));
5388
        $form->addElement('hidden', 'id', intval($dirId));
5389
        $form->addElement('header', get_lang('Create folder'));
5390
        $form->addText('dirname', get_lang('Name of the new folder'), ['autofocus' => 'autofocus']);
5391
        $form->addButtonCreate(get_lang('Create the folder'));
5392
5393
        return $form->returnForm();
5394
    }
5395
5396
    /**
5397
     * Checks whether the user is in shared folder.
5398
     *
5399
     * @param string $curdirpath
5400
     * @param int    $sessionId
5401
     *
5402
     * @return bool Return true when user is into shared folder
5403
     */
5404
    public static function is_shared_folder($curdirpath, $sessionId)
5405
    {
5406
        $clean_curdirpath = Security::remove_XSS($curdirpath);
5407
        if ('/shared_folder' == $clean_curdirpath) {
5408
            return true;
5409
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$sessionId) {
5410
            return true;
5411
        } else {
5412
            return false;
5413
        }
5414
    }
5415
5416
    /**
5417
     * Checks whether the user is into any user shared folder.
5418
     *
5419
     * @param string $path
5420
     * @param int    $sessionId
5421
     *
5422
     * @return bool Return true when user is in any user shared folder
5423
     */
5424
    public static function is_any_user_shared_folder($path, $sessionId)
5425
    {
5426
        $clean_path = Security::remove_XSS($path);
5427
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
5428
            return true;
5429
        } elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
5430
            return true;
5431
        } else {
5432
            return false;
5433
        }
5434
    }
5435
5436
    /**
5437
     * Create users shared folder for course.
5438
     *
5439
     * @param int $userId
5440
     * @param int $sessionId
5441
     */
5442
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
5443
    {
5444
        return false;
5445
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
0 ignored issues
show
Unused Code introduced by
$documentDirectory = api...rectory'] . '/document' is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5446
        $userInfo = api_get_user_info($userId);
5447
5448
        if (!$sessionId) {
5449
            //Create shared folder. Necessary for recycled courses.
5450
            if (!file_exists($documentDirectory.'/shared_folder')) {
5451
                create_unexisting_directory(
5452
                    $courseInfo,
5453
                    $userId,
5454
                    0,
5455
                    0,
5456
                    0,
5457
                    $documentDirectory,
5458
                    '/shared_folder',
5459
                    get_lang('Folders of users'),
5460
                    0,
5461
                    false,
5462
                    false
5463
                );
5464
            }
5465
            // Create dynamic user shared folder
5466
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
5467
                create_unexisting_directory(
5468
                    $courseInfo,
5469
                    $userId,
5470
                    0,
5471
                    0,
5472
                    0,
5473
                    $documentDirectory,
5474
                    '/shared_folder/sf_user_'.$userId,
5475
                    $userInfo['complete_name'],
5476
                    1,
5477
                    false,
5478
                    false
5479
                );
5480
            }
5481
5482
            return;
5483
        }
5484
5485
        // Create shared folder session.
5486
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
5487
            create_unexisting_directory(
5488
                $courseInfo,
5489
                api_get_user_id(),
5490
                $sessionId,
5491
                0,
5492
                0,
5493
                $documentDirectory,
5494
                '/shared_folder_session_'.$sessionId,
5495
                get_lang('Folders of users').' ('.api_get_session_name($sessionId).')',
5496
                0,
5497
                false,
5498
                false
5499
            );
5500
        }
5501
        //Create dynamic user shared folder into a shared folder session
5502
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
5503
            create_unexisting_directory(
5504
                $courseInfo,
5505
                $userId,
5506
                $sessionId,
5507
                0,
5508
                0,
5509
                $documentDirectory,
5510
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
5511
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
5512
                1,
5513
                false,
5514
                false
5515
            );
5516
        }
5517
    }
5518
5519
    /**
5520
     * Checks whether the user is into his shared folder or into a subfolder.
5521
     *
5522
     * @param int    $user_id
5523
     * @param string $path
5524
     * @param int    $sessionId
5525
     *
5526
     * @return bool Return true when user is in his user shared folder or into a subfolder
5527
     */
5528
    public static function is_my_shared_folder($user_id, $path, $sessionId)
5529
    {
5530
        $clean_path = Security::remove_XSS($path).'/';
5531
        //for security does not remove the last slash
5532
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
5533
        //for security does not remove the last slash
5534
        $main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
5535
5536
        if (preg_match($main_user_shared_folder, $clean_path)) {
5537
            return true;
5538
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
5539
            return true;
5540
        } else {
5541
            return false;
5542
        }
5543
    }
5544
5545
    public static function isBasicCourseFolder($path, $sessionId)
5546
    {
5547
        $cleanPath = Security::remove_XSS($path);
5548
        $basicCourseFolder = '/basic-course-documents__'.$sessionId.'__0';
5549
5550
        return $cleanPath == $basicCourseFolder;
5551
    }
5552
5553
    /**
5554
     * Check if the file name or folder searched exist.
5555
     *
5556
     * @return bool Return true when exist
5557
     */
5558
    public static function search_keyword($document_name, $keyword)
5559
    {
5560
        if (false !== api_strripos($document_name, $keyword)) {
5561
            return true;
5562
        } else {
5563
            return false;
5564
        }
5565
    }
5566
5567
    /**
5568
     * Checks whether a document can be previewed by using the browser.
5569
     *
5570
     * @param string $file_extension the filename extension of the document (it must be in lower case)
5571
     *
5572
     * @return bool returns TRUE or FALSE
5573
     */
5574
    public static function isBrowserViewable($file_extension)
5575
    {
5576
        static $allowed_extensions = [
5577
            'htm', 'html', 'xhtml',
5578
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
5579
            'pdf', 'svg', 'swf',
5580
            'txt', 'log',
5581
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
5582
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar',
5583
        ];
5584
5585
        /*
5586
          //TODO: make a admin switch to strict mode
5587
          1. global default $allowed_extensions
5588
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
5589
          return true;
5590
          }
5591
          2. check native support
5592
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
5593
         */
5594
5595
        if (!($result = in_array($file_extension, $allowed_extensions))) {
5596
            // Assignment + a logical check.
5597
            return false;
5598
        }
5599
5600
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
5601
        if ("pdf" == $file_extension) {
5602
            return api_browser_support('pdf');
5603
        } elseif ("mp3" == $file_extension) {
5604
            return api_browser_support('mp3');
5605
        } elseif ("mp4" == $file_extension) {
5606
            return api_browser_support('mp4');
5607
        } elseif ("ogg" == $file_extension || "ogx" == $file_extension || "ogv" == $file_extension || "oga" == $file_extension) {
5608
            return api_browser_support('ogg');
5609
        } elseif ("svg" == $file_extension) {
5610
            return api_browser_support('svg');
5611
        } elseif ("mpg" == $file_extension || "mpeg" == $file_extension) {
5612
            return api_browser_support('mpg');
5613
        } elseif ("mov" == $file_extension) {
5614
            return api_browser_support('mov');
5615
        } elseif ("wav" == $file_extension) {
5616
            return api_browser_support('wav');
5617
        } elseif ("mid" == $file_extension || "kar" == $file_extension) {
5618
            return api_browser_support('mid');
5619
        } elseif ("avi" == $file_extension) {
5620
            return api_browser_support('avi');
5621
        } elseif ("wma" == $file_extension) {
5622
            return api_browser_support('wma');
5623
        } elseif ("wmv" == $file_extension) {
5624
            return api_browser_support('wmv');
5625
        } elseif ("tif" == $file_extension || "tiff" == $file_extension) {
5626
            return api_browser_support('tif');
5627
        } elseif ("mov" == $file_extension) {
5628
            return api_browser_support('mov');
5629
        } elseif ("au" == $file_extension) {
5630
            return api_browser_support('au');
5631
        } elseif ("webm" == $file_extension) {
5632
            return api_browser_support('webm');
5633
        }
5634
5635
        return $result;
5636
    }
5637
5638
    /**
5639
     * @param array $courseInfo
5640
     * @param int   $sessionId
5641
     *
5642
     * @return array
5643
     */
5644
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
5645
    {
5646
        $table = Database::get_course_table(TABLE_DOCUMENT);
5647
        $courseId = $courseInfo['real_id'];
5648
        $sessionCondition = api_get_session_condition($sessionId);
5649
        $sql = "SELECT * FROM $table
5650
                WHERE
5651
                  path LIKE '%DELETED%' AND
5652
                  c_id = $courseId
5653
                  $sessionCondition
5654
                ORDER BY path
5655
        ";
5656
5657
        $result = Database::query($sql);
5658
        $files = [];
5659
        while ($document = Database::fetch_array($result, 'ASSOC')) {
5660
            $files[] = $document;
5661
        }
5662
5663
        return $files;
5664
    }
5665
5666
    /**
5667
     * @param int   $id
5668
     * @param array $courseInfo
5669
     * @param int   $sessionId
5670
     *
5671
     * @return array
5672
     */
5673
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
5674
    {
5675
        if (empty($courseInfo)) {
5676
            return false;
5677
        }
5678
5679
        $table = Database::get_course_table(TABLE_DOCUMENT);
5680
        $courseId = $courseInfo['real_id'];
5681
        $sessionCondition = api_get_session_condition($sessionId);
5682
        $sql = "SELECT * FROM $table
5683
                WHERE
5684
                  path LIKE '%DELETED%' AND
5685
                  id = $id AND
5686
                  c_id = $courseId
5687
                  $sessionCondition
5688
                LIMIT 1
5689
        ";
5690
        $result = Database::query($sql);
5691
        if (Database::num_rows($result)) {
5692
            $result = Database::fetch_array($result, 'ASSOC');
5693
5694
            return $result;
5695
        }
5696
5697
        return [];
5698
    }
5699
5700
    /**
5701
     * @param int   $id
5702
     * @param array $courseInfo
5703
     * @param int   $sessionId
5704
     *
5705
     * @return bool
5706
     */
5707
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
5708
    {
5709
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
5710
        if (!empty($document)) {
5711
            $path = $document['path'];
5712
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
5713
            my_delete($coursePath.$path);
5714
            // Hard delete.
5715
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
5716
5717
            return true;
5718
        }
5719
5720
        return false;
5721
    }
5722
5723
    /**
5724
     * @param array $courseInfo
5725
     * @param int   $sessionId
5726
     */
5727
    public static function purgeDocuments($courseInfo, $sessionId)
5728
    {
5729
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
5730
        foreach ($files as $file) {
5731
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
5732
        }
5733
    }
5734
5735
    /**
5736
     * @param int   $id
5737
     * @param array $courseInfo
5738
     * @param int   $sessionId
5739
     */
5740
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
5741
    {
5742
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
5743
        if (!empty($document)) {
5744
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
5745
5746
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
5747
                self::file_send_for_download($coursePath.$document['path']);
5748
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
5749
            }
5750
        }
5751
    }
5752
5753
    /**
5754
     * @param array $courseInfo
5755
     * @param int   $sessionId
5756
     *
5757
     * @return bool
5758
     */
5759
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
5760
    {
5761
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
5762
5763
        if (empty($files)) {
5764
            return false;
5765
        }
5766
5767
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
5768
5769
        // Creating a ZIP file.
5770
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
5771
        $zip = new PclZip($tempZipFile);
5772
        foreach ($files as $file) {
5773
            $zip->add(
5774
                $coursePath.$file['path'],
5775
                PCLZIP_OPT_REMOVE_PATH,
5776
                $coursePath
5777
            );
5778
        }
5779
5780
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
5781
            self::file_send_for_download($tempZipFile, true);
5782
            @unlink($tempZipFile);
5783
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
5784
        }
5785
    }
5786
5787
    /**
5788
     * Delete documents from a session in a course.
5789
     *
5790
     * @param array $courseInfo
5791
     * @param int   $sessionId
5792
     *
5793
     * @return bool
5794
     */
5795
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
5796
    {
5797
        if (empty($courseInfo)) {
5798
            return false;
5799
        }
5800
5801
        if (empty($sessionId)) {
5802
            return false;
5803
        }
5804
5805
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
5806
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
5807
        $courseId = $courseInfo['real_id'];
5808
5809
        // get invisible folders
5810
        $sql = "SELECT DISTINCT d.id, path
5811
                FROM $documentTable d
5812
                WHERE
5813
                    $conditionSession AND
5814
                    d.c_id = $courseId ";
5815
5816
        $result = Database::query($sql);
5817
        $documents = Database::store_result($result, 'ASSOC');
5818
        if ($documents) {
5819
            foreach ($documents as $document) {
5820
                $documentId = $document['id'];
5821
                self::delete_document(
5822
                    $courseInfo,
5823
                    null,
5824
                    null,
5825
                    $sessionId,
5826
                    $documentId
5827
                );
5828
            }
5829
        }
5830
5831
        /*
5832
        $sql = "DELETE FROM $documentTable
5833
                WHERE c_id = $courseId AND session_id = $sessionId";
5834
        Database::query($sql);
5835
5836
        $sql = "DELETE FROM $itemPropertyTable
5837
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
5838
        Database::query($sql);*/
5839
    }
5840
5841
    /**
5842
     * Update the file or directory path in the document db document table.
5843
     *
5844
     * @author - Hugues Peeters <[email protected]>
5845
     *
5846
     * @param string $action   - action type require : 'delete' or 'update'
5847
     * @param string $old_path - old path info stored to change
5848
     * @param string $new_path - new path info to substitute
5849
     *
5850
     * @desc Update the file or directory path in the document db document table
5851
     */
5852
    public static function updateDbInfo($action, $old_path, $new_path = '')
5853
    {
5854
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
5855
        $course_id = api_get_course_int_id();
5856
        $old_path = Database::escape_string($old_path);
5857
        switch ($action) {
5858
            case 'delete':
5859
                $query = "DELETE FROM $dbTable
5860
                          WHERE
5861
                            c_id = $course_id AND
5862
                            (
5863
                                path LIKE BINARY '".$old_path."' OR
5864
                                path LIKE BINARY '".$old_path."/%'
5865
                            )";
5866
                Database::query($query);
5867
                break;
5868
            case 'update':
5869
                if ('.' == $new_path[0]) {
5870
                    $new_path = substr($new_path, 1);
5871
                }
5872
                $new_path = str_replace('//', '/', $new_path);
5873
5874
                // Attempt to update	- tested & working for root	dir
5875
                $new_path = Database::escape_string($new_path);
5876
                $query = "UPDATE $dbTable SET
5877
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
5878
                          WHERE
5879
                                c_id = $course_id AND
5880
                                (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
5881
                Database::query($query);
5882
                break;
5883
        }
5884
    }
5885
5886
    /**
5887
     * Adds a cloud link to the database.
5888
     *
5889
     * @author - Aquilino Blanco Cores <[email protected]>
5890
     *
5891
     * @param array  $_course
5892
     * @param string $path
5893
     * @param string $url
5894
     * @param string $name
5895
     *
5896
     * @return int id of document or 0 if already exists or there was a problem creating it
5897
     */
5898
    public static function addCloudLink($_course, $path, $url, $name)
5899
    {
5900
        $file_path = $path;
5901
        if (!self::cloudLinkExists($_course, $path, $url)) {
5902
            $doc = self::addDocument($_course, $file_path, 'link', 0, $name, $url);
5903
5904
            return $doc->getIid();
5905
        } else {
5906
            return 0;
5907
        }
5908
    }
5909
5910
    /**
5911
     * Deletes a cloud link from the database.
5912
     *
5913
     * @author - Aquilino Blanco Cores <[email protected]>
5914
     *
5915
     * @param array  $courseInfo
5916
     * @param string $documentId
5917
     *
5918
     * @return bool true if success / false if an error occurred
5919
     */
5920
    public static function deleteCloudLink($courseInfo, $documentId)
5921
    {
5922
        if (empty($documentId) || empty($courseInfo)) {
5923
            return false;
5924
        }
5925
5926
        $documentId = (int) $documentId;
5927
        $fileDeletedFromDb = false;
5928
        if (!empty($documentId)) {
5929
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
5930
            // checking
5931
            $table = Database::get_course_table(TABLE_DOCUMENT);
5932
            $courseId = $courseInfo['real_id'];
5933
            echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
5934
            $result = Database::query($sql);
5935
            $exists = Database::num_rows($result) > 0;
5936
            $fileDeletedFromDb = !$exists;
5937
        }
5938
5939
        return $fileDeletedFromDb;
5940
    }
5941
5942
    /**
5943
     * Gets the id of a cloud link with a given path.
5944
     *
5945
     * @author - Aquilino Blanco Cores <[email protected]>
5946
     *
5947
     * @param array  $courseInfo
5948
     * @param string $path
5949
     * @param string $url
5950
     *
5951
     * @return int link's id / false if no link found
5952
     */
5953
    public static function getCloudLinkId($courseInfo, $path, $url)
5954
    {
5955
        $table = Database::get_course_table(TABLE_DOCUMENT);
5956
5957
        if (empty($courseInfo)) {
5958
            return false;
5959
        }
5960
5961
        $courseId = (int) $courseInfo['real_id'];
5962
        $path = Database::escape_string($path);
5963
5964
        if ('/' != substr($path, -1)) {
5965
            // Add final slash to path if not present
5966
            $path .= '/';
5967
        }
5968
5969
        if (!empty($courseId) && !empty($path)) {
5970
            $sql = "SELECT id FROM $table
5971
                    WHERE
5972
                        c_id = $courseId AND
5973
                        path LIKE BINARY '$path' AND
5974
                        comment = '$url' AND
5975
                        filetype = 'link'
5976
                    LIMIT 1";
5977
            $result = Database::query($sql);
5978
            if ($result && Database::num_rows($result)) {
5979
                $row = Database::fetch_array($result);
5980
5981
                return (int) $row[0];
5982
            }
5983
        }
5984
5985
        return false;
5986
    }
5987
5988
    /**
5989
     * Checks if a cloud link exists.
5990
     *
5991
     * @author - Aquilino Blanco Cores <[email protected]>
5992
     *
5993
     * @param array  $courseInfo
5994
     * @param string $path
5995
     * @param string $url
5996
     *
5997
     * @return bool true if it exists false in other case
5998
     */
5999
    public static function cloudLinkExists($courseInfo, $path, $url)
6000
    {
6001
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6002
6003
        return $exists;
6004
    }
6005
6006
    /**
6007
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications.
6008
     *
6009
     * @author Aquilino Blanco Cores <[email protected]>
6010
     *
6011
     * @return string the well formed URLs regular expressions string
6012
     */
6013
    public static function getWellFormedUrlRegex()
6014
    {
6015
        return '/\(?((http|https|ftp):\/\/)(?:((?:[^\W\s]|\.|-|[:]{1})+)@{1})?((?:www.)?(?:[^\W\s]|\.|-)+[\.][^\W\s]{2,4}|localhost(?=\/)|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(\d*))?([\/]?[^\s\?]*[\/]{1})*(?:\/?([^\s\n\?\[\]\{\}\#]*(?:(?=\.)){1}|[^\s\n\?\[\]\{\}\.\#]*)?([\.]{1}[^\s\?\#]*)?)?(?:\?{1}([^\s\n\#\[\]]*))?([\#][^\s\n]*)?\)?/i';
6016
    }
6017
6018
    /**
6019
     * Gets the files hosting sites' whitelist.
6020
     *
6021
     * @author Aquilino Blanco Cores <[email protected]>
6022
     *
6023
     * @return array the sites list
6024
     */
6025
    public static function getFileHostingWhiteList()
6026
    {
6027
        return [
6028
            'asuswebstorage.com',
6029
            'dropbox.com',
6030
            'dropboxusercontent.com',
6031
            'fileserve.com',
6032
            'drive.google.com',
6033
            'docs.google.com',
6034
            'icloud.com',
6035
            'mediafire.com',
6036
            'mega.nz',
6037
            'onedrive.live.com',
6038
            'slideshare.net',
6039
            'scribd.com',
6040
            'wetransfer.com',
6041
            'box.com',
6042
            'livefilestore.com', // OneDrive
6043
        ];
6044
    }
6045
6046
    /**
6047
     * @param int $userId
6048
     *
6049
     * @return array Example [ 0 => ['code' => 'ABC', 'directory' => 'ABC0', 'path' => '/images/gallery/test.png', 'code_path' => 'ABC:/images/gallery/test.png'], 1 => ...]
6050
     */
6051
    public static function getAllDocumentsCreatedByUser($userId)
6052
    {
6053
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6054
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
6055
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
6056
        $userId = (int) $userId;
6057
6058
        $sql = "SELECT DISTINCT c.code, c.directory, docs.path
6059
                FROM $tblItemProperty AS last
6060
                INNER JOIN $tblDocument AS docs
6061
                ON (
6062
                    docs.id = last.ref AND
6063
                    docs.c_id = last.c_id AND
6064
                    docs.filetype <> 'folder'
6065
                )
6066
                INNER JOIN $tblCourse as c
6067
                ON (
6068
                    docs.c_id = c.id
6069
                )
6070
                WHERE
6071
                    last.tool = '".TOOL_DOCUMENT."' AND
6072
                    last.insert_user_id = $userId AND
6073
                    docs.path NOT LIKE '%_DELETED_%'
6074
                ORDER BY c.directory, docs.path
6075
                ";
6076
        $result = Database::query($sql);
6077
6078
        $list = [];
6079
        if (0 != Database::num_rows($result)) {
6080
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6081
                $row['code_path'] = $row['code'].':'.$row['path'];
6082
                $list[] = $row;
6083
            }
6084
        }
6085
6086
        return $list;
6087
    }
6088
6089
    /**
6090
     * Adds a new document to the database.
6091
     *
6092
     * @param array  $courseInfo
6093
     * @param string $path
6094
     * @param string $fileType
6095
     * @param int    $fileSize
6096
     * @param string $title
6097
     * @param string $comment
6098
     * @param int    $readonly
6099
     * @param int    $visibility       see ResourceLink constants
6100
     * @param int    $groupId          group.id
6101
     * @param int    $sessionId        Session ID, if any
6102
     * @param int    $userId           creator user id
6103
     * @param bool   $sendNotification
6104
     * @param string $content
6105
     * @param int    $parentId
6106
     * @param string $realPath
6107
     *
6108
     * @return CDocument|false
6109
     */
6110
    public static function addDocument(
6111
        $courseInfo,
6112
        $path,
6113
        $fileType,
6114
        $fileSize,
6115
        $title,
6116
        $comment = null,
6117
        $readonly = 0,
6118
        $visibility = null,
6119
        $groupId = 0,
6120
        $sessionId = 0,
6121
        $userId = 0,
6122
        $sendNotification = true,
6123
        $content = '',
6124
        $parentId = 0,
6125
        $realPath = ''
6126
    ) {
6127
        $userId = empty($userId) ? api_get_user_id() : $userId;
6128
        if (empty($userId)) {
6129
            return false;
6130
        }
6131
6132
        $userEntity = api_get_user_entity($userId);
6133
        if (empty($userEntity)) {
6134
            return false;
6135
        }
6136
6137
        $courseEntity = api_get_course_entity($courseInfo['real_id']);
6138
        if (empty($courseEntity)) {
6139
            return false;
6140
        }
6141
6142
        $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6143
        $session = api_get_session_entity($sessionId);
6144
        $group = api_get_group_entity($groupId);
6145
        $readonly = (int) $readonly;
6146
        $documentRepo = Container::getDocumentRepository();
6147
6148
        /** @var \Chamilo\CoreBundle\Entity\AbstractResource $parentResource */
6149
        $parentResource = $courseEntity;
6150
        if (!empty($parentId)) {
6151
            $parent = $documentRepo->find($parentId);
6152
            if ($parent) {
6153
                $parentResource = $parent;
6154
            }
6155
        }
6156
6157
        $document = $documentRepo->findCourseResourceByTitle(
6158
            $title,
6159
            $parentResource->getResourceNode(),
6160
            $courseEntity,
6161
            $session,
6162
            $group
6163
        );
6164
6165
        // Document already exists
6166
        if (null !== $document) {
6167
            return $document;
6168
        }
6169
6170
//        $criteria = ['path' => $path, 'course' => $courseEntity];
6171
//        $document = $documentRepo->findOneBy($criteria);
6172
//
6173
//        // Document already exists
6174
//        if ($document) {
6175
//            return false;
6176
//        }
6177
6178
        // is updated using the title
6179
        $document = new CDocument();
6180
        $document
6181
            ->setFiletype($fileType)
6182
            ->setTitle($title)
6183
            ->setComment($comment)
6184
            ->setReadonly(1 === $readonly)
6185
            ->setParent($parentResource)
6186
            ->addCourseLink($courseEntity, $session, $group)
6187
        ;
6188
6189
        $em = Database::getManager();
6190
        $em->persist($document);
6191
        $em->flush();
6192
        $repo = Container::getDocumentRepository();
6193
        $repo->addFileFromString($document, $realPath, 'text/html', $content, true);
6194
6195
        if ($document) {
6196
            $allowNotification = api_get_configuration_value('send_notification_when_document_added');
6197
            if ($sendNotification && $allowNotification) {
6198
                $courseTitle = $courseEntity->getTitle();
6199
                if (!empty($sessionId)) {
6200
                    $sessionInfo = api_get_session_info($sessionId);
6201
                    $courseTitle .= ' ( '.$sessionInfo['name'].') ';
6202
                }
6203
6204
                $url = api_get_path(WEB_CODE_PATH).
6205
                    'document/showinframes.php?cid='.$courseEntity->getId().'&sid='.$sessionId.'&id='.$document->getIid();
6206
                $link = Display::url(basename($title), $url, ['target' => '_blank']);
6207
                $userInfo = api_get_user_info($userId);
6208
                $message = sprintf(
6209
                    get_lang('A new document %s has been added to the document tool in your course %s by %s.'),
6210
                    $link,
6211
                    $courseTitle,
6212
                    $userInfo['complete_name']
6213
                );
6214
                $subject = sprintf(get_lang('New document added to course %s'), $courseTitle);
6215
                MessageManager::sendMessageToAllUsersInCourse($subject, $message, $courseEntity, $sessionId);
6216
            }
6217
6218
            return $document;
6219
        }
6220
6221
        return false;
6222
    }
6223
6224
    /**
6225
     * @param array  $documentAndFolders
6226
     * @param array  $courseInfo
6227
     * @param bool   $is_certificate_mode
6228
     * @param array  $groupMemberWithUploadRights
6229
     * @param string $path
6230
     * @param bool   $addToEditor
6231
     * @param string $editorUrl
6232
     *
6233
     * @return array
6234
     */
6235
    public static function processDocumentAndFolders(
6236
        $documentAndFolders,
6237
        $courseInfo,
6238
        $is_certificate_mode,
6239
        $groupMemberWithUploadRights,
6240
        $path,
6241
        $addToEditor = false,
6242
        $editorUrl = ''
6243
    ) {
6244
        if (empty($documentAndFolders) || empty($courseInfo)) {
6245
            return [];
6246
        }
6247
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
6248
        $userId = api_get_user_id();
6249
        $currentUserInfo = api_get_user_info();
6250
        $sessionId = api_get_session_id();
6251
        $groupId = api_get_group_id();
6252
        $userIsSubscribed = CourseManager::is_user_subscribed_in_course($userId, $courseInfo['code']);
6253
6254
        $url = api_get_path(WEB_COURSE_PATH).$courseInfo['directory'].'/document';
6255
        if ($addToEditor) {
6256
            $url = api_get_path(REL_COURSE_PATH).$courseInfo['directory'].'/document';
6257
        }
6258
6259
        $courseId = $courseInfo['real_id'];
6260
        $group = api_get_group_entity($groupId);
6261
6262
        $sortable_data = [];
6263
        foreach ($documentAndFolders as $key => $document_data) {
6264
            $row = [];
6265
            $row['id'] = $document_data['id'];
6266
            $row['type'] = $document_data['filetype'];
6267
6268
            // If the item is invisible, wrap it in a span with class invisible.
6269
            $is_visible = self::is_visible_by_id(
6270
                $document_data['id'],
6271
                $courseInfo,
6272
                $sessionId,
6273
                $userId,
6274
                false,
6275
                $userIsSubscribed
6276
            );
6277
            $invisibility_span_open = 0 == $is_visible ? '<span class="muted">' : '';
6278
            $invisibility_span_close = 0 == $is_visible ? '</span>' : '';
6279
            $size = 1;
6280
            // Get the title or the basename depending on what we're using
6281
            if ('' != $document_data['title']) {
6282
                $document_name = $document_data['title'];
6283
            } else {
6284
                $document_name = basename($document_data['path']);
6285
            }
6286
            $row['name'] = $document_name;
6287
            // Data for checkbox
6288
            if (($isAllowedToEdit || $groupMemberWithUploadRights) && count($documentAndFolders) > 1) {
6289
                $row[] = $document_data['id'];
6290
            }
6291
6292
            if (self::is_folder_to_avoid($document_data['path'], $is_certificate_mode)) {
6293
                continue;
6294
            }
6295
6296
            // Show the owner of the file only in groups
6297
            $user_link = '';
6298
            if (!empty($groupId)) {
6299
                if (!empty($document_data['insert_user_id'])) {
6300
                    $userInfo = api_get_user_info(
6301
                        $document_data['insert_user_id'],
6302
                        false,
6303
                        false,
6304
                        false,
6305
                        false,
6306
                        false
6307
                    );
6308
                    $user_link = '<div class="document_owner">'
6309
                        .get_lang('Owner').': '.UserManager::getUserProfileLink($userInfo)
6310
                        .'</div>';
6311
                }
6312
            }
6313
6314
            // Hack in order to avoid the download icon appearing on cloud links
6315
            if ('link' == $document_data['filetype']) {
6316
                $size = 0;
6317
            }
6318
6319
            // Icons (clickable)
6320
            $row[] = self::create_document_link(
6321
                $url,
6322
                $document_data,
6323
                true,
6324
                $is_visible,
6325
                $size,
6326
                $isAllowedToEdit,
6327
                $is_certificate_mode,
6328
                $addToEditor,
6329
                $editorUrl
6330
            );
6331
6332
            // Validation when belongs to a session
6333
            $session_img = api_get_session_image($document_data['session_id'], $currentUserInfo['status']);
6334
6335
            $link = self::create_document_link(
6336
                $url,
6337
                $document_data,
6338
                false,
6339
                $is_visible,
6340
                $size,
6341
                $isAllowedToEdit,
6342
                $is_certificate_mode,
6343
                $addToEditor,
6344
                $editorUrl
6345
            );
6346
6347
            // Document title with link
6348
            $row[] = $link.$session_img.'<br />'.$invisibility_span_open.'<i>'
6349
                .nl2br(htmlspecialchars($document_data['comment'], ENT_QUOTES, 'utf-8'))
6350
                .'</i>'.$invisibility_span_close.$user_link;
6351
6352
            if ('folder' == $document_data['filetype']) {
6353
                $displaySize = '<span id="document_size_'.$document_data['id']
6354
                    .'" data-path= "'.$document_data['path']
6355
                    .'" class="document_size"></span>';
6356
            } else {
6357
                $displaySize = format_file_size($document_data['size']);
6358
            }
6359
6360
            $row[] = '<span style="display:none;">'.$size.'</span>'.
6361
                $invisibility_span_open.
6362
                $displaySize.
6363
                $invisibility_span_close;
6364
6365
            // Last edit date
6366
            $last_edit_date = api_get_local_time($document_data['updated_at']);
6367
            $display_date = date_to_str_ago($document_data['updated_at']).
6368
                ' <div class="muted"><small>'.$last_edit_date."</small></div>";
6369
6370
            $row[] = $invisibility_span_open.$display_date.$invisibility_span_close;
6371
6372
            $groupMemberWithEditRightsCheckDocument = GroupManager::allowUploadEditDocument(
6373
                $userId,
6374
                $courseId,
6375
                $group,
6376
                $document_data
6377
            );
6378
6379
            // Admins get an edit column
6380
            if ($isAllowedToEdit ||
6381
                $groupMemberWithEditRightsCheckDocument ||
6382
                self::is_my_shared_folder(api_get_user_id(), $path, $sessionId)
6383
            ) {
6384
                $is_template = isset($document_data['is_template']) ? $document_data['is_template'] : false;
6385
6386
                // If readonly, check if it the owner of the file or if the user is an admin
6387
                if ($document_data['creator_id'] == api_get_user_id() || api_is_platform_admin()) {
6388
                    $edit_icons = self::build_edit_icons(
6389
                        $document_data,
6390
                        $key,
6391
                        $is_template,
6392
                        $is_visible
6393
                    );
6394
                } else {
6395
                    $edit_icons = self::build_edit_icons(
6396
                        $document_data,
6397
                        $key,
6398
                        $is_template,
6399
                        $is_visible
6400
                    );
6401
                }
6402
                $row[] = $edit_icons;
6403
            } else {
6404
                $row[] = '';
6405
            }
6406
            $row[] = $last_edit_date;
6407
            $row[] = $size;
6408
            $row[] = $document_name;
6409
6410
            if ((isset($_GET['keyword']) && self::search_keyword($document_name, $_GET['keyword'])) ||
6411
                !isset($_GET['keyword']) ||
6412
                empty($_GET['keyword'])
6413
            ) {
6414
                $sortable_data[] = $row;
6415
            }
6416
        }
6417
6418
        return $sortable_data;
6419
    }
6420
6421
    /**
6422
     * Parse file information into a link.
6423
     *
6424
     * @param array  $userInfo        Current user info
6425
     * @param array  $course_info
6426
     * @param int    $session_id
6427
     * @param array  $resource
6428
     * @param int    $lp_id
6429
     * @param bool   $add_move_button
6430
     * @param string $target
6431
     * @param string $overwrite_url
6432
     *
6433
     * @return string|null
6434
     */
6435
    private static function parseFile(
6436
        $userInfo,
6437
        $course_info,
6438
        $session_id,
6439
        $resource,
6440
        $lp_id,
6441
        $add_move_button,
6442
        $target,
6443
        $overwrite_url
6444
    ) {
6445
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
6446
        $web_code_path = api_get_path(WEB_CODE_PATH);
6447
6448
        $documentId = $resource['id'];
6449
        $path = $resource['path'];
6450
6451
        if (empty($path)) {
6452
            $num = 0;
6453
        } else {
6454
            $num = substr_count($path, '/') - 1;
6455
        }
6456
6457
        // It's a file.
6458
        $icon = choose_image($path);
6459
        $position = strrpos($icon, '.');
6460
        $icon = substr($icon, 0, $position).'_small.gif';
6461
        $my_file_title = $resource['title'];
6462
        $visibility = $resource['visibility'];
6463
6464
        // If title is empty we try to use the path
6465
        if (empty($my_file_title)) {
6466
            $my_file_title = basename($path);
6467
        }
6468
6469
        // Show the "image name" not the filename of the image.
6470
        if ($lp_id) {
6471
            // LP URL
6472
            $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq().'&action=add_item&type='.TOOL_DOCUMENT.'&file='.$documentId.'&lp_id='.$lp_id;
6473
        } else {
6474
            // Direct document URL
6475
            $url = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&sid='.$session_id.'&id='.$documentId;
6476
        }
6477
6478
        if (!empty($overwrite_url)) {
6479
            $overwrite_url = Security::remove_XSS($overwrite_url);
6480
            $url = $overwrite_url.'&cidReq='.$course_info['code'].'&sid='.$session_id.'&document_id='.$documentId;
6481
        }
6482
6483
        $img = Display::returnIconPath($icon);
6484
        if (!file_exists($img_sys_path.$icon)) {
6485
            $img = Display::returnIconPath('default_small.png');
6486
        }
6487
6488
        $link = Display::url(
6489
            '<img alt="" src="'.$img.'" title="" />&nbsp;'.$my_file_title,
6490
            $url,
6491
            ['target' => $target, 'class' => 'moved']
6492
        );
6493
6494
        $directUrl = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&sid='.$session_id.'&id='.$documentId;
6495
        $link .= '&nbsp;'.Display::url(
6496
            Display::return_icon('preview_view.png', get_lang('Preview')),
6497
            $directUrl,
6498
            ['target' => '_blank']
6499
        );
6500
6501
        $visibilityClass = null;
6502
        if (0 == $visibility) {
6503
            $visibilityClass = ' text-muted ';
6504
        }
6505
        $return = null;
6506
6507
        if (false == $lp_id) {
6508
            $return .= '<li
6509
                class="doc_resource '.$visibilityClass.' "
6510
                data_id="'.$documentId.'"
6511
                data_type="document"
6512
                title="'.$my_file_title.'" >';
6513
        } else {
6514
            $return .= '<li
6515
                class="doc_resource lp_resource_element '.$visibilityClass.' "
6516
                data_id="'.$documentId.'"
6517
                data_type="document"
6518
                title="'.$my_file_title.'" >';
6519
        }
6520
6521
        $return .= '<div class="flex flex-row item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
6522
        if ($add_move_button) {
6523
            $return .= '<a class="moved" href="#">';
6524
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
6525
            $return .= '</a> ';
6526
        }
6527
        $return .= $link;
6528
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
6529
        $return .= $sessionStar;
6530
6531
        $return .= '</div></li>';
6532
6533
        return $return;
6534
    }
6535
6536
    /**
6537
     * @param int   $folderId
6538
     * @param array $resource
6539
     * @param int   $lp_id
6540
     *
6541
     * @return string|null
6542
     */
6543
    private static function parseFolder($folderId, $resource, $lp_id)
6544
    {
6545
        $title = isset($resource['title']) ? $resource['title'] : null;
6546
        $path = isset($resource['path']) ? $resource['path'] : null;
6547
6548
        if (empty($path)) {
6549
            $num = 0;
6550
        } else {
6551
            $num = substr_count($path, '/');
6552
        }
6553
6554
        // It's a folder.
6555
        //hide some folders
6556
        if (in_array(
6557
            $path,
6558
            ['shared_folder', 'chat_files', 'HotPotatoes_files', 'css', 'certificates']
6559
        )) {
6560
            return null;
6561
        } elseif (preg_match('/_groupdocs/', $path)) {
6562
            return null;
6563
        } elseif (preg_match('/sf_user_/', $path)) {
6564
            return null;
6565
        } elseif (preg_match('/shared_folder_session_/', $path)) {
6566
            return null;
6567
        }
6568
6569
        //$onclick = '';
6570
        // if in LP, hidden folder are displayed in grey
6571
        $folder_class_hidden = '';
6572
        if ($lp_id) {
6573
            if (isset($resource['visible']) && 0 == $resource['visible']) {
6574
                $folder_class_hidden = ' doc_folder_hidden'; // in base.css
6575
            }
6576
        }
6577
        $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
6578
        $return = null;
6579
6580
        if (empty($path)) {
6581
            $return = '<ul class="lp_resource">';
6582
        }
6583
6584
        $return .= '<li class="doc_folder '.$folder_class_hidden.'" id="doc_id_'.$resource['id'].'"  style="margin-left:'.($num * 18).'px; ">';
6585
6586
        $image = Display::returnIconPath('nolines_plus.gif');
6587
        if (empty($path)) {
6588
            $image = Display::returnIconPath('nolines_minus.gif');
6589
        }
6590
        $return .= '<img style="cursor: pointer;" src="'.$image.'" align="absmiddle" id="img_'.$resource['id'].'" '.$onclick.'>';
6591
        $return .= Display::return_icon('lp_folder.png').'&nbsp;';
6592
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.$title.'</span>';
6593
        $return .= '</li>';
6594
6595
        if (empty($path)) {
6596
            if (false == $folderId) {
6597
                $return .= '<div id="res_'.$resource['id'].'" >';
6598
            } else {
6599
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
6600
            }
6601
        }
6602
6603
        return $return;
6604
    }
6605
6606
    /**
6607
     * Get the button to edit document.
6608
     *
6609
     * @param bool   $isReadOnly
6610
     * @param string $extension
6611
     * @param bool   $isCertificateMode
6612
     *
6613
     * @return string
6614
     */
6615
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
6616
    {
6617
        $extension = strtolower($extension);
6618
        $iconEn = Display::return_icon('edit.png', get_lang('Edit'));
6619
        $iconDis = Display::return_icon('edit_na.png', get_lang('Edit'));
6620
        $courseParams = api_get_cidreq();
6621
        $webOdfExtensionList = self::get_web_odf_extension_list();
6622
        $path = $documentData['path'];
6623
        $documentId = $documentData['id'];
6624
6625
        if ($isReadOnly) {
6626
            if (!api_is_course_admin() && !api_is_platform_admin()) {
6627
                return $iconDis;
6628
            }
6629
6630
            if (
6631
                'svg' == $extension && api_browser_support('svg') &&
6632
                'true' == api_get_setting('enabled_support_svg')
6633
            ) {
6634
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$documentId");
6635
            }
6636
6637
            if (
6638
                in_array($extension, $webOdfExtensionList) &&
6639
                true === api_get_configuration_value('enabled_support_odf')
6640
            ) {
6641
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$documentId");
6642
            }
6643
6644
            if (
6645
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
6646
                    'true' == api_get_setting('enabled_support_pixlr')
6647
            ) {
6648
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$documentId");
6649
            }
6650
6651
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId");
6652
        }
6653
6654
        if (in_array($path, self::get_system_folders())) {
6655
            return $iconDis;
6656
        }
6657
6658
        if ($isCertificateMode) {
6659
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId&curdirpath=/certificates");
6660
        }
6661
6662
        $sessionId = api_get_session_id();
6663
6664
        if ($sessionId && $documentData['session_id'] != $sessionId) {
6665
            return $iconDis;
6666
        }
6667
6668
        if (
6669
            'svg' == $extension && api_browser_support('svg') &&
6670
            'true' == api_get_setting('enabled_support_svg')
6671
        ) {
6672
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$documentId");
6673
        }
6674
6675
        if (
6676
            in_array($extension, $webOdfExtensionList) &&
6677
            true === api_get_configuration_value('enabled_support_odf')
6678
        ) {
6679
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$documentId");
6680
        }
6681
6682
        if (
6683
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
6684
                'true' == api_get_setting('enabled_support_pixlr')
6685
        ) {
6686
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$documentId");
6687
        }
6688
6689
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId");
6690
    }
6691
6692
    /**
6693
     * Get the button to move document.
6694
     *
6695
     * @param bool $isReadOnly
6696
     * @param bool $isCertificateMode
6697
     * @param int  $parentId
6698
     *
6699
     * @return string
6700
     */
6701
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
6702
    {
6703
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
6704
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
6705
6706
        if ($isReadOnly) {
6707
            return $iconDis;
6708
        }
6709
6710
        $path = $documentData['path'];
6711
        $document_id = $documentData['id'];
6712
        $sessionId = api_get_session_id();
6713
        $courseParams = api_get_cidreq();
6714
6715
        if ($isCertificateMode || in_array($path, self::get_system_folders())) {
6716
            return $iconDis;
6717
        }
6718
6719
        if ($sessionId) {
6720
            if ($documentData['session_id'] != $sessionId) {
6721
                return $iconDis;
6722
            }
6723
        }
6724
6725
        $urlMoveParams = http_build_query(['id' => $parentId, 'move' => $document_id]);
6726
6727
        return Display::url(
6728
            $iconEn,
6729
            api_get_self()."?$courseParams&$urlMoveParams"
6730
        );
6731
    }
6732
6733
    /**
6734
     * Get the button to set visibility to document.
6735
     *
6736
     * @param bool $isReadOnly
6737
     * @param int  $visibility
6738
     * @param bool $isCertificateMode
6739
     * @param int  $parentId
6740
     *
6741
     * @return string|null
6742
     */
6743
    private static function getButtonVisibility(
6744
        $isReadOnly,
6745
        $visibility,
6746
        array $documentData,
6747
        $isCertificateMode,
6748
        $parentId
6749
    ) {
6750
        $visibility_icon = 0 == $visibility ? 'invisible' : 'visible';
6751
        $visibility_command = 0 == $visibility ? 'set_visible' : 'set_invisible';
6752
        $courseParams = api_get_cidreq();
6753
6754
        if ($isReadOnly) {
6755
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6756
                return Display::return_icon($visibility_icon.'.png', get_lang('The visibility cannot be changed'));
6757
            }
6758
6759
            return null;
6760
        }
6761
6762
        if ($isCertificateMode) {
6763
            return Display::return_icon($visibility_icon.'.png', get_lang('The visibility cannot be changed'));
6764
        }
6765
6766
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6767
            $tip_visibility = 'invisible' == $visibility_icon ? get_lang('Show') : get_lang('Hide');
6768
6769
            return Display::url(
6770
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
6771
                api_get_self()."?$courseParams&id=$parentId&action=$visibility_command&document_id={$documentData['id']}"
6772
            );
6773
        }
6774
6775
        return null;
6776
    }
6777
6778
    /**
6779
     * GEt the button to delete a document.
6780
     *
6781
     * @param bool   $isReadOnly
6782
     * @param bool   $isCertificateMode
6783
     * @param string $curDirPath
6784
     * @param int    $parentId
6785
     *
6786
     * @return string
6787
     */
6788
    private static function getButtonDelete(
6789
        $isReadOnly,
6790
        array $documentData,
6791
        $isCertificateMode,
6792
        $curDirPath,
6793
        $parentId
6794
    ) {
6795
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
6796
        $iconDis = Display::return_icon('delete_na.png', get_lang('This folder cannot be deleted'));
6797
        $path = $documentData['path'];
6798
        $id = $documentData['id'];
6799
        $courseParams = api_get_cidreq();
6800
6801
        if ($isReadOnly) {
6802
            return $iconDis;
6803
        }
6804
6805
        if (in_array($path, self::get_system_folders())) {
6806
            return $iconDis;
6807
        }
6808
6809
        $titleToShow = addslashes(basename($documentData['title']));
6810
        $urlDeleteParams = http_build_query([
6811
            'curdirpath' => $curDirPath,
6812
            'action' => 'delete_item',
6813
            'id' => $parentId,
6814
            'deleteid' => $documentData['id'],
6815
        ]);
6816
6817
        $btn = Display::url(
6818
            $iconEn,
6819
            api_get_self()."?$courseParams&$urlDeleteParams",
6820
            [
6821
                'title' => get_lang('Do you want to delete the file?').': '.$titleToShow,
6822
                'class' => 'delete-swal',
6823
            ]
6824
        );
6825
6826
        if (isset($_GET['curdirpath']) &&
6827
            '/certificates' === $_GET['curdirpath'] &&
6828
            self::get_default_certificate_id(api_get_course_int_id()) == $id
6829
        ) {
6830
            return $btn;
6831
        }
6832
6833
        if ($isCertificateMode) {
6834
            return $btn;
6835
        }
6836
6837
        $sessionId = api_get_session_id();
6838
6839
        if ($sessionId) {
6840
            if ($documentData['session_id'] != $sessionId) {
6841
                return $iconDis;
6842
            }
6843
        }
6844
6845
        return $btn;
6846
    }
6847
}
6848