Passed
Push — 1.11.x ( 63aafb...adbe21 )
by Angel Fernando Quiroz
11:14
created

DocumentManager::createUserSharedFolder()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 72
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 42
nc 8
nop 3
dl 0
loc 72
rs 8.6257
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use ChamiloSession as Session;
6
use enshrined\svgSanitize\Sanitizer;
7
8
/**
9
 *  Class DocumentManager
10
 *  This is the document library for Chamilo.
11
 *  It is / will be used to provide a service layer to all document-using tools.
12
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
13
 *  Include/require it in your code to use its functionality.
14
 */
15
class DocumentManager
16
{
17
    /**
18
     * Construct.
19
     */
20
    private function __construct()
21
    {
22
    }
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 ($filename === true) {
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 && (connection_status() == 0)) {
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 to 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: $full_file_name");
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 = api_get_configuration_value('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
                    $enableMathJaxScript = api_get_setting('enabled_mathjax') && api_get_configuration_value('mathjax_enable_script_header_in_all_HTML_document');
432
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
433
                        $contentType .= '; charset=UTF-8';
434
                    } else {
435
                        $encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
436
                        if (!empty($encoding)) {
437
                            $contentType .= '; charset='.$encoding;
438
                        }
439
                    }
440
                    break;
441
                case 'text/plain':
442
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
443
                        $contentType .= '; charset=UTF-8';
444
                    } else {
445
                        $encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
446
                        if (!empty($encoding)) {
447
                            $contentType .= '; charset='.$encoding;
448
                        }
449
                    }
450
                    break;
451
                case 'video/mp4':
452
                case 'audio/mpeg':
453
                case 'audio/mp4':
454
                case 'audio/ogg':
455
                case 'audio/webm':
456
                case 'audio/wav':
457
                case 'video/ogg':
458
                case 'video/webm':
459
                    self::smartReadFile($full_file_name, $filename, $contentType);
460
                    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...
461
                case 'application/vnd.dwg':
462
                case 'application/vnd.dwf':
463
                    header('Content-type: application/octet-stream');
464
                    break;
465
            }
466
467
            header('Content-type: '.$contentType);
468
            header('Content-Length: '.$len);
469
            $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
470
471
            if (strpos($userAgent, 'msie')) {
472
                header('Content-Disposition: ; filename= '.$filename);
473
            } else {
474
                //header('Content-Disposition: inline');
475
                header('Content-Disposition: inline;');
476
            }
477
478
            if ($fixLinksHttpToHttps) {
479
                $content = file_get_contents($full_file_name);
480
                $content = str_replace(
481
                    ['http%3A%2F%2F', 'http://'],
482
                    ['https%3A%2F%2F', 'https://'],
483
                    $content
484
                );
485
                if ($enableMathJaxScript === true) {
486
                    $content = self::includeMathJaxScript($content);
487
                }
488
                echo $content;
489
            } else {
490
                if ('image/svg+xml' === $contentType) {
491
                    $svgContent = file_get_contents($full_file_name);
492
493
                    echo (new Sanitizer())->sanitize($svgContent);
494
                    return true;
495
                }
496
497
                if (isset($enableMathJaxScript) && $enableMathJaxScript === true) {
498
                    $content = file_get_contents($full_file_name);
499
                    $content = self::includeMathJaxScript($content);
500
                    echo $content;
501
                } else {
502
                    if (function_exists('ob_end_clean') && ob_get_length()) {
503
                        // Use ob_end_clean() to avoid weird buffering situations
504
                        // where file is sent broken/incomplete for download
505
                        ob_end_clean();
506
                    }
507
                    readfile($full_file_name);
508
                }
509
            }
510
511
            return true;
512
        }
513
    }
514
515
    /**
516
     * Session folder filters.
517
     *
518
     * @param string $path
519
     * @param int    $sessionId
520
     *
521
     * @return string|null
522
     */
523
    public static function getSessionFolderFilters($path, $sessionId)
524
    {
525
        $sessionId = (int) $sessionId;
526
        $condition = null;
527
528
        if (!empty($sessionId)) {
529
            // Chat folder filter
530
            if ($path == '/chat_files') {
531
                $condition .= " AND (docs.session_id = '$sessionId') ";
532
            }
533
            // share_folder filter
534
            $condition .= " AND docs.path != '/shared_folder' ";
535
        }
536
537
        return $condition;
538
    }
539
540
    /**
541
     * Fetches all document data for the given user/group.
542
     *
543
     * @param array  $courseInfo
544
     * @param string $path
545
     * @param int    $toGroupId       iid
546
     * @param int    $toUserId
547
     * @param bool   $canSeeInvisible
548
     * @param bool   $search
549
     * @param int    $sessionId
550
     *
551
     * @return array with all document data
552
     */
553
    public static function getAllDocumentData(
554
        $courseInfo,
555
        $path = '/',
556
        $toGroupId = 0,
557
        $toUserId = null,
558
        $canSeeInvisible = false,
559
        $search = false,
560
        $sessionId = 0
561
    ) {
562
        if (empty($courseInfo)) {
563
            return [];
564
        }
565
566
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
567
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
568
569
        if (!is_null($toUserId)) {
570
            $toUserId = (int) $toUserId;
571
            $userGroupFilter = "last.to_user_id = $toUserId";
572
            if (empty($toUserId)) {
573
                $userGroupFilter = ' (last.to_user_id = 0 OR last.to_user_id IS NULL) ';
574
            }
575
        } else {
576
            $toGroupId = (int) $toGroupId;
577
            $userGroupFilter = "last.to_group_id = $toGroupId";
578
            if (empty($toGroupId)) {
579
                $userGroupFilter = '( last.to_group_id = 0 OR last.to_group_id IS NULL) ';
580
            }
581
        }
582
583
        // Escape underscores in the path so they don't act as a wildcard
584
        $originalPath = $path;
585
        $path = str_replace('_', '\_', $path);
586
587
        $visibilityBit = ' <> 2';
588
589
        // The given path will not end with a slash, unless it's the root '/'
590
        // so no root -> add slash
591
        $addedSlash = $path == '/' ? '' : '/';
592
593
        // Condition for the session
594
        $sessionId = $sessionId ?: api_get_session_id();
595
        $conditionSession = " AND (last.session_id = '$sessionId' OR (last.session_id = '0' OR last.session_id IS NULL) )";
596
        $conditionSession .= self::getSessionFolderFilters($originalPath, $sessionId);
597
598
        $sharedCondition = null;
599
        if ($originalPath == '/shared_folder') {
600
            $students = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
601
            if (!empty($students)) {
602
                $conditionList = [];
603
                foreach ($students as $studentInfo) {
604
                    $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
605
                }
606
                $sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
607
            }
608
        }
609
        $where = '';
610
        if ($search != true) {
611
            $where .= "docs.path NOT LIKE '".Database::escape_string($path.$addedSlash.'%/%')."' AND";
612
        }
613
        $sql = "SELECT
614
                    docs.id,
615
                    docs.filetype,
616
                    docs.path,
617
                    docs.title,
618
                    docs.comment,
619
                    docs.size,
620
                    docs.readonly,
621
                    docs.session_id,
622
                    last.session_id item_property_session_id,
623
                    last.lastedit_date,
624
                    last.visibility,
625
                    last.insert_user_id
626
                FROM $tblItemProperty AS last
627
                INNER JOIN $tblDocument AS docs
628
                ON (
629
                    docs.id = last.ref AND
630
                    docs.c_id = last.c_id
631
                )
632
                WHERE
633
                    last.tool = '".TOOL_DOCUMENT."' AND
634
                    docs.c_id = {$courseInfo['real_id']} AND
635
                    last.c_id = {$courseInfo['real_id']} AND
636
                    docs.path LIKE '".Database::escape_string($path.$addedSlash.'%')."' AND
637
                    $where
638
                    docs.path NOT LIKE '%_DELETED_%' AND
639
                    $userGroupFilter AND
640
                    last.visibility $visibilityBit
641
                    $conditionSession
642
                    $sharedCondition
643
                ORDER BY last.iid DESC, last.session_id DESC
644
                ";
645
646
        $result = Database::query($sql);
647
648
        $documentData = [];
649
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
650
        $isCoach = api_is_coach();
651
        if ($result !== false && Database::num_rows($result) != 0) {
652
            $rows = [];
653
654
            $hideInvisibleDocuments = api_get_configuration_value('hide_invisible_course_documents_in_sessions');
655
656
            while ($row = Database::fetch_array($result, 'ASSOC')) {
657
                if (isset($rows[$row['id']])) {
658
                    continue;
659
                }
660
661
                // If we are in session and hide_invisible_course_documents_in_sessions is enabled
662
                // Then we avoid the documents that have visibility in session but that they come from a base course
663
                if ($hideInvisibleDocuments && $sessionId) {
664
                    if ($row['item_property_session_id'] == $sessionId && empty($row['session_id'])) {
665
                        continue;
666
                    }
667
                }
668
669
                if (self::isBasicCourseFolder($row['path'], $sessionId)) {
670
                    $basicCourseDocumentsContent = self::getAllDocumentData(
671
                        $courseInfo,
672
                        $row['path']
673
                    );
674
675
                    if (empty($basicCourseDocumentsContent)) {
676
                        continue;
677
                    }
678
                }
679
680
                $rows[$row['id']] = $row;
681
            }
682
683
            // If we are in session and hide_invisible_course_documents_in_sessions is enabled
684
            // Or if we are students
685
            // Then don't list the invisible or deleted documents
686
            if (($sessionId && $hideInvisibleDocuments) || (!$isCoach && !$isAllowedToEdit)) {
687
                $rows = array_filter($rows, function ($row) {
688
                    if (in_array($row['visibility'], ['0', '2'])) {
689
                        return false;
690
                    }
691
692
                    return true;
693
                });
694
            }
695
696
            foreach ($rows as $row) {
697
                if ($row['filetype'] === 'file' &&
698
                    pathinfo($row['path'], PATHINFO_EXTENSION) == 'html'
699
                ) {
700
                    // Templates management
701
                    $tblTemplate = Database::get_main_table(TABLE_MAIN_TEMPLATES);
702
                    $sql = "SELECT id FROM $tblTemplate
703
                            WHERE
704
                                course_code = '".$courseInfo['code']."' AND
705
                                user_id = '".api_get_user_id()."' AND
706
                                ref_doc = '".$row['id']."'";
707
                    $templateResult = Database::query($sql);
708
                    $row['is_template'] = (Database::num_rows($templateResult) > 0) ? 1 : 0;
709
                }
710
                $row['basename'] = basename($row['path']);
711
                // Just filling $document_data.
712
                $documentData[$row['id']] = $row;
713
            }
714
715
            // Only for the student we filter the results see BT#1652
716
            if (!$isCoach && !$isAllowedToEdit) {
717
                // Checking parents visibility.
718
                $finalDocumentData = [];
719
720
                foreach ($documentData as $row) {
721
                    $isVisible = self::check_visibility_tree(
722
                        $row['id'],
723
                        $courseInfo,
724
                        $sessionId,
725
                        api_get_user_id(),
726
                        $toGroupId
727
                    );
728
                    if ($isVisible) {
729
                        $finalDocumentData[$row['id']] = $row;
730
                    }
731
                }
732
            } else {
733
                $finalDocumentData = $documentData;
734
            }
735
736
            return $finalDocumentData;
737
        }
738
739
        return [];
740
    }
741
742
    /**
743
     * Gets the paths of all folders in a course
744
     * can show all folders (except for the deleted ones) or only visible ones.
745
     *
746
     * @param array  $_course
747
     * @param int    $groupIid          iid
748
     * @param bool   $can_see_invisible
749
     * @param bool   $getInvisibleList
750
     * @param string $path              current path
751
     *
752
     * @return array with paths
753
     */
754
    public static function get_all_document_folders(
755
        $_course,
756
        $groupIid = 0,
757
        $can_see_invisible = false,
758
        $getInvisibleList = false,
759
        $path = ''
760
    ) {
761
        if (empty($_course)) {
762
            return [];
763
        }
764
765
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
766
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
767
        $groupIid = (int) $groupIid;
768
        $document_folders = [];
769
770
        $students = CourseManager::get_user_list_from_course_code(
771
            $_course['code'],
772
            api_get_session_id()
773
        );
774
775
        $conditionList = [];
776
        if (!empty($students)) {
777
            foreach ($students as $studentId => $studentInfo) {
778
                $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
779
            }
780
        }
781
782
        $groupCondition = " last.to_group_id = $groupIid";
783
        if (empty($groupIid)) {
784
            $groupCondition = ' (last.to_group_id = 0 OR last.to_group_id IS NULL)';
785
        }
786
787
        $show_users_condition = '';
788
        if (api_get_setting('show_users_folders') === 'false') {
789
            $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
790
        }
791
792
        if ($can_see_invisible) {
793
            // condition for the session
794
            $session_id = api_get_session_id();
795
            $session_id = $session_id ?: api_get_session_id();
796
            $condition_session = " AND (last.session_id = '$session_id' OR (last.session_id = '0' OR last.session_id IS NULL) )";
797
            $condition_session .= self::getSessionFolderFilters($path, $session_id);
798
799
            if ($groupIid != 0) {
800
                $sql = "SELECT DISTINCT docs.id, path
801
                       FROM $TABLE_ITEMPROPERTY  AS last
802
                       INNER JOIN $TABLE_DOCUMENT  AS docs
803
                       ON (
804
                            docs.id = last.ref AND
805
                            docs.c_id = last.c_id
806
                       )
807
                       WHERE
808
                            last.tool = '".TOOL_DOCUMENT."' AND
809
                            last.c_id = {$_course['real_id']} AND
810
                            docs.c_id = {$_course['real_id']} AND
811
                            docs.filetype = 'folder' AND
812
                            $groupCondition AND
813
                            docs.path NOT LIKE '%shared_folder%' AND
814
                            docs.path NOT LIKE '%_DELETED_%' AND
815
                            last.visibility <> 2
816
                            $condition_session ";
817
            } else {
818
                $sql = "SELECT DISTINCT docs.id, path
819
                        FROM $TABLE_ITEMPROPERTY  AS last
820
                        INNER JOIN $TABLE_DOCUMENT  AS docs
821
                        ON (
822
                            docs.id = last.ref AND
823
                            docs.c_id = last.c_id
824
                        )
825
                        WHERE
826
                            last.tool = '".TOOL_DOCUMENT."' AND
827
                            last.c_id = {$_course['real_id']} AND
828
                            docs.c_id = {$_course['real_id']} AND
829
                            docs.filetype = 'folder' AND
830
                            docs.path NOT LIKE '%_DELETED_%' AND
831
                            $groupCondition AND
832
                            last.visibility <> 2
833
                            $show_users_condition
834
                            $condition_session
835
                        ";
836
            }
837
            $result = Database::query($sql);
838
839
            if ($result && Database::num_rows($result) != 0) {
840
                while ($row = Database::fetch_array($result, 'ASSOC')) {
841
                    if (self::is_folder_to_avoid($row['path'])) {
842
                        continue;
843
                    }
844
845
                    if (strpos($row['path'], '/shared_folder/') !== false) {
846
                        if (!in_array($row['path'], $conditionList)) {
847
                            continue;
848
                        }
849
                    }
850
851
                    $document_folders[$row['id']] = $row['path'];
852
                }
853
854
                if (!empty($document_folders)) {
855
                    // natsort($document_folders);
856
                    natcasesort($document_folders);
857
                }
858
859
                return $document_folders;
860
            } else {
861
                return false;
862
            }
863
        } else {
864
            // No invisible folders
865
            // Condition for the session
866
            $session_id = api_get_session_id();
867
            $condition_session = api_get_session_condition(
868
                $session_id,
869
                true,
870
                true, // needed to don't show files in elfinder browser
871
                'docs.session_id'
872
            );
873
874
            $visibilityCondition = 'last.visibility = 1';
875
            $fileType = "docs.filetype = 'folder' AND";
876
            if ($getInvisibleList) {
877
                $visibilityCondition = 'last.visibility = 0';
878
                $fileType = '';
879
            }
880
881
            //get visible folders
882
            $sql = "SELECT DISTINCT docs.id, path
883
                    FROM
884
                    $TABLE_ITEMPROPERTY AS last
885
                    INNER JOIN $TABLE_DOCUMENT AS docs
886
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
887
                    WHERE
888
                        $fileType
889
                        last.tool = '".TOOL_DOCUMENT."' AND
890
                        $groupCondition AND
891
                        $visibilityCondition
892
                        $show_users_condition
893
                        $condition_session AND
894
                        last.c_id = {$_course['real_id']}  AND
895
                        docs.c_id = {$_course['real_id']} ";
896
897
            $result = Database::query($sql);
898
899
            $visibleFolders = [];
900
            while ($row = Database::fetch_array($result, 'ASSOC')) {
901
                $visibleFolders[$row['id']] = $row['path'];
902
            }
903
904
            if ($getInvisibleList) {
905
                return $visibleFolders;
906
            }
907
908
            //get invisible folders
909
            $sql = "SELECT DISTINCT docs.id, path
910
                    FROM $TABLE_ITEMPROPERTY AS last
911
                    INNER JOIN $TABLE_DOCUMENT AS docs
912
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
913
                    WHERE
914
                        docs.filetype = 'folder' AND
915
                        last.tool = '".TOOL_DOCUMENT."' AND
916
                        $groupCondition AND
917
                        last.visibility = 0 $condition_session AND
918
                        last.c_id = {$_course['real_id']} AND
919
                        docs.c_id = {$_course['real_id']} ";
920
            $result = Database::query($sql);
921
            $invisibleFolders = [];
922
            while ($row = Database::fetch_array($result, 'ASSOC')) {
923
                //get visible folders in the invisible ones -> they are invisible too
924
                $sql = "SELECT DISTINCT docs.id, path
925
                        FROM $TABLE_ITEMPROPERTY AS last
926
                        INNER JOIN $TABLE_DOCUMENT AS docs
927
                        ON (docs.id = last.ref AND docs.c_id = last.c_id)
928
                        WHERE
929
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
930
                            docs.filetype = 'folder' AND
931
                            last.tool = '".TOOL_DOCUMENT."' AND
932
                            $groupCondition AND
933
                            last.visibility = 1 $condition_session AND
934
                            last.c_id = {$_course['real_id']} AND
935
                            docs.c_id = {$_course['real_id']}  ";
936
                $folder_in_invisible_result = Database::query($sql);
937
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
938
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
939
                }
940
            }
941
942
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
943
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
944
                $document_folders = array_diff($visibleFolders, $invisibleFolders);
945
                // natsort($document_folders);
946
                natcasesort($document_folders);
947
948
                return $document_folders;
949
            } elseif (is_array($visibleFolders)) {
950
                natcasesort($visibleFolders);
951
                // natsort($visibleFolders);
952
953
                return $visibleFolders;
954
            } else {
955
                //no visible folders found
956
                return false;
957
            }
958
        }
959
    }
960
961
    /**
962
     * This check if a document has the readonly property checked, then see if the user
963
     * is the owner of this file, if all this is true then return true.
964
     *
965
     * @param array  $_course
966
     * @param int    $user_id     id of the current user
967
     * @param string $file        path stored in the database (if not defined, $documentId must be used)
968
     * @param int    $document_id in case you dont have the file path ,
969
     *                            insert the id of the file here and leave $file in blank ''
970
     * @param bool   $to_delete
971
     * @param int    $sessionId
972
     *
973
     * @return bool true/false
974
     * */
975
    public static function check_readonly(
976
        $_course,
977
        $user_id,
978
        $file = null,
979
        $document_id = 0,
980
        $to_delete = false,
981
        $sessionId = null,
982
        $documentId = null
983
    ) {
984
        if (empty($sessionId)) {
985
            $sessionId = api_get_session_id();
986
        } else {
987
            $sessionId = intval($sessionId);
988
        }
989
990
        if (empty($document_id) || !is_numeric($document_id)) {
991
            $document_id = self::get_document_id($_course, $file, $sessionId);
992
        } else {
993
            $document_id = intval($document_id);
994
        }
995
996
        $TABLE_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
997
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
998
        $course_id = $_course['real_id'];
999
1000
        if ($to_delete) {
1001
            if (self::is_folder($_course, $document_id)) {
1002
                if (!empty($file)) {
1003
                    $path = Database::escape_string($file);
1004
                    // Check
1005
                    $sql = "SELECT td.id, readonly, tp.insert_user_id
1006
                            FROM $TABLE_DOCUMENT td
1007
                            INNER JOIN $TABLE_PROPERTY tp
1008
                            ON (td.c_id = tp.c_id AND tp.ref= td.id)
1009
                            WHERE
1010
                                td.c_id = $course_id AND
1011
                                tp.c_id = $course_id AND
1012
                                td.session_id = $sessionId AND
1013
                                (path='".$path."' OR path LIKE BINARY '".$path."/%' ) ";
1014
                    // Get all id's of documents that are deleted
1015
                    $what_to_check_result = Database::query($sql);
1016
1017
                    if ($what_to_check_result && Database::num_rows($what_to_check_result) != 0) {
1018
                        // file with readonly set to 1 exist?
1019
                        $readonly_set = false;
1020
                        while ($row = Database::fetch_array($what_to_check_result)) {
1021
                            //query to delete from item_property table
1022
                            if ($row['readonly'] == 1) {
1023
                                if (!($row['insert_user_id'] == $user_id)) {
1024
                                    $readonly_set = true;
1025
                                    break;
1026
                                }
1027
                            }
1028
                        }
1029
1030
                        if ($readonly_set) {
1031
                            return true;
1032
                        }
1033
                    }
1034
                }
1035
1036
                return false;
1037
            }
1038
        }
1039
1040
        if (!empty($document_id)) {
1041
            $sql = "SELECT a.insert_user_id, b.readonly
1042
                   FROM $TABLE_PROPERTY a
1043
                   INNER JOIN $TABLE_DOCUMENT b
1044
                   ON (a.c_id = b.c_id AND a.ref= b.id)
1045
                   WHERE
1046
            			a.c_id = $course_id AND
1047
                        b.c_id = $course_id AND
1048
            			a.ref = $document_id
1049
                    LIMIT 1";
1050
            $result = Database::query($sql);
1051
            $doc_details = Database::fetch_array($result, 'ASSOC');
1052
1053
            if ($doc_details['readonly'] == 1) {
1054
                return !($doc_details['insert_user_id'] == $user_id || api_is_platform_admin());
1055
            }
1056
        }
1057
1058
        return false;
1059
    }
1060
1061
    /**
1062
     * This check if a document is a folder or not.
1063
     *
1064
     * @param array $_course
1065
     * @param int   $id      document id
1066
     *
1067
     * @return bool true/false
1068
     * */
1069
    public static function is_folder($_course, $id)
1070
    {
1071
        $table = Database::get_course_table(TABLE_DOCUMENT);
1072
        if (empty($_course)) {
1073
            return false;
1074
        }
1075
        $course_id = $_course['real_id'];
1076
        $id = (int) $id;
1077
        $sql = "SELECT filetype FROM $table
1078
                WHERE c_id = $course_id AND id= $id";
1079
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
1080
1081
        return $result['filetype'] == 'folder';
1082
    }
1083
1084
    /**
1085
     * @param int   $document_id
1086
     * @param array $course_info
1087
     * @param int   $session_id
1088
     * @param bool  $remove_content_from_db
1089
     */
1090
    public static function deleteDocumentFromDb(
1091
        $document_id,
1092
        $course_info = [],
1093
        $session_id = 0,
1094
        $remove_content_from_db = false
1095
    ) {
1096
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1097
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
1098
1099
        // Deleting from the DB
1100
        $user_id = api_get_user_id();
1101
        $document_id = (int) $document_id;
1102
1103
        if (empty($course_info)) {
1104
            $course_info = api_get_course_info();
1105
        }
1106
1107
        if (empty($session_id)) {
1108
            $session_id = api_get_session_id();
1109
        }
1110
        // Soft DB delete
1111
        api_item_property_update(
1112
            $course_info,
1113
            TOOL_DOCUMENT,
1114
            $document_id,
1115
            'delete',
1116
            $user_id,
1117
            null,
1118
            null,
1119
            null,
1120
            null,
1121
            $session_id
1122
        );
1123
        self::delete_document_from_search_engine($course_info['code'], $document_id);
1124
        self::unset_document_as_template($document_id, $course_info['code'], $user_id);
1125
1126
        //Hard DB delete
1127
        if ($remove_content_from_db) {
1128
            $sql = "DELETE FROM $TABLE_ITEMPROPERTY
1129
                    WHERE
1130
                        c_id = {$course_info['real_id']} AND
1131
                        ref = ".$document_id." AND
1132
                        tool='".TOOL_DOCUMENT."'";
1133
            Database::query($sql);
1134
1135
            $sql = "DELETE FROM $TABLE_DOCUMENT
1136
                    WHERE c_id = {$course_info['real_id']} AND id = ".$document_id;
1137
            Database::query($sql);
1138
        }
1139
    }
1140
1141
    /**
1142
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
1143
     * Files/folders that are inside a deleted folder get visibility 2.
1144
     *
1145
     * @param array  $_course
1146
     * @param string $path          Path stored in the database
1147
     * @param string $base_work_dir Path to the "/document" folder of this course. Will be built if not provided.
1148
     * @param int    $sessionId     The ID of the session, if any
1149
     * @param int    $documentId    The document id, if available
1150
     * @param int    $groupId       iid
1151
     *
1152
     * @return bool true/false
1153
     *
1154
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1155
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1156
     */
1157
    public static function delete_document(
1158
        $_course,
1159
        $path = null,
1160
        $base_work_dir = null,
1161
        $sessionId = null,
1162
        $documentId = null,
1163
        $groupId = 0
1164
    ) {
1165
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1166
1167
        $documentId = (int) $documentId;
1168
        $groupId = (int) $groupId;
1169
        if (empty($groupId)) {
1170
            $groupId = api_get_group_id();
1171
        }
1172
1173
        $sessionId = (int) $sessionId;
1174
        if (empty($sessionId)) {
1175
            $sessionId = api_get_session_id();
1176
        }
1177
1178
        $course_id = $_course['real_id'];
1179
1180
        if (empty($course_id)) {
1181
            return false;
1182
        }
1183
1184
        if (empty($base_work_dir)) {
1185
            if (empty($_course['directory'])) {
1186
                return false;
1187
            }
1188
            $courseDir = $_course['directory'].'/document';
1189
            $sysCoursePath = api_get_path(SYS_COURSE_PATH);
1190
            $base_work_dir = $sysCoursePath.$courseDir;
1191
        }
1192
1193
        if (empty($documentId)) {
1194
            $documentId = self::get_document_id($_course, $path, $sessionId);
1195
            $docInfo = self::get_document_data_by_id(
1196
                $documentId,
1197
                $_course['code'],
1198
                false,
1199
                $sessionId
1200
            );
1201
            $path = $docInfo['path'];
1202
        } else {
1203
            $docInfo = self::get_document_data_by_id(
1204
                $documentId,
1205
                $_course['code'],
1206
                false,
1207
                $sessionId
1208
            );
1209
            if (empty($docInfo)) {
1210
                return false;
1211
            }
1212
            $path = $docInfo['path'];
1213
        }
1214
1215
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1216
            return false;
1217
        }
1218
1219
        $itemInfo = api_get_item_property_info(
1220
            $_course['real_id'],
1221
            TOOL_DOCUMENT,
1222
            $documentId,
1223
            $sessionId,
1224
            $groupId
1225
        );
1226
1227
        if (empty($itemInfo)) {
1228
            return false;
1229
        }
1230
1231
        // File was already deleted.
1232
        if ($itemInfo['lastedit_type'] == 'DocumentDeleted' ||
1233
            $itemInfo['lastedit_type'] == 'delete' ||
1234
            $itemInfo['visibility'] == 2
1235
        ) {
1236
            return false;
1237
        }
1238
1239
        // Filtering by group.
1240
        if ($itemInfo['to_group_id'] != $groupId) {
1241
            return false;
1242
        }
1243
1244
        $document_exists_in_disk = file_exists($base_work_dir.$path);
1245
        $new_path = $path.'_DELETED_'.$documentId;
1246
1247
        $file_deleted_from_db = false;
1248
        $file_deleted_from_disk = false;
1249
        $file_renamed_from_disk = false;
1250
1251
        if ($documentId) {
1252
            // Deleting doc from the DB.
1253
            self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1254
            // Checking
1255
            // $file_exists_in_db = self::get_document_data_by_id($documentId, $_course['code']);
1256
            $file_deleted_from_db = true;
1257
        }
1258
1259
        // Looking for children.
1260
        if ($docInfo['filetype'] == 'folder') {
1261
            $cleanPath = Database::escape_string($path);
1262
1263
            // Deleted files inside this folder.
1264
            $sql = "SELECT id FROM $TABLE_DOCUMENT
1265
                    WHERE
1266
                        c_id = $course_id AND
1267
                        session_id = $sessionId AND
1268
                        path LIKE BINARY '".$cleanPath."/%'";
1269
1270
            // Get all id's of documents that are deleted.
1271
            $result = Database::query($sql);
1272
1273
            if ($result && Database::num_rows($result) != 0) {
1274
                // Recursive delete.
1275
                while ($row = Database::fetch_array($result)) {
1276
                    self::delete_document(
1277
                        $_course,
1278
                        null,
1279
                        $base_work_dir,
1280
                        $sessionId,
1281
                        $row['id'],
1282
                        $groupId
1283
                    );
1284
                }
1285
            }
1286
        }
1287
1288
        if ($document_exists_in_disk) {
1289
            if (api_get_setting('permanently_remove_deleted_files') === 'true') {
1290
                // Delete documents, do it like this so metadata gets deleted too
1291
                my_delete($base_work_dir.$path);
1292
                // Hard delete.
1293
                self::deleteDocumentFromDb($documentId, $_course, $sessionId, true);
1294
                $file_deleted_from_disk = true;
1295
            } else {
1296
                // Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
1297
                if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
1298
                    if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
1299
                        $new_path = Database::escape_string($new_path);
1300
1301
                        $sql = "UPDATE $TABLE_DOCUMENT
1302
                                SET path = '".$new_path."'
1303
                                WHERE
1304
                                    c_id = $course_id AND
1305
                                    session_id = $sessionId AND
1306
                                    id = ".$documentId;
1307
                        Database::query($sql);
1308
1309
                        // Soft delete.
1310
                        self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1311
1312
                        // Change path of sub folders and documents in database.
1313
                        $old_item_path = $docInfo['path'];
1314
                        $new_item_path = $new_path.substr($old_item_path, strlen($path));
1315
                        $new_item_path = Database::escape_string($new_item_path);
1316
1317
                        $sql = "UPDATE $TABLE_DOCUMENT
1318
                                SET path = '".$new_item_path."'
1319
                                WHERE
1320
                                    c_id = $course_id AND
1321
                                    session_id = $sessionId AND
1322
                                    id = ".$documentId;
1323
                        Database::query($sql);
1324
1325
                        $file_renamed_from_disk = true;
1326
                    } else {
1327
                        // Couldn't rename - file permissions problem?
1328
                        error_log(
1329
                            __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '.$base_work_dir.$new_path.'. This is probably due to file permissions',
1330
                            0
1331
                        );
1332
                    }
1333
                }
