DocumentManager::createUserSharedFolder()   B
last analyzed

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

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

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