Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/inc/lib/document.lib.php (1 issue)

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