1334
            }
1335
        }
1336
        // Checking inconsistency
1337
        //error_log('Doc status: (1 del db :'.($file_deleted_from_db?'yes':'no').') - (2 del disk: '.($file_deleted_from_disk?'yes':'no').') - (3 ren disk: '.($file_renamed_from_disk?'yes':'no').')');
1338
        if ($file_deleted_from_db && $file_deleted_from_disk ||
1339
            $file_deleted_from_db && $file_renamed_from_disk
1340
        ) {
1341
            return true;
1342
        } else {
1343
            //Something went wrong
1344
            //The file or directory isn't there anymore (on the filesystem)
1345
            // This means it has been removed externally. To prevent a
1346
            // blocking error from happening, we drop the related items from the
1347
            // item_property and the document table.
1348
            error_log(
1349
                __FILE__.' '.__LINE__.': System inconsistency detected. The file or directory '.$base_work_dir.$path.' seems to have been removed from the filesystem independently from the web platform. To restore consistency, the elements using the same path will be removed from the database',
1350
                0
1351
            );
1352
1353
            return false;
1354
        }
1355
    }
1356
1357
    /**
1358
     * Removes documents from search engine database.
1359
     *
1360
     * @param string $course_id   Course code
1361
     * @param int    $document_id Document id to delete
1362
     */
1363
    public static function delete_document_from_search_engine($course_id, $document_id)
1364
    {
1365
        // remove from search engine if enabled
1366
        if (api_get_setting('search_enabled') === 'true') {
1367
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1368
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1369
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1370
            $res = Database::query($sql);
1371
            if (Database::num_rows($res) > 0) {
1372
                $row2 = Database::fetch_array($res);
1373
                $di = new ChamiloIndexer();
1374
                $di->remove_document($row2['search_did']);
1375
            }
1376
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1377
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1378
            Database::query($sql);
1379
1380
            // remove terms from db
1381
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1382
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1383
        }
1384
    }
1385
1386
    /**
1387
     * Gets the id of a document with a given path.
1388
     *
1389
     * @param array  $courseInfo
1390
     * @param string $path
1391
     * @param int    $sessionId
1392
     *
1393
     * @return int id of document / false if no doc found
1394
     */
1395
    public static function get_document_id($courseInfo, $path, $sessionId = null, $forceFileTypeFolder = false)
1396
    {
1397
        $table = Database::get_course_table(TABLE_DOCUMENT);
1398
        $courseId = $courseInfo['real_id'];
1399
1400
        if (!isset($sessionId)) {
1401
            $sessionId = api_get_session_id();
1402
        } else {
1403
            $sessionId = intval($sessionId);
1404
        }
1405
1406
        // Special case: cache doc ID for 3600s if "/" has already been queried
1407
        // recently.
1408
        // This is a hack. Better solutions: use apcu (not always available)
1409
        // and even better: reduce the amount of calls by not processing
1410
        // documents that will not be shown (e.g. on other pages of a table)
1411
        $isSlash = ($path === '/');
1412
        if ($isSlash) {
1413
            $cSSlashString = 'docIdSlash2C'.$courseId.'S'.$sessionId;
1414
            $storedSlashId = Session::read($cSSlashString);
1415
            $now = time();
1416
            if (is_array($storedSlashId)) {
1417
                if ($storedSlashId['t'] >= $now - 3600) {
1418
                    return $storedSlashId['id'];
1419
                }
1420
            }
1421
        }
1422
1423
        $path = Database::escape_string($path);
1424
        if (!empty($courseId) && !empty($path)) {
1425
            $folderCondition = '';
1426
            if ($forceFileTypeFolder) {
1427
                $folderCondition = ' AND filetype = "folder" ';
1428
            }
1429
            $sql = "SELECT id FROM $table
1430
                    WHERE
1431
                        c_id = $courseId AND
1432
                        path LIKE BINARY '$path' AND
1433
                        session_id = $sessionId
1434
                        $folderCondition
1435
                    LIMIT 1";
1436
1437
            $result = Database::query($sql);
1438
            if (Database::num_rows($result)) {
1439
                $row = Database::fetch_array($result);
1440
                if ($isSlash) {
1441
                    Session::write($cSSlashString, ['t' => $now, 'id' => intval($row['id'])]);
1442
                }
1443
1444
                return intval($row['id']);
1445
            }
1446
            if ($isSlash) {
1447
                // Even if there is no "/" document/folder, store a result to avoid querying the database again
1448
                Session::write($cSSlashString, ['t' => $now, 'id' => false]);
1449
            }
1450
        }
1451
1452
        return false;
1453
    }
1454
1455
    /**
1456
     * Gets the document data with a given id.
1457
     *
1458
     * @param int    $id            Document Id (id field in c_document table)
1459
     * @param string $course_code   Course code
1460
     * @param bool   $load_parents  load folder parents
1461
     * @param int    $session_id    The session ID,
1462
     *                              0 if requires context *out of* session, and null to use global context
1463
     * @param bool   $ignoreDeleted
1464
     *
1465
     * @return array document content
1466
     */
1467
    public static function get_document_data_by_id(
1468
        $id,
1469
        $course_code,
1470
        $load_parents = false,
1471
        $session_id = null,
1472
        $ignoreDeleted = false
1473
    ) {
1474
        $course_info = api_get_course_info($course_code);
1475
        $course_id = $course_info['real_id'];
1476
1477
        if (empty($course_info)) {
1478
            return false;
1479
        }
1480
1481
        $session_id = empty($session_id) ? api_get_session_id() : (int) $session_id;
1482
        $groupId = api_get_group_id();
1483
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
1484
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1485
        $id = (int) $id;
1486
        $sessionCondition = api_get_session_condition($session_id, true, true);
1487
1488
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1489
                WHERE c_id = $course_id $sessionCondition AND id = $id";
1490
1491
        if ($ignoreDeleted) {
1492
            $sql .= " AND path NOT LIKE '%_DELETED_%' ";
1493
        }
1494
1495
        $result = Database::query($sql);
1496
        $courseParam = '&cidReq='.$course_code.'&id='.$id.'&id_session='.$session_id.'&gidReq='.$groupId;
1497
        if ($result && Database::num_rows($result) == 1) {
1498
            $row = Database::fetch_array($result, 'ASSOC');
1499
            //@todo need to clarify the name of the URLs not nice right now
1500
            $url_path = urlencode($row['path']);
1501
            $path = str_replace('%2F', '/', $url_path);
1502
            $pathinfo = pathinfo($row['path']);
1503
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.$courseParam;
1504
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?id='.$id.$courseParam;
1505
            $row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1506
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1507
            $row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1508
            $row['direct_url'] = $www.$path;
1509
            $row['basename'] = basename($row['path']);
1510
            if (dirname($row['path']) === '.') {
1511
                $row['parent_id'] = '0';
1512
            } else {
1513
                $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), $session_id, true);
1514
                if (empty($row['parent_id'])) {
1515
                    // Try one more with session id = 0
1516
                    $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), 0, true);
1517
                }
1518
            }
1519
            $parents = [];
1520
1521
            //Use to generate parents (needed for the breadcrumb)
1522
            //@todo sorry but this for is here because there's not a parent_id in the document table so we parsed the path!!
1523
            if ($load_parents) {
1524
                $dir_array = explode('/', $row['path']);
1525
                $dir_array = array_filter($dir_array);
1526
                $array_len = count($dir_array) + 1;
1527
                $real_dir = '';
1528
1529
                for ($i = 1; $i < $array_len; $i++) {
1530
                    $real_dir .= '/'.(isset($dir_array[$i]) ? $dir_array[$i] : '');
1531
                    $parent_id = self::get_document_id($course_info, $real_dir);
1532
                    if ($session_id != 0 && empty($parent_id)) {
1533
                        $parent_id = self::get_document_id($course_info, $real_dir, 0);
1534
                    }
1535
                    if (!empty($parent_id)) {
1536
                        $sub_document_data = self::get_document_data_by_id(
1537
                            $parent_id,
1538
                            $course_code,
1539
                            false,
1540
                            $session_id
1541
                        );
1542
                        if ($session_id != 0 and !$sub_document_data) {
1543
                            $sub_document_data = self::get_document_data_by_id(
1544
                                $parent_id,
1545
                                $course_code,
1546
                                false,
1547
                                0
1548
                            );
1549
                        }
1550
                        //@todo add visibility here
1551
                        $parents[] = $sub_document_data;
1552
                    }
1553
                }
1554
            }
1555
            $row['parents'] = $parents;
1556
1557
            return $row;
1558
        }
1559
1560
        return false;
1561
    }
1562
1563
    /**
1564
     * Allow to set a specific document as a new template for CKeditor
1565
     * for a particular user in a particular course.
1566
     *
1567
     * @param string $title
1568
     * @param string $description
1569
     * @param int    $document_id_for_template the document id
1570
     * @param string $course_code
1571
     * @param int    $user_id
1572
     * @param string $image
1573
     *
1574
     * @return bool
1575
     */
1576
    public static function set_document_as_template(
1577
        $title,
1578
        $description,
1579
        $document_id_for_template,
1580
        $course_code,
1581
        $user_id,
1582
        $image
1583
    ) {
1584
        // Database table definition
1585
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1586
        $params = [
1587
            'title' => $title,
1588
            'description' => $description,
1589
            'course_code' => $course_code,
1590
            'user_id' => $user_id,
1591
            'ref_doc' => $document_id_for_template,
1592
            'image' => $image,
1593
        ];
1594
        Database::insert($table_template, $params);
1595
1596
        return true;
1597
    }
1598
1599
    /**
1600
     * Unset a document as template.
1601
     *
1602
     * @param int    $document_id
1603
     * @param string $course_code
1604
     * @param int    $user_id
1605
     */
1606
    public static function unset_document_as_template(
1607
        $document_id,
1608
        $course_code,
1609
        $user_id
1610
    ) {
1611
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1612
        $course_code = Database::escape_string($course_code);
1613
        $user_id = intval($user_id);
1614
        $document_id = intval($document_id);
1615
        $sql = 'SELECT id FROM '.$table_template.'
1616
                WHERE
1617
                    course_code="'.$course_code.'" AND
1618
                    user_id="'.$user_id.'" AND
1619
                    ref_doc="'.$document_id.'"';
1620
        $result = Database::query($sql);
1621
        $template_id = Database::result($result, 0, 0);
1622
1623
        my_delete(api_get_path(SYS_CODE_PATH).'upload/template_thumbnails/'.$template_id.'.jpg');
1624
1625
        $sql = 'DELETE FROM '.$table_template.'
1626
                WHERE
1627
                    course_code="'.$course_code.'" AND
1628
                    user_id="'.$user_id.'" AND
1629
                    ref_doc="'.$document_id.'"';
1630
1631
        Database::query($sql);
1632
    }
1633
1634
    /**
1635
     * Return true if the documentpath have visibility=1 as
1636
     * item_property (you should use the is_visible_by_id).
1637
     *
1638
     * @param string $doc_path the relative complete path of the document
1639
     * @param array  $course   the _course array info of the document's course
1640
     * @param int
1641
     * @param string
1642
     *
1643
     * @return bool
1644
     */
1645
    public static function is_visible(
1646
        $doc_path,
1647
        $course,
1648
        $session_id = 0,
1649
        $file_type = 'file'
1650
    ) {
1651
        $docTable = Database::get_course_table(TABLE_DOCUMENT);
1652
        $propTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
1653
1654
        $userId = api_get_user_id();
1655
        $course_id = $course['real_id'];
1656
        // note the extra / at the end of doc_path to match every path in
1657
        // the document table that is part of the document path
1658
        $session_id = (int) $session_id;
1659
1660
        $drhAccessContent = api_drh_can_access_all_session_content() &&
1661
            $session_id &&
1662
            SessionManager::isSessionFollowedByDrh($session_id, $userId);
1663
1664
        $sessionAdminAccessContent = api_get_configuration_value('session_admins_access_all_content');
1665
1666
        $hasAccess = api_is_allowed_in_course() || api_is_platform_admin() || $drhAccessContent || $sessionAdminAccessContent;
1667
1668
        if (false === $hasAccess) {
1669
            return false;
1670
        }
1671
1672
        $condition = "AND d.session_id IN  ('$session_id', '0') ";
1673
        // The " d.filetype='file' " let the user see a file even if the folder is hidden see #2198
1674
1675
        /*
1676
          When using hotpotatoes files, a new html files are generated
1677
          in the hotpotatoes folder to display the test.
1678
          The genuine html file is copied to math4.htm(user_id).t.html
1679
          Images files are not copied, and keep same name.
1680
          To check the html file visibility, we don't have to check file math4.htm(user_id).t.html but file math4.htm
1681
          In this case, we have to remove (user_id).t.html to check the visibility of the file
1682
          For images, we just check the path of the image file.
1683
1684
          Exemple of hotpotatoes folder :
1685
          A.jpg
1686
          maths4-consigne.jpg
1687
          maths4.htm
1688
          maths4.htm1.t.html
1689
          maths4.htm52.t.html
1690
          maths4.htm654.t.html
1691
          omega.jpg
1692
          theta.jpg
1693
         */
1694
1695
        if (strpos($doc_path, 'HotPotatoes_files') && preg_match("/\.t\.html$/", $doc_path)) {
1696
            $doc_path = substr($doc_path, 0, strlen($doc_path) - 7 - strlen($userId));
1697
        }
1698
1699
        if (!in_array($file_type, ['file', 'folder'])) {
1700
            $file_type = 'file';
1701
        }
1702
        $doc_path = Database::escape_string($doc_path).'/';
1703
1704
        $sql = "SELECT visibility, ip.session_id
1705
                FROM $docTable d
1706
                INNER JOIN $propTable ip
1707
                ON (d.id = ip.ref AND d.c_id = ip.c_id)
1708
        		WHERE
1709
        		    d.c_id  = $course_id AND
1710
        		    ip.c_id = $course_id AND
1711
        		    ip.tool = '".TOOL_DOCUMENT."' $condition AND
1712
        			filetype = '$file_type' AND
1713
        			locate(concat(path,'/'), '$doc_path')=1
1714
                ";
1715
1716
        $result = Database::query($sql);
1717
        $isVisible = false;
1718
        $numRows = (int) Database::num_rows($result);
1719
1720
        if ($numRows) {
1721
            if (1 === $numRows) {
1722
                $row = Database::fetch_array($result, 'ASSOC');
1723
                if ($row['visibility'] == 1) {
1724
                    $isVisible = true;
1725
                }
1726
            } else {
1727
                $sessionVisibility = null;
1728
                $courseVisibility = null;
1729
                while ($row = Database::fetch_array($result, 'ASSOC')) {
1730
                    $checkSessionId = (int) $row['session_id'];
1731
                    if (empty($checkSessionId)) {
1732
                        $courseVisibility = 1 === (int) $row['visibility'];
1733
                    } else {
1734
                        if ($session_id === $checkSessionId) {
1735
                            $sessionVisibility = 1 === (int) $row['visibility'];
1736
                        }
1737
                    }
1738
                }
1739
1740
                if (empty($session_id) || (!empty($session_id) && null === $sessionVisibility)) {
1741
                    if ($courseVisibility) {
1742
                        $isVisible = true;
1743
                    }
1744
                } else {
1745
                    if ($sessionVisibility) {
1746
                        $isVisible = true;
1747
                    }
1748
                }
1749
            }
1750
        }
1751
1752
        /* improved protection of documents viewable directly through the url:
1753
            incorporates the same protections of the course at the url of
1754
            documents:
1755
            access allowed for the whole world Open, access allowed for
1756
            users registered on the platform Private access, document accessible
1757
            only to course members (see the Users list), Completely closed;
1758
            the document is only accessible to the course admin and
1759
            teaching assistants.*/
1760
        //return $_SESSION ['is_allowed_in_course'] || api_is_platform_admin();
1761
        return $isVisible;
1762
    }
1763
1764
    /**
1765
     * Return true if user can see a file.
1766
     *
1767
     * @param   int     document id
1768
     * @param   array   course info
1769
     * @param   int
1770
     * @param   int
1771
     * @param bool
1772
     *
1773
     * @return bool
1774
     */
1775
    public static function is_visible_by_id(
1776
        $doc_id,
1777
        $course_info,
1778
        $session_id,
1779
        $user_id,
1780
        $admins_can_see_everything = true,
1781
        $userIsSubscribed = null
1782
    ) {
1783
        $user_in_course = false;
1784
1785
        //1. Checking the course array
1786
        if (empty($course_info)) {
1787
            $course_info = api_get_course_info();
1788
            if (empty($course_info)) {
1789
                return false;
1790
            }
1791
        }
1792
1793
        $doc_id = (int) $doc_id;
1794
        $session_id = (int) $session_id;
1795
        // 2. Course and Session visibility are handle in local.inc.php/global.inc.php
1796
        // 3. Checking if user exist in course/session
1797
        if ($session_id == 0) {
1798
            if (is_null($userIsSubscribed)) {
1799
                $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
1800
                    $user_id,
1801
                    $course_info['code']
1802
                );
1803
            }
1804
1805
            if ($userIsSubscribed === true || api_is_platform_admin()) {
1806
                $user_in_course = true;
1807
            }
1808
1809
            // Check if course is open then we can consider that the student is registered to the course
1810
            if (isset($course_info) &&
1811
                in_array(
1812
                    $course_info['visibility'],
1813
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
1814
                )
1815
            ) {
1816
                $user_in_course = true;
1817
            }
1818
        } else {
1819
            $user_status = SessionManager::get_user_status_in_course_session(
1820
                $user_id,
1821
                $course_info['real_id'],
1822
                $session_id
1823
            );
1824
1825
            if (in_array($user_status, ['0', '2', '6'])) {
1826
                //is true if is an student, course session teacher or coach
1827
                $user_in_course = true;
1828
            }
1829
1830
            if (api_is_platform_admin()) {
1831
                $user_in_course = true;
1832
            }
1833
        }
1834
1835
        // 4. Checking document visibility (i'm repeating the code in order to be more clear when reading ) - jm
1836
        if ($user_in_course) {
1837
            if (true === api_get_configuration_value('document_enable_accessible_from_date')) {
1838
                $extraFieldValue = new ExtraFieldValue('document');
1839
                $extraValue = $extraFieldValue->get_values_by_handler_and_field_variable($doc_id, 'accessible_from');
1840
                if (!empty($extraValue) && isset($extraValue['value'])) {
1841
                    $now = new DateTime();
1842
                    $accessibleDate = new DateTime($extraValue['value']);
1843
                    if ($now < $accessibleDate) {
1844
                        return false;
1845
                    }
1846
                }
1847
            }
1848
            // 4.1 Checking document visibility for a Course
1849
            if ($session_id == 0) {
1850
                $item_info = api_get_item_property_info(
1851
                    $course_info['real_id'],
1852
                    'document',
1853
                    $doc_id,
1854
                    0
1855
                );
1856
1857
                if (isset($item_info['visibility'])) {
1858
                    // True for admins if document exists
1859
                    if ($admins_can_see_everything && api_is_platform_admin()) {
1860
                        return true;
1861
                    }
1862
                    if ($item_info['visibility'] == 1) {
1863
                        return true;
1864
                    }
1865
                }
1866
            } else {
1867
                // 4.2 Checking document visibility for a Course in a Session
1868
                $item_info = api_get_item_property_info(
1869
                    $course_info['real_id'],
1870
                    'document',
1871
                    $doc_id,
1872
                    0
1873
                );
1874
1875
                $item_info_in_session = api_get_item_property_info(
1876
                    $course_info['real_id'],
1877
                    'document',
1878
                    $doc_id,
1879
                    $session_id
1880
                );
1881
1882
                // True for admins if document exists
1883
                if (isset($item_info['visibility'])) {
1884
                    if ($admins_can_see_everything && api_is_platform_admin()) {
1885
                        return true;
1886
                    }
1887
                }
1888
1889
                if (isset($item_info_in_session['visibility'])) {
1890
                    if ($item_info_in_session['visibility'] == 1) {
1891
                        return true;
1892
                    }
1893
                } else {
1894
                    if ($item_info['visibility'] == 1) {
1895
                        return true;
1896
                    }
1897
                }
1898
            }
1899
        } elseif ($admins_can_see_everything && api_is_platform_admin()) {
1900
            return true;
1901
        }
1902
1903
        return false;
1904
    }
1905
1906
    /**
1907
     * Allow attach a certificate to a course.
1908
     *
1909
     * @todo move to certificate.lib.php
1910
     *
1911
     * @param string $course_id
1912
     * @param int    $document_id
1913
     * @param int    $session_id
1914
     */
1915
    public static function attach_gradebook_certificate($course_id, $document_id, $session_id = 0)
1916
    {
1917
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1918
        $session_id = intval($session_id);
1919
        if (empty($session_id)) {
1920
            $session_id = api_get_session_id();
1921
        }
1922
1923
        if (empty($session_id)) {
1924
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1925
        } elseif ($session_id > 0) {
1926
            $sql_session = 'AND session_id='.intval($session_id);
1927
        } else {
1928
            $sql_session = '';
1929
        }
1930
        $sql = 'UPDATE '.$tbl_category.' SET document_id="'.intval($document_id).'"
1931
                WHERE course_code="'.Database::escape_string($course_id).'" '.$sql_session;
1932
        Database::query($sql);
1933
    }
1934
1935
    /**
1936
     * get the document id of default certificate.
1937
     *
1938
     * @todo move to certificate.lib.php
1939
     *
1940
     * @param string $course_id
1941
     * @param int    $session_id
1942
     *
1943
     * @return int The default certificate id
1944
     */
1945
    public static function get_default_certificate_id($course_id, $session_id = 0)
1946
    {
1947
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1948
        $session_id = (int) $session_id;
1949
        if (empty($session_id)) {
1950
            $session_id = api_get_session_id();
1951
        }
1952
1953
        if (empty($session_id)) {
1954
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1955
        } elseif ($session_id > 0) {
1956
            $sql_session = 'AND session_id='.$session_id;
1957
        } else {
1958
            $sql_session = '';
1959
        }
1960
        $sql = 'SELECT document_id FROM '.$tbl_category.'
1961
                WHERE course_code="'.Database::escape_string($course_id).'" '.$sql_session;
1962
1963
        $rs = Database::query($sql);
1964
        $num = Database::num_rows($rs);
1965
        if ($num == 0) {
1966
            return null;
1967
        }
1968
        $row = Database::fetch_array($rs);
1969
1970
        return $row['document_id'];
1971
    }
1972
1973
    /**
1974
     * Allow replace user info in file html.
1975
     *
1976
     * @param int    $user_id
1977
     * @param string $course_code
1978
     * @param int    $sessionId
1979
     * @param bool   $is_preview
1980
     *
1981
     * @return array
1982
     */
1983
    public static function replace_user_info_into_html(
1984
        $user_id,
1985
        $course_code,
1986
        $sessionId,
1987
        $is_preview = false
1988
    ) {
1989
        $user_id = (int) $user_id;
1990
        $sessionId = (int) $sessionId;
1991
        $course_info = api_get_course_info($course_code);
1992
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1993
        $course_id = $course_info['real_id'];
1994
1995
        $document_id = self::get_default_certificate_id(
1996
            $course_code,
1997
            $sessionId
1998
        );
1999
2000
        $my_content_html = null;
2001
        if ($document_id) {
2002
            $sql = "SELECT path FROM $tbl_document
2003
                    WHERE c_id = $course_id AND id = $document_id";
2004
            $rs = Database::query($sql);
2005
            $new_content = '';
2006
            $all_user_info = [];
2007
            if (Database::num_rows($rs)) {
2008
                $row = Database::fetch_array($rs);
2009
                $filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
2010
                if (is_file($filepath)) {
2011
                    $my_content_html = file_get_contents($filepath);
2012
                }
2013
                $all_user_info = self::get_all_info_to_certificate(
2014
                    $user_id,
2015
                    $course_code,
2016
                    $sessionId,
2017
                    $is_preview
2018
                );
2019
2020
                $info_to_be_replaced_in_content_html = $all_user_info[0];
2021
                $info_to_replace_in_content_html = $all_user_info[1];
2022
                $new_content = str_replace(
2023
                    $info_to_be_replaced_in_content_html,
2024
                    $info_to_replace_in_content_html,
2025
                    $my_content_html
2026
                );
2027
            }
2028
2029
            return [
2030
                'content' => $new_content,
2031
                'variables' => $all_user_info,
2032
            ];
2033
        }
2034
2035
        return [];
2036
    }
2037
2038
    /**
2039
     * Return all content to replace and all content to be replace.
2040
     *
2041
     * @param int    $user_id
2042
     * @param string $courseCode
2043
     * @param bool   $is_preview
2044
     *
2045
     * @return array
2046
     */
2047
    public static function get_all_info_to_certificate($user_id, $courseCode, $sessionId, $is_preview = false)
2048
    {
2049
        $info_list = [];
2050
        $user_id = (int) $user_id;
2051
        $course_info = api_get_course_info($courseCode);
2052
        $sessionId = (int) $sessionId;
2053
2054
        // Portal info
2055
        $organization_name = api_get_setting('Institution');
2056
        $portal_name = api_get_setting('siteName');
2057
2058
        // Extra user data information
2059
        $extra_user_info_data = UserManager::get_extra_user_data(
2060
            $user_id,
2061
            false,
2062
            false,
2063
            false,
2064
            true
2065
        );
2066
2067
        // get extra fields
2068
        $extraField = new ExtraField('user');
2069
        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
2070
2071
        // Student information
2072
        $user_info = api_get_user_info($user_id);
2073
        $first_name = $user_info['firstname'];
2074
        $last_name = $user_info['lastname'];
2075
        $username = $user_info['username'];
2076
        $user_picture = UserManager::getUserPicture($user_id, USER_IMAGE_SIZE_ORIGINAL);
2077
        $official_code = $user_info['official_code'];
2078
2079
        // Teacher information
2080
        $teacher_first_name = '';
2081
        $teacher_last_name = '';
2082
        $info_teacher_id = UserManager::get_user_id_of_course_admin_or_session_admin($course_info);
2083
2084
        if ($info_teacher_id) {
2085
            [
2086
                'firstname' => $teacher_first_name,
2087
                'lastname' => $teacher_last_name,
2088
            ] = api_get_user_info($info_teacher_id);
2089
        }
2090
2091
        // info gradebook certificate
2092
        $info_grade_certificate = UserManager::get_info_gradebook_certificate($courseCode, $sessionId, $user_id);
2093
        $date_long_certificate = '';
2094
        $date_certificate = '';
2095
        $date_short_no_time = '';
2096
        $url = '';
2097
        if ($info_grade_certificate) {
2098
            $date_certificate = $info_grade_certificate['created_at'];
2099
            $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$info_grade_certificate['id'];
2100
        }
2101
        $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
2102
        $date_short_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_NUMBER);
2103
        if (!empty($date_certificate)) {
2104
            $date_long_certificate = api_convert_and_format_date($date_certificate);
2105
            $date_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_LONG_NO_DAY);
2106
            $date_short_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_NUMBER);
2107
        }
2108
2109
        if ($is_preview) {
2110
            $date_long_certificate = api_convert_and_format_date(api_get_utc_datetime());
2111
            $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
2112
            $date_short_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_NUMBER);
2113
        }
2114
2115
        $externalStyleFile = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/certificate.css';
2116
        $externalStyle = '';
2117
        if (is_file($externalStyleFile)) {
2118
            $externalStyle = file_get_contents($externalStyleFile);
2119
        }
2120
        $timeInCourse = Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $sessionId);
2121
        $timeInCourse = api_time_to_hms($timeInCourse, ':', false, true);
2122
2123
        $timeInCourseInAllSessions = 0;
2124
        $sessions = SessionManager::get_session_by_course($course_info['real_id']);
2125
2126
        if (!empty($sessions)) {
2127
            foreach ($sessions as $session) {
2128
                $timeInCourseInAllSessions += Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $session['id']);
2129
            }
2130
        }
2131
        $timeInCourseInAllSessions = api_time_to_hms($timeInCourseInAllSessions, ':', false, true);
2132
2133
        $first = Tracking::get_first_connection_date_on_the_course($user_id, $course_info['real_id'], $sessionId, false);
2134
        $first = api_convert_and_format_date($first, DATE_FORMAT_NUMBER);
2135
2136
        $last = Tracking::get_last_connection_date_on_the_course($user_id, $course_info, $sessionId, false);
2137
        $last = api_convert_and_format_date($last, DATE_FORMAT_NUMBER);
2138
2139
        if ($first === $last) {
2140
            $startDateAndEndDate = get_lang('The').' '.$first;
2141
        } else {
2142
            $startDateAndEndDate = sprintf(
2143
                get_lang('FromDateXToDateY'),
2144
                $first,
2145
                $last
2146
            );
2147
        }
2148
        $courseDescription = new CourseDescription();
2149
        $description = $courseDescription->get_data_by_description_type(2, $course_info['real_id'], $sessionId);
2150
        $courseObjectives = '';
2151
        if ($description) {
2152
            $courseObjectives = $description['description_content'];
2153
        }
2154
2155
        // Replace content
2156
        $info_to_replace_in_content_html = [
2157
            $first_name,
2158
            $last_name,
2159
            $username,
2160
            $user_picture,
2161
            $organization_name,
2162
            $portal_name,
2163
            $teacher_first_name,
2164
            $teacher_last_name,
2165
            $official_code,
2166
            $date_long_certificate,
2167
            $date_no_time,
2168
            $date_short_no_time,
2169
            $courseCode,
2170
            $course_info['name'],
2171
            isset($info_grade_certificate['grade']) ? $info_grade_certificate['grade'] : '',
2172
            $url,
2173
            '<a href="'.$url.'" target="_blank">'.get_lang('CertificateOnlineLink').'</a>',
2174
            '((certificate_barcode))',
2175
            $externalStyle,
2176
            $timeInCourse,
2177
            $timeInCourseInAllSessions,
2178
            $startDateAndEndDate,
2179
            $courseObjectives,
2180
        ];
2181
2182
        $tags = [
2183
            '((user_firstname))',
2184
            '((user_lastname))',
2185
            '((user_username))',
2186
            '((user_picture))',
2187
            '((gradebook_institution))',
2188
            '((gradebook_sitename))',
2189
            '((teacher_firstname))',
2190
            '((teacher_lastname))',
2191
            '((official_code))',
2192
            '((date_certificate))',
2193
            '((date_certificate_no_time))',
2194
            '((date_simple_certificate))',
2195
            '((course_code))',
2196
            '((course_title))',
2197
            '((gradebook_grade))',
2198
            '((certificate_link))',
2199
            '((certificate_link_html))',
2200
            '((certificate_barcode))',
2201
            '((external_style))',
2202
            '((time_in_course))',
2203
            '((time_in_course_in_all_sessions))',
2204
            '((start_date_and_end_date))',
2205
            '((course_objectives))',
2206
        ];
2207
2208
        if (!empty($extraFields)) {
2209
            $efv = new ExtraFieldValue('user');
2210
2211
            foreach ($extraFields as $extraField) {
2212
                $valueExtra = $efv->get_values_by_handler_and_field_variable(
2213
                    $user_id,
2214
                    $extraField['variable'],
2215
                    true
2216
                );
2217
                $tags[] = '(('.strtolower($extraField['variable']).'))';
2218
                $info_to_replace_in_content_html[] = $valueExtra['value'];
2219
            }
2220
        }
2221
2222
        $info_list[] = $tags;
2223
        $info_list[] = $info_to_replace_in_content_html;
2224
2225
        return $info_list;
2226
    }
2227
2228
    /**
2229
     * Remove default certificate.
2230
     *
2231
     * @param string $course_id              The course code
2232
     * @param int    $default_certificate_id The document id of the default certificate
2233
     */
2234
    public static function remove_attach_certificate($course_id, $default_certificate_id)
2235
    {
2236
        if (empty($default_certificate_id)) {
2237
            return false;
2238
        }
2239
2240
        $default_certificate = self::get_default_certificate_id($course_id);
2241
        if ((int) $default_certificate == (int) $default_certificate_id) {
2242
            $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
2243
            $session_id = api_get_session_id();
2244
            if ($session_id == 0 || is_null($session_id)) {
2245
                $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
2246
            } elseif ($session_id > 0) {
2247
                $sql_session = 'AND session_id='.intval($session_id);
2248
            } else {
2249
                $sql_session = '';
2250
            }
2251
2252
            $sql = 'UPDATE '.$tbl_category.' SET document_id = null
2253
                    WHERE
2254
                        course_code = "'.Database::escape_string($course_id).'" AND
2255
                        document_id="'.$default_certificate_id.'" '.$sql_session;
2256
            Database::query($sql);
2257
        }
2258
    }
2259
2260
    /**
2261
     * Create directory certificate.
2262
     *
2263
     * @param array $courseInfo
2264
     */
2265
    public static function create_directory_certificate_in_course($courseInfo)
2266
    {
2267
        if (!empty($courseInfo)) {
2268
            $to_group_id = 0;
2269
            $to_user_id = null;
2270
            $course_dir = $courseInfo['path']."/document/";
2271
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
2272
            $base_work_dir = $sys_course_path.$course_dir;
2273
            $dir_name = '/certificates';
2274
            $post_dir_name = get_lang('CertificatesFiles');
2275
            $visibility_command = 'invisible';
2276
2277
            $id = self::get_document_id_of_directory_certificate();
2278
2279
            if (empty($id)) {
2280
                create_unexisting_directory(
2281
                    $courseInfo,
2282
                    api_get_user_id(),
2283
                    api_get_session_id(),
2284
                    $to_group_id,
2285
                    $to_user_id,
2286
                    $base_work_dir,
2287
                    $dir_name,
2288
                    $post_dir_name,
2289
                    null,
2290
                    false,
2291
                    false
2292
                );
2293
2294
                $id = self::get_document_id_of_directory_certificate();
2295
2296
                if (empty($id)) {
2297
                    $id = add_document(
2298
                        $courseInfo,
2299
                        $dir_name,
2300
                        'folder',
2301
                        0,
2302
                        $post_dir_name,
2303
                        null,
2304
                        0,
2305
                        true,
2306
                        $to_group_id,
2307
                        0,
2308
                        0,
2309
                        false
2310
                    );
2311
                }
2312
2313
                if (!empty($id)) {
2314
                    api_item_property_update(
2315
                        $courseInfo,
2316
                        TOOL_DOCUMENT,
2317
                        $id,
2318
                        $visibility_command,
2319
                        api_get_user_id()
2320
                    );
2321
                }
2322
            }
2323
        }
2324
    }
2325
2326
    /**
2327
     * Get the document id of the directory certificate.
2328
     *
2329
     * @return int The document id of the directory certificate
2330
     *
2331
     * @todo move to certificate.lib.php
2332
     */
2333
    public static function get_document_id_of_directory_certificate()
2334
    {
2335
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
2336
        $course_id = api_get_course_int_id();
2337
        $sql = "SELECT id FROM $tbl_document
2338
                WHERE c_id = $course_id AND path='/certificates' ";
2339
        $rs = Database::query($sql);
2340
        $row = Database::fetch_array($rs);
2341
        if ($row) {
2342
            return $row['id'];
2343
        }
2344
2345
        return 0;
2346
    }
2347
2348
    /**
2349
     * Check if a directory given is for certificate.
2350
     *
2351
     * @todo move to certificate.lib.php
2352
     *
2353
     * @param string $dir path of directory
2354
     *
2355
     * @return bool true if is a certificate or false otherwise
2356
     */
2357
    public static function is_certificate_mode($dir)
2358
    {
2359
        // I'm in the certification module?
2360
        $is_certificate_mode = false;
2361
        $is_certificate_array = explode('/', $dir);
2362
        array_shift($is_certificate_array);
2363
        if (isset($is_certificate_array[0]) && $is_certificate_array[0] == 'certificates') {
2364
            $is_certificate_mode = true;
2365
        }
2366
2367
        return $is_certificate_mode || (isset($_GET['certificate']) && $_GET['certificate'] === 'true');
2368
    }
2369
2370
    /**
2371
     * Gets the list of included resources as a list of absolute or relative paths from a html file or string html
2372
     * This allows for a better SCORM export or replace urls inside content html from copy course
2373
     * The list will generally include pictures, flash objects, java applets, or any other
2374
     * stuff included in the source of the current item. The current item is expected
2375
     * to be an HTML file or string html. If it is not, then the function will return and empty list.
2376
     *
2377
     * @param    string  source html (content or path)
2378
     * @param    bool    is file or string html
2379
     * @param    string    type (one of the app tools) - optional (otherwise takes the current item's type)
2380
     * @param    int        level of recursivity we're in
2381
     *
2382
     * @return array List of file paths. An additional field containing 'local' or 'remote' helps determine
2383
     *               if the file should be copied into the zip or just linked
2384
     */
2385
    public static function get_resources_from_source_html(
2386
        $source_html,
2387
        $is_file = false,
2388
        $type = null,
2389
        $recursivity = 1
2390
    ) {
2391
        $max = 5;
2392
        $attributes = [];
2393
        $wanted_attributes = [
2394
            'src',
2395
            'url',
2396
            '@import',
2397
            'href',
2398
            'value',
2399
            'flashvars',
2400
            'poster',
2401
        ];
2402
        $explode_attributes = ['flashvars' => 'file'];
2403
        $abs_path = '';
2404
2405
        if ($recursivity > $max) {
2406
            return [];
2407
        }
2408
2409
        if (!isset($type)) {
2410
            $type = TOOL_DOCUMENT;
2411
        }
2412
2413
        if (!$is_file) {
2414
            $attributes = self::parse_HTML_attributes(
2415
                $source_html,
2416
                $wanted_attributes,
2417
                $explode_attributes
2418
            );
2419
        } else {
2420
            if (is_file($source_html)) {
2421
                $abs_path = $source_html;
2422
                //for now, read the whole file in one go (that's gonna be a problem when the file is too big)
2423
                $info = pathinfo($abs_path);
2424
                $ext = $info['extension'];
2425
                switch (strtolower($ext)) {
2426
                    case 'html':
2427
                    case 'htm':
2428
                    case 'shtml':
2429
                    case 'css':
2430
                        $file_content = file_get_contents($abs_path);
2431
                        // get an array of attributes from the HTML source
2432
                        $attributes = self::parse_HTML_attributes(
2433
                            $file_content,
2434
                            $wanted_attributes,
2435
                            $explode_attributes
2436
                        );
2437
                        break;
2438
                    default:
2439
                        break;
2440
                }
2441
            } else {
2442
                return false;
2443
            }
2444
        }
2445
2446
        $files_list = [];
2447
        switch ($type) {
2448
            case TOOL_DOCUMENT:
2449
            case TOOL_QUIZ:
2450
            case 'sco':
2451
                foreach ($wanted_attributes as $attr) {
2452
                    if (isset($attributes[$attr])) {
2453
                        //find which kind of path these are (local or remote)
2454
                        $sources = $attributes[$attr];
2455
                        foreach ($sources as $source) {
2456
                            //skip what is obviously not a resource
2457
                            if (strpos($source, '+this.')) {
2458
                                continue; //javascript code - will still work unaltered
2459
                            }
2460
                            if (strpos($source, '.') === false) {
2461
                                continue; //no dot, should not be an external file anyway
2462
                            }
2463
                            if (strpos($source, 'mailto:')) {
2464
                                continue; //mailto link
2465
                            }
2466
                            if (strpos($source, ';') && !strpos($source, '&amp;')) {
2467
                                continue; //avoid code - that should help
2468
                            }
2469
2470
                            if ($attr == 'value') {
2471
                                if (strpos($source, 'mp3file')) {
2472
                                    $files_list[] = [
2473
                                        substr($source, 0, strpos($source, '.swf') + 4),
2474
                                        'local',
2475
                                        'abs',
2476
                                    ];
2477
                                    $mp3file = substr($source, strpos($source, 'mp3file=') + 8);
2478
                                    if (substr($mp3file, 0, 1) == '/') {
2479
                                        $files_list[] = [$mp3file, 'local', 'abs'];
2480
                                    } else {
2481
                                        $files_list[] = [$mp3file, 'local', 'rel'];
2482
                                    }
2483
                                } elseif (strpos($source, 'flv=') === 0) {
2484
                                    $source = substr($source, 4);
2485
                                    if (strpos($source, '&') > 0) {
2486
                                        $source = substr($source, 0, strpos($source, '&'));
2487
                                    }
2488
                                    if (strpos($source, '://') > 0) {
2489
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2490
                                            //we found the current portal url
2491
                                            $files_list[] = [$source, 'local', 'url'];
2492
                                        } else {
2493
                                            //we didn't find any trace of current portal
2494
                                            $files_list[] = [$source, 'remote', 'url'];
2495
                                        }
2496
                                    } else {
2497
                                        $files_list[] = [$source, 'local', 'abs'];
2498
                                    }
2499
                                    /* skipping anything else to avoid two entries
2500
                                    (while the others can have sub-files in their url, flv's can't)*/
2501
                                    continue;
2502
                                }
2503
                            }
2504
                            if (strpos($source, '://') > 0) {
2505
                                //cut at '?' in a URL with params
2506
                                if (strpos($source, '?') > 0) {
2507
                                    $second_part = substr($source, strpos($source, '?'));
2508
                                    if (strpos($second_part, '://') > 0) {
2509
                                        //if the second part of the url contains a url too, treat the second one before cutting
2510
                                        $pos1 = strpos($second_part, '=');
2511
                                        $pos2 = strpos($second_part, '&');
2512
                                        $second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
2513
                                        if (strpos($second_part, api_get_path(WEB_PATH)) !== false) {
2514
                                            //we found the current portal url
2515
                                            $files_list[] = [$second_part, 'local', 'url'];
2516
                                            $in_files_list[] = self::get_resources_from_source_html(
2517
                                                $second_part,
2518
                                                true,
2519
                                                TOOL_DOCUMENT,
2520
                                                $recursivity + 1
2521
                                            );
2522
                                            if (count($in_files_list) > 0) {
2523
                                                $files_list = array_merge($files_list, $in_files_list);
2524
                                            }
2525
                                        } else {
2526
                                            //we didn't find any trace of current portal
2527
                                            $files_list[] = [$second_part, 'remote', 'url'];
2528
                                        }
2529
                                    } elseif (strpos($second_part, '=') > 0) {
2530
                                        if (substr($second_part, 0, 1) === '/') {
2531
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2532
                                            $files_list[] = [$second_part, 'local', 'abs'];
2533
                                            $in_files_list[] = self::get_resources_from_source_html(
2534
                                                $second_part,
2535
                                                true,
2536
                                                TOOL_DOCUMENT,
2537
                                                $recursivity + 1
2538
                                            );
2539
                                            if (count($in_files_list) > 0) {
2540
                                                $files_list = array_merge($files_list, $in_files_list);
2541
                                            }
2542
                                        } elseif (strstr($second_part, '..') === 0) {
2543
                                            //link is relative but going back in the hierarchy
2544
                                            $files_list[] = [$second_part, 'local', 'rel'];
2545
                                            //$dir = api_get_path(SYS_CODE_PATH);//dirname($abs_path);
2546
                                            //$new_abs_path = realpath($dir.'/'.$second_part);
2547
                                            $dir = '';
2548
                                            if (!empty($abs_path)) {
2549
                                                $dir = dirname($abs_path).'/';
2550
                                            }
2551
                                            $new_abs_path = realpath($dir.$second_part);
2552
                                            $in_files_list[] = self::get_resources_from_source_html(
2553
                                                $new_abs_path,
2554
                                                true,
2555
                                                TOOL_DOCUMENT,
2556
                                                $recursivity + 1
2557
                                            );
2558
                                            if (count($in_files_list) > 0) {
2559
                                                $files_list = array_merge($files_list, $in_files_list);
2560
                                            }
2561
                                        } else {
2562
                                            //no starting '/', making it relative to current document's path
2563
                                            if (substr($second_part, 0, 2) == './') {
2564
                                                $second_part = substr($second_part, 2);
2565
                                            }
2566
                                            $files_list[] = [$second_part, 'local', 'rel'];
2567
                                            $dir = '';
2568
                                            if (!empty($abs_path)) {
2569
                                                $dir = dirname($abs_path).'/';
2570
                                            }
2571
                                            $new_abs_path = realpath($dir.$second_part);
2572
                                            $in_files_list[] = self::get_resources_from_source_html(
2573
                                                $new_abs_path,
2574
                                                true,
2575
                                                TOOL_DOCUMENT,
2576
                                                $recursivity + 1
2577
                                            );
2578
                                            if (count($in_files_list) > 0) {
2579
                                                $files_list = array_merge($files_list, $in_files_list);
2580
                                            }
2581
                                        }
2582
                                    }
2583
                                    //leave that second part behind now
2584
                                    $source = substr($source, 0, strpos($source, '?'));
2585
                                    if (strpos($source, '://') > 0) {
2586
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2587
                                            //we found the current portal url
2588
                                            $files_list[] = [$source, 'local', 'url'];
2589
                                            $in_files_list[] = self::get_resources_from_source_html(
2590
                                                $source,
2591
                                                true,
2592
                                                TOOL_DOCUMENT,
2593
                                                $recursivity + 1
2594
                                            );
2595
                                            if (count($in_files_list) > 0) {
2596
                                                $files_list = array_merge($files_list, $in_files_list);
2597
                                            }
2598
                                        } else {
2599
                                            //we didn't find any trace of current portal
2600
                                            $files_list[] = [$source, 'remote', 'url'];
2601
                                        }
2602
                                    } else {
2603
                                        //no protocol found, make link local
2604
                                        if (substr($source, 0, 1) === '/') {
2605
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2606
                                            $files_list[] = [$source, 'local', 'abs'];
2607
                                            $in_files_list[] = self::get_resources_from_source_html(
2608
                                                $source,
2609
                                                true,
2610
                                                TOOL_DOCUMENT,
2611
                                                $recursivity + 1
2612
                                            );
2613
                                            if (count($in_files_list) > 0) {
2614
                                                $files_list = array_merge($files_list, $in_files_list);
2615
                                            }
2616
                                        } elseif (strstr($source, '..') === 0) {
2617
                                            //link is relative but going back in the hierarchy
2618
                                            $files_list[] = [$source, 'local', 'rel'];
2619
                                            $dir = '';
2620
                                            if (!empty($abs_path)) {
2621
                                                $dir = dirname($abs_path).'/';
2622
                                            }
2623
                                            $new_abs_path = realpath($dir.$source);
2624
                                            $in_files_list[] = self::get_resources_from_source_html(
2625
                                                $new_abs_path,
2626
                                                true,
2627
                                                TOOL_DOCUMENT,
2628
                                                $recursivity + 1
2629
                                            );
2630
                                            if (count($in_files_list) > 0) {
2631
                                                $files_list = array_merge($files_list, $in_files_list);
2632
                                            }
2633
                                        } else {
2634
                                            //no starting '/', making it relative to current document's path
2635
                                            if (substr($source, 0, 2) == './') {
2636
                                                $source = substr($source, 2);
2637
                                            }
2638
                                            $files_list[] = [$source, 'local', 'rel'];
2639
                                            $dir = '';
2640
                                            if (!empty($abs_path)) {
2641
                                                $dir = dirname($abs_path).'/';
2642
                                            }
2643
                                            $new_abs_path = realpath($dir.$source);
2644
                                            $in_files_list[] = self::get_resources_from_source_html(
2645
                                                $new_abs_path,
2646
                                                true,
2647
                                                TOOL_DOCUMENT,
2648
                                                $recursivity + 1
2649
                                            );
2650
                                            if (count($in_files_list) > 0) {
2651
                                                $files_list = array_merge($files_list, $in_files_list);
2652
                                            }
2653
                                        }
2654
                                    }
2655
                                }
2656
                                //found some protocol there
2657
                                if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2658
                                    //we found the current portal url
2659
                                    $files_list[] = [$source, 'local', 'url'];
2660
                                    $in_files_list[] = self::get_resources_from_source_html(
2661
                                        $source,
2662
                                        true,
2663
                                        TOOL_DOCUMENT,
2664
                                        $recursivity + 1
2665
                                    );
2666
                                    if (count($in_files_list) > 0) {
2667
                                        $files_list = array_merge($files_list, $in_files_list);
2668
                                    }
2669
                                } else {
2670
                                    //we didn't find any trace of current portal
2671
                                    $files_list[] = [$source, 'remote', 'url'];
2672
                                }
2673
                            } else {
2674
                                //no protocol found, make link local
2675
                                if (substr($source, 0, 1) === '/') {
2676
                                    //link starts with a /, making it absolute (relative to DocumentRoot)
2677
                                    $files_list[] = [$source, 'local', 'abs'];
2678
                                    $in_files_list[] = self::get_resources_from_source_html(
2679
                                        $source,
2680
                                        true,
2681
                                        TOOL_DOCUMENT,
2682
                                        $recursivity + 1
2683
                                    );
2684
                                    if (count($in_files_list) > 0) {
2685
                                        $files_list = array_merge($files_list, $in_files_list);
2686
                                    }
2687
                                } elseif (strpos($source, '..') === 0) {
2688
                                    //link is relative but going back in the hierarchy
2689
                                    $files_list[] = [$source, 'local', 'rel'];
2690
                                    $dir = '';
2691
                                    if (!empty($abs_path)) {
2692
                                        $dir = dirname($abs_path).'/';
2693
                                    }
2694
                                    $new_abs_path = realpath($dir.$source);
2695
                                    $in_files_list[] = self::get_resources_from_source_html(
2696
                                        $new_abs_path,
2697
                                        true,
2698
                                        TOOL_DOCUMENT,
2699
                                        $recursivity + 1
2700
                                    );
2701
                                    if (count($in_files_list) > 0) {
2702
                                        $files_list = array_merge($files_list, $in_files_list);
2703
                                    }
2704
                                } else {
2705
                                    //no starting '/', making it relative to current document's path
2706
                                    if (substr($source, 0, 2) == './') {
2707
                                        $source = substr($source, 2);
2708
                                    }
2709
                                    $files_list[] = [$source, 'local', 'rel'];
2710
                                    $dir = '';
2711
                                    if (!empty($abs_path)) {
2712
                                        $dir = dirname($abs_path).'/';
2713
                                    }
2714
                                    $new_abs_path = realpath($dir.$source);
2715
                                    $in_files_list[] = self::get_resources_from_source_html(
2716
                                        $new_abs_path,
2717
                                        true,
2718
                                        TOOL_DOCUMENT,
2719
                                        $recursivity + 1
2720
                                    );
2721
                                    if (count($in_files_list) > 0) {
2722
                                        $files_list = array_merge($files_list, $in_files_list);
2723
                                    }
2724
                                }
2725
                            }
2726
                        }
2727
                    }
2728
                }
2729
                break;
2730
            default: //ignore
2731
                break;
2732
        }
2733
2734
        $checked_files_list = [];
2735
        $checked_array_list = [];
2736
2737
        if (count($files_list) > 0) {
2738
            foreach ($files_list as $idx => $file) {
2739
                if (!empty($file[0])) {
2740
                    if (!in_array($file[0], $checked_files_list)) {
2741
                        $checked_files_list[] = $files_list[$idx][0];
2742
                        $checked_array_list[] = $files_list[$idx];
2743
                    }
2744
                }
2745
            }
2746
        }
2747
2748
        return $checked_array_list;
2749
    }
2750
2751
    /**
2752
     * Parses the HTML attributes given as string.
2753
     *
2754
     * @param string HTML attribute string
2755
     * @param array List of attributes that we want to get back
2756
     * @param array
2757
     *
2758
     * @return array An associative array of attributes
2759
     *
2760
     * @author Based on a function from the HTML_Common2 PEAR module     *
2761
     */
2762
    public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
2763
    {
2764
        $attributes = [];
2765
        $regs = [];
2766
        $reduced = false;
2767
        if (count($wanted) > 0) {
2768
            $reduced = true;
2769
        }
2770
        try {
2771
            //Find all occurences of something that looks like a URL
2772
            // The structure of this regexp is:
2773
            // (find protocol) then
2774
            // (optionally find some kind of space 1 or more times) then
2775
            // find (either an equal sign or a bracket) followed by an optional space
2776
            // followed by some text without quotes (between quotes itself or not)
2777
            // then possible closing brackets if we were in the opening bracket case
2778
            // OR something like @import()
2779
            $res = preg_match_all(
2780
                '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]*))'.
2781
                // '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]|[^\x00-\x7F])*)' . -> seems to be taking too much
2782
                // '/(((([A-Za-z_:])([^\x00-\x7F])*)' . -> takes only last letter of parameter name
2783
                '([ \n\t\r]+)?('.
2784
                // '(=([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+))' . -> doesn't restrict close enough to the url itself
2785
                '(=([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+))'.
2786
                '|'.
2787
                // '(\(([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)\))' . -> doesn't restrict close enough to the url itself
2788
                '(\(([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+)\))'.
2789
                '))'.
2790
                '|'.
2791
                // '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))?/', -> takes a lot (like 100's of thousands of empty possibilities)
2792
                '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))/',
2793
                $attrString,
2794
                $regs
2795
            );
2796
        } catch (Exception $e) {
2797
            error_log('Caught exception: '.$e->getMessage(), 0);
2798
        }
2799
        if ($res) {
2800
            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...
2801
                $name = trim($regs[3][$i]);
2802
                $check = trim($regs[0][$i]);
2803
                $value = trim($regs[10][$i]);
2804
                if (empty($value) and !empty($regs[13][$i])) {
2805
                    $value = $regs[13][$i];
2806
                }
2807
                if (empty($name) && !empty($regs[16][$i])) {
2808
                    $name = '@import';
2809
                    $value = trim($regs[16][$i]);
2810
                }
2811
                if (!empty($name)) {
2812
                    if (!$reduced || in_array(strtolower($name), $wanted)) {
2813
                        if ($name == $check) {
2814
                            $attributes[strtolower($name)][] = strtolower($name);
2815
                        } else {
2816
                            if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) {
2817
                                $value = substr($value, 1, -1);
2818
                            }
2819
2820
                            if ($value == 'API.LMSGetValue(name') {
2821
                                $value = 'API.LMSGetValue(name)';
2822
                            }
2823
                            //Gets the xx.flv value from the string flashvars="width=320&height=240&autostart=false&file=xxx.flv&repeat=false"
2824
                            if (isset($explode_variables[$name])) {
2825
                                $value_modified = str_replace('&amp;', '&', $value);
2826
                                $value_array = explode('&', $value_modified);
2827
                                foreach ($value_array as $item) {
2828
                                    $itemParts = explode('=', $item);
2829
                                    $key = $itemParts[0];
2830
                                    $item_value = !empty($itemParts[1]) ? $itemParts[1] : '';
2831
                                    if ($key == $explode_variables[$name]) {
2832
                                        $attributes[strtolower($name)][] = $item_value;
2833
                                    }
2834
                                }
2835
                            }
2836
                            $attributes[strtolower($name)][] = $value;
2837
                        }
2838
                    }
2839
                }
2840
            }
2841
        }
2842
2843
        return $attributes;
2844
    }
2845
2846
    /**
2847
     * Replace urls inside content html from a copy course.
2848
     *
2849
     * @param string $content_html
2850
     * @param string $origin_course_code
2851
     * @param string $destination_course_directory
2852
     * @param string $origin_course_path_from_zip
2853
     * @param string $origin_course_info_path
2854
     *
2855
     * @return string new content html with replaced urls or return false if content is not a string
2856
     */
2857
    public static function replaceUrlWithNewCourseCode(
2858
        $content_html,
2859
        $origin_course_code,
2860
        $destination_course_directory,
2861
        $origin_course_path_from_zip = null,
2862
        $origin_course_info_path = null
2863
    ) {
2864
        if (empty($content_html)) {
2865
            return false;
2866
        }
2867
2868
        $orig_source_html = self::get_resources_from_source_html($content_html);
2869
        $orig_course_info = api_get_course_info($origin_course_code);
2870
2871
        // Course does not exist in the current DB probably this came from a zip file?
2872
        if (empty($orig_course_info)) {
2873
            if (!empty($origin_course_path_from_zip)) {
2874
                $orig_course_path = $origin_course_path_from_zip.'/';
2875
                $orig_course_info_path = $origin_course_info_path;
2876
            }
2877
        } else {
2878
            $orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
2879
            $orig_course_info_path = $orig_course_info['path'];
2880
        }
2881
2882
        $destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
2883
        $destination_course_info = api_get_course_info($destination_course_code);
2884
        $dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
2885
        $dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
2886
2887
        $user_id = api_get_user_id();
2888
2889
        if (!empty($orig_source_html)) {
2890
            foreach ($orig_source_html as $source) {
2891
                // Get information about source url
2892
                $real_orig_url = $source[0]; // url
2893
                $scope_url = $source[1]; // scope (local, remote)
2894
                $type_url = $source[2]; // type (rel, abs, url)
2895
2896
                // Get path and query from origin url
2897
                $orig_parse_url = parse_url($real_orig_url);
2898
                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
2899
                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
2900
2901
                // Replace origin course code by destination course code from origin url query
2902
                $dest_url_query = '';
2903
2904
                if (!empty($real_orig_query)) {
2905
                    $dest_url_query = '?'.$real_orig_query;
2906
                    if (strpos($dest_url_query, $origin_course_code) !== false) {
2907
                        $dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
2908
                    }
2909
                }
2910
2911
                if ($scope_url == 'local') {
2912
                    if ($type_url == 'abs' || $type_url == 'rel') {
2913
                        $document_file = strstr($real_orig_path, 'document');
2914
2915
                        if ($document_file && strpos($real_orig_path, $document_file) !== false) {
2916
                            $origin_filepath = $orig_course_path.$document_file;
2917
                            $destination_filepath = $dest_course_path.$document_file;
2918
2919
                            // copy origin file inside destination course
2920
                            if (file_exists($origin_filepath)) {
2921
                                $filepath_dir = dirname($destination_filepath);
2922
2923
                                if (!is_dir($filepath_dir)) {
2924
                                    $perm = api_get_permissions_for_new_directories();
2925
                                    $result = @mkdir($filepath_dir, $perm, true);
2926
                                    if ($result) {
2927
                                        $filepath_to_add = str_replace(
2928
                                            [$dest_course_path, 'document'],
2929
                                            '',
2930
                                            $filepath_dir
2931
                                        );
2932
2933
                                        //Add to item properties to the new folder
2934
                                        $doc_id = add_document(
2935
                                            $destination_course_info,
2936
                                            $filepath_to_add,
2937
                                            'folder',
2938
                                            0,
2939
                                            basename($filepath_to_add)
2940
                                        );
2941
                                        api_item_property_update(
2942
                                            $destination_course_info,
2943
                                            TOOL_DOCUMENT,
2944
                                            $doc_id,
2945
                                            'FolderCreated',
2946
                                            $user_id,
2947
                                            null,
2948
                                            null,
2949
                                            null,
2950
                                            null
2951
                                        );
2952
                                    }
2953
                                }
2954
2955
                                if (!file_exists($destination_filepath)) {
2956
                                    $result = @copy($origin_filepath, $destination_filepath);
2957
                                    if ($result) {
2958
                                        $filepath_to_add = str_replace(
2959
                                            [$dest_course_path, 'document'],
2960
                                            '',
2961
                                            $destination_filepath
2962
                                        );
2963
                                        $size = filesize($destination_filepath);
2964
2965
                                        // Add to item properties to the file
2966
                                        $doc_id = add_document(
2967
                                            $destination_course_info,
2968
                                            $filepath_to_add,
2969
                                            'file',
2970
                                            $size,
2971
                                            basename($filepath_to_add)
2972
                                        );
2973
                                        api_item_property_update(
2974
                                            $destination_course_info,
2975
                                            TOOL_DOCUMENT,
2976
                                            $doc_id,
2977
                                            'FolderCreated',
2978
                                            $user_id,
2979
                                            null,
2980
                                            null,
2981
                                            null,
2982
                                            null
2983
                                        );
2984
                                    }
2985
                                }
2986
                            }
2987
2988
                            // Replace origin course path by destination course path.
2989
                            if (strpos($content_html, $real_orig_url) !== false) {
2990
                                $url_course_path = str_replace(
2991
                                    $orig_course_info_path.'/'.$document_file,
2992
                                    '',
2993
                                    $real_orig_path
2994
                                );
2995
                                // See BT#7780
2996
                                $destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
2997
                                // If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
2998
                                if (strpos($real_orig_path, $origin_course_code) === false) {
2999
                                    $url_course_path = $real_orig_path;
3000
                                    $destination_url = $real_orig_path;
3001
                                }
3002
                                $content_html = str_replace($real_orig_url, $destination_url, $content_html);
3003
                            }
3004
                        }
3005
3006
                        // replace origin course code by destination course code  from origin url
3007
                        if (strpos($real_orig_url, '?') === 0) {
3008
                            $dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
3009
                            $content_html = str_replace($real_orig_url, $dest_url, $content_html);
3010
                        }
3011
                    }
3012
                }
3013
            }
3014
        }
3015
3016
        return $content_html;
3017
    }
3018
3019
    /**
3020
     * Export document to PDF.
3021
     *
3022
     * @param int    $document_id
3023
     * @param string $courseCode
3024
     * @param string $orientation
3025
     * @param bool   $showHeaderAndFooter
3026
     */
3027
    public static function export_to_pdf(
3028
        $document_id,
3029
        $courseCode,
3030
        $orientation = 'landscape',
3031
        $showHeaderAndFooter = true
3032
    ) {
3033
        $course_data = api_get_course_info($courseCode);
3034
        $document_data = self::get_document_data_by_id($document_id, $courseCode);
3035
        $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$document_data['path'];
3036
3037
        $pageFormat = 'A4';
3038
        $pdfOrientation = 'P';
3039
        if ($orientation === 'landscape') {
3040
            $pageFormat = 'A4-L';
3041
            $pdfOrientation = 'L';
3042
        }
3043
        $pdf = new PDF(
3044
            $pageFormat,
3045
            $pdfOrientation,
3046
            $showHeaderAndFooter ? [] : ['top' => 0, 'left' => 0, 'bottom' => 0, 'right' => 0]
3047
        );
3048
3049
        if (api_get_configuration_value('use_alternative_document_pdf_footer')) {
3050
            $view = new Template('', false, false, false, true, false, false);
3051
            $template = $view->get_template('export/alt_pdf_footer.tpl');
3052
            $pdf->set_custom_footer(['html' => $view->fetch($template)]);
3053
        }
3054
3055
        $pdf->html_to_pdf(
3056
            $file_path,
3057
            $document_data['title'],
3058
            $courseCode,
3059
            false,
3060
            $showHeaderAndFooter
3061
        );
3062
    }
3063
3064
    /**
3065
     * Uploads a document.
3066
     *
3067
     * @param array  $files                   the $_FILES variable
3068
     * @param string $path
3069
     * @param string $title
3070
     * @param string $comment
3071
     * @param int    $unzip                   unzip or not the file
3072
     * @param string $ifExists                overwrite, rename or warn (default)
3073
     * @param bool   $index_document          index document (search xapian module)
3074
     * @param bool   $show_output             print html messages
3075
     * @param string $fileKey
3076
     * @param bool   $treat_spaces_as_hyphens
3077
     * @param int    $userId                  Optional. User ID who upload file
3078
     * @param array  $courseInfo              Optional. Course info
3079
     * @param int    $sessionId               Optional. Session ID
3080
     * @param int    $groupId                 Optional. Group ID
3081
     * @param bool   $recordAudioExercise
3082
     *
3083
     * @return array|bool
3084
     */
3085
    public static function upload_document(
3086
        $files,
3087
        $path,
3088
        $title = '',
3089
        $comment = '',
3090
        $unzip = 0,
3091
        $ifExists = '',
3092
        $index_document = false,
3093
        $show_output = false,
3094
        $fileKey = 'file',
3095
        $treat_spaces_as_hyphens = true,
3096
        $userId = 0,
3097
        array $courseInfo = [],
3098
        $sessionId = 0,
3099
        $groupId = 0,
3100
        $recordAudioExercise = false
3101
    ) {
3102
        $course_info = $courseInfo ?: api_get_course_info();
3103
        $sessionId = $sessionId ?: api_get_session_id();
3104
        $course_dir = $course_info['path'].'/document';
3105
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3106
        $base_work_dir = $sys_course_path.$course_dir;
3107
        $userId = $userId ?: api_get_user_id();
3108
        $groupId = $groupId ?: api_get_group_id();
3109
3110
        if ($recordAudioExercise) {
3111
            $base_work_dir = $sys_course_path.$course_info['path'].'/exercises';
3112
            $path = str_replace('/../exercises/', '/', $path);
3113
        }
3114
3115
        if (isset($files[$fileKey])) {
3116
            $uploadOk = process_uploaded_file($files[$fileKey], $show_output);
3117
            if ($uploadOk) {
3118
                $new_path = handle_uploaded_document(
3119
                    $course_info,
3120
                    $files[$fileKey],
3121
                    $base_work_dir,
3122
                    $path,
3123
                    $userId,
3124
                    $groupId,
3125
                    null,
3126
                    $unzip,
3127
                    $ifExists,
3128
                    $show_output,
3129
                    false,
3130
                    null,
3131
                    $sessionId,
3132
                    $treat_spaces_as_hyphens
3133
                );
3134
3135
                // When sending zip files
3136
                if ($new_path === true && $unzip == 1) {
3137
                    return [
3138
                        'title' => $files[$fileKey]['name'],
3139
                        'url' => '#',
3140
                    ];
3141
                }
3142
3143
                if ($new_path) {
3144
                    $documentId = self::get_document_id(
3145
                        $course_info,
3146
                        $new_path,
3147
                        $sessionId
3148
                    );
3149
3150
                    if (!empty($documentId)) {
3151
                        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3152
                        $params = [];
3153
3154
                        if (!empty($title)) {
3155
                            $params['title'] = $title;
3156
                        }
3157
3158
                        if (!empty($comment)) {
3159
                            $params['comment'] = trim($comment);
3160
                        }
3161
3162
                        Database::update(
3163
                            $table_document,
3164
                            $params,
3165
                            [
3166
                                'id = ? AND c_id = ? ' => [
3167
                                    $documentId,
3168
                                    $course_info['real_id'],
3169
                                ],
3170
                            ]
3171
                        );
3172
                    }
3173
3174
                    if ($index_document) {
3175
                        self::index_document(
3176
                            $documentId,
3177
                            $course_info['code'],
3178
                            null,
3179
                            $_POST['language'],
3180
                            $_REQUEST,
3181
                            $ifExists
3182
                        );
3183
                    }
3184
3185
                    if (!empty($documentId) && is_numeric($documentId)) {
3186
                        $documentData = self::get_document_data_by_id(
3187
                            $documentId,
3188
                            $course_info['code'],
3189
                            false,
3190
                            $sessionId
3191
                        );
3192
3193
                        return $documentData;
3194
                    }
3195
                }
3196
            }
3197
        }
3198
3199
        return false;
3200
    }
3201
3202
    /**
3203
     * Obtains the text inside the file with the right parser.
3204
     */
3205
    public static function get_text_content($doc_path, $doc_mime)
3206
    {
3207
        // TODO: review w$ compatibility
3208
        // Use usual exec output lines array to store stdout instead of a temp file
3209
        // because we need to store it at RAM anyway before index on ChamiloIndexer object
3210
        $ret_val = null;
3211
        switch ($doc_mime) {
3212
            case 'text/plain':
3213
                $handle = fopen($doc_path, 'r');
3214
                $output = [fread($handle, filesize($doc_path))];
3215
                fclose($handle);
3216
                break;
3217
            case 'application/pdf':
3218
                exec("pdftotext $doc_path -", $output, $ret_val);
3219
                break;
3220
            case 'application/postscript':
3221
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
3222
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
3223
                if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
3224
                    return false;
3225
                }
3226
                exec("pdftotext $temp_file -", $output, $ret_val);
3227
                unlink($temp_file);
3228
                break;
3229
            case 'application/msword':
3230
                exec("catdoc $doc_path", $output, $ret_val);
3231
                break;
3232
            case 'text/html':
3233
                exec("html2text $doc_path", $output, $ret_val);
3234
                break;
3235
            case 'text/rtf':
3236
                // Note: correct handling of code pages in unrtf
3237
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
3238
                exec("unrtf --text $doc_path", $output, $ret_val);
3239
                if ($ret_val == 127) { // command not found
3240
                    return false;
3241
                }
3242
                // Avoid index unrtf comments
3243
                if (is_array($output) && count($output) > 1) {
3244
                    $parsed_output = [];
3245
                    foreach ($output as &$line) {
3246
                        if (!preg_match('/^###/', $line, $matches)) {
3247
                            if (!empty($line)) {
3248
                                $parsed_output[] = $line;
3249
                            }
3250
                        }
3251
                    }
3252
                    $output = $parsed_output;
3253
                }
3254
                break;
3255
            case 'application/vnd.ms-powerpoint':
3256
                exec("catppt $doc_path", $output, $ret_val);
3257
                break;
3258
            case 'application/vnd.ms-excel':
3259
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
3260
                break;
3261
        }
3262
3263
        $content = '';
3264
        if (!is_null($ret_val)) {
3265
            if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
3266
                return false;
3267
            }
3268
        }
3269
        if (isset($output)) {
3270
            foreach ($output as &$line) {
3271
                $content .= $line."\n";
3272
            }
3273
3274
            return $content;
3275
        } else {
3276
            return false;
3277
        }
3278
    }
3279
3280
    /**
3281
     * Calculates the total size of all documents in a course.
3282
     *
3283
     * @author Bert vanderkimpen
3284
     *
3285
     * @param int $course_id
3286
     * @param int $group_id   (to calculate group document space)
3287
     * @param int $session_id
3288
     *
3289
     * @return int total size
3290
     */
3291
    public static function documents_total_space($course_id = null, $group_id = null, $session_id = null)
3292
    {
3293
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
3294
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
3295
3296
        $session_id = (int) $session_id;
3297
        $group_id = (int) $group_id;
3298
        $course_id = (int) $course_id;
3299
3300
        if (!$course_id) {
3301
            $course_id = api_get_course_int_id();
3302
        }
3303
3304
        $group_condition = '';
3305
        if ($group_id) {
3306
            $group_condition = " AND props.to_group_id='".$group_id."' ";
3307
        }
3308
3309
        $session_condition = '';
3310
        if ($session_id) {
3311
            $session_condition = " AND props.session_id='".$session_id."' ";
3312
        }
3313
3314
        $sql = "SELECT SUM(size)
3315
                FROM (
3316
                    SELECT ref, size
3317
                    FROM $TABLE_ITEMPROPERTY AS props
3318
                    INNER JOIN $TABLE_DOCUMENT AS docs
3319
                    ON (docs.id = props.ref AND props.c_id = docs.c_id)
3320
                    WHERE
3321
                        props.c_id = $course_id AND
3322
                        docs.c_id = $course_id AND
3323
                        props.tool = '".TOOL_DOCUMENT."' AND
3324
                        props.ref not in (
3325
                            SELECT ref
3326
                            FROM $TABLE_ITEMPROPERTY as cip
3327
                            WHERE
3328
                                cip.c_id = $course_id AND
3329
                                cip.tool = '".TOOL_DOCUMENT."' AND
3330
                                cip.visibility = 2
3331
                        )
3332
                        $group_condition
3333
                        $session_condition
3334
                    GROUP BY props.ref
3335
                    ) AS table1
3336
                ";
3337
        $result = Database::query($sql);
3338
3339
        if ($result && Database::num_rows($result) != 0) {
3340
            $row = Database::fetch_row($result);
3341
3342
            return (int) $row[0];
3343
        } else {
3344
            return 0;
3345
        }
3346
    }
3347
3348
    /**
3349
     * Display the document quota in a simple way.
3350
     *
3351
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
3352
     */
3353
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
3354
    {
3355
        $course_quota_m = round($course_quota / 1048576);
3356
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
3357
        $percentage = $already_consumed_space / $course_quota * 100;
3358
        $percentage = round($percentage, 1);
3359
        $message = get_lang('YouAreCurrentlyUsingXOfYourX');
3360
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
3361
3362
        return Display::div($message, ['id' => 'document_quota']);
3363
    }
3364
3365
    /**
3366
     * Checks if there is enough place to add a file on a directory
3367
     * on the base of a maximum directory size allowed.
3368
     *
3369
     * @author Bert Vanderkimpen
3370
     *
3371
     * @param int $file_size     size of the file in byte
3372
     * @param int $max_dir_space maximum size
3373
     *
3374
     * @return bool true if there is enough space, false otherwise
3375
     *
3376
     * @see enough_space() uses  documents_total_space() function
3377
     */
3378
    public static function enough_space($file_size, $max_dir_space)
3379
    {
3380
        if ($max_dir_space) {
3381
            $already_filled_space = self::documents_total_space();
3382
            if (($file_size + $already_filled_space) > $max_dir_space) {
3383
                return false;
3384
            }
3385
        }
3386
3387
        return true;
3388
    }
3389
3390
    /**
3391
     * @param array $params count, url, extension
3392
     *
3393
     * @return string
3394
     */
3395
    public static function generateAudioJavascript($params = [])
3396
    {
3397
        return '
3398
            $(\'audio.audio_preview\').mediaelementplayer({
3399
                features: [\'playpause\'],
3400
                audioWidth: 30,
3401
                audioHeight: 30,
3402
                success: function(mediaElement, originalNode, instance) {
3403
                }
3404
            });';
3405
    }
3406
3407
    /**
3408
     * Shows a play icon next to the document title in the document list.
3409
     *
3410
     * @param string $documentWebPath
3411
     * @param array  $documentInfo
3412
     *
3413
     * @return string
3414
     */
3415
    public static function generateAudioPreview($documentWebPath, $documentInfo)
3416
    {
3417
        $filePath = $documentWebPath.$documentInfo['path'];
3418
        $extension = $documentInfo['file_extension'];
3419
3420
        if (!in_array($extension, ['mp3', 'wav', 'ogg'])) {
3421
            return '';
3422
        }
3423
3424
        return '<span class="preview"> <audio class="audio_preview skip" src="'.$filePath.'" type="audio/'.$extension.'" > </audio></span>';
3425
    }
3426
3427
    /**
3428
     * @param string $file
3429
     * @param string $extension
3430
     *
3431
     * @return string
3432
     */
3433
    public static function generateMediaPreview($file, $extension)
3434
    {
3435
        $id = api_get_unique_id();
3436
        switch ($extension) {
3437
            case 'wav':
3438
            case 'ogg':
3439
            case 'mp3':
3440
                $html = '<div style="margin: 0; position: absolute; top: 50%; left: 35%;">';
3441
                $html .= '<audio id="'.$id.'" controls="controls" src="'.$file.'" type="audio/mp3" ></audio></div>';
3442
                break;
3443
            default:
3444
                $html = '<video id="'.$id.'" width="100%" height="100%" controls>';
3445
                $html .= '<source src="'.$file.'" >';
3446
                $html .= '</video>';
3447
                break;
3448
        }
3449
3450
        return $html;
3451
    }
3452
3453
    /**
3454
     * @param array  $course_info
3455
     * @param bool   $lp_id
3456
     * @param string $target
3457
     * @param int    $session_id
3458
     * @param bool   $add_move_button
3459
     * @param string $filter_by_folder
3460
     * @param string $overwrite_url
3461
     * @param bool   $showInvisibleFiles
3462
     * @param bool   $showOnlyFolders
3463
     * @param int    $folderId
3464
     * @param bool   $addCloseButton
3465
     * @param bool   $addAudioPreview
3466
     * @param array  $filterByExtension
3467
     *
3468
     * @return string
3469
     */
3470
    public static function get_document_preview(
3471
        $course_info,
3472
        $lp_id = false,
3473
        $target = '',
3474
        $session_id = 0,
3475
        $add_move_button = false,
3476
        $filter_by_folder = null,
3477
        $overwrite_url = '',
3478
        $showInvisibleFiles = false,
3479
        $showOnlyFolders = false,
3480
        $folderId = false,
3481
        $addCloseButton = true,
3482
        $addAudioPreview = false,
3483
        $filterByExtension = []
3484
    ) {
3485
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
3486
            return '';
3487
        }
3488
3489
        $user_id = api_get_user_id();
3490
        $userInfo = api_get_user_info();
3491
        $user_in_course = api_is_platform_admin();
3492
        if (!$user_in_course) {
3493
            if (CourseManager::is_course_teacher($user_id, $course_info['code'])) {
3494
                $user_in_course = true;
3495
            }
3496
        }
3497
3498
        // Condition for the session
3499
        $session_id = (int) $session_id;
3500
3501
        if (!$user_in_course) {
3502
            if (empty($session_id)) {
3503
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
3504
                    $user_in_course = true;
3505
                }
3506
                // Check if course is open then we can consider that the student is registered to the course
3507
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3508
                    $user_in_course = true;
3509
                }
3510
            } else {
3511
                $user_status = SessionManager::get_user_status_in_course_session(
3512
                    $user_id,
3513
                    $course_info['real_id'],
3514
                    $session_id
3515
                );
3516
                //is true if is an student, course session teacher or coach
3517
                if (in_array($user_status, ['0', '2', '6'])) {
3518
                    $user_in_course = true;
3519
                }
3520
            }
3521
        }
3522
3523
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3524
        $tbl_item_prop = Database::get_course_table(TABLE_ITEM_PROPERTY);
3525
        $condition_session = " AND (last.session_id = '$session_id' OR last.session_id = '0' OR last.session_id IS NULL)";
3526
3527
        $add_folder_filter = null;
3528
        if (!empty($filter_by_folder)) {
3529
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3530
        }
3531
3532
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3533
        $lp_visibility_condition = null;
3534
        if ($lp_id || $showOnlyFolders) {
3535
            if ($showInvisibleFiles || $showOnlyFolders) {
3536
                $lp_visibility_condition .= ' OR last.visibility = 0';
3537
            }
3538
        }
3539
3540
        $folderCondition = " AND docs.path LIKE '/%' ";
3541
        if (!api_is_allowed_to_edit()) {
3542
            $protectedFolders = self::getProtectedFolderFromStudent();
3543
            foreach ($protectedFolders as $folder) {
3544
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3545
            }
3546
        }
3547
3548
        $extensionConditionToString = '';
3549
        if (!empty($filterByExtension)) {
3550
            $extensionCondition = [];
3551
            foreach ($filterByExtension as $extension) {
3552
                $extensionCondition[] = " docs.path LIKE '%.$extension' ";
3553
            }
3554
            if (!empty($extensionCondition)) {
3555
                $extensionConditionToString .= " AND (".implode('OR', $extensionCondition).") ";
3556
            }
3557
        }
3558
3559
        $parentData = [];
3560
        if ($folderId !== false) {
3561
            $parentData = self::get_document_data_by_id(
3562
                $folderId,
3563
                $course_info['code'],
3564
                false,
3565
                $session_id
3566
            );
3567
            if (!empty($parentData)) {
3568
                $cleanedPath = $parentData['path'];
3569
                $num = substr_count($cleanedPath, '/');
3570
3571
                $notLikeCondition = '';
3572
                for ($i = 1; $i <= $num; $i++) {
3573
                    $repeat = str_repeat('/%', $i + 1);
3574
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3575
                }
3576
3577
                $folderId = (int) $folderId;
3578
                $folderCondition = " AND
3579
                    docs.id <> $folderId AND
3580
                    docs.path LIKE '".$cleanedPath."/%'
3581
                    $notLikeCondition
3582
                ";
3583
            } else {
3584
                $folderCondition = " AND docs.filetype = 'file' ";
3585
            }
3586
        }
3587
3588
        $levelCondition = '';
3589
        if ($folderId === false) {
3590
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3591
        }
3592
3593
        $sql = "SELECT DISTINCT last.visibility, docs.*
3594
                FROM $tbl_item_prop AS last
3595
                INNER JOIN $tbl_doc AS docs
3596
                ON (docs.id = last.ref AND docs.c_id = last.c_id)
3597
                WHERE
3598
                    docs.path NOT LIKE '%_DELETED_%' AND
3599
                    last.tool = '".TOOL_DOCUMENT."' $condition_session AND
3600
                    (last.visibility = '1' $lp_visibility_condition) AND
3601
                    last.visibility <> 2 AND
3602
                    docs.c_id = {$course_info['real_id']} AND
3603
                    last.c_id = {$course_info['real_id']}
3604
                    $folderCondition
3605
                    $levelCondition
3606
                    $extensionConditionToString
3607
                    $add_folder_filter
3608
                ORDER BY docs.filetype DESC, docs.title ASC";
3609
3610
        $res_doc = Database::query($sql);
3611
        $resources = Database::store_result($res_doc, 'ASSOC');
3612
3613
        $return = '';
3614
        if ($lp_id == false && $addCloseButton) {
3615
            if ($folderId === false) {
3616
                $return .= Display::div(
3617
                    Display::url(
3618
                        Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3619
                        ' javascript:void(0);',
3620
                        ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3621
                    ),
3622
                    ['style' => 'position:absolute;right:10px']
3623
                );
3624
            }
3625
        }
3626
3627
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3628
        $newResources = [];
3629
        $added = [];
3630
        if (!empty($resources) && $user_in_course) {
3631
            foreach ($resources as $resource) {
3632
                $docId = $resource['id'];
3633
                if (in_array($docId, $added)) {
3634
                    continue;
3635
                }
3636
3637
                $is_visible = self::is_visible_by_id(
3638
                    $docId,
3639
                    $course_info,
3640
                    $session_id,
3641
                    api_get_user_id()
3642
                );
3643
3644
                if ($showOnlyFolders) {
3645
                    $isFolder = ('folder' === $resource['filetype']);
3646
                    $visibility = (int) $resource['visibility'];
3647
                    if (!$isFolder && 0 == $visibility) {
3648
                        continue;
3649
                    }
3650
                }
3651
3652
                if ($showInvisibleFiles === false) {
3653
                    if (!$is_visible) {
3654
                        continue;
3655
                    }
3656
                }
3657
                $added[] = $docId;
3658
                $newResources[] = $resource;
3659
            }
3660
        }
3661
3662
        $label = get_lang('Documents');
3663
3664
        $documents = [];
3665
        if ($folderId === false) {
3666
            $documents[$label] = [
3667
                'id' => 0,
3668
                'files' => $newResources,
3669
            ];
3670
        } else {
3671
            if (is_array($parentData)) {
3672
                $documents[$parentData['title']] = [
3673
                    'id' => (int) $folderId,
3674
                    'files' => $newResources,
3675
                ];
3676
            }
3677
        }
3678
3679
        $writeResult = self::write_resources_tree(
3680
            $userInfo,
3681
            $course_info,
3682
            $session_id,
3683
            $documents,
3684
            $lp_id,
3685
            $target,
3686
            $add_move_button,
3687
            $overwrite_url,
3688
            $folderId,
3689
            $addAudioPreview
3690
        );
3691
3692
        $return .= $writeResult;
3693
        $lpAjaxUrl = api_get_path(WEB_AJAX_PATH).'lp.ajax.php';
3694
        $extraAjaxParams = ($showOnlyFolders ? '&showOnlyFolders='.(int) $showOnlyFolders : '');
3695
        if ($lp_id === false) {
3696
            $url = $lpAjaxUrl.'?a=get_documents&lp_id=&cidReq='.$course_info['code'].$extraAjaxParams;
3697
            $return .= "<script>
3698
            $(function() {
3699
                $('.close_div').click(function() {
3700
                    var course_id = this.id.split('_')[2];
3701
                    var session_id = this.id.split('_')[3];
3702
                    $('#document_result_'+course_id+'_'+session_id).hide();
3703
                    $('.lp_resource').remove();
3704
                    $('.document_preview_container').html('');
3705
                });
3706
            });
3707
            </script>";
3708
        } else {
3709
            // For LPs
3710
            $url = $lpAjaxUrl.'?a=get_documents&lp_id='.(int) $lp_id.'&'.api_get_cidreq().$extraAjaxParams;
3711
        }
3712
3713
        if (!empty($overwrite_url)) {
3714
            $url .= '&url='.urlencode(Security::remove_XSS($overwrite_url));
3715
        }
3716
3717
        if ($add_move_button) {
3718
            $url .= '&add_move_button=1';
3719
        }
3720
3721
        $return .= "<script>
3722
            function testResources(id, img) {
3723
                var numericId = id.split('_')[1];
3724
                var parentId = 'doc_id_'+numericId;
3725
                var tempId = 'temp_'+numericId;
3726
                var image = $('#'+img);
3727
3728
                if (image.hasClass('open')) {
3729
                    image.removeClass('open');
3730
                    image.attr('src', '".Display::returnIconPath('nolines_plus.gif')."');
3731
                    $('#'+id).show();
3732
                    $('#'+tempId).hide();
3733
                } else {
3734
                    image.addClass('open');
3735
                    image.attr('src', '".Display::returnIconPath('nolines_minus.gif')."');
3736
                    $('#'+id).hide();
3737
                    $('#'+tempId).show();
3738
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3739
                    if (tempDiv.length == 0) {
3740
                        $.ajax({
3741
                            type: 'GET',
3742
                            url:  '".$url."',
3743
                            data: 'folder_id='+numericId,
3744
                            success: function(data) {
3745
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3746
                            }
3747
                        });
3748
                    }
3749
                }
3750
            }
3751
            </script>";
3752
3753
        if (!$user_in_course) {
3754
            $return = '';
3755
        }
3756
3757
        return $return;
3758
    }
3759
3760
    /**
3761
     * Generate and return an HTML list of resources based on a given array.
3762
     * This list is used to show the course creator a list of available resources to choose from
3763
     * when creating a learning path.
3764
     *
3765
     * @param array  $userInfo        current user info
3766
     * @param array  $course_info
3767
     * @param int    $session_id
3768
     * @param array  $documents
3769
     * @param bool   $lp_id
3770
     * @param string $target
3771
     * @param bool   $add_move_button
3772
     * @param string $overwrite_url
3773
     * @param int    $folderId
3774
     * @param bool   $addAudioPreview
3775
     *
3776
     * @return string
3777
     */
3778
    public static function write_resources_tree(
3779
        $userInfo,
3780
        $course_info,
3781
        $session_id,
3782
        $documents,
3783
        $lp_id = false,
3784
        $target = '',
3785
        $add_move_button = false,
3786
        $overwrite_url = '',
3787
        $folderId = false,
3788
        $addAudioPreview = false
3789
    ) {
3790
        $return = '';
3791
        if (!empty($documents)) {
3792
            foreach ($documents as $key => $resource) {
3793
                if (isset($resource['id']) && isset($resource['files'])) {
3794
                    $mainFolderResource = [
3795
                        'id' => $resource['id'],
3796
                        'title' => $key,
3797
                    ];
3798
                    $close = false;
3799
3800
                    if ($folderId === false) {
3801
                        $parsedFolder = self::parseFolder($folderId, $mainFolderResource, $lp_id);
3802
                        $close = (bool) $parsedFolder;
3803
3804
                        $return .= $parsedFolder;
3805
                    }
3806
3807
                    if (isset($resource['files'])) {
3808
                        $return .= self::write_resources_tree(
3809
                            $userInfo,
3810
                            $course_info,
3811
                            $session_id,
3812
                            $resource['files'],
3813
                            $lp_id,
3814
                            $target,
3815
                            $add_move_button,
3816
                            $overwrite_url,
3817
                            false,
3818
                            $addAudioPreview
3819
                        );
3820
                    }
3821
3822
                    if ($close) {
3823
                        $return .= '</div>';
3824
                        $return .= '</ul>';
3825
                    }
3826
                } else {
3827
                    if ($resource['filetype'] === 'folder') {
3828
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3829
                    } else {
3830
                        $return .= self::parseFile(
3831
                            $userInfo,
3832
                            $course_info,
3833
                            $session_id,
3834
                            $resource,
3835
                            $lp_id,
3836
                            $add_move_button,
3837
                            $target,
3838
                            $overwrite_url,
3839
                            $addAudioPreview
3840
                        );
3841
                    }
3842
                }
3843
            }
3844
        }
3845
3846
        return $return;
3847
    }
3848
3849
    /**
3850
     * @param int   $doc_id
3851
     * @param array $courseInfo
3852
     * @param int   $sessionId
3853
     * @param int   $user_id
3854
     * @param int   $groupId               iid
3855
     * @param bool  $checkParentVisibility
3856
     *
3857
     * @return bool
3858
     */
3859
    public static function check_visibility_tree(
3860
        $doc_id,
3861
        $courseInfo,
3862
        $sessionId,
3863
        $user_id,
3864
        $groupId = 0,
3865
        $checkParentVisibility = true
3866
    ) {
3867
        if (empty($courseInfo)) {
3868
            return false;
3869
        }
3870
3871
        $courseCode = $courseInfo['code'];
3872
3873
        if (empty($courseCode)) {
3874
            return false;
3875
        }
3876
3877
        $document_data = self::get_document_data_by_id(
3878
            $doc_id,
3879
            $courseCode,
3880
            null,
3881
            $sessionId
3882
        );
3883
3884
        if ($sessionId != 0 && !$document_data) {
3885
            $document_data = self::get_document_data_by_id(
3886
                $doc_id,
3887
                $courseCode,
3888
                null,
3889
                0
3890
            );
3891
        }
3892
3893
        if (!empty($document_data)) {
3894
            // If admin or course teacher, allow anyway
3895
            if (api_is_platform_admin() || CourseManager::is_course_teacher($user_id, $courseCode)) {
3896
                return true;
3897
            }
3898
            if ($document_data['parent_id'] == false || empty($document_data['parent_id'])) {
3899
                if (!empty($groupId)) {
3900
                    return true;
3901
                }
3902
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3903
3904
                return $visible;
3905
            } else {
3906
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3907
3908
                if (!$visible) {
3909
                    return false;
3910
                } else {
3911
                    if ($checkParentVisibility && $doc_id != $document_data['parent_id']) {
3912
                        return self::check_visibility_tree(
3913
                            $document_data['parent_id'],
3914
                            $courseInfo,
3915
                            $sessionId,
3916
                            $user_id,
3917
                            $groupId
3918
                        );
3919
                    }
3920
3921
                    return true;
3922
                }
3923
            }
3924
        } else {
3925
            return false;
3926
        }
3927
    }
3928
3929
    /**
3930
     * Index a given document.
3931
     *
3932
     * @param   int     Document ID inside its corresponding course
3933
     * @param   string  Course code
3934
     * @param   int     Session ID (not used yet)
3935
     * @param   string  Language of document's content (defaults to course language)
3936
     * @param   array   Array of specific fields (['code'=>'value',...])
3937
     * @param   string  What to do if the file already exists (default or overwrite)
3938
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3939
     *
3940
     * @return bool Returns true on presumed success, false on failure
3941
     */
3942
    public static function index_document(
3943
        $docid,
3944
        $course_code,
3945
        $session_id = 0,
3946
        $lang = 'english',
3947
        $specific_fields_values = [],
3948
        $if_exists = '',
3949
        $simulation = false
3950
    ) {
3951
        if (api_get_setting('search_enabled') !== 'true') {
3952
            return false;
3953
        }
3954
        if (empty($docid) or $docid != intval($docid)) {
3955
            return false;
3956
        }
3957
        if (empty($session_id)) {
3958
            $session_id = api_get_session_id();
3959
        }
3960
        $course_info = api_get_course_info($course_code);
3961
        $course_dir = $course_info['path'].'/document';
3962
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3963
        $base_work_dir = $sys_course_path.$course_dir;
3964
3965
        $course_id = $course_info['real_id'];
3966
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3967
3968
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3969
        $result = Database::query($qry);
3970
        if (Database::num_rows($result) == 1) {
3971
            $row = Database::fetch_array($result);
3972
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3973
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3974
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3975
            $doc_mime = mime_content_type($doc_path);
3976
            $allowed_mime_types = self::file_get_mime_type(true);
3977
3978
            // mime_content_type does not detect correctly some formats that
3979
            // are going to be supported for index, so an extensions array is used for the moment
3980
            if (empty($doc_mime)) {
3981
                $allowed_extensions = [
3982
                    'doc',
3983
                    'docx',
3984
                    'ppt',
3985
                    'pptx',
3986
                    'pps',
3987
                    'ppsx',
3988
                    'xls',
3989
                    'xlsx',
3990
                    'odt',
3991
                    'odp',
3992
                    'ods',
3993
                    'pdf',
3994
                    'txt',
3995
                    'rtf',
3996
                    'msg',
3997
                    'csv',
3998
                    'html',
3999
                    'htm',
4000
                ];
4001
                $extensions = preg_split("/[\/\\.]/", $doc_path);
4002
                $doc_ext = strtolower($extensions[count($extensions) - 1]);
4003
                if (in_array($doc_ext, $allowed_extensions)) {
4004
                    switch ($doc_ext) {
4005
                        case 'ppt':
4006
                        case 'pps':
4007
                            $doc_mime = 'application/vnd.ms-powerpoint';
4008
                            break;
4009
                        case 'xls':
4010
                            $doc_mime = 'application/vnd.ms-excel';
4011
                            break;
4012
                    }
4013
                }
4014
            }
4015
4016
            //@todo move this nightmare in a search controller or something like that!!! J.M
4017
4018
            if (in_array($doc_mime, $allowed_mime_types)) {
4019
                $file_title = $row['title'];
4020
                $file_content = self::get_text_content($doc_path, $doc_mime);
4021
                $course_code = Database::escape_string($course_code);
4022
                $ic_slide = new IndexableChunk();
4023
                $ic_slide->addValue('title', $file_title);
4024
                $ic_slide->addCourseId($course_code);
4025
                $ic_slide->addToolId(TOOL_DOCUMENT);
4026
                $xapian_data = [
4027
                    SE_COURSE_ID => $course_code,
4028
                    SE_TOOL_ID => TOOL_DOCUMENT,
4029
                    SE_DATA => ['doc_id' => $docid],
4030
                    SE_USER => api_get_user_id(),
4031
                ];
4032
4033
                $ic_slide->xapian_data = serialize($xapian_data);
4034
                $di = new ChamiloIndexer();
4035
                $return = $di->connectDb(null, null, $lang);
4036
4037
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
4038
                $specific_fields = get_specific_field_list();
4039
4040
                // process different depending on what to do if file exists
4041
                /**
4042
                 * @TODO Find a way to really verify if the file had been
4043
                 * overwriten. Now all work is done at
4044
                 * handle_uploaded_document() and it's difficult to verify it
4045
                 */
4046
                if (!empty($if_exists) && $if_exists == 'overwrite') {
4047
                    // Overwrite the file on search engine
4048
                    // Actually, it consists on a delete of terms from db,
4049
                    // insert new ones, create a new search engine document,
4050
                    // and remove the old one
4051
                    // Get search_did
4052
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4053
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
4054
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
4055
4056
                    $res = Database::query($sql);
4057
4058
                    if (Database::num_rows($res) > 0) {
4059
                        $se_ref = Database::fetch_array($res);
4060
                        if (!$simulation) {
4061
                            $di->remove_document($se_ref['search_did']);
4062
                        }
4063
                        $all_specific_terms = '';
4064
                        foreach ($specific_fields as $specific_field) {
4065
                            if (!$simulation) {
4066
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
4067
                            }
4068
                            // Update search engine
4069
                            if (isset($specific_fields_values[$specific_field['code']])) {
4070
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
4071
                            } else { //if the specific field is not defined, force an empty one
4072
                                $sterms = '';
4073
                            }
4074
                            $all_specific_terms .= ' '.$sterms;
4075
                            $sterms = explode(',', $sterms);
4076
                            foreach ($sterms as $sterm) {
4077
                                $sterm = trim($sterm);
4078
                                if (!empty($sterm)) {
4079
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
4080
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
4081
                                    if (!$simulation) {
4082
                                        add_specific_field_value(
4083
                                            $specific_field['id'],
4084
                                            $course_code,
4085
                                            TOOL_DOCUMENT,
4086
                                            $docid,
4087
                                            $sterm
4088
                                        );
4089
                                    }
4090
                                }
4091
                            }
4092
                        }
4093
                        // Add terms also to content to make terms findable by probabilistic search
4094
                        $file_content = $all_specific_terms.' '.$file_content;
4095
4096
                        if (!$simulation) {
4097
                            $ic_slide->addValue('content', $file_content);
4098
                            $di->addChunk($ic_slide);
4099
                            // Index and return a new search engine document id
4100
                            $did = $di->index();
4101
4102
                            if ($did) {
4103
                                // update the search_did on db
4104
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4105
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
4106
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
4107
                                Database::query($sql);
4108
                            }
4109
                        }
4110
                    }
4111
                } else {
4112
                    // Add all terms
4113
                    $all_specific_terms = '';
4114
                    foreach ($specific_fields as $specific_field) {
4115
                        if (isset($specific_fields_values[$specific_field['code']])) {
4116
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
4117
                        } else { //if the specific field is not defined, force an empty one
4118
                            $sterms = '';
4119
                        }
4120
                        $all_specific_terms .= ' '.$sterms;
4121
                        if (!empty($sterms)) {
4122
                            $sterms = explode(',', $sterms);
4123
                            foreach ($sterms as $sterm) {
4124
                                if (!$simulation) {
4125
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
4126
                                    add_specific_field_value(
4127
                                        $specific_field['id'],
4128
                                        $course_code,
4129
                                        TOOL_DOCUMENT,
4130
                                        $docid,
4131
                                        $sterm
4132
                                    );
4133
                                }
4134
                            }
4135
                        }
4136
                    }
4137
                    // Add terms also to content to make terms findable by probabilistic search
4138
                    $file_content = $all_specific_terms.' '.$file_content;
4139
                    if (!$simulation) {
4140
                        $ic_slide->addValue('content', $file_content);
4141
                        $di->addChunk($ic_slide);
4142
                        // Index and return search engine document id
4143
                        $did = $di->index();
4144
                        if ($did) {
4145
                            // Save it to db
4146
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4147
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
4148
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
4149
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
4150
                            Database::query($sql);
4151
                        } else {
4152
                            return false;
4153
                        }
4154
                    }
4155
                }
4156
            } else {
4157
                return false;
4158
            }
4159
        }
4160
4161
        return true;
4162
    }
4163
4164
    /**
4165
     * @return array
4166
     */
4167
    public static function get_web_odf_extension_list()
4168
    {
4169
        return ['ods', 'odt', 'odp'];
4170
    }
4171
4172
    /**
4173
     * Set of extension allowed to use Jodconverter.
4174
     *
4175
     * @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...
4176
     *              'to'
4177
     *              'all'
4178
     * @param $format   'text'
4179
     *                  'spreadsheet'
4180
     *                  'presentation'
4181
     *                  'drawing'
4182
     *                  'all'
4183
     *
4184
     * @return array
4185
     */
4186
    public static function getJodconverterExtensionList($mode, $format)
4187
    {
4188
        $extensionList = [];
4189
        $extensionListFromText = [
4190
            'odt',
4191
            'sxw',
4192
            'rtf',
4193
            'doc',
4194
            'docx',
4195
            'wpd',
4196
            'txt',
4197
        ];
4198
        $extensionListToText = [
4199
            'pdf',
4200
            'odt',
4201
            'sxw',
4202
            'rtf',
4203
            'doc',
4204
            'docx',
4205
            'txt',
4206
        ];
4207
        $extensionListFromSpreadsheet = [
4208
            'ods',
4209
            'sxc',
4210
            'xls',
4211
            'xlsx',
4212
            'csv',
4213
            'tsv',
4214
        ];
4215
        $extensionListToSpreadsheet = [
4216
            'pdf',
4217
            'ods',
4218
            'sxc',
4219
            'xls',
4220
            'xlsx',
4221
            'csv',
4222
            'tsv',
4223
        ];
4224
        $extensionListFromPresentation = [
4225
            'odp',
4226
            'sxi',
4227
            'ppt',
4228
            'pptx',
4229
        ];
4230
        $extensionListToPresentation = [
4231
            'pdf',
4232
            'swf',
4233
            'odp',
4234
            'sxi',
4235
            'ppt',
4236
            'pptx',
4237
        ];
4238
        $extensionListFromDrawing = ['odg'];
4239
        $extensionListToDrawing = ['svg', 'swf'];
4240
4241
        if ($mode === 'from') {
4242
            if ($format === 'text') {
4243
                $extensionList = array_merge($extensionList, $extensionListFromText);
4244
            } elseif ($format === 'spreadsheet') {
4245
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4246
            } elseif ($format === 'presentation') {
4247
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4248
            } elseif ($format === 'drawing') {
4249
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4250
            } elseif ($format === 'all') {
4251
                $extensionList = array_merge($extensionList, $extensionListFromText);
4252
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4253
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4254
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4255
            }
4256
        } elseif ($mode === 'to') {
4257
            if ($format === 'text') {
4258
                $extensionList = array_merge($extensionList, $extensionListToText);
4259
            } elseif ($format === 'spreadsheet') {
4260
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4261
            } elseif ($format === 'presentation') {
4262
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4263
            } elseif ($format === 'drawing') {
4264
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4265
            } elseif ($format === 'all') {
4266
                $extensionList = array_merge($extensionList, $extensionListToText);
4267
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4268
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4269
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4270
            }
4271
        } elseif ($mode === 'all') {
4272
            if ($format === 'text') {
4273
                $extensionList = array_merge($extensionList, $extensionListFromText);
4274
                $extensionList = array_merge($extensionList, $extensionListToText);
4275
            } elseif ($format === 'spreadsheet') {
4276
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4277
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4278
            } elseif ($format === 'presentation') {
4279
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4280
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4281
            } elseif ($format === 'drawing') {
4282
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4283
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4284
            } elseif ($format === 'all') {
4285
                $extensionList = array_merge($extensionList, $extensionListFromText);
4286
                $extensionList = array_merge($extensionList, $extensionListToText);
4287
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4288
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4289
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4290
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4291
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4292
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4293
            }
4294
        }
4295
4296
        return $extensionList;
4297
    }
4298
4299
    /**
4300
     * Get Format type list by extension and mode.
4301
     *
4302
     * @param string $mode Mode to search format type list
4303
     *
4304
     * @example 'from'
4305
     * @example 'to'
4306
     *
4307
     * @param string $extension file extension to check file type
4308
     *
4309
     * @return array
4310
     */
4311
    public static function getFormatTypeListConvertor($mode, $extension)
4312
    {
4313
        $formatTypesList = [];
4314
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
4315
        foreach ($formatTypes as $formatType) {
4316
            if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
4317
                $formatTypesList[] = $formatType;
4318
            }
4319
        }
4320
4321
        return $formatTypesList;
4322
    }
4323
4324
    /**
4325
     * @param string $path
4326
     * @param bool   $is_certificate_mode
4327
     *
4328
     * @return bool
4329
     */
4330
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
4331
    {
4332
        if (basename($path) == 'css') {
4333
            return true;
4334
        }
4335
4336
        if ($is_certificate_mode == false) {
4337
            //Certificate results
4338
            if (strstr($path, 'certificates')) {
4339
                return true;
4340
            }
4341
        }
4342
4343
        $foldersToAvoid = [
4344
            '/HotPotatoes_files',
4345
            '/certificates',
4346
        ];
4347
        $showSystemFolder = api_get_course_setting('show_system_folders');
4348
4349
        // Admin setting for Hide/Show Default folders to all users
4350
        if (api_get_setting('show_default_folders') == 'false'
4351
            || 2 == $showSystemFolder
4352
        ) {
4353
            $foldersToAvoid[] = '/images';
4354
            $foldersToAvoid[] = '/flash';
4355
            $foldersToAvoid[] = '/audio';
4356
            $foldersToAvoid[] = '/video';
4357
            $foldersToAvoid[] = '/learning_path';
4358
        }
4359
4360
        // Admin setting for Hide/Show the folders of all users
4361
        if (api_get_setting('show_users_folders') == 'false') {
4362
            $foldersToAvoid[] = '/shared_folder';
4363
4364
            if (strstr($path, 'shared_folder_session_')) {
4365
                return true;
4366
            }
4367
        }
4368
4369
        // Admin setting for Hide/Show chat history folder
4370
        if (api_get_setting('show_chat_folder') == 'false') {
4371
            $foldersToAvoid[] = '/chat_files';
4372
        }
4373
4374
        if (is_array($foldersToAvoid)) {
4375
            return in_array($path, $foldersToAvoid);
4376
        } else {
4377
            return false;
4378
        }
4379
    }
4380
4381
    /**
4382
     * @return array
4383
     */
4384
    public static function get_system_folders()
4385
    {
4386
        return [
4387
            '/certificates',
4388
            '/HotPotatoes_files',
4389
            '/chat_files',
4390
            '/images',
4391
            '/flash',
4392
            '/audio',
4393
            '/video',
4394
            '/shared_folder',
4395
            '/learning_path',
4396
        ];
4397
    }
4398
4399
    /**
4400
     * @return array
4401
     */
4402
    public static function getProtectedFolderFromStudent()
4403
    {
4404
        return [
4405
            '/certificates',
4406
            '/HotPotatoes_files',
4407
            '/chat_files',
4408
            '/shared_folder',
4409
            '/learning_path',
4410
        ];
4411
    }
4412
4413
    /**
4414
     * @param array $courseInfo
4415
     *
4416
     * @return string 'visible' or 'invisible' string
4417
     */
4418
    public static function getDocumentDefaultVisibility($courseInfo)
4419
    {
4420
        $settings = api_get_setting('tool_visible_by_default_at_creation');
4421
        $defaultVisibility = 'visible';
4422
4423
        if (isset($settings['documents'])) {
4424
            $portalDefaultVisibility = 'invisible';
4425
            if ($settings['documents'] == 'true') {
4426
                $portalDefaultVisibility = 'visible';
4427
            }
4428
4429
            $defaultVisibility = $portalDefaultVisibility;
4430
        }
4431
4432
        if (api_get_setting('documents_default_visibility_defined_in_course') == 'true') {
4433
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseInfo);
4434
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
4435
                $defaultVisibility = $courseVisibility;
4436
            }
4437
        }
4438
4439
        return $defaultVisibility;
4440
    }
4441
4442
    /**
4443
     * @param array  $courseInfo
4444
     * @param int    $id         doc id
4445
     * @param string $visibility visible/invisible
4446
     * @param int    $userId
4447
     */
4448
    public static function updateVisibilityFromAllSessions($courseInfo, $id, $visibility, $userId)
4449
    {
4450
        $sessionList = SessionManager::get_session_by_course($courseInfo['real_id']);
4451
4452
        if (!empty($sessionList)) {
4453
            foreach ($sessionList as $session) {
4454
                $sessionId = $session['id'];
4455
                api_item_property_update(
4456
                    $courseInfo,
4457
                    TOOL_DOCUMENT,
4458
                    $id,
4459
                    $visibility,
4460
                    $userId,
4461
                    null,
4462
                    null,
4463
                    null,
4464
                    null,
4465
                    $sessionId
4466
                );
4467
            }
4468
        }
4469
    }
4470
4471
    /**
4472
     * @param string $filePath
4473
     * @param string $path
4474
     * @param array  $courseInfo
4475
     * @param int    $sessionId
4476
     * @param string $whatIfFileExists overwrite|rename
4477
     * @param int    $userId
4478
     * @param int    $groupId
4479
     * @param int    $toUserId
4480
     * @param string $comment
4481
     *
4482
     * @return bool|path
4483
     */
4484
    public static function addFileToDocumentTool(
4485
        $filePath,
4486
        $path,
4487
        $courseInfo,
4488
        $sessionId,
4489
        $userId,
4490
        $whatIfFileExists = 'overwrite',
4491
        $groupId = null,
4492
        $toUserId = null,
4493
        $comment = null
4494
    ) {
4495
        if (!file_exists($filePath)) {
4496
            return false;
4497
        }
4498
4499
        $fileInfo = pathinfo($filePath);
4500
4501
        $file = [
4502
            'name' => $fileInfo['basename'],
4503
            'tmp_name' => $filePath,
4504
            'size' => filesize($filePath),
4505
            'from_file' => true,
4506
        ];
4507
4508
        $course_dir = $courseInfo['path'].'/document';
4509
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
4510
4511
        $filePath = handle_uploaded_document(
4512
            $courseInfo,
4513
            $file,
4514
            $baseWorkDir,
4515
            $path,
4516
            $userId,
4517
            $groupId,
4518
            $toUserId,
4519
            false,
4520
            $whatIfFileExists,
4521
            false,
4522
            false,
4523
            $comment,
4524
            $sessionId
4525
        );
4526
4527
        if ($filePath) {
4528
            return self::get_document_id(
4529
                $courseInfo,
4530
                $filePath,
4531
                $sessionId
4532
            );
4533
        }
4534
4535
        return false;
4536
    }
4537
4538
    /**
4539
     * Converts wav to mp3 file.
4540
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg.
4541
     *
4542
     * @param string $wavFile
4543
     * @param bool   $removeWavFileIfSuccess
4544
     *
4545
     * @return bool
4546
     */
4547
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
4548
    {
4549
        if (file_exists($wavFile)) {
4550
            try {
4551
                $ffmpeg = \FFMpeg\FFMpeg::create();
4552
                $video = $ffmpeg->open($wavFile);
4553
4554
                $mp3File = str_replace('wav', 'mp3', $wavFile);
4555
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
4556
                if ($result && $removeWavFileIfSuccess) {
4557
                    unlink($wavFile);
4558
                }
4559
4560
                if (file_exists($mp3File)) {
4561
                    return $mp3File;
4562
                }
4563
            } catch (Exception $e) {
4564
                error_log($e->getMessage());
4565
                error_log($e->getPrevious()->getMessage());
4566
            }
4567
        }
4568
4569
        return false;
4570
    }
4571
4572
    /**
4573
     * @param string $documentData     wav document information
4574
     * @param array  $courseInfo
4575
     * @param int    $sessionId
4576
     * @param int    $userId           user that adds the document
4577
     * @param string $whatIfFileExists
4578
     * @param bool   $deleteWavFile
4579
     *
4580
     * @return bool
4581
     */
4582
    public static function addAndConvertWavToMp3(
4583
        $documentData,
4584
        $courseInfo,
4585
        $sessionId,
4586
        $userId,
4587
        $whatIfFileExists = 'overwrite',
4588
        $deleteWavFile = false
4589
    ) {
4590
        if (empty($documentData)) {
4591
            return false;
4592
        }
4593
4594
        if (isset($documentData['absolute_path']) &&
4595
            file_exists($documentData['absolute_path'])
4596
        ) {
4597
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4598
4599
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4600
                $documentId = self::addFileToDocumentTool(
4601
                    $mp3FilePath,
4602
                    dirname($documentData['path']),
4603
                    $courseInfo,
4604
                    $sessionId,
4605
                    $userId,
4606
                    $whatIfFileExists,
4607
                    null,
4608
                    null,
4609
                    $documentData['comment']
4610
                );
4611
4612
                if (!empty($documentId)) {
4613
                    if ($deleteWavFile) {
4614
                        $coursePath = $courseInfo['directory'].'/document';
4615
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4616
                        self::delete_document(
4617
                            $courseInfo,
4618
                            null,
4619
                            $documentPath,
4620
                            $sessionId,
4621
                            $documentData['id']
4622
                        );
4623
                    }
4624
4625
                    return $documentId;
4626
                }
4627
            }
4628
        }
4629
4630
        return false;
4631
    }
4632
4633
    /**
4634
     * Sets.
4635
     *
4636
     * @param string $file         ($document_data['path'])
4637
     * @param string $file_url_sys
4638
     *
4639
     * @return string
4640
     */
4641
    public static function generateAudioTempFile($file, $file_url_sys)
4642
    {
4643
        //make temp audio
4644
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4645
        if (!file_exists($temp_folder)) {
4646
            @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);
4647
        }
4648
4649
        //make htaccess with allow from all, and file index.html into temp/audio
4650
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4651
        if (!file_exists($htaccess)) {
4652
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4653
            $fp = @fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4654
            if ($fp) {
4655
                fwrite($fp, $htaccess_content);
4656
                fclose($fp);
4657
            }
4658
        }
4659
4660
        //encript temp name file
4661
        $name_crip = sha1(uniqid()); //encript
4662
        $findext = explode(".", $file);
4663
        $extension = $findext[count($findext) - 1];
4664
        $file_crip = $name_crip.'.'.$extension;
4665
4666
        //copy file to temp/audio directory
4667
        $from_sys = $file_url_sys;
4668
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4669
4670
        if (file_exists($from_sys)) {
4671
            copy($from_sys, $to_sys);
4672
        }
4673
4674
        // get file from tmp directory
4675
        Session::write('temp_audio_nanogong', $to_sys);
4676
4677
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4678
    }
4679
4680
    /**
4681
     * Erase temp nanogong audio.
4682
     */
4683
    public static function removeGeneratedAudioTempFile()
4684
    {
4685
        $tempAudio = Session::read('temp_audio_nanogong');
4686
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4687
            unlink($tempAudio);
4688
            Session::erase('temp_audio_nanogong');
4689
        }
4690
    }
4691
4692
    /**
4693
     * Check if the path is used in this course.
4694
     *
4695
     * @param array  $courseInfo
4696
     * @param string $path
4697
     *
4698
     * @return array
4699
     */
4700
    public static function getDocumentByPathInCourse($courseInfo, $path)
4701
    {
4702
        $table = Database::get_course_table(TABLE_DOCUMENT);
4703
        $path = Database::escape_string($path);
4704
        $courseId = $courseInfo['real_id'];
4705
        if (empty($courseId)) {
4706
            return false;
4707
        }
4708
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4709
        $result = Database::query($sql);
4710
4711
        return Database::store_result($result, 'ASSOC');
4712
    }
4713
4714
    /**
4715
     * @param array $_course
4716
     *
4717
     * @return int
4718
     */
4719
    public static function createDefaultAudioFolder($_course)
4720
    {
4721
        if (!isset($_course['path'])) {
4722
            return false;
4723
        }
4724
4725
        $audioId = null;
4726
        $path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
4727
        if (!is_dir($path.'audio')) {
4728
            mkdir($path.'audio', api_get_permissions_for_new_directories());
4729
            $audioId = add_document($_course, '/audio', 'folder', 0, get_lang('Audio'));
4730
            api_item_property_update(
4731
                $_course,
4732
                TOOL_DOCUMENT,
4733
                $audioId,
4734
                'FolderCreated',
4735
                api_get_user_id(),
4736
                null,
4737
                null,
4738
                null,
4739
                null,
4740
                api_get_session_id()
4741
            );
4742
        }
4743
4744
        return $audioId;
4745
    }
4746
4747
    /**
4748
     * Generate a default certificate for a courses.
4749
     *
4750
     * @todo move to certificate lib
4751
     *
4752
     * @global string $css CSS directory
4753
     * @global string $img_dir image directory
4754
     * @global string $default_course_dir Course directory
4755
     * @global string $js JS directory
4756
     *
4757
     * @param array $courseData     The course info
4758
     * @param bool  $fromBaseCourse
4759
     * @param int   $sessionId
4760
     */
4761
    public static function generateDefaultCertificate(
4762
        $courseData,
4763
        $fromBaseCourse = false,
4764
        $sessionId = 0
4765
    ) {
4766
        if (empty($courseData)) {
4767
            return false;
4768
        }
4769
4770
        global $css, $img_dir, $default_course_dir, $js;
4771
        $codePath = api_get_path(REL_CODE_PATH);
4772
        $dir = '/certificates';
4773
        $comment = null;
4774
        $title = get_lang('DefaultCertificate');
4775
        $fileName = api_replace_dangerous_char($title);
4776
        $filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4777
4778
        if (!is_dir($filePath)) {
4779
            mkdir($filePath, api_get_permissions_for_new_directories());
4780
        }
4781
4782
        $fileFullPath = "$filePath/$fileName.html";
4783
        $fileType = 'file';
4784
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4785
4786
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4787
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4788
4789
        $fileContent = str_replace($search, $replace, $templateContent);
4790
        $saveFilePath = "$dir/$fileName.html";
4791
4792
        if ($fromBaseCourse) {
4793
            $defaultCertificateId = self::get_default_certificate_id(
4794
                $courseData['code'],
4795
                0
4796
            );
4797
            if (!empty($defaultCertificateId)) {
4798
                // We have a certificate from the course base
4799
                $documentData = self::get_document_data_by_id(
4800
                    $defaultCertificateId,
4801
                    $courseData['code'],
4802
                    false,
4803
                    0
4804
                );
4805
4806
                if ($documentData) {
4807
                    $fileContent = file_get_contents($documentData['absolute_path']);
4808
                }
4809
            }
4810
        }
4811
4812
        if (file_exists($fileFullPath) === false) {
4813
            $result = file_put_contents($fileFullPath, $fileContent);
4814
            if ($result) {
4815
                $fileSize = filesize($fileFullPath);
4816
4817
                $documentId = add_document(
4818
                    $courseData,
4819
                    $saveFilePath,
4820
                    $fileType,
4821
                    $fileSize,
4822
                    $title,
4823
                    $comment,
4824
                    0, //$readonly = 0,
4825
                    true, //$save_visibility = true,
4826
                    null, //$group_id = null,
4827
                    $sessionId
4828
                );
4829
4830
                api_item_property_update(
4831
                    $courseData,
4832
                    TOOL_DOCUMENT,
4833
                    $documentId,
4834
                    'DocumentAdded',
4835
                    api_get_user_id(),
4836
                    null,
4837
                    null,
4838
                    null,
4839
                    null,
4840
                    $sessionId
4841
                );
4842
4843
                $defaultCertificateId = self::get_default_certificate_id(
4844
                    $courseData['code'],
4845
                    $sessionId
4846
                );
4847
4848
                if (!isset($defaultCertificateId)) {
4849
                    self::attach_gradebook_certificate(
4850
                        $courseData['code'],
4851
                        $documentId,
4852
                        $sessionId
4853
                    );
4854
                }
4855
            }
4856
        }
4857
    }
4858
4859
    /**
4860
     * Update the document name.
4861
     *
4862
     * @param int    $documentId The document id
4863
     * @param string $newName    The new name
4864
     */
4865
    public static function renameDocument($documentId, $newName)
4866
    {
4867
        $documentId = intval($documentId);
4868
        $newName = Database::escape_string($newName);
4869
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4870
4871
        $values = [
4872
            'title' => $newName,
4873
        ];
4874
4875
        $whereConditions = [
4876
            'id = ?' => $documentId,
4877
        ];
4878
4879
        Database::update($docuentTable, $values, $whereConditions);
4880
    }
4881
4882
    /**
4883
     * Get folder/file suffix.
4884
     *
4885
     * @param array $courseInfo
4886
     * @param int   $sessionId
4887
     * @param int   $groupId
4888
     *
4889
     * @return string
4890
     */
4891
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4892
    {
4893
        // If no session or group, then no suffix.
4894
        if (empty($sessionId) && empty($groupId)) {
4895
            return '';
4896
        }
4897
4898
        return '__'.intval($sessionId).'__'.intval($groupId);
4899
    }
4900
4901
    /**
4902
     * Fix a document name adding session id and group id
4903
     * Turns picture.jpg -> picture__1__2.jpg
4904
     * Where 1 = session id and 2 group id
4905
     * Of session id and group id are empty then the function returns:
4906
     * picture.jpg ->  picture.jpg.
4907
     *
4908
     * @param string $name       folder or file name
4909
     * @param string $type       'folder' or 'file'
4910
     * @param array  $courseInfo
4911
     * @param int    $sessionId
4912
     * @param int    $groupId
4913
     *
4914
     * @return string
4915
     */
4916
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4917
    {
4918
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4919
4920
        switch ($type) {
4921
            case 'folder':
4922
                $name = $name.$suffix;
4923
                break;
4924
            case 'file':
4925
                $name = self::addSuffixToFileName($name, $suffix);
4926
                break;
4927
        }
4928
4929
        return $name;
4930
    }
4931
4932
    /**
4933
     * Add a suffix to a file Example:
4934
     * /folder/picture.jpg => to /folder/picture_this.jpg
4935
     * where "_this" is the suffix.
4936
     *
4937
     * @param string $name
4938
     * @param string $suffix
4939
     *
4940
     * @return string
4941
     */
4942
    public static function addSuffixToFileName($name, $suffix)
4943
    {
4944
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4945
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4946
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4947
4948
        if ($dir == '.') {
4949
            $dir = null;
4950
        }
4951
4952
        if (!empty($dir) && $dir != '/') {
4953
            $dir = $dir.'/';
4954
        }
4955
4956
        $name = $dir.$fileName.$suffix.'.'.$extension;
4957
4958
        return $name;
4959
    }
4960
4961
    /**
4962
     * Check if folder exist in the course base or in the session course.
4963
     *
4964
     * @param string $folder     Example: /folder/folder2
4965
     * @param array  $courseInfo
4966
     * @param int    $sessionId
4967
     * @param int    $groupId    group.id
4968
     *
4969
     * @return bool
4970
     */
4971
    public static function folderExists(
4972
        $folder,
4973
        $courseInfo,
4974
        $sessionId,
4975
        $groupId
4976
    ) {
4977
        $courseId = $courseInfo['real_id'];
4978
4979
        if (empty($courseId)) {
4980
            return false;
4981
        }
4982
4983
        $sessionId = (int) $sessionId;
4984
        $folderWithSuffix = self::fixDocumentName(
4985
            $folder,
4986
            'folder',
4987
            $courseInfo,
4988
            $sessionId,
4989
            $groupId
4990
        );
4991
4992
        $folder = Database::escape_string($folder);
4993
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4994
4995
        // Check if pathname already exists inside document table
4996
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4997
        $sql = "SELECT id, path FROM $tbl_document
4998
                WHERE
4999
                    filetype = 'folder' AND
5000
                    c_id = $courseId AND
5001
                    (path = '$folder' OR path = '$folderWithSuffix') AND
5002
                    (session_id = 0 OR session_id = $sessionId)
5003
        ";
5004
5005
        $rs = Database::query($sql);
5006
        if (Database::num_rows($rs)) {
5007
            return true;
5008
        }
5009
5010
        return false;
5011
    }
5012
5013
    /**
5014
     * Check if file exist in the course base or in the session course.
5015
     *
5016
     * @param string $fileName   Example: /folder/picture.jpg
5017
     * @param array  $courseInfo
5018
     * @param int    $sessionId
5019
     * @param int    $groupId
5020
     *
5021
     * @return bool
5022
     */
5023
    public static function documentExists(
5024
        $fileName,
5025
        $courseInfo,
5026
        $sessionId,
5027
        $groupId
5028
    ) {
5029
        $courseId = $courseInfo['real_id'];
5030
5031
        if (empty($courseId)) {
5032
            return false;
5033
        }
5034
5035
        $sessionId = (int) $sessionId;
5036
        $fileNameEscape = Database::escape_string($fileName);
5037
5038
        $fileNameWithSuffix = self::fixDocumentName(
5039
            $fileName,
5040
            'file',
5041
            $courseInfo,
5042
            $sessionId,
5043
            $groupId
5044
        );
5045
5046
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
5047
5048
        // Check if pathname already exists inside document table
5049
        $table = Database::get_course_table(TABLE_DOCUMENT);
5050
        $sql = "SELECT id, path FROM $table
5051
                WHERE
5052
                    filetype = 'file' AND
5053
                    c_id = $courseId AND
5054
                    (
5055
                        path = '".$fileNameEscape."' OR
5056
                        path = '$fileNameWithSuffix'
5057
                    ) AND
5058
                    (session_id = 0 OR session_id = $sessionId)
5059
        ";
5060
        $rs = Database::query($sql);
5061
        if (Database::num_rows($rs)) {
5062
            return true;
5063
        }
5064
5065
        return false;
5066
    }
5067
5068
    /**
5069
     * Undo the suffix applied to a file example:
5070
     * turns picture__1__1.jpg to picture.jpg.
5071
     *
5072
     * @param string $name
5073
     * @param int    $courseId
5074
     * @param int    $sessionId
5075
     * @param int    $groupId
5076
     *
5077
     * @return string
5078
     */
5079
    public static function undoFixDocumentName(
5080
        $name,
5081
        $courseId,
5082
        $sessionId,
5083
        $groupId
5084
    ) {
5085
        if (empty($sessionId) && empty($groupId)) {
5086
            return $name;
5087
        }
5088
5089
        $suffix = self::getDocumentSuffix(
5090
            ['real_id' => $courseId],
5091
            $sessionId,
5092
            $groupId
5093
        );
5094
5095
        $name = str_replace($suffix, '', $name);
5096
5097
        return $name;
5098
    }
5099
5100
    /**
5101
     * @param string $path
5102
     * @param string $name
5103
     * @param array  $courseInfo
5104
     * @param int    $sessionId
5105
     * @param int    $groupId
5106
     *
5107
     * @return string
5108
     */
5109
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
5110
    {
5111
        $counter = 1;
5112
        $filePath = $path.$name;
5113
        $uniqueName = $name;
5114
        while ($documentExists = self::documentExists(
5115
            $filePath,
5116
            $courseInfo,
5117
            $sessionId,
5118
            $groupId
5119
        )) {
5120
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
5121
            $filePath = $path.$uniqueName;
5122
            $counter++;
5123
        }
5124
5125
        return $uniqueName;
5126
    }
5127
5128
    /**
5129
     * Builds the form that enables the user to
5130
     * select a directory to browse/upload in.
5131
     *
5132
     * @param array    An array containing the folders we want to be able to select
5133
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
5134
     * @param string    Group directory, if empty, prevents documents to be uploaded
5135
     * (because group documents cannot be uploaded in root)
5136
     * @param bool    Whether to change the renderer (this will add a template <span>
5137
     * to the QuickForm object displaying the form)
5138
     *
5139
     * @return string html form
5140
     */
5141
    public static function build_directory_selector(
5142
        $folders,
5143
        $document_id,
5144
        $group_dir = '',
5145
        $change_renderer = false,
5146
        &$form = null,
5147
        $selectName = 'id',
5148
        $attributes = []
5149
    ) {
5150
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
5151
        $course_id = api_get_course_int_id();
5152
        $folder_titles = [];
5153
5154
        if (is_array($folders)) {
5155
            $escaped_folders = [];
5156
            foreach ($folders as $key => $val) {
5157
                $escaped_folders[$key] = Database::escape_string($val);
5158
            }
5159
            $folder_sql = implode("','", $escaped_folders);
5160
5161
            $sql = "SELECT path, title
5162
                    FROM $doc_table
5163
                    WHERE
5164
                        filetype = 'folder' AND
5165
                        c_id = $course_id AND
5166
                        path IN ('".$folder_sql."')
5167
                        ORDER BY path";
5168
            $res = Database::query($sql);
5169
            $folder_titles = [];
5170
            while ($obj = Database::fetch_object($res)) {
5171
                $folder_titles[$obj->path] = $obj->title;
5172
            }
5173
            //natcasesort($folder_titles);
5174
        }
5175
5176
        if (empty($form)) {
5177
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
5178
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
5179
        }
5180
        $form->addElement('hidden', 'cidReq', api_get_course_id());
5181
        $form->addElement('hidden', 'id_session', api_get_session_id());
5182
        $form->addElement('hidden', 'gidReq', api_get_group_id());
5183
5184
        $parent_select = $form->addSelect(
5185
            $selectName,
5186
            get_lang('CurrentDirectory'),
5187
            '',
5188
            $attributes
5189
        );
5190
5191
        // Group documents cannot be uploaded in the root
5192
        if (empty($group_dir)) {
5193
            $parent_select->addOption(get_lang('Documents'), '/');
5194
5195
            if (is_array($folders)) {
5196
                $foldersSortedByTitles = [];
5197
                foreach ($folders as $folder_id => &$folder) {
5198
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5199
                    $path_parts = explode('/', $folder);
5200
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
5201
                    $counter = count($path_parts) - 2;
5202
                    if ($counter > 0) {
5203
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
5204
                    } else {
5205
                        $label = ' &mdash; '.$folder_titles[$folder];
5206
                    }
5207
                    $label = Security::remove_XSS($label);
5208
                    $foldersSortedByTitles[$folder_id] = [
5209
                        'id' => $folder_id,
5210
                        'title' => $folder_titles[$folder],
5211
                        'selected' => $selected,
5212
                        'label' => $label,
5213
                    ];
5214
                }
5215
                foreach ($folders as $id => $title) {
5216
                    $parent_select->addOption(
5217
                        $foldersSortedByTitles[$id]['label'],
5218
                        $foldersSortedByTitles[$id]['id']
5219
                    );
5220
                    if ($foldersSortedByTitles[$id]['selected'] != '') {
5221
                        $parent_select->setSelected($foldersSortedByTitles[$id]['id']);
5222
                    }
5223
                }
5224
            }
5225
        } else {
5226
            if (!empty($folders)) {
5227
                $foldersSortedByTitles = [];
5228
                foreach ($folders as $folder_id => &$folder) {
5229
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5230
                    $label = $folder_titles[$folder];
5231
                    if ($folder == $group_dir) {
5232
                        $label = get_lang('Documents');
5233
                    } else {
5234
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
5235
                        $label = cut($label, 80);
5236
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
5237
                    }
5238
                    $foldersSortedByTitles[$folder_titles[$folder]] = [
5239
                        'id' => $folder_id,
5240
                        'selected' => $selected,
5241
                        'label' => $label,
5242
                    ];
5243
                }
5244
                foreach ($folder_titles as $title) {
5245
                    $parent_select->addOption(
5246
                        $foldersSortedByTitles[$title]['label'],
5247
                        $foldersSortedByTitles[$title]['id']
5248
                    );
5249
                    if ($foldersSortedByTitles[$title]['selected'] != '') {
5250
                        $parent_select->setSelected($foldersSortedByTitles[$title]['id']);
5251
                    }
5252
                }
5253
            }
5254
        }
5255
5256
        return $form->toHtml();
5257
    }
5258
5259
    /**
5260
     * Create a html hyperlink depending on if it's a folder or a file.
5261
     *
5262
     * @param string $documentWebPath
5263
     * @param array  $document_data
5264
     * @param bool   $show_as_icon      - if it is true, only a clickable icon will be shown
5265
     * @param int    $visibility        (1/0)
5266
     * @param int    $counter
5267
     * @param int    $size
5268
     * @param bool   $isAllowedToEdit
5269
     * @param bool   $isCertificateMode
5270
     *
5271
     * @return string url
5272
     */
5273
    public static function create_document_link(
5274
        $documentWebPath,
5275
        $document_data,
5276
        $show_as_icon = false,
5277
        $counter = null,
5278
        $visibility = true,
5279
        $size = 0,
5280
        $isAllowedToEdit = false,
5281
        $isCertificateMode = false
5282
    ) {
5283
        global $dbl_click_id;
5284
        $www = $documentWebPath;
5285
5286
        $secToken = Security::getTokenFromSession();
5287
5288
        $sessionId = api_get_session_id();
5289
        $courseParams = api_get_cidreq();
5290
        $webODFList = self::get_web_odf_extension_list();
5291
5292
        // Get the title or the basename depending on what we're using
5293
        if ($document_data['title'] != '') {
5294
            $title = $document_data['title'];
5295
        } else {
5296
            $title = basename($document_data['path']);
5297
        }
5298
5299
        if (api_get_configuration_value('save_titles_as_html')) {
5300
            $title = strip_tags($title);
5301
        }
5302
5303
        $filetype = $document_data['filetype'];
5304
        $path = $document_data['path'];
5305
        $url_path = urlencode($document_data['path']);
5306
        $basePageUrl = api_get_path(WEB_CODE_PATH).'document/';
5307
        $pageUrl = $basePageUrl.'document.php';
5308
5309
        // Add class="invisible" on invisible files
5310
        $visibility_class = $visibility == false ? ' class="muted"' : '';
5311
        $forcedownload_link = '';
5312
        $forcedownload_icon = '';
5313
        $prevent_multiple_click = '';
5314
        $force_download_html = '';
5315
5316
        if (!$show_as_icon) {
5317
            // Build download link (icon)
5318
            $forcedownload_link = $filetype === 'folder'
5319
                ? $pageUrl.'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id'].'&sec_token='.$secToken
5320
                : $pageUrl.'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'].'&sec_token='.$secToken;
5321
            // Folder download or file download?
5322
            $forcedownload_icon = $filetype === 'folder' ? 'save_pack.png' : 'save.png';
5323
            // Prevent multiple clicks on zipped folder download
5324
            $prevent_multiple_click = $filetype === 'folder' ? " 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; }\"" : '';
5325
        }
5326
5327
        $target = '_self';
5328
        $is_browser_viewable_file = false;
5329
5330
        if ($filetype === 'file') {
5331
            // Check the extension
5332
            $ext = explode('.', $path);
5333
            $ext = strtolower($ext[count($ext) - 1]);
5334
5335
            // HTML-files an some other types are shown in a frameset by default.
5336
            $is_browser_viewable_file = self::isBrowserViewable($ext);
5337
            if ($is_browser_viewable_file) {
5338
                $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5339
            } else {
5340
                // url-encode for problematic characters (we may not call them dangerous characters...)
5341
                //$path = str_replace('%2F', '/', $url_path).'?'.$courseParams;
5342
                $url = $www.str_replace('%2F', '/', $url_path).'?'.$courseParams;
5343
            }
5344
        } else {
5345
            $url = $pageUrl.'?'.$courseParams.'&id='.$document_data['id'];
5346
        }
5347
5348
        if ($isCertificateMode) {
5349
            $url .= '&certificate=true&selectcat='.(isset($_GET['selectcat']) ? $_GET['selectcat'] : '');
5350
        }
5351
5352
        // The little download icon
5353
        $tooltip_title = $title;
5354
        $tooltip_title_alt = $tooltip_title;
5355
5356
        if ($filetype === 'link') {
5357
            $tooltip_title_alt = $title;
5358
            $url = $document_data['comment'].'" target="_blank';
5359
        }
5360
5361
        if ($path === '/shared_folder') {
5362
            $tooltip_title_alt = get_lang('UserFolders');
5363
        } elseif (strstr($path, 'shared_folder_session_')) {
5364
            $tooltip_title_alt = get_lang('UserFolders').' ('.api_get_session_name(api_get_session_id()).')';
5365
        } elseif (strstr($tooltip_title, 'sf_user_')) {
5366
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
5367
            $tooltip_title_alt = get_lang('UserFolder').' '.$userinfo['complete_name'];
5368
        } elseif ($path == '/chat_files') {
5369
            $tooltip_title_alt = get_lang('ChatFiles');
5370
        } elseif ($path == '/learning_path') {
5371
            $tooltip_title_alt = get_lang('LearningPaths');
5372
        } elseif ($path == '/video') {
5373
            $tooltip_title_alt = get_lang('Video');
5374
        } elseif ($path == '/audio') {
5375
            $tooltip_title_alt = get_lang('Audio');
5376
        } elseif ($path == '/flash') {
5377
            $tooltip_title_alt = get_lang('Flash');
5378
        } elseif ($path == '/images') {
5379
            $tooltip_title_alt = get_lang('Images');
5380
        } elseif ($path == '/images/gallery') {
5381
            $tooltip_title_alt = get_lang('DefaultCourseImages');
5382
        }
5383
5384
        $copyToMyFiles = $open_in_new_window_link = '';
5385
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
5386
        $send_to = null;
5387
        $checkExtension = $path;
5388
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5389
        $document_data['file_extension'] = $extension;
5390
5391
        if (!$show_as_icon) {
5392
            // to force download if a document can be downloaded or not
5393
            $hideDownloadIcon = false;
5394
            if (true === api_get_configuration_value('documents_hide_download_icon')) {
5395
                $hideDownloadIcon = true;
5396
            }
5397
            if (self::getHideDownloadIcon($document_data['id'])) {
5398
                $hideDownloadIcon = false;
5399
            }
5400
            if (!$hideDownloadIcon) {
5401
                if ($filetype == 'folder') {
5402
                    if ($isAllowedToEdit ||
5403
                        api_is_platform_admin() ||
5404
                        api_get_setting('students_download_folders') == 'true'
5405
                    ) {
5406
                        // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
5407
                        if (self::is_shared_folder($curdirpath, $sessionId)) {
5408
                            if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5409
                                preg_match('/shared_folder_session_'.$sessionId.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5410
                                $isAllowedToEdit || api_is_platform_admin()
5411
                            ) {
5412
                                $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5413
                                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5414
                            }
5415
                        } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
5416
                            $isAllowedToEdit ||
5417
                            api_is_platform_admin()
5418
                        ) {
5419
                            $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5420
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5421
                        }
5422
                    }
5423
                } else {
5424
                    $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
5425
                        Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5426
                }
5427
            }
5428
5429
            // Copy files to user's myfiles
5430
            if (api_get_setting('allow_my_files') === 'true' &&
5431
                api_get_setting('users_copy_files') === 'true' && api_is_anonymous() === false
5432
            ) {
5433
                $copy_myfiles_link = $filetype === 'file' ? $pageUrl.'?'.$courseParams.'&action=copytomyfiles&id='.$document_data['id'].'&sec_token='.$secToken : api_get_self().'?'.$courseParams.'&sec_token='.$secToken;
5434
                if ($filetype === 'file') {
5435
                    $copyToMyFiles = '<a href="'.$copy_myfiles_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5436
                        Display::return_icon('briefcase.png', get_lang('CopyToMyFiles'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5437
5438
                    if (api_get_setting('allow_my_files') === 'false') {
5439
                        $copyToMyFiles = '';
5440
                    }
5441
                }
5442
            }
5443
5444
            $pdf_icon = '';
5445
            if (!$isAllowedToEdit &&
5446
                $filetype === 'file' &&
5447
                api_get_setting('students_export2pdf') == 'true' &&
5448
                in_array($extension, ['html', 'htm'])
5449
            ) {
5450
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.$pageUrl.'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&sec_token='.$secToken.'&curdirpath='.$curdirpath.'">'.
5451
                    Display::return_icon('pdf.png', get_lang('Export2PDF'), [], ICON_SIZE_SMALL).'</a> ';
5452
            }
5453
5454
            if ($is_browser_viewable_file) {
5455
                $open_in_new_window_link = '<a href="'.$www.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
5456
                    Display::return_icon('open_in_new_window.png', get_lang('OpenInANewWindow'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5457
            }
5458
5459
            $hook = HookDocumentItemView::create();
5460
            $hookItemViewTools = $hook->setEventData($document_data)->notifyDocumentItemView();
5461
5462
            $rightTools = implode(
5463
                PHP_EOL,
5464
                [
5465
                    $force_download_html,
5466
                    $send_to,
5467
                    $copyToMyFiles,
5468
                    $open_in_new_window_link,
5469
                    $pdf_icon,
5470
                    $hookItemViewTools ? implode(PHP_EOL, $hookItemViewTools) : '',
5471
                ]
5472
            );
5473
5474
            if ($filetype === 'file') {
5475
                // Sound preview
5476
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5477
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
5478
                    preg_match('/ogg$/i', urldecode($checkExtension))
5479
                ) {
5480
                    return '<span style="float:left" '.$visibility_class.'>'.
5481
                        $title.
5482
                        '</span>'.$rightTools;
5483
                } elseif (
5484
                    // Show preview
5485
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
5486
                    preg_match('/png$/i', urldecode($checkExtension)) ||
5487
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
5488
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
5489
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5490
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
5491
                    preg_match('/svg$/i', urldecode($checkExtension))
5492
                ) {
5493
                    // Simpler version of showinframesmin.php with no headers
5494
                    $url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
5495
                    $class = 'ajax';
5496
                    if ($visibility == false) {
5497
                        $class = 'ajax text-muted';
5498
                    }
5499
5500
                    return Display::url(
5501
                            $title,
5502
                            $url,
5503
                            [
5504
                                'class' => $class,
5505
                                'title' => $tooltip_title_alt,
5506
                                'data-title' => $title,
5507
                                'style' => 'float:left;',
5508
                            ]
5509
                        )
5510
                        .$rightTools;
5511
                } else {
5512
                    // For a "PDF Download" of the file.
5513
                    $pdfPreview = null;
5514
5515
                    if (OnlyofficePlugin::create()->isEnabled() &&
5516
                        OnlyofficePlugin::isExtensionAllowed($document_data['file_extension']) &&
5517
                        method_exists('OnlyofficeTools', 'getPathToView')
5518
                    ) {
5519
                        $url = OnlyofficeTools::getPathToView($document_data['id']);
5520
                    } elseif ($ext != 'pdf' && !in_array($ext, $webODFList)) {
5521
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5522
                    } else {
5523
                        $pdfPreview = Display::url(
5524
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
5525
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
5526
                            ['style' => 'float:right']
5527
                        );
5528
                    }
5529
                    // No plugin just the old and good showinframes.php page
5530
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
5531
                        $pdfPreview.$rightTools;
5532
                }
5533
            } else {
5534
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'
5535
                    .$rightTools;
5536
            }
5537
            // end copy files to users myfiles
5538
        } else {
5539
            // Icon column
5540
            if (preg_match('/shared_folder/', urldecode($checkExtension)) &&
5541
                preg_match('/shared_folder$/', urldecode($checkExtension)) == false &&
5542
                preg_match('/shared_folder_session_'.$sessionId.'$/', urldecode($url)) == false
5543
            ) {
5544
                if ($filetype == 'file') {
5545
                    //Sound preview
5546
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5547
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5548
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5549
                        return self::generateAudioPreview($documentWebPath, $document_data);
5550
                    } elseif (
5551
                        // Show preview
5552
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5553
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5554
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5555
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5556
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5557
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5558
                        preg_match('/svg$/i', urldecode($checkExtension))
5559
                    ) {
5560
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5561
5562
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5563
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5564
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5565
                            '</a>';
5566
                    } else {
5567
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5568
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5569
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5570
                            '</a>';
5571
                    }
5572
                } else {
5573
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5574
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5575
                        Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5576
                        '</a>';
5577
                }
5578
            } else {
5579
                if ($filetype === 'file') {
5580
                    // Sound preview with jplayer
5581
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5582
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5583
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5584
                        return self::generateAudioPreview($documentWebPath, $document_data);
5585
                    } elseif (
5586
                        //Show preview
5587
                        preg_match('/html$/i', urldecode($checkExtension)) ||
5588
                        preg_match('/htm$/i', urldecode($checkExtension)) ||
5589
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5590
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5591
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5592
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5593
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5594
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5595
                        preg_match('/svg$/i', urldecode($checkExtension))
5596
                    ) {
5597
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
5598
5599
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5600
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5601
                            '</a>';
5602
                    } else {
5603
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5604
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5605
                            '</a>';
5606
                    }
5607
                } else {
5608
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5609
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5610
                        '</a>';
5611
                }
5612
            }
5613
        }
5614
    }
5615
5616
    /**
5617
     * Builds an img html tag for the file type.
5618
     *
5619
     * @param string $type            (file/folder)
5620
     * @param string $path
5621
     * @param bool   $isAllowedToEdit
5622
     *
5623
     * @return string img html tag
5624
     */
5625
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
5626
    {
5627
        $basename = basename($path);
5628
        $sessionId = api_get_session_id();
5629
        if (is_null($isAllowedToEdit)) {
5630
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
5631
        }
5632
        $user_image = false;
5633
        if ($type == 'file') {
5634
            $icon = choose_image($basename);
5635
            $basename = substr(strrchr($basename, '.'), 1);
5636
        } elseif ($type == 'link') {
5637
            $icon = 'clouddoc.png';
5638
            $basename = get_lang('CloudFileLink');
5639
        } else {
5640
            if ($path == '/shared_folder') {
5641
                $icon = 'folder_users.png';
5642
                if ($isAllowedToEdit) {
5643
                    $basename = get_lang('HelpUsersFolder');
5644
                } else {
5645
                    $basename = get_lang('UserFolders');
5646
                }
5647
            } elseif (strstr($basename, 'sf_user_')) {
5648
                $userInfo = api_get_user_info(substr($basename, 8));
5649
                $icon = $userInfo['avatar_small'];
5650
                $basename = get_lang('UserFolder').' '.$userInfo['complete_name'];
5651
                $user_image = true;
5652
            } elseif (strstr($path, 'shared_folder_session_')) {
5653
                $sessionName = api_get_session_name($sessionId);
5654
                if ($isAllowedToEdit) {
5655
                    $basename = '***('.$sessionName.')*** '.get_lang('HelpUsersFolder');
5656
                } else {
5657
                    $basename = get_lang('UserFolders').' ('.$sessionName.')';
5658
                }
5659
                $icon = 'folder_users.png';
5660
            } else {
5661
                $icon = 'folder_document.png';
5662
5663
                if ($path == '/audio') {
5664
                    $icon = 'folder_audio.png';
5665
                    if ($isAllowedToEdit) {
5666
                        $basename = get_lang('HelpDefaultDirDocuments');
5667
                    } else {
5668
                        $basename = get_lang('Audio');
5669
                    }
5670
                } elseif ($path == '/flash') {
5671
                    $icon = 'folder_flash.png';
5672
                    if ($isAllowedToEdit) {
5673
                        $basename = get_lang('HelpDefaultDirDocuments');
5674
                    } else {
5675
                        $basename = get_lang('Flash');
5676
                    }
5677
                } elseif ($path == '/images') {
5678
                    $icon = 'folder_images.png';
5679
                    if ($isAllowedToEdit) {
5680
                        $basename = get_lang('HelpDefaultDirDocuments');
5681
                    } else {
5682
                        $basename = get_lang('Images');
5683
                    }
5684
                } elseif ($path == '/video') {
5685
                    $icon = 'folder_video.png';
5686
                    if ($isAllowedToEdit) {
5687
                        $basename = get_lang('HelpDefaultDirDocuments');
5688
                    } else {
5689
                        $basename = get_lang('Video');
5690
                    }
5691
                } elseif ($path == '/images/gallery') {
5692
                    $icon = 'folder_gallery.png';
5693
                    if ($isAllowedToEdit) {
5694
                        $basename = get_lang('HelpDefaultDirDocuments');
5695
                    } else {
5696
                        $basename = get_lang('Gallery');
5697
                    }
5698
                } elseif ($path == '/chat_files') {
5699
                    $icon = 'folder_chat.png';
5700
                    if ($isAllowedToEdit) {
5701
                        $basename = get_lang('HelpFolderChat');
5702
                    } else {
5703
                        $basename = get_lang('ChatFiles');
5704
                    }
5705
                } elseif ($path == '/learning_path') {
5706
                    $icon = 'folder_learningpath.png';
5707
                    if ($isAllowedToEdit) {
5708
                        $basename = get_lang('HelpFolderLearningPaths');
5709
                    } else {
5710
                        $basename = get_lang('LearningPaths');
5711
                    }
5712
                }
5713
            }
5714
        }
5715
5716
        if ($user_image) {
5717
            return Display::img($icon, $basename, [], false);
5718
        }
5719
5720
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5721
    }
5722
5723
    /**
5724
     * Creates the row of edit icons for a file/folder.
5725
     *
5726
     * @param array $document_data
5727
     * @param int   $id
5728
     * @param bool  $is_template
5729
     * @param int   $is_read_only
5730
     * @param int   $visibility    (1/0)
5731
     *
5732
     * @return string html img tags with hyperlinks
5733
     */
5734
    public static function build_edit_icons($document_data, $id, $is_template, $is_read_only, $visibility)
5735
    {
5736
        $sessionId = api_get_session_id();
5737
        $courseParams = api_get_cidreq();
5738
        $document_id = $document_data['id'];
5739
        $type = $document_data['filetype'];
5740
        $is_read_only = $document_data['readonly'];
5741
        $path = $document_data['path'];
5742
5743
        $secToken = Security::getTokenFromSession();
5744
5745
        if ($type == 'link') {
5746
            $parent_id = self::get_document_id(
5747
                api_get_course_info(),
5748
                rtrim($path, '/'),
5749
                0
5750
            );
5751
        } else {
5752
            $parent_id = self::get_document_id(
5753
                api_get_course_info(),
5754
                dirname($path),
5755
                0
5756
            );
5757
        }
5758
5759
        if (empty($parent_id) && !empty($sessionId)) {
5760
            $parent_id = self::get_document_id(
5761
                api_get_course_info(),
5762
                dirname($path),
5763
                $sessionId
5764
            );
5765
        }
5766
5767
        $curdirpath = dirname($document_data['path']);
5768
        $is_certificate_mode = self::is_certificate_mode($path);
5769
        $curdirpath = urlencode($curdirpath);
5770
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5771
        //@todo Implement remote support for converter
5772
        $usePpt2lp = api_get_setting('service_ppt2lp', 'active') == 'true' && api_get_setting('service_ppt2lp', 'host') == 'localhost';
5773
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5774
        $formatType = current($formatTypeList);
5775
5776
        // If document is read only *or* we're in a session and the document
5777
        // is from a non-session context, hide the edition capabilities
5778
        $modify_icons = [];
5779
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5780
5781
        $hook = HookDocumentItemAction::create();
5782
        if (!empty($hook)) {
5783
            $hook->setEventData($document_data);
5784
            $data = $hook->notifyDocumentItemAction(HOOK_EVENT_TYPE_PRE);
5785
            if (isset($data['actions'])) {
5786
                foreach ($data['actions'] as $action) {
5787
                    $modify_icons[] = $action;
5788
                }
5789
            }
5790
        }
5791
5792
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5793
        $modify_icons[] = self::getButtonVisibility(
5794
            $is_read_only,
5795
            $visibility,
5796
            $document_data,
5797
            $is_certificate_mode,
5798
            $parent_id
5799
        );
5800
        $modify_icons[] = self::getButtonDelete(
5801
            $is_read_only,
5802
            $document_data,
5803
            $is_certificate_mode,
5804
            $curdirpath,
5805
            $parent_id
5806
        );
5807
5808
        if (!$is_read_only) {
5809
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5810
            // @TODO: add prompt to select a format target
5811
            if (!in_array($path, self::get_system_folders())) {
5812
                if ($usePpt2lp && $formatType) {
5813
                    $modify_icons[] = Display::url(
5814
                        Display::return_icon('convert.png', get_lang('Convert')),
5815
                        '#',
5816
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5817
                    );
5818
                }
5819
            }
5820
        }
5821
5822
        if ($type == 'file' && ($extension == 'html' || $extension == 'htm')) {
5823
            if ($is_template == 0) {
5824
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] != '/certificates') || !isset($_GET['curdirpath'])) {
5825
                    $modify_icons[] = Display::url(
5826
                        Display::return_icon('wizard.png', get_lang('AddAsTemplate')),
5827
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id&sec_token=$secToken"
5828
                    );
5829
                }
5830
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] == '/certificates') || $is_certificate_mode) {//allow attach certificate to course
5831
                    $visibility_icon_certificate = 'nocertificate';
5832
                    if (self::get_default_certificate_id(api_get_course_id()) == $id) {
5833
                        $visibility_icon_certificate = 'certificate';
5834
                        $certificate = get_lang('DefaultCertificate');
5835
                        $preview = get_lang('PreviewCertificate');
5836
                        $is_preview = true;
5837
                    } else {
5838
                        $is_preview = false;
5839
                        $certificate = get_lang('NoDefaultCertificate');
5840
                    }
5841
                    if (isset($_GET['selectcat'])) {
5842
                        $modify_icons[] = Display::url(
5843
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5844
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id&sec_token=$secToken"
5845
                        );
5846
                        if ($is_preview) {
5847
                            $modify_icons[] = Display::url(
5848
                                Display::return_icon('preview_view.png', $preview),
5849
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id&sec_token=$secToken"
5850
                            );
5851
                        }
5852
                    }
5853
                }
5854
            } else {
5855
                $modify_icons[] = Display::url(
5856
                    Display::return_icon('wizard_na.png', get_lang('RemoveAsTemplate')),
5857
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id&sec_token=$secToken"
5858
                );
5859
            }
5860
5861
            $modify_icons[] = Display::url(
5862
                Display::return_icon('pdf.png', get_lang('Export2PDF')),
5863
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath&sec_token=$secToken"
5864
            );
5865
        }
5866
        if ($type == 'file') {
5867
            $randomUploadName = md5(uniqid(mt_rand(), true));
5868
            $modify_icons[] = Display::url(
5869
                Display::return_icon('upload_file.png', get_lang('ReplaceFile')),
5870
                "#!",
5871
                [
5872
                    'data-id' => $randomUploadName,
5873
                    'class' => 'removeHiddenFile',
5874
                ]
5875
            );
5876
            $form = new FormValidator(
5877
                'upload',
5878
                'POST',
5879
                api_get_self().'?'.api_get_cidreq()."&sec_token=$secToken",
5880
                '',
5881
                ['enctype' => 'multipart/form-data']
5882
            );
5883
            $form->addElement('html', "<div class='replaceIndividualFile upload_element_".$randomUploadName." hidden'>");
5884
            $form->addElement('hidden', 'id_'.$randomUploadName, $randomUploadName);
5885
            $form->addElement('hidden', 'currentFile', '');
5886
            $form->addElement('hidden', 'currentUrl', api_get_self().'?'.api_get_cidreq().'&id='.$document_id);
5887
            $form->addElement('hidden', 'id_'.$randomUploadName, $document_id);
5888
            $label = '';
5889
            $form->addElement('file', 'file_'.$randomUploadName, [get_lang('File'), $label], 'style="width: 250px" id="user_upload"');
5890
            $form->addButtonSend(get_lang('SendDocument'), 'submitDocument');
5891
            $form->addProgress('DocumentUpload', 'file');
5892
            $form->addElement('html', '</div>');
5893
5894
            $html = $form->returnForm();
5895
5896
            $modify_icons[] = $html;
5897
        }
5898
5899
        // Specific case to remove action icons for students on files in the Chat conversation history inside a group -refs BT#21165
5900
        if (strpos($document_data['path'], 'chat_files') !== false && $document_data['filetype'] === 'file' && api_is_student()) {
5901
            $modify_icons = [];
5902
        }
5903
5904
        return implode(PHP_EOL, $modify_icons);
5905
    }
5906
5907
    /**
5908
     * @param $folders
5909
     * @param $curdirpath
5910
     * @param $move_file
5911
     * @param string $group_dir
5912
     *
5913
     * @return string
5914
     */
5915
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5916
    {
5917
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5918
5919
        // Form title
5920
        $form->addHidden('move_file', $move_file);
5921
5922
        $options = [];
5923
5924
        // Group documents cannot be uploaded in the root
5925
        if ($group_dir == '') {
5926
            if ($curdirpath != '/') {
5927
                $options['/'] = get_lang('Documents');
5928
            }
5929
5930
            if (is_array($folders)) {
5931
                foreach ($folders as &$folder) {
5932
                    // Hide some folders
5933
                    if ($folder == '/HotPotatoes_files' ||
5934
                        $folder == '/certificates' ||
5935
                        basename($folder) == 'css'
5936
                    ) {
5937
                        continue;
5938
                    }
5939
                    // Admin setting for Hide/Show the folders of all users
5940
                    if (api_get_setting('show_users_folders') == 'false' &&
5941
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5942
                    ) {
5943
                        continue;
5944
                    }
5945
5946
                    // Admin setting for Hide/Show Default folders to all users
5947
                    if (api_get_setting('show_default_folders') == 'false' &&
5948
                        (
5949
                            $folder == '/images' ||
5950
                            $folder == '/flash' ||
5951
                            $folder == '/audio' ||
5952
                            $folder == '/video' ||
5953
                            strstr($folder, '/images/gallery') ||
5954
                            $folder == '/video/flv'
5955
                        )
5956
                    ) {
5957
                        continue;
5958
                    }
5959
5960
                    // Admin setting for Hide/Show chat history folder
5961
                    if (api_get_setting('show_chat_folder') == 'false' &&
5962
                        $folder == '/chat_files') {
5963
                        continue;
5964
                    }
5965
5966
                    // You cannot move a file to:
5967
                    // 1. current directory
5968
                    // 2. inside the folder you want to move
5969
                    // 3. inside a subfolder of the folder you want to move
5970
                    if (($curdirpath != $folder) &&
5971
                        ($folder != $move_file) &&
5972
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5973
                    ) {
5974
                        // If document title is used, we have to display titles instead of real paths...
5975
                        $path_displayed = self::get_titles_of_path($folder);
5976
                        if (empty($path_displayed)) {
5977
                            $path_displayed = get_lang('Untitled');
5978
                        }
5979
                        $options[$folder] = $path_displayed;
5980
                    }
5981
                }
5982
            }
5983
        } else {
5984
            $firstPath = '';
5985
            foreach ($folders as $folder) {
5986
                if (($curdirpath != $folder) &&
5987
                    ($folder != $move_file) &&
5988
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5989
                ) {
5990
                    // Cannot copy dir into his own subdir
5991
                    $path_displayed = self::get_titles_of_path($folder);
5992
                    if ($firstPath == '' && $path_displayed != '') {
5993
                        $firstPath = $path_displayed;
5994
                        $group_dir = $firstPath;
5995
                    }
5996
                    $display_folder = substr($path_displayed, strlen($group_dir));
5997
                    if ($display_folder == '') {
5998
                        $display_folder = get_lang('Documents');
5999
                    }
6000
                    $options[$folder] = $display_folder;
6001
                }
6002
            }
6003
        }
6004
        $form->addElement('select', 'move_to', get_lang('MoveTo'), $options);
6005
        $form->addButtonNext(get_lang('MoveElement'), 'move_file_submit');
6006
6007
        return $form->returnForm();
6008
    }
6009
6010
    /**
6011
     * Gets the path translated with title of docs and folders.
6012
     *
6013
     * @param string $path the real path
6014
     *
6015
     * @return the path which should be displayed
6016
     */
6017
    public static function get_titles_of_path($path)
6018
    {
6019
        global $tmp_folders_titles;
6020
        $course_id = api_get_course_int_id();
6021
        $nb_slashes = substr_count($path, '/');
6022
        $current_slash_pos = 0;
6023
        $path_displayed = '';
6024
        for ($i = 0; $i < $nb_slashes; $i++) {
6025
            // For each folder of the path, retrieve title.
6026
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
6027
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
6028
6029
            if (empty($tmp_path)) {
6030
                // If empty, then we are in the final part of the path
6031
                $tmp_path = $path;
6032
            }
6033
6034
            if (!empty($tmp_folders_titles[$tmp_path])) {
6035
                // If this path has soon been stored here we don't need a new query
6036
                $path_displayed .= $tmp_folders_titles[$tmp_path];
6037
            } else {
6038
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
6039
                        WHERE c_id = '.$course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
6040
                $rs = Database::query($sql);
6041
                $tmp_title = '/'.Database::result($rs, 0, 0);
6042
                $path_displayed .= $tmp_title;
6043
                $tmp_folders_titles[$tmp_path] = $tmp_title;
6044
            }
6045
        }
6046
6047
        return $path_displayed;
6048
    }
6049
6050
    /**
6051
     * Creates form that asks for the directory name.
6052
     *
6053
     * @return string html-output text for the form
6054
     */
6055
    public static function create_dir_form($dirId)
6056
    {
6057
        global $document_id;
6058
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
6059
        $form->addElement('hidden', 'create_dir', 1);
6060
        $form->addElement('hidden', 'dir_id', intval($document_id));
6061
        $form->addElement('hidden', 'id', intval($dirId));
6062
        $form->addElement('header', get_lang('CreateDir'));
6063
        $form->addText('dirname', get_lang('NewDir'), ['autofocus' => 'autofocus']);
6064
        $form->addButtonCreate(get_lang('CreateFolder'));
6065
6066
        return $form->returnForm();
6067
    }
6068
6069
    /**
6070
     * Checks whether the user is in shared folder.
6071
     *
6072
     * @param string $curdirpath
6073
     * @param int    $sessionId
6074
     *
6075
     * @return bool Return true when user is into shared folder
6076
     */
6077
    public static function is_shared_folder($curdirpath, $sessionId)
6078
    {
6079
        $clean_curdirpath = Security::remove_XSS($curdirpath);
6080
        if ($clean_curdirpath == '/shared_folder') {
6081
            return true;
6082
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$sessionId) {
6083
            return true;
6084
        }
6085
6086
        return false;
6087
    }
6088
6089
    /**
6090
     * Checks whether the user is into any user shared folder.
6091
     *
6092
     * @param string $path
6093
     * @param int    $sessionId
6094
     *
6095
     * @return bool Return true when user is in any user shared folder
6096
     */
6097
    public static function is_any_user_shared_folder($path, $sessionId)
6098
    {
6099
        $clean_path = Security::remove_XSS($path);
6100
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
6101
            return true;
6102
        } elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
6103
            return true;
6104
        }
6105
6106
        return false;
6107
    }
6108
6109
    /**
6110
     * Create users shared folder for course.
6111
     *
6112
     * @param int $userId
6113
     * @param int $sessionId
6114
     */
6115
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
6116
    {
6117
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
6118
        $userInfo = api_get_user_info($userId);
6119
6120
        if (!$sessionId) {
6121
            //Create shared folder. Necessary for recycled courses.
6122
            if (!file_exists($documentDirectory.'/shared_folder')) {
6123
                create_unexisting_directory(
6124
                    $courseInfo,
6125
                    $userId,
6126
                    0,
6127
                    0,
6128
                    0,
6129
                    $documentDirectory,
6130
                    '/shared_folder',
6131
                    get_lang('UserFolders'),
6132
                    0,
6133
                    false,
6134
                    false
6135
                );
6136
            }
6137
            // Create dynamic user shared folder
6138
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
6139
                create_unexisting_directory(
6140
                    $courseInfo,
6141
                    $userId,
6142
                    0,
6143
                    0,
6144
                    0,
6145
                    $documentDirectory,
6146
                    '/shared_folder/sf_user_'.$userId,
6147
                    $userInfo['complete_name'],
6148
                    1,
6149
                    false,
6150
                    false
6151
                );
6152
            }
6153
6154
            return;
6155
        }
6156
6157
        // Create shared folder session.
6158
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
6159
            create_unexisting_directory(
6160
                $courseInfo,
6161
                api_get_user_id(),
6162
                $sessionId,
6163
                0,
6164
                0,
6165
                $documentDirectory,
6166
                '/shared_folder_session_'.$sessionId,
6167
                get_lang('UserFolders').' ('.api_get_session_name($sessionId).')',
6168
                0,
6169
                false,
6170
                false
6171
            );
6172
        }
6173
        //Create dynamic user shared folder into a shared folder session
6174
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
6175
            create_unexisting_directory(
6176
                $courseInfo,
6177
                $userId,
6178
                $sessionId,
6179
                0,
6180
                0,
6181
                $documentDirectory,
6182
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
6183
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
6184
                1,
6185
                false,
6186
                false
6187
            );
6188
        }
6189
    }
6190
6191
    /**
6192
     * Checks whether the user is into his shared folder or into a subfolder.
6193
     *
6194
     * @param int    $user_id
6195
     * @param string $path
6196
     * @param int    $sessionId
6197
     *
6198
     * @return bool Return true when user is in his user shared folder or into a subfolder
6199
     */
6200
    public static function is_my_shared_folder($user_id, $path, $sessionId)
6201
    {
6202
        $clean_path = Security::remove_XSS($path).'/';
6203
        $user_id = (int) $user_id;
6204
        //for security does not remove the last slash
6205
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
6206
        //for security does not remove the last slash
6207
        $main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
6208
6209
        if (preg_match($main_user_shared_folder, $clean_path)) {
6210
            return true;
6211
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
6212
            return true;
6213
        } else {
6214
            return false;
6215
        }
6216
    }
6217
6218
    public static function isBasicCourseFolder($path, $sessionId)
6219
    {
6220
        $cleanPath = Security::remove_XSS($path);
6221
        $basicCourseFolder = '/basic-course-documents__'.$sessionId.'__0';
6222
6223
        return $cleanPath == $basicCourseFolder;
6224
    }
6225
6226
    /**
6227
     * Check if the file name or folder searched exist.
6228
     *
6229
     * @return bool Return true when exist
6230
     */
6231
    public static function searchKeyword($name, $keyword)
6232
    {
6233
        if (api_strripos($name, $keyword) !== false) {
6234
            return true;
6235
        }
6236
6237
        return false;
6238
    }
6239
6240
    /**
6241
     * Checks whether a document can be previewed by using the browser.
6242
     *
6243
     * @param string $file_extension the filename extension of the document (it must be in lower case)
6244
     *
6245
     * @return bool returns TRUE or FALSE
6246
     */
6247
    public static function isBrowserViewable($file_extension)
6248
    {
6249
        static $allowed_extensions = [
6250
            'htm', 'html', 'xhtml',
6251
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
6252
            'pdf', 'svg', 'swf',
6253
            'txt', 'log',
6254
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
6255
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar',
6256
        ];
6257
6258
        /*
6259
          //TODO: make a admin switch to strict mode
6260
          1. global default $allowed_extensions
6261
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
6262
          return true;
6263
          }
6264
          2. check native support
6265
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
6266
         */
6267
6268
        if (!($result = in_array($file_extension, $allowed_extensions))) {
6269
            // Assignment + a logical check.
6270
            return false;
6271
        }
6272
6273
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
6274
        if ($file_extension == "pdf") {
6275
            return api_browser_support('pdf');
6276
        } elseif ($file_extension == "mp3") {
6277
            return api_browser_support('mp3');
6278
        } elseif ($file_extension == "mp4") {
6279
            return api_browser_support('mp4');
6280
        } elseif ($file_extension == "ogg" || $file_extension == "ogx" || $file_extension == "ogv" || $file_extension == "oga") {
6281
            return api_browser_support('ogg');
6282
        } elseif ($file_extension == "svg") {
6283
            return api_browser_support('svg');
6284
        } elseif ($file_extension == "mpg" || $file_extension == "mpeg") {
6285
            return api_browser_support('mpg');
6286
        } elseif ($file_extension == "mov") {
6287
            return api_browser_support('mov');
6288
        } elseif ($file_extension == "wav") {
6289
            return api_browser_support('wav');
6290
        } elseif ($file_extension == "mid" || $file_extension == "kar") {
6291
            return api_browser_support('mid');
6292
        } elseif ($file_extension == "avi") {
6293
            return api_browser_support('avi');
6294
        } elseif ($file_extension == "wma") {
6295
            return api_browser_support('wma');
6296
        } elseif ($file_extension == "wmv") {
6297
            return api_browser_support('wmv');
6298
        } elseif ($file_extension == "tif" || $file_extension == "tiff") {
6299
            return api_browser_support('tif');
6300
        } elseif ($file_extension == "mov") {
6301
            return api_browser_support('mov');
6302
        } elseif ($file_extension == "au") {
6303
            return api_browser_support('au');
6304
        } elseif ($file_extension == "webm") {
6305
            return api_browser_support('webm');
6306
        }
6307
6308
        return $result;
6309
    }
6310
6311
    /**
6312
     * @param array $courseInfo
6313
     * @param int   $sessionId
6314
     *
6315
     * @return array
6316
     */
6317
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
6318
    {
6319
        $table = Database::get_course_table(TABLE_DOCUMENT);
6320
        $courseId = $courseInfo['real_id'];
6321
        $sessionCondition = api_get_session_condition($sessionId);
6322
        $sql = "SELECT * FROM $table
6323
                WHERE
6324
                  path LIKE '%DELETED%' AND
6325
                  c_id = $courseId
6326
                  $sessionCondition
6327
                ORDER BY path
6328
        ";
6329
6330
        $result = Database::query($sql);
6331
        $files = [];
6332
        while ($document = Database::fetch_array($result, 'ASSOC')) {
6333
            $files[] = $document;
6334
        }
6335
6336
        return $files;
6337
    }
6338
6339
    /**
6340
     * @param int   $id
6341
     * @param array $courseInfo
6342
     * @param int   $sessionId
6343
     *
6344
     * @return array
6345
     */
6346
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
6347
    {
6348
        if (empty($courseInfo)) {
6349
            return false;
6350
        }
6351
6352
        $table = Database::get_course_table(TABLE_DOCUMENT);
6353
        $courseId = $courseInfo['real_id'];
6354
        $id = (int) $id;
6355
        $sessionCondition = api_get_session_condition($sessionId);
6356
        $sql = "SELECT * FROM $table
6357
                WHERE
6358
                  path LIKE '%DELETED%' AND
6359
                  id = $id AND
6360
                  c_id = $courseId
6361
                  $sessionCondition
6362
                LIMIT 1
6363
        ";
6364
        $result = Database::query($sql);
6365
        if (Database::num_rows($result)) {
6366
            $result = Database::fetch_array($result, 'ASSOC');
6367
6368
            return $result;
6369
        }
6370
6371
        return [];
6372
    }
6373
6374
    /**
6375
     * @param int   $id
6376
     * @param array $courseInfo
6377
     * @param int   $sessionId
6378
     *
6379
     * @return bool
6380
     */
6381
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
6382
    {
6383
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6384
        if (!empty($document)) {
6385
            $path = $document['path'];
6386
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6387
            my_delete($coursePath.$path);
6388
            // Hard delete.
6389
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
6390
6391
            return true;
6392
        }
6393
6394
        return false;
6395
    }
6396
6397
    /**
6398
     * @param array $courseInfo
6399
     * @param int   $sessionId
6400
     */
6401
    public static function purgeDocuments($courseInfo, $sessionId)
6402
    {
6403
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6404
        foreach ($files as $file) {
6405
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
6406
        }
6407
    }
6408
6409
    /**
6410
     * @param int   $id
6411
     * @param array $courseInfo
6412
     * @param int   $sessionId
6413
     */
6414
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
6415
    {
6416
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6417
        if (!empty($document)) {
6418
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6419
6420
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
6421
                self::file_send_for_download($coursePath.$document['path'], true);
6422
                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...
6423
            }
6424
        }
6425
    }
6426
6427
    /**
6428
     * @param array $courseInfo
6429
     * @param int   $sessionId
6430
     *
6431
     * @return bool
6432
     */
6433
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
6434
    {
6435
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6436
6437
        if (empty($files)) {
6438
            return false;
6439
        }
6440
6441
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
6442
6443
        // Creating a ZIP file.
6444
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().'.zip';
6445
        $zip = new PclZip($tempZipFile);
6446
        foreach ($files as $file) {
6447
            $zip->add(
6448
                $coursePath.$file['path'],
6449
                PCLZIP_OPT_REMOVE_PATH,
6450
                $coursePath
6451
            );
6452
        }
6453
6454
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
6455
            self::file_send_for_download($tempZipFile, true);
6456
            @unlink($tempZipFile);
6457
            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...
6458
        }
6459
    }
6460
6461
    /**
6462
     * Delete documents from a session in a course.
6463
     *
6464
     * @param array $courseInfo
6465
     * @param int   $sessionId
6466
     *
6467
     * @return bool
6468
     */
6469
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
6470
    {
6471
        if (empty($courseInfo)) {
6472
            return false;
6473
        }
6474
6475
        if (empty($sessionId)) {
6476
            return false;
6477
        }
6478
6479
        $itemPropertyTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
6480
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
6481
6482
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
6483
        $courseId = $courseInfo['real_id'];
6484
6485
        // get invisible folders
6486
        $sql = "SELECT DISTINCT d.id, path
6487
                FROM $itemPropertyTable i
6488
                INNER JOIN $documentTable d
6489
                ON (i.c_id = d.c_id)
6490
                WHERE
6491
                    d.id = i.ref AND
6492
                    i.tool = '".TOOL_DOCUMENT."'
6493
                    $conditionSession AND
6494
                    i.c_id = $courseId AND
6495
                    d.c_id = $courseId ";
6496
6497
        $result = Database::query($sql);
6498
        $documents = Database::store_result($result, 'ASSOC');
6499
        if ($documents) {
6500
            $course_dir = $courseInfo['directory'].'/document';
6501
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
6502
            $base_work_dir = $sys_course_path.$course_dir;
6503
6504
            foreach ($documents as $document) {
6505
                $documentId = $document['id'];
6506
                self::delete_document(
6507
                    $courseInfo,
6508
                    null,
6509
                    $base_work_dir,
6510
                    $sessionId,
6511
                    $documentId
6512
                );
6513
            }
6514
        }
6515
6516
        $sql = "DELETE FROM $documentTable
6517
                WHERE c_id = $courseId AND session_id = $sessionId";
6518
        Database::query($sql);
6519
6520
        $sql = "DELETE FROM $itemPropertyTable
6521
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
6522
        Database::query($sql);
6523
    }
6524
6525
    /**
6526
     * Update the file or directory path in the document db document table.
6527
     *
6528
     * @author - Hugues Peeters <[email protected]>
6529
     *
6530
     * @param string $action     - action type require : 'delete' or 'update'
6531
     * @param string $old_path   - old path info stored to change
6532
     * @param string $new_path   - new path info to substitute
6533
     * @param int    $documentId - iid of specific document
6534
     *
6535
     * @desc Update the file or directory path in the document db document table
6536
     */
6537
    public static function updateDbInfo($action, $old_path, $new_path = '', $documentId = 0)
6538
    {
6539
        $documentId = (int) $documentId;
6540
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
6541
        $course_id = api_get_course_int_id();
6542
        $old_path = Database::escape_string($old_path);
6543
        switch ($action) {
6544
            case 'delete':
6545
                $query = "DELETE FROM $dbTable
6546
                          WHERE
6547
                            c_id = $course_id AND
6548
                            (
6549
                                path LIKE BINARY '".$old_path."' OR
6550
                                path LIKE BINARY '".$old_path."/%'
6551
                            )";
6552
                Database::query($query);
6553
                break;
6554
            case 'update':
6555
                if ($new_path[0] == '.') {
6556
                    $new_path = substr($new_path, 1);
6557
                }
6558
                $new_path = str_replace('//', '/', $new_path);
6559
6560
                // Attempt to update	- tested & working for root	dir
6561
                $new_path = Database::escape_string($new_path);
6562
                $query = "UPDATE $dbTable SET
6563
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
6564
                          WHERE
6565
                                c_id = $course_id AND
6566
                                (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
6567
                if ($documentId != 0) {
6568
                    $query .= " AND iid = $documentId";
6569
                }
6570
                Database::query($query);
6571
                break;
6572
        }
6573
    }
6574
6575
    /**
6576
     * This function calculates the resized width and resized heigt
6577
     * according to the source and target widths
6578
     * and heights, height so that no distortions occur
6579
     * parameters.
6580
     *
6581
     * @param $image = the absolute path to the image
0 ignored issues
show
Documentation Bug introduced by
The doc comment = at position 0 could not be parsed: Unknown type name '=' at position 0 in =.
Loading history...
6582
     * @param $target_width = how large do you want your resized image
6583
     * @param $target_height = how large do you want your resized image
6584
     * @param $slideshow (default=0) =
6585
     *      indicates weither we are generating images for a slideshow or not,
6586
     *		this overrides the $_SESSION["image_resizing"] a bit so that a thumbnail
6587
     *	    view is also possible when you choose not to resize the source images
6588
     *
6589
     * @return array
6590
     */
6591
    public static function resizeImageSlideShow(
6592
        $image,
6593
        $target_width,
6594
        $target_height,
6595
        $slideshow = 0
6596
    ) {
6597
        // Modifications by Ivan Tcholakov, 04-MAY-2009.
6598
        $result = [];
6599
        $imageResize = Session::read('image_resizing');
6600
        if ($imageResize == 'resizing' || $slideshow == 1) {
6601
            $new_sizes = api_resize_image($image, $target_width, $target_height);
6602
            $result[] = $new_sizes['height'];
6603
            $result[] = $new_sizes['width'];
6604
        } else {
6605
            $size = api_getimagesize($image);
6606
            $result[] = $size['height'];
6607
            $result[] = $size['width'];
6608
        }
6609
6610
        return $result;
6611
    }
6612
6613
    /**
6614
     * Calculates the total size of a directory by adding the sizes (that
6615
     * are stored in the database) of all files & folders in this directory.
6616
     *
6617
     * @param string $value           Document path or document id
6618
     * @param bool   $canSeeInvisible
6619
     * @param bool   $byId            Default true, if is getting size by document id or false if getting by path
6620
     *
6621
     * @return int Total size
6622
     */
6623
    public static function getTotalFolderSize($value, $canSeeInvisible = false, $byId = true)
6624
    {
6625
        $table_itemproperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6626
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
6627
        $tool_document = TOOL_DOCUMENT;
6628
6629
        $course_id = api_get_course_int_id();
6630
        $session_id = api_get_session_id();
6631
        $session_condition = api_get_session_condition(
6632
            $session_id,
6633
            true,
6634
            true,
6635
            'props.session_id'
6636
        );
6637
6638
        if (empty($course_id)) {
6639
            return 0;
6640
        }
6641
6642
        $visibility_rule = ' props.visibility '.($canSeeInvisible ? '<> 2' : '= 1');
6643
6644
        if ($byId) {
6645
            $id = (int) $value;
6646
            $query = "SELECT path FROM $table_document WHERE id = $id";
6647
            $result1 = Database::query($query);
6648
            if ($result1 && Database::num_rows($result1) != 0) {
6649
                $row = Database::fetch_row($result1);
6650
                $path = $row[0];
6651
            } else {
6652
                return 0;
6653
            }
6654
        } else {
6655
            $path = Database::escape_string($value);
6656
        }
6657
6658
        $sql = "SELECT SUM(table1.size) FROM (
6659
                SELECT props.ref, size
6660
                FROM $table_itemproperty AS props
6661
                INNER JOIN $table_document AS docs
6662
                ON (docs.id = props.ref AND docs.c_id = props.c_id)
6663
                WHERE
6664
                    docs.c_id = $course_id AND
6665
                    docs.path LIKE '$path/%' AND
6666
                    props.c_id = $course_id AND
6667
                    props.tool = '$tool_document' AND
6668
                    $visibility_rule AND
6669
                    props.ref not in (
6670
                        SELECT ref
6671
                        FROM $table_itemproperty as cip
6672
                        WHERE
6673
                            cip.c_id = $course_id AND
6674
                            cip.tool = '$tool_document' AND
6675
                            cip.visibility = 2
6676
                    )
6677
                    $session_condition
6678
                GROUP BY ref
6679
            ) as table1";
6680
6681
        $result = Database::query($sql);
6682
        if ($result && Database::num_rows($result) != 0) {
6683
            $row = Database::fetch_row($result);
6684
6685
            return $row[0] == null ? 0 : $row[0];
6686
        } else {
6687
            return 0;
6688
        }
6689
    }
6690
6691
    /**
6692
     * Adds a cloud link to the database.
6693
     *
6694
     * @author - Aquilino Blanco Cores <[email protected]>
6695
     *
6696
     * @param array  $_course
6697
     * @param string $path
6698
     * @param string $url
6699
     * @param string $name
6700
     *
6701
     * @return int id of document or 0 if already exists or there was a problem creating it
6702
     */
6703
    public static function addCloudLink($_course, $path, $url, $name)
6704
    {
6705
        $file_path = $path;
6706
        if (!self::cloudLinkExists($_course, $path, $url)) {
6707
            $doc_id = add_document($_course, $file_path, 'link', 0, $name, $url);
6708
            if ($doc_id) {
6709
                // Update document item_property
6710
                api_item_property_update(
6711
                    $_course,
6712
                    TOOL_DOCUMENT,
6713
                    $doc_id,
6714
                    'DocumentAdded',
6715
                    api_get_user_id(),
6716
                    api_get_group_id(),
6717
                    api_get_user_id(),
6718
                    null,
6719
                    null,
6720
                    api_get_session_id()
6721
                );
6722
            }
6723
6724
            // If the file is in a folder, we need to update all parent folders
6725
            item_property_update_on_folder($_course, $file_path, api_get_user_id());
6726
6727
            return $doc_id;
6728
        } else {
6729
            return 0;
6730
        }
6731
    }
6732
6733
    /**
6734
     * Deletes a cloud link from the database.
6735
     *
6736
     * @author - Aquilino Blanco Cores <[email protected]>
6737
     *
6738
     * @param array  $courseInfo
6739
     * @param string $documentId
6740
     *
6741
     * @return bool true if success / false if an error occurred
6742
     */
6743
    public static function deleteCloudLink($courseInfo, $documentId)
6744
    {
6745
        if (empty($documentId) || empty($courseInfo)) {
6746
            return false;
6747
        }
6748
6749
        $documentId = (int) $documentId;
6750
        $fileDeletedFromDb = false;
6751
        if (!empty($documentId)) {
6752
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
6753
            // checking
6754
            $table = Database::get_course_table(TABLE_DOCUMENT);
6755
            $courseId = $courseInfo['real_id'];
6756
            $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
6757
            $result = Database::query($sql);
6758
            $exists = Database::num_rows($result) > 0;
6759
            $fileDeletedFromDb = !$exists;
6760
        }
6761
6762
        return $fileDeletedFromDb;
6763
    }
6764
6765
    /**
6766
     * Gets the id of a cloud link with a given path.
6767
     *
6768
     * @author - Aquilino Blanco Cores <[email protected]>
6769
     *
6770
     * @param array  $courseInfo
6771
     * @param string $path
6772
     * @param string $url
6773
     *
6774
     * @return int link's id / false if no link found
6775
     */
6776
    public static function getCloudLinkId($courseInfo, $path, $url)
6777
    {
6778
        $table = Database::get_course_table(TABLE_DOCUMENT);
6779
6780
        if (empty($courseInfo)) {
6781
            return false;
6782
        }
6783
6784
        $courseId = (int) $courseInfo['real_id'];
6785
        $path = Database::escape_string($path);
6786
6787
        if (substr($path, -1) != '/') {
6788
            // Add final slash to path if not present
6789
            $path .= '/';
6790
        }
6791
6792
        if (!empty($courseId) && !empty($path)) {
6793
            $sql = "SELECT id FROM $table
6794
                    WHERE
6795
                        c_id = $courseId AND
6796
                        path LIKE BINARY '$path' AND
6797
                        comment = '$url' AND
6798
                        filetype = 'link'
6799
                    LIMIT 1";
6800
            $result = Database::query($sql);
6801
            if ($result && Database::num_rows($result)) {
6802
                $row = Database::fetch_array($result);
6803
6804
                return intval($row[0]);
6805
            }
6806
        }
6807
6808
        return false;
6809
    }
6810
6811
    /**
6812
     * Checks if a cloud link exists.
6813
     *
6814
     * @author - Aquilino Blanco Cores <[email protected]>
6815
     *
6816
     * @param array  $courseInfo
6817
     * @param string $path
6818
     * @param string $url
6819
     *
6820
     * @return bool true if it exists false in other case
6821
     */
6822
    public static function cloudLinkExists($courseInfo, $path, $url)
6823
    {
6824
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6825
6826
        return $exists;
6827
    }
6828
6829
    /**
6830
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications.
6831
     *
6832
     * @author Aquilino Blanco Cores <[email protected]>
6833
     *
6834
     * @return string the well formed URLs regular expressions string
6835
     */
6836
    public static function getWellFormedUrlRegex()
6837
    {
6838
        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';
6839
    }
6840
6841
    /**
6842
     * Gets the files hosting sites' whitelist.
6843
     *
6844
     * @author Aquilino Blanco Cores <[email protected]>
6845
     *
6846
     * @return array the sites list
6847
     */
6848
    public static function getFileHostingWhiteList()
6849
    {
6850
        $links = api_get_configuration_value('documents_custom_cloud_link_list');
6851
6852
        if (!empty($links) && isset($links['links'])) {
6853
            return $links['links'];
6854
        }
6855
6856
        return [
6857
            'asuswebstorage.com',
6858
            'box.com',
6859
            'dropbox.com',
6860
            'dropboxusercontent.com',
6861
            'docs.google.com',
6862
            'drive.google.com',
6863
            'fileserve.com',
6864
            'icloud.com',
6865
            'livefilestore.com', // OneDrive
6866
            'mediafire.com',
6867
            'mega.nz',
6868
            'onedrive.live.com',
6869
            'scribd.com',
6870
            'slideshare.net',
6871
            'sharepoint.com',
6872
            'wetransfer.com',
6873
        ];
6874
    }
6875
6876
    /**
6877
     * @param int $userId
6878
     *
6879
     * @return array Example [ 0 => ['code' => 'ABC', 'directory' => 'ABC0', 'path' => '/images/gallery/test.png', 'code_path' => 'ABC:/images/gallery/test.png'], 1 => ...]
6880
     */
6881
    public static function getAllDocumentsCreatedByUser($userId)
6882
    {
6883
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6884
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
6885
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
6886
        $userId = (int) $userId;
6887
6888
        $sql = "SELECT DISTINCT c.code, c.directory, docs.path
6889
                FROM $tblItemProperty AS last
6890
                INNER JOIN $tblDocument AS docs
6891
                ON (
6892
                    docs.id = last.ref AND
6893
                    docs.c_id = last.c_id AND
6894
                    docs.filetype <> 'folder'
6895
                )
6896
                INNER JOIN $tblCourse as c
6897
                ON (
6898
                    docs.c_id = c.id
6899
                )
6900
                WHERE
6901
                    last.tool = '".TOOL_DOCUMENT."' AND
6902
                    last.insert_user_id = $userId AND
6903
                    docs.path NOT LIKE '%_DELETED_%'
6904
                ORDER BY c.directory, docs.path
6905
                ";
6906
        $result = Database::query($sql);
6907
6908
        $list = [];
6909
        if (Database::num_rows($result) != 0) {
6910
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6911
                $row['code_path'] = $row['code'].':'.$row['path'];
6912
                $list[] = $row;
6913
            }
6914
        }
6915
6916
        return $list;
6917
    }
6918
6919
    /**
6920
     * Writes the content of a sent file to an existing one in the system, backing up the previous one.
6921
     *
6922
     * @param array  $_course
6923
     * @param string $path          Path stored in the database
6924
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
6925
     * @param int    $sessionId     The ID of the session, if any
6926
     * @param int    $documentId    The document id, if available
6927
     * @param int    $groupId       iid
6928
     * @param file   $file          $_FILES content
6929
     *
6930
     * @return bool true/false
6931
     */
6932
    public static function writeContentIntoDocument(
6933
        $_course,
6934
        $path = null,
6935
        $base_work_dir = null,
6936
        $sessionId = null,
6937
        $documentId = null,
6938
        $groupId = 0,
6939
        $file = null
6940
    ) {
6941
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
6942
6943
        $documentId = (int) $documentId;
6944
        $groupId = (int) $groupId;
6945
        if (empty($groupId)) {
6946
            $groupId = api_get_group_id();
6947
        }
6948
6949
        $sessionId = (int) $sessionId;
6950
        if (empty($sessionId)) {
6951
            $sessionId = api_get_session_id();
6952
        }
6953
6954
        $course_id = $_course['real_id'];
6955
6956
        if (empty($course_id)) {
6957
            Display::addFlash(Display::return_message(get_lang('NoCourse'), 'error'));
6958
6959
            return false;
6960
        }
6961
6962
        if (empty($base_work_dir)) {
6963
            Display::addFlash(get_lang('Path'));
6964
6965
            return false;
6966
        }
6967
6968
        if (isset($file) && $file['error'] == 4) {
6969
            //no file
6970
            Display::addFlash(Display::return_message(get_lang('NoArchive'), 'error'));
6971
6972
            return false;
6973
        }
6974
6975
        if (empty($documentId)) {
6976
            $documentId = self::get_document_id($_course, $path, $sessionId);
6977
            $docInfo = self::get_document_data_by_id(
6978
                $documentId,
6979
                $_course['code'],
6980
                false,
6981
                $sessionId
6982
            );
6983
            $path = $docInfo['path'];
6984
        } else {
6985
            $docInfo = self::get_document_data_by_id(
6986
                $documentId,
6987
                $_course['code'],
6988
                false,
6989
                $sessionId
6990
            );
6991
            if (empty($docInfo)) {
6992
                Display::addFlash(Display::return_message(get_lang('ArchiveName'), 'error'));
6993
6994
                return false;
6995
            }
6996
            $path = $docInfo['path'];
6997
        }
6998
6999
        if (empty($path) || empty($docInfo) || empty($documentId)) {
7000
            $str = '';
7001
            $str .= "<br>".get_lang('Path');
7002
            $str .= "<br>".get_lang('ArchiveName');
7003
            $str .= "<br>".get_lang('NoFileSpecified');
7004
            Display::addFlash(Display::return_message($str, 'error'));
7005
7006
            return false;
7007
        }
7008
7009
        $itemInfo = api_get_item_property_info(
7010
            $_course['real_id'],
7011
            TOOL_DOCUMENT,
7012
            $documentId,
7013
            $sessionId,
7014
            $groupId
7015
        );
7016
7017
        if (empty($itemInfo)) {
7018
            Display::addFlash(Display::return_message(get_lang('NoFileSpecified'), 'error'));
7019
7020
            return false;
7021
        }
7022
7023
        // Filtering by group.
7024
        if ($itemInfo['to_group_id'] != $groupId) {
7025
            Display::addFlash(Display::return_message(get_lang("NoGroupsAvailable"), 'error'));
7026
7027
            return false;
7028
        }
7029
        $now = new DateTime();
7030
        $now = $now->format('Y_m_d__H_i_s_');
7031
7032
        $document_exists_in_disk = file_exists($base_work_dir.$path);
7033
        $new_path = $path.'_REPLACED_DATE_'.$now.'_ID_'.$documentId;
7034
7035
        $fileMoved = false;
7036
        $file_renamed_from_disk = false;
7037
7038
        $originalMime = self::file_get_mime_type($base_work_dir.$path);
7039
        $newMime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file['tmp_name']);
7040
        if ($originalMime != $newMime) {
7041
            Display::addFlash(Display::return_message(get_lang('FileError'), 'error'));
7042
7043
            return false;
7044
        }
7045
7046
        if ($document_exists_in_disk) {
7047
            // Move old file to xxx_REPLACED_DATE_#date_ID_#id (soft delete)
7048
            if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
7049
                if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
7050
                    $file_renamed_from_disk = true;
7051
                } else {
7052
                    // Couldn't rename - file permissions problem?
7053
                    Display::addFlash(Display::return_message(get_lang('ErrorImportingFile'), 'error'));
7054
                    error_log(
7055
                        __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '
7056
                        .$base_work_dir.$new_path.'. This is probably due to file permissions',
7057
                        0
7058
                    );
7059
                }
7060
            }
7061
7062
            if (move_uploaded_file($file['tmp_name'], $base_work_dir.$path)) {
7063
                //update file size into db
7064
                $size = filesize($base_work_dir.$path);
7065
                $sql = "UPDATE $TABLE_DOCUMENT
7066
                                SET size = '".$size."'
7067
                                WHERE
7068
                                    c_id = $course_id AND
7069
                                    session_id = $sessionId AND
7070
                                    id = ".$documentId;
7071
                Database::query($sql);
7072
                $fileMoved = true;
7073
            }
7074
        }
7075
        // Checking inconsistency
7076
        if ($fileMoved &&
7077
            $file_renamed_from_disk
7078
        ) {
7079
            return true;
7080
        } else {
7081
            Display::addFlash(Display::return_message(get_lang('ErrorImportingFile'), 'warning'));
7082
            //Something went wrong
7083
            //The file or directory isn't there anymore (on the filesystem)
7084
            // This means it has been removed externally. To prevent a
7085
            // blocking error from happening, we drop the related items from the
7086
            // item_property and the document table.
7087
            error_log(
7088
                __FILE__.' '.__LINE__.': System inconsistency detected. '.
7089
                'The file or directory '.$base_work_dir.$path.
7090
                ' seems to have been removed from the filesystem independently from '.
7091
                'the web platform. To restore consistency, the elements using the same '.
7092
                'path will be removed from the database',
7093
                0
7094
            );
7095
7096
            return false;
7097
        }
7098
    }
7099
7100
    /**
7101
     * It gest extra value to define if download icon is visible or not.
7102
     *
7103
     * @param $documentId
7104
     *
7105
     * @return bool
7106
     */
7107
    public static function getHideDownloadIcon($documentId)
7108
    {
7109
        $extraFieldValue = new ExtraFieldValue('document');
7110
        $extraValue = $extraFieldValue->get_values_by_handler_and_field_variable($documentId, 'can_be_downloaded');
7111
        $canBeDownloadedIcon = false;
7112
        if (!empty($extraValue) && isset($extraValue['value'])) {
7113
            $canBeDownloadedIcon = (bool) $extraValue['value'];
7114
        }
7115
7116
        return $canBeDownloadedIcon;
7117
    }
7118
7119
    /**
7120
     * Retrieves all documents in a course by their parent folder ID.
7121
     *
7122
     * @param array    $courseInfo      Information about the course.
7123
     * @param int      $parentId        The ID of the parent folder.
7124
     * @param int      $toGroupId       (Optional) The ID of the group to filter by. Default is 0.
7125
     * @param int|null $toUserId        (Optional) The ID of the user to filter by. Default is null.
7126
     * @param bool     $canSeeInvisible (Optional) Whether to include invisible documents. Default is false.
7127
     * @param bool     $search          (Optional) Whether to perform a search or fetch all documents. Default is true.
7128
     * @param int      $sessionId       (Optional) The session ID to filter by. Default is 0.
7129
     *
7130
     * @return array List of documents that match the criteria.
7131
     */
7132
    public static function getAllDocumentsByParentId(
7133
        $courseInfo,
7134
        $parentId,
7135
        $toGroupId = 0,
7136
        $toUserId = null,
7137
        $canSeeInvisible = false,
7138
        $search = true,
7139
        $sessionId = 0
7140
    ) {
7141
        if (empty($courseInfo)) {
7142
            return [];
7143
        }
7144
7145
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
7146
7147
        $parentId = (int) $parentId;
7148
7149
        $sql = "SELECT path, filetype
7150
            FROM $tblDocument
7151
            WHERE id = $parentId
7152
              AND c_id = {$courseInfo['real_id']}";
7153
7154
        $result = Database::query($sql);
7155
7156
        if ($result === false || Database::num_rows($result) == 0) {
7157
            return [];
7158
        }
7159
7160
        $parentRow = Database::fetch_array($result, 'ASSOC');
7161
        $parentPath = $parentRow['path'];
7162
        $filetype = $parentRow['filetype'];
7163
7164
        if ($filetype !== 'folder') {
7165
            return [];
7166
        }
7167
7168
        return self::getAllDocumentData(
7169
            $courseInfo,
7170
            $parentPath,
7171
            $toGroupId,
7172
            $toUserId,
7173
            $canSeeInvisible,
7174
            $search,
7175
            $sessionId
7176
        );
7177
    }
7178
7179
    /**
7180
     * Parse file information into a link.
7181
     *
7182
     * @param array  $userInfo        Current user info
7183
     * @param array  $courseInfo
7184
     * @param int    $session_id
7185
     * @param array  $resource
7186
     * @param int    $lp_id
7187
     * @param bool   $add_move_button
7188
     * @param string $target
7189
     * @param string $overwrite_url
7190
     * @param bool   $addAudioPreview
7191
     *
7192
     * @return string|null
7193
     */
7194
    private static function parseFile(
7195
        $userInfo,
7196
        $courseInfo,
7197
        $session_id,
7198
        $resource,
7199
        $lp_id,
7200
        $add_move_button,
7201
        $target,
7202
        $overwrite_url,
7203
        $addAudioPreview = false
7204
    ) {
7205
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
7206
        $web_code_path = api_get_path(WEB_CODE_PATH);
7207
7208
        $documentId = $resource['id'];
7209
        $path = $resource['path'];
7210
7211
        if (empty($path)) {
7212
            $num = 0;
7213
        } else {
7214
            $num = substr_count($path, '/') - 1;
7215
        }
7216
7217
        // It's a file.
7218
        $icon = choose_image($path);
7219
        $position = strrpos($icon, '.');
7220
        $icon = substr($icon, 0, $position).'_small.gif';
7221
        $my_file_title = Security::remove_XSS($resource['title']);
7222
        $visibility = $resource['visibility'];
7223
7224
        // If title is empty we try to use the path
7225
        if (empty($my_file_title)) {
7226
            $my_file_title = basename($path);
7227
        }
7228
7229
        if (api_get_configuration_value('save_titles_as_html')) {
7230
            $my_file_title = strip_tags($my_file_title);
7231
        }
7232
7233
        // Show the "image name" not the filename of the image.
7234
        if ($lp_id) {
7235
            // LP URL
7236
            $url = api_get_path(WEB_CODE_PATH).
7237
                'lp/lp_controller.php?'.api_get_cidreq().'&action=add_item&type='.TOOL_DOCUMENT.'&file='.$documentId.'&lp_id='.(int) $lp_id;
7238
        } else {
7239
            // Direct document URL
7240
            $url = $web_code_path.'document/document.php?cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&id='.$documentId;
7241
        }
7242
7243
        if (!empty($overwrite_url)) {
7244
            $overwrite_url = Security::remove_XSS($overwrite_url);
7245
            $url = $overwrite_url.'&cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&document_id='.$documentId;
7246
        }
7247
7248
        $img = Display::returnIconPath($icon);
7249
        if (!file_exists($img_sys_path.$icon)) {
7250
            $img = Display::returnIconPath('default_small.gif');
7251
        }
7252
7253
        $icon = '<img alt="" src="'.$img.'" title="" />';
7254
7255
        $preview = '';
7256
        if ($addAudioPreview) {
7257
            $path = api_get_path(WEB_COURSE_PATH).$courseInfo['directory'].'/document';
7258
            $resource['file_extension'] = pathinfo($resource['path'], PATHINFO_EXTENSION);
7259
            $preview = self::generateAudioPreview($path, $resource);
7260
            if (!empty($preview)) {
7261
                $preview .= '&nbsp;';
7262
                $icon = '';
7263
            }
7264
        }
7265
7266
        $link = Display::url(
7267
            $icon.'&nbsp;'.
7268
            $my_file_title,
7269
            $url,
7270
            ['target' => $target, 'class' => 'moved']
7271
        );
7272
7273
        $directUrl = $web_code_path.'document/document.php?cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&id='.$documentId;
7274
        $link .= '&nbsp;'.Display::url(
7275
                Display::return_icon('preview_view.png', get_lang('Preview')),
7276
                $directUrl,
7277
                ['target' => '_blank']
7278
            );
7279
7280
        $visibilityClass = null;
7281
        if ($visibility == 0) {
7282
            $visibilityClass = ' text-muted ';
7283
        }
7284
        $return = null;
7285
7286
        if ($lp_id == false) {
7287
            $return .= '<li class="doc_resource '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
7288
        } else {
7289
            $return .= '<li class="doc_resource lp_resource_element '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
7290
        }
7291
7292
        $return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
7293
        $return .= $preview;
7294
7295
        if ($add_move_button) {
7296
            $return .= '<a class="moved" href="#">';
7297
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
7298
            $return .= '</a> ';
7299
        }
7300
7301
        $return .= $link;
7302
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
7303
        $return .= $sessionStar;
7304
7305
        $return .= '</div></li>';
7306
7307
        return $return;
7308
    }
7309
7310
    /**
7311
     * @param int   $folderId
7312
     * @param array $resource
7313
     * @param int   $lp_id
7314
     *
7315
     * @return string|null
7316
     */
7317
    private static function parseFolder($folderId, $resource, $lp_id)
7318
    {
7319
        $title = isset($resource['title']) ? $resource['title'] : null;
7320
        $path = isset($resource['path']) ? $resource['path'] : null;
7321
7322
        if (empty($path)) {
7323
            $num = 0;
7324
        } else {
7325
            $num = substr_count($path, '/');
7326
        }
7327
7328
        // It's a folder.
7329
        //hide some folders
7330
        if (in_array(
7331
            $path,
7332
            ['/shared_folder', '/chat_files', '/HotPotatoes_files', '/css', '/certificates']
7333
        )) {
7334
            return null;
7335
        } elseif (preg_match('/_groupdocs/', $path)) {
7336
            return null;
7337
        } elseif (preg_match('/sf_user_/', $path)) {
7338
            return null;
7339
        } elseif (preg_match('/shared_folder_session_/', $path)) {
7340
            return null;
7341
        }
7342
7343
        // if in LP, hidden folder are displayed in grey
7344
        $folder_class_hidden = '';
7345
        if ($lp_id) {
7346
            if (isset($resource['visibility']) && $resource['visibility'] == 0) {
7347
                $folder_class_hidden = ' doc_folder_hidden'; // in base.css
7348
            }
7349
        }
7350
        $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
7351
        $return = null;
7352
7353
        if (empty($path)) {
7354
            $return = '<ul class="lp_resource">';
7355
        }
7356
7357
        $return .= '<li
7358
            class="doc_folder'.$folder_class_hidden.'"
7359
            id="doc_id_'.$resource['id'].'"
7360
            style="margin-left:'.($num * 18).'px;"
7361
            >';
7362
7363
        $image = Display::returnIconPath('nolines_plus.gif');
7364
        if (empty($path)) {
7365
            $image = Display::returnIconPath('nolines_minus.gif');
7366
        }
7367
        $return .= '<img
7368
            style="cursor: pointer;"
7369
            src="'.$image.'"
7370
            align="absmiddle"
7371
            id="img_'.$resource['id'].'" '.$onclick.'
7372
        >';
7373
7374
        $return .= Display::return_icon('lp_folder.gif').'&nbsp;';
7375
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.
7376
            Security::remove_XSS($title).
7377
            '</span>';
7378
        $return .= '</li>';
7379
7380
        if (empty($path)) {
7381
            if ($folderId == false) {
7382
                $return .= '<div id="res_'.$resource['id'].'" >';
7383
            } else {
7384
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
7385
            }
7386
        }
7387
7388
        return $return;
7389
    }
7390
7391
    /**
7392
     * Get the button to edit document.
7393
     *
7394
     * @param bool   $isReadOnly
7395
     * @param string $extension
7396
     * @param bool   $isCertificateMode
7397
     *
7398
     * @return string
7399
     */
7400
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
7401
    {
7402
        $extension = strtolower($extension);
7403
        $iconEn = Display::return_icon('edit.png', get_lang('Modify'));
7404
        $iconDis = Display::return_icon('edit_na.png', get_lang('Modify'));
7405
        $courseParams = api_get_cidreq();
7406
        $webOdfExtensionList = self::get_web_odf_extension_list();
7407
        $path = $documentData['path'];
7408
        $document_id = $documentData['id'];
7409
7410
        if ($isReadOnly) {
7411
            if (!api_is_course_admin() && !api_is_platform_admin()) {
7412
                return $iconDis;
7413
            }
7414
7415
            if (
7416
                $extension == 'svg' && api_browser_support('svg') &&
7417
                api_get_setting('enabled_support_svg') == 'true'
7418
            ) {
7419
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
7420
            }
7421
7422
            if (
7423
                in_array($extension, $webOdfExtensionList) &&
7424
                api_get_configuration_value('enabled_support_odf') === true
7425
            ) {
7426
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
7427
            }
7428
7429
            if (
7430
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
7431
                api_get_setting('enabled_support_pixlr') == 'true'
7432
            ) {
7433
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
7434
            }
7435
7436
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
7437
        }
7438
7439
        if (in_array($path, self::get_system_folders()) || $documentData['filetype'] === 'folder' && strpos($path, 'chat_files') !== false) {
7440
            return $iconDis;
7441
        }
7442
7443
        if ($isCertificateMode) {
7444
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id&curdirpath=/certificates");
7445
        }
7446
7447
        $sessionId = api_get_session_id();
7448
7449
        if ($sessionId && $documentData['session_id'] != $sessionId) {
7450
            return $iconDis;
7451
        }
7452
7453
        if (
7454
            $extension == 'svg' && api_browser_support('svg') &&
7455
            api_get_setting('enabled_support_svg') == 'true'
7456
        ) {
7457
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
7458
        }
7459
7460
        if (
7461
            in_array($extension, $webOdfExtensionList) &&
7462
            api_get_configuration_value('enabled_support_odf') === true
7463
        ) {
7464
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
7465
        }
7466
7467
        if (
7468
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
7469
            api_get_setting('enabled_support_pixlr') == 'true'
7470
        ) {
7471
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
7472
        }
7473
7474
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
7475
    }
7476
7477
    /**
7478
     * Get the button to move document.
7479
     *
7480
     * @param bool $isReadOnly
7481
     * @param bool $isCertificateMode
7482
     * @param int  $parentId
7483
     *
7484
     * @return string
7485
     */
7486
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
7487
    {
7488
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
7489
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
7490
7491
        if ($isReadOnly) {
7492
            return $iconDis;
7493
        }
7494
7495
        $path = $documentData['path'];
7496
        $document_id = $documentData['id'];
7497
        $sessionId = api_get_session_id();
7498
        $courseParams = api_get_cidreq();
7499
7500
        if ($isCertificateMode || in_array($path, self::get_system_folders()) || $documentData['filetype'] === 'folder' && strpos($path, 'chat_files') !== false) {
7501
            return $iconDis;
7502
        }
7503
7504
        if ($sessionId) {
7505
            if ($documentData['session_id'] != $sessionId) {
7506
                return $iconDis;
7507
            }
7508
        }
7509
7510
        $urlMoveParams = http_build_query([
7511
            'id' => $parentId,
7512
            'move' => $document_id,
7513
            'sec_token' => Security::getTokenFromSession(),
7514
        ]);
7515
7516
        return Display::url(
7517
            $iconEn,
7518
            api_get_self()."?$courseParams&$urlMoveParams"
7519
        );
7520
    }
7521
7522
    /**
7523
     * Get the button to set visibility to document.
7524
     *
7525
     * @param bool $isReadOnly
7526
     * @param int  $visibility
7527
     * @param bool $isCertificateMode
7528
     * @param int  $parentId
7529
     *
7530
     * @return string|null
7531
     */
7532
    private static function getButtonVisibility(
7533
        $isReadOnly,
7534
        $visibility,
7535
        array $documentData,
7536
        $isCertificateMode,
7537
        $parentId
7538
    ) {
7539
        $visibility_icon = $visibility == 0 ? 'invisible' : 'visible';
7540
        $visibility_command = $visibility == 0 ? 'set_visible' : 'set_invisible';
7541
        $courseParams = api_get_cidreq();
7542
7543
        if ($isReadOnly) {
7544
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
7545
                return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
7546
            }
7547
7548
            return null;
7549
        }
7550
7551
        if ($isCertificateMode) {
7552
            return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
7553
        }
7554
7555
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
7556
            $tip_visibility = $visibility_icon == 'invisible' ? get_lang('Show') : get_lang('Hide');
7557
            $secToken = Security::getTokenFromSession();
7558
7559
            return Display::url(
7560
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
7561
                api_get_self()."?$courseParams&id=$parentId&$visibility_command={$documentData['id']}&sec_token=$secToken"
7562
            );
7563
        }
7564
7565
        return null;
7566
    }
7567
7568
    /**
7569
     * GEt the button to delete a document.
7570
     *
7571
     * @param bool   $isReadOnly
7572
     * @param bool   $isCertificateMode
7573
     * @param string $curDirPath
7574
     * @param int    $parentId
7575
     *
7576
     * @return string
7577
     */
7578
    private static function getButtonDelete(
7579
        $isReadOnly,
7580
        array $documentData,
7581
        $isCertificateMode,
7582
        $curDirPath,
7583
        $parentId
7584
    ) {
7585
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
7586
        $iconDis = Display::return_icon('delete_na.png', get_lang('ThisFolderCannotBeDeleted'));
7587
        $path = $documentData['path'];
7588
        $id = $documentData['id'];
7589
        $courseParams = api_get_cidreq();
7590
7591
        if ($isReadOnly) {
7592
            return $iconDis;
7593
        }
7594
7595
        if (in_array($path, self::get_system_folders()) || $documentData['filetype'] === 'folder' && strpos($path, 'chat_files') !== false) {
7596
            return $iconDis;
7597
        }
7598
7599
        $titleToShow = addslashes(basename($documentData['title']));
7600
        $urlDeleteParams = http_build_query([
7601
            'curdirpath' => $curDirPath,
7602
            'action' => 'delete_item',
7603
            'id' => $parentId,
7604
            'deleteid' => $documentData['id'],
7605
            'sec_token' => Security::getTokenFromSession(),
7606
        ]);
7607
7608
        $btn = Display::url(
7609
            $iconEn,
7610
            '#',
7611
            [
7612
                'data-item-title' => $titleToShow,
7613
                'data-href' => api_get_self()."?$courseParams&$urlDeleteParams",
7614
                'data-toggle' => 'modal',
7615
                'data-target' => '#confirm-delete',
7616
            ]
7617
        );
7618
7619
        if (
7620
            isset($_GET['curdirpath']) &&
7621
            $_GET['curdirpath'] == '/certificates' &&
7622
            self::get_default_certificate_id(api_get_course_id()) == $id
7623
        ) {
7624
            return $btn;
7625
        }
7626
7627
        if ($isCertificateMode) {
7628
            return $btn;
7629
        }
7630
7631
        $sessionId = api_get_session_id();
7632
7633
        if ($sessionId) {
7634
            if ($documentData['session_id'] != $sessionId) {
7635
                return $iconDis;
7636
            }
7637
        }
7638
7639
        return $btn;
7640
    }
7641
7642
    /**
7643
     * Include MathJax script in document.
7644
     *
7645
     * @param string file content $content
7646
     *
7647
     * @return string file content
7648
     */
7649
    private static function includeMathJaxScript($content)
7650
    {
7651
        $scriptTag = '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/MathJax/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>';
7652
        // Find position of </body> tag
7653
        $pos = strpos($content, '</body>');
7654
        // If </body> tag found, insert the script tag before it
7655
        if ($pos !== false) {
7656
            $content = substr_replace($content, $scriptTag, $pos, 0);
7657
        } else {
7658
            // If </body> tag not found, just append the script tag at the end
7659
            $content .= $scriptTag;
7660
        }
7661
7662
        return $content;
7663
    }
7664
7665
    public static function autoResizeImageIfNeeded(int $size, string $tmpName): int
7666
    {
7667
        $resizeMax = api_get_configuration_value('wysiwyg_image_auto_resize_max');
7668
7669
        if (is_array($resizeMax)) {
7670
            if ($size > ($resizeMax['mb'] * 1024 * 1024)) {
7671
                throw new Exception(get_lang('UplFileTooBig'));
7672
            }
7673
7674
            $temp = new Image($tmpName);
7675
            $pictureInfo = $temp->get_image_info();
7676
7677
            $thumbSize = 0;
7678
7679
            if ($pictureInfo['width'] > $pictureInfo['height']) {
7680
                if ($pictureInfo['width'] > $resizeMax['w']) {
7681
                    $thumbSize = $resizeMax['w'];
7682
                }
7683
            } else {
7684
                if ($pictureInfo['height'] > $resizeMax['h']) {
7685
                    $thumbSize = $resizeMax['h'];
7686
                }
7687
            }
7688
7689
            if ($thumbSize) {
7690
                $temp->resize($thumbSize);
7691
                $temp->send_image($tmpName);
7692
7693
                return filesize($tmpName);
7694
            }
7695
        }
7696
7697
        return $size;
7698
    }
7699
}
7700