Passed
Push — 1.11.x ( a686d5...173904 )
by Julito
11:08 queued 10s
created

DocumentManager::writeContentIntoDocument()   F

Complexity

Conditions 22
Paths 160

Size

Total Lines 145
Code Lines 83

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 22
eloc 83
c 4
b 0
f 0
nc 160
nop 7
dl 0
loc 145
rs 3.6666

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

Loading history...
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
                }
840
841
                return $document_folders;
842
            } else {
843
                return false;
844
            }
845
        } else {
846
            // No invisible folders
847
            // Condition for the session
848
            $session_id = api_get_session_id();
849
            $condition_session = api_get_session_condition(
850
                $session_id,
851
                true,
852
                true, // needed to don't show files in elfinder browser
853
                'docs.session_id'
854
            );
855
856
            $visibilityCondition = 'last.visibility = 1';
857
            $fileType = "docs.filetype = 'folder' AND";
858
            if ($getInvisibleList) {
859
                $visibilityCondition = 'last.visibility = 0';
860
                $fileType = '';
861
            }
862
863
            //get visible folders
864
            $sql = "SELECT DISTINCT docs.id, path
865
                    FROM
866
                    $TABLE_ITEMPROPERTY AS last
867
                    INNER JOIN $TABLE_DOCUMENT AS docs
868
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
869
                    WHERE
870
                        $fileType
871
                        last.tool = '".TOOL_DOCUMENT."' AND
872
                        $groupCondition AND
873
                        $visibilityCondition
874
                        $show_users_condition
875
                        $condition_session AND
876
                        last.c_id = {$_course['real_id']}  AND
877
                        docs.c_id = {$_course['real_id']} ";
878
879
            $result = Database::query($sql);
880
881
            $visibleFolders = [];
882
            while ($row = Database::fetch_array($result, 'ASSOC')) {
883
                $visibleFolders[$row['id']] = $row['path'];
884
            }
885
886
            if ($getInvisibleList) {
887
                return $visibleFolders;
888
            }
889
890
            //get invisible folders
891
            $sql = "SELECT DISTINCT docs.id, path
892
                    FROM $TABLE_ITEMPROPERTY AS last
893
                    INNER JOIN $TABLE_DOCUMENT AS docs
894
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
895
                    WHERE
896
                        docs.filetype = 'folder' AND
897
                        last.tool = '".TOOL_DOCUMENT."' AND
898
                        $groupCondition AND
899
                        last.visibility = 0 $condition_session AND
900
                        last.c_id = {$_course['real_id']} AND
901
                        docs.c_id = {$_course['real_id']} ";
902
            $result = Database::query($sql);
903
            $invisibleFolders = [];
904
            while ($row = Database::fetch_array($result, 'ASSOC')) {
905
                //get visible folders in the invisible ones -> they are invisible too
906
                $sql = "SELECT DISTINCT docs.id, path
907
                        FROM $TABLE_ITEMPROPERTY AS last
908
                        INNER JOIN $TABLE_DOCUMENT AS docs
909
                        ON (docs.id = last.ref AND docs.c_id = last.c_id)
910
                        WHERE
911
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
912
                            docs.filetype = 'folder' AND
913
                            last.tool = '".TOOL_DOCUMENT."' AND
914
                            $groupCondition AND
915
                            last.visibility = 1 $condition_session AND
916
                            last.c_id = {$_course['real_id']} AND
917
                            docs.c_id = {$_course['real_id']}  ";
918
                $folder_in_invisible_result = Database::query($sql);
919
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
920
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
921
                }
922
            }
923
924
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
925
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
926
                $document_folders = array_diff($visibleFolders, $invisibleFolders);
927
                natsort($document_folders);
928
929
                return $document_folders;
930
            } elseif (is_array($visibleFolders)) {
931
                natsort($visibleFolders);
932
933
                return $visibleFolders;
934
            } else {
935
                //no visible folders found
936
                return false;
937
            }
938
        }
939
    }
940
941
    /**
942
     * This check if a document has the readonly property checked, then see if the user
943
     * is the owner of this file, if all this is true then return true.
944
     *
945
     * @param array  $_course
946
     * @param int    $user_id     id of the current user
947
     * @param string $file        path stored in the database (if not defined, $documentId must be used)
948
     * @param int    $document_id in case you dont have the file path ,
949
     *                            insert the id of the file here and leave $file in blank ''
950
     * @param bool   $to_delete
951
     * @param int    $sessionId
952
     *
953
     * @return bool true/false
954
     * */
955
    public static function check_readonly(
956
        $_course,
957
        $user_id,
958
        $file = null,
959
        $document_id = 0,
960
        $to_delete = false,
961
        $sessionId = null,
962
        $documentId = null
963
    ) {
964
        if (empty($sessionId)) {
965
            $sessionId = api_get_session_id();
966
        } else {
967
            $sessionId = intval($sessionId);
968
        }
969
970
        if (empty($document_id) || !is_numeric($document_id)) {
971
            $document_id = self::get_document_id($_course, $file, $sessionId);
972
        } else {
973
            $document_id = intval($document_id);
974
        }
975
976
        $TABLE_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
977
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
978
        $course_id = $_course['real_id'];
979
980
        if ($to_delete) {
981
            if (self::is_folder($_course, $document_id)) {
982
                if (!empty($file)) {
983
                    $path = Database::escape_string($file);
984
                    // Check
985
                    $sql = "SELECT td.id, readonly, tp.insert_user_id
986
                            FROM $TABLE_DOCUMENT td
987
                            INNER JOIN $TABLE_PROPERTY tp
988
                            ON (td.c_id = tp.c_id AND tp.ref= td.id)
989
                            WHERE
990
                                td.c_id = $course_id AND
991
                                tp.c_id = $course_id AND
992
                                td.session_id = $sessionId AND
993
                                (path='".$path."' OR path LIKE BINARY '".$path."/%' ) ";
994
                    // Get all id's of documents that are deleted
995
                    $what_to_check_result = Database::query($sql);
996
997
                    if ($what_to_check_result && Database::num_rows($what_to_check_result) != 0) {
998
                        // file with readonly set to 1 exist?
999
                        $readonly_set = false;
1000
                        while ($row = Database::fetch_array($what_to_check_result)) {
1001
                            //query to delete from item_property table
1002
                            if ($row['readonly'] == 1) {
1003
                                if (!($row['insert_user_id'] == $user_id)) {
1004
                                    $readonly_set = true;
1005
                                    break;
1006
                                }
1007
                            }
1008
                        }
1009
1010
                        if ($readonly_set) {
1011
                            return true;
1012
                        }
1013
                    }
1014
                }
1015
1016
                return false;
1017
            }
1018
        }
1019
1020
        if (!empty($document_id)) {
1021
            $sql = "SELECT a.insert_user_id, b.readonly
1022
                   FROM $TABLE_PROPERTY a
1023
                   INNER JOIN $TABLE_DOCUMENT b
1024
                   ON (a.c_id = b.c_id AND a.ref= b.id)
1025
                   WHERE
1026
            			a.c_id = $course_id AND
1027
                        b.c_id = $course_id AND
1028
            			a.ref = $document_id
1029
                    LIMIT 1";
1030
            $result = Database::query($sql);
1031
            $doc_details = Database::fetch_array($result, 'ASSOC');
1032
1033
            if ($doc_details['readonly'] == 1) {
1034
                return !($doc_details['insert_user_id'] == $user_id || api_is_platform_admin());
1035
            }
1036
        }
1037
1038
        return false;
1039
    }
1040
1041
    /**
1042
     * This check if a document is a folder or not.
1043
     *
1044
     * @param array $_course
1045
     * @param int   $id      document id
1046
     *
1047
     * @return bool true/false
1048
     * */
1049
    public static function is_folder($_course, $id)
1050
    {
1051
        $table = Database::get_course_table(TABLE_DOCUMENT);
1052
        if (empty($_course)) {
1053
            return false;
1054
        }
1055
        $course_id = $_course['real_id'];
1056
        $id = (int) $id;
1057
        $sql = "SELECT filetype FROM $table
1058
                WHERE c_id = $course_id AND id= $id";
1059
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
1060
1061
        return $result['filetype'] == 'folder';
1062
    }
1063
1064
    /**
1065
     * @param int   $document_id
1066
     * @param array $course_info
1067
     * @param int   $session_id
1068
     * @param bool  $remove_content_from_db
1069
     */
1070
    public static function deleteDocumentFromDb(
1071
        $document_id,
1072
        $course_info = [],
1073
        $session_id = 0,
1074
        $remove_content_from_db = false
1075
    ) {
1076
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1077
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
1078
1079
        // Deleting from the DB
1080
        $user_id = api_get_user_id();
1081
        $document_id = (int) $document_id;
1082
1083
        if (empty($course_info)) {
1084
            $course_info = api_get_course_info();
1085
        }
1086
1087
        if (empty($session_id)) {
1088
            $session_id = api_get_session_id();
1089
        }
1090
        // Soft DB delete
1091
        api_item_property_update(
1092
            $course_info,
1093
            TOOL_DOCUMENT,
1094
            $document_id,
1095
            'delete',
1096
            $user_id,
1097
            null,
1098
            null,
1099
            null,
1100
            null,
1101
            $session_id
1102
        );
1103
        self::delete_document_from_search_engine($course_info['code'], $document_id);
1104
        self::unset_document_as_template($document_id, $course_info['code'], $user_id);
1105
1106
        //Hard DB delete
1107
        if ($remove_content_from_db) {
1108
            $sql = "DELETE FROM $TABLE_ITEMPROPERTY
1109
                    WHERE
1110
                        c_id = {$course_info['real_id']} AND
1111
                        ref = ".$document_id." AND
1112
                        tool='".TOOL_DOCUMENT."'";
1113
            Database::query($sql);
1114
1115
            $sql = "DELETE FROM $TABLE_DOCUMENT
1116
                    WHERE c_id = {$course_info['real_id']} AND id = ".$document_id;
1117
            Database::query($sql);
1118
        }
1119
    }
1120
1121
    /**
1122
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
1123
     * Files/folders that are inside a deleted folder get visibility 2.
1124
     *
1125
     * @param array  $_course
1126
     * @param string $path          Path stored in the database
1127
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
1128
     * @param int    $sessionId     The ID of the session, if any
1129
     * @param int    $documentId    The document id, if available
1130
     * @param int    $groupId       iid
1131
     *
1132
     * @return bool true/false
1133
     *
1134
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1135
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1136
     */
1137
    public static function delete_document(
1138
        $_course,
1139
        $path = null,
1140
        $base_work_dir = null,
1141
        $sessionId = null,
1142
        $documentId = null,
1143
        $groupId = 0
1144
    ) {
1145
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1146
1147
        $documentId = (int) $documentId;
1148
        $groupId = (int) $groupId;
1149
        if (empty($groupId)) {
1150
            $groupId = api_get_group_id();
1151
        }
1152
1153
        $sessionId = (int) $sessionId;
1154
        if (empty($sessionId)) {
1155
            $sessionId = api_get_session_id();
1156
        }
1157
1158
        $course_id = $_course['real_id'];
1159
1160
        if (empty($course_id)) {
1161
            return false;
1162
        }
1163
1164
        if (empty($base_work_dir)) {
1165
            return false;
1166
        }
1167
1168
        if (empty($documentId)) {
1169
            $documentId = self::get_document_id($_course, $path, $sessionId);
1170
            $docInfo = self::get_document_data_by_id(
1171
                $documentId,
1172
                $_course['code'],
1173
                false,
1174
                $sessionId
1175
            );
1176
            $path = $docInfo['path'];
1177
        } else {
1178
            $docInfo = self::get_document_data_by_id(
1179
                $documentId,
1180
                $_course['code'],
1181
                false,
1182
                $sessionId
1183
            );
1184
            if (empty($docInfo)) {
1185
                return false;
1186
            }
1187
            $path = $docInfo['path'];
1188
        }
1189
1190
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1191
            return false;
1192
        }
1193
1194
        $itemInfo = api_get_item_property_info(
1195
            $_course['real_id'],
1196
            TOOL_DOCUMENT,
1197
            $documentId,
1198
            $sessionId,
1199
            $groupId
1200
        );
1201
1202
        if (empty($itemInfo)) {
1203
            return false;
1204
        }
1205
1206
        // File was already deleted.
1207
        if ($itemInfo['lastedit_type'] == 'DocumentDeleted' ||
1208
            $itemInfo['lastedit_type'] == 'delete' ||
1209
            $itemInfo['visibility'] == 2
1210
        ) {
1211
            return false;
1212
        }
1213
1214
        // Filtering by group.
1215
        if ($itemInfo['to_group_id'] != $groupId) {
1216
            return false;
1217
        }
1218
1219
        $document_exists_in_disk = file_exists($base_work_dir.$path);
1220
        $new_path = $path.'_DELETED_'.$documentId;
1221
1222
        $file_deleted_from_db = false;
1223
        $file_deleted_from_disk = false;
1224
        $file_renamed_from_disk = false;
1225
1226
        if ($documentId) {
1227
            // Deleting doc from the DB.
1228
            self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1229
            // Checking
1230
            // $file_exists_in_db = self::get_document_data_by_id($documentId, $_course['code']);
1231
            $file_deleted_from_db = true;
1232
        }
1233
1234
        // Looking for children.
1235
        if ($docInfo['filetype'] == 'folder') {
1236
            $cleanPath = Database::escape_string($path);
1237
1238
            // Deleted files inside this folder.
1239
            $sql = "SELECT id FROM $TABLE_DOCUMENT
1240
                    WHERE
1241
                        c_id = $course_id AND
1242
                        session_id = $sessionId AND
1243
                        path LIKE BINARY '".$cleanPath."/%'";
1244
1245
            // Get all id's of documents that are deleted.
1246
            $result = Database::query($sql);
1247
1248
            if ($result && Database::num_rows($result) != 0) {
1249
                // Recursive delete.
1250
                while ($row = Database::fetch_array($result)) {
1251
                    self::delete_document(
1252
                        $_course,
1253
                        null,
1254
                        $base_work_dir,
1255
                        $sessionId,
1256
                        $row['id'],
1257
                        $groupId
1258
                    );
1259
                }
1260
            }
1261
        }
1262
1263
        if ($document_exists_in_disk) {
1264
            if (api_get_setting('permanently_remove_deleted_files') === 'true') {
1265
                // Delete documents, do it like this so metadata gets deleted too
1266
                my_delete($base_work_dir.$path);
1267
                // Hard delete.
1268
                self::deleteDocumentFromDb($documentId, $_course, $sessionId, true);
1269
                $file_deleted_from_disk = true;
1270
            } else {
1271
                // Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
1272
                if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
1273
                    if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
1274
                        $new_path = Database::escape_string($new_path);
1275
1276
                        $sql = "UPDATE $TABLE_DOCUMENT
1277
                                SET path = '".$new_path."'
1278
                                WHERE
1279
                                    c_id = $course_id AND
1280
                                    session_id = $sessionId AND
1281
                                    id = ".$documentId;
1282
                        Database::query($sql);
1283
1284
                        // Soft delete.
1285
                        self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1286
1287
                        // Change path of sub folders and documents in database.
1288
                        $old_item_path = $docInfo['path'];
1289
                        $new_item_path = $new_path.substr($old_item_path, strlen($path));
1290
                        $new_item_path = Database::escape_string($new_item_path);
1291
1292
                        $sql = "UPDATE $TABLE_DOCUMENT
1293
                                SET path = '".$new_item_path."'
1294
                                WHERE
1295
                                    c_id = $course_id AND
1296
                                    session_id = $sessionId AND
1297
                                    id = ".$documentId;
1298
                        Database::query($sql);
1299
1300
                        $file_renamed_from_disk = true;
1301
                    } else {
1302
                        // Couldn't rename - file permissions problem?
1303
                        error_log(
1304
                            __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '.$base_work_dir.$new_path.'. This is probably due to file permissions',
1305
                            0
1306
                        );
1307
                    }
1308
                }
1309
            }
1310
        }
1311
        // Checking inconsistency
1312
        //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').')');
1313
        if ($file_deleted_from_db && $file_deleted_from_disk ||
1314
            $file_deleted_from_db && $file_renamed_from_disk
1315
        ) {
1316
            return true;
1317
        } else {
1318
            //Something went wrong
1319
            //The file or directory isn't there anymore (on the filesystem)
1320
            // This means it has been removed externally. To prevent a
1321
            // blocking error from happening, we drop the related items from the
1322
            // item_property and the document table.
1323
            error_log(
1324
                __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',
1325
                0
1326
            );
1327
1328
            return false;
1329
        }
1330
    }
1331
1332
    /**
1333
     * Removes documents from search engine database.
1334
     *
1335
     * @param string $course_id   Course code
1336
     * @param int    $document_id Document id to delete
1337
     */
1338
    public static function delete_document_from_search_engine($course_id, $document_id)
1339
    {
1340
        // remove from search engine if enabled
1341
        if (api_get_setting('search_enabled') === 'true') {
1342
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1343
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1344
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1345
            $res = Database::query($sql);
1346
            if (Database::num_rows($res) > 0) {
1347
                $row2 = Database::fetch_array($res);
1348
                $di = new ChamiloIndexer();
1349
                $di->remove_document($row2['search_did']);
1350
            }
1351
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1352
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1353
            Database::query($sql);
1354
1355
            // remove terms from db
1356
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1357
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1358
        }
1359
    }
1360
1361
    /**
1362
     * Gets the id of a document with a given path.
1363
     *
1364
     * @param array  $courseInfo
1365
     * @param string $path
1366
     * @param int    $sessionId
1367
     *
1368
     * @return int id of document / false if no doc found
1369
     */
1370
    public static function get_document_id($courseInfo, $path, $sessionId = null, $forceFileTypeFolder = false)
1371
    {
1372
        $table = Database::get_course_table(TABLE_DOCUMENT);
1373
        $courseId = $courseInfo['real_id'];
1374
1375
        if (!isset($sessionId)) {
1376
            $sessionId = api_get_session_id();
1377
        } else {
1378
            $sessionId = intval($sessionId);
1379
        }
1380
1381
        $path = Database::escape_string($path);
1382
        if (!empty($courseId) && !empty($path)) {
1383
            $folderCondition = '';
1384
            if ($forceFileTypeFolder) {
1385
                $folderCondition = ' AND filetype = "folder" ';
1386
            }
1387
            $sql = "SELECT id FROM $table
1388
                    WHERE
1389
                        c_id = $courseId AND
1390
                        path LIKE BINARY '$path' AND
1391
                        session_id = $sessionId
1392
                        $folderCondition
1393
                    LIMIT 1";
1394
1395
            $result = Database::query($sql);
1396
            if (Database::num_rows($result)) {
1397
                $row = Database::fetch_array($result);
1398
1399
                return intval($row['id']);
1400
            }
1401
        }
1402
1403
        return false;
1404
    }
1405
1406
    /**
1407
     * Gets the document data with a given id.
1408
     *
1409
     * @param int    $id            Document Id (id field in c_document table)
1410
     * @param string $course_code   Course code
1411
     * @param bool   $load_parents  load folder parents
1412
     * @param int    $session_id    The session ID,
1413
     *                              0 if requires context *out of* session, and null to use global context
1414
     * @param bool   $ignoreDeleted
1415
     *
1416
     * @return array document content
1417
     */
1418
    public static function get_document_data_by_id(
1419
        $id,
1420
        $course_code,
1421
        $load_parents = false,
1422
        $session_id = null,
1423
        $ignoreDeleted = false
1424
    ) {
1425
        $course_info = api_get_course_info($course_code);
1426
        $course_id = $course_info['real_id'];
1427
1428
        if (empty($course_info)) {
1429
            return false;
1430
        }
1431
1432
        $session_id = empty($session_id) ? api_get_session_id() : (int) $session_id;
1433
        $groupId = api_get_group_id();
1434
1435
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
1436
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1437
        $id = (int) $id;
1438
        $sessionCondition = api_get_session_condition($session_id, true, true);
1439
1440
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1441
                WHERE c_id = $course_id $sessionCondition AND id = $id";
1442
1443
        if ($ignoreDeleted) {
1444
            $sql .= " AND path NOT LIKE '%_DELETED_%' ";
1445
        }
1446
1447
        $result = Database::query($sql);
1448
        $courseParam = '&cidReq='.$course_code.'&id='.$id.'&id_session='.$session_id.'&gidReq='.$groupId;
1449
        if ($result && Database::num_rows($result) == 1) {
1450
            $row = Database::fetch_array($result, 'ASSOC');
1451
            //@todo need to clarify the name of the URLs not nice right now
1452
            $url_path = urlencode($row['path']);
1453
            $path = str_replace('%2F', '/', $url_path);
1454
            $pathinfo = pathinfo($row['path']);
1455
1456
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.$courseParam;
1457
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?id='.$id.$courseParam;
1458
            $row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1459
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1460
            $row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1461
            $row['direct_url'] = $www.$path;
1462
            $row['basename'] = basename($row['path']);
1463
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
        $info_teacher_id = UserManager::get_user_id_of_course_admin_or_session_admin($course_info);
2021
        $teacher_info = api_get_user_info($info_teacher_id);
2022
        $teacher_first_name = $teacher_info['firstname'];
2023
        $teacher_last_name = $teacher_info['lastname'];
2024
2025
        // info gradebook certificate
2026
        $info_grade_certificate = UserManager::get_info_gradebook_certificate($courseCode, $sessionId, $user_id);
2027
        $date_long_certificate = '';
2028
        $date_certificate = '';
2029
        $url = '';
2030
        if ($info_grade_certificate) {
2031
            $date_certificate = $info_grade_certificate['created_at'];
2032
            $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$info_grade_certificate['id'];
2033
        }
2034
        $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
2035
        if (!empty($date_certificate)) {
2036
            $date_long_certificate = api_convert_and_format_date($date_certificate);
2037
            $date_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_LONG_NO_DAY);
2038
        }
2039
2040
        if ($is_preview) {
2041
            $date_long_certificate = api_convert_and_format_date(api_get_utc_datetime());
2042
            $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
2043
        }
2044
2045
        $externalStyleFile = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/certificate.css';
2046
        $externalStyle = '';
2047
        if (is_file($externalStyleFile)) {
2048
            $externalStyle = file_get_contents($externalStyleFile);
2049
        }
2050
        $timeInCourse = Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $sessionId);
2051
        $timeInCourse = api_time_to_hms($timeInCourse, ':', false, true);
2052
2053
        $timeInCourseInAllSessions = 0;
2054
        $sessions = SessionManager::get_session_by_course($course_info['real_id']);
2055
2056
        if (!empty($sessions)) {
2057
            foreach ($sessions as $session) {
2058
                $timeInCourseInAllSessions += Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $session['id']);
2059
            }
2060
        }
2061
        $timeInCourseInAllSessions = api_time_to_hms($timeInCourseInAllSessions, ':', false, true);
2062
2063
        $first = Tracking::get_first_connection_date_on_the_course($user_id, $course_info['real_id'], $sessionId, false);
2064
        $first = substr($first, 0, 10);
2065
        $last = Tracking::get_last_connection_date_on_the_course($user_id, $course_info, $sessionId, false);
2066
        $last = substr($last, 0, 10);
2067
2068
        if ($first === $last) {
2069
            $startDateAndEndDate = get_lang('From').' '.$first;
2070
        } else {
2071
            $startDateAndEndDate = sprintf(
2072
                get_lang('FromDateXToDateY'),
2073
                $first,
2074
                $last
2075
            );
2076
        }
2077
        $courseDescription = new CourseDescription();
2078
        $description = $courseDescription->get_data_by_description_type(2, $course_info['real_id'], $sessionId);
2079
        $courseObjectives = '';
2080
        if ($description) {
2081
            $courseObjectives = $description['description_content'];
2082
        }
2083
2084
        // Replace content
2085
        $info_to_replace_in_content_html = [
2086
            $first_name,
2087
            $last_name,
2088
            $username,
2089
            $organization_name,
2090
            $portal_name,
2091
            $teacher_first_name,
2092
            $teacher_last_name,
2093
            $official_code,
2094
            $date_long_certificate,
2095
            $date_no_time,
2096
            $courseCode,
2097
            $course_info['name'],
2098
            isset($info_grade_certificate['grade']) ? $info_grade_certificate['grade'] : '',
2099
            $url,
2100
            '<a href="'.$url.'" target="_blank">'.get_lang('CertificateOnlineLink').'</a>',
2101
            '((certificate_barcode))',
2102
            $externalStyle,
2103
            $timeInCourse,
2104
            $timeInCourseInAllSessions,
2105
            $startDateAndEndDate,
2106
            $courseObjectives,
2107
        ];
2108
2109
        $tags = [
2110
            '((user_firstname))',
2111
            '((user_lastname))',
2112
            '((user_username))',
2113
            '((gradebook_institution))',
2114
            '((gradebook_sitename))',
2115
            '((teacher_firstname))',
2116
            '((teacher_lastname))',
2117
            '((official_code))',
2118
            '((date_certificate))',
2119
            '((date_certificate_no_time))',
2120
            '((course_code))',
2121
            '((course_title))',
2122
            '((gradebook_grade))',
2123
            '((certificate_link))',
2124
            '((certificate_link_html))',
2125
            '((certificate_barcode))',
2126
            '((external_style))',
2127
            '((time_in_course))',
2128
            '((time_in_course_in_all_sessions))',
2129
            '((start_date_and_end_date))',
2130
            '((course_objectives))',
2131
        ];
2132
2133
        if (!empty($extraFields)) {
2134
            foreach ($extraFields as $extraField) {
2135
                $valueExtra = isset($extra_user_info_data[$extraField['variable']]) ? $extra_user_info_data[$extraField['variable']] : '';
2136
                $tags[] = '(('.strtolower($extraField['variable']).'))';
2137
                $info_to_replace_in_content_html[] = $valueExtra;
2138
            }
2139
        }
2140
2141
        $info_list[] = $tags;
2142
        $info_list[] = $info_to_replace_in_content_html;
2143
2144
        return $info_list;
2145
    }
2146
2147
    /**
2148
     * Remove default certificate.
2149
     *
2150
     * @param string $course_id              The course code
2151
     * @param int    $default_certificate_id The document id of the default certificate
2152
     */
2153
    public static function remove_attach_certificate($course_id, $default_certificate_id)
2154
    {
2155
        if (empty($default_certificate_id)) {
2156
            return false;
2157
        }
2158
2159
        $default_certificate = self::get_default_certificate_id($course_id);
2160
        if ((int) $default_certificate == (int) $default_certificate_id) {
2161
            $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
2162
            $session_id = api_get_session_id();
2163
            if ($session_id == 0 || is_null($session_id)) {
2164
                $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
2165
            } elseif ($session_id > 0) {
2166
                $sql_session = 'AND session_id='.intval($session_id);
2167
            } else {
2168
                $sql_session = '';
2169
            }
2170
2171
            $sql = 'UPDATE '.$tbl_category.' SET document_id = null
2172
                    WHERE
2173
                        course_code = "'.Database::escape_string($course_id).'" AND
2174
                        document_id="'.$default_certificate_id.'" '.$sql_session;
2175
            Database::query($sql);
2176
        }
2177
    }
2178
2179
    /**
2180
     * Create directory certificate.
2181
     *
2182
     * @param array $courseInfo
2183
     */
2184
    public static function create_directory_certificate_in_course($courseInfo)
2185
    {
2186
        if (!empty($courseInfo)) {
2187
            $to_group_id = 0;
2188
            $to_user_id = null;
2189
            $course_dir = $courseInfo['path']."/document/";
2190
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
2191
            $base_work_dir = $sys_course_path.$course_dir;
2192
            $dir_name = '/certificates';
2193
            $post_dir_name = get_lang('CertificatesFiles');
2194
            $visibility_command = 'invisible';
2195
2196
            $id = self::get_document_id_of_directory_certificate();
2197
2198
            if (empty($id)) {
2199
                create_unexisting_directory(
2200
                    $courseInfo,
2201
                    api_get_user_id(),
2202
                    api_get_session_id(),
2203
                    $to_group_id,
2204
                    $to_user_id,
2205
                    $base_work_dir,
2206
                    $dir_name,
2207
                    $post_dir_name,
2208
                    null,
2209
                    false,
2210
                    false
2211
                );
2212
2213
                $id = self::get_document_id_of_directory_certificate();
2214
2215
                if (empty($id)) {
2216
                    $id = add_document(
2217
                        $courseInfo,
2218
                        $dir_name,
2219
                        'folder',
2220
                        0,
2221
                        $post_dir_name,
2222
                        null,
2223
                        0,
2224
                        true,
2225
                        $to_group_id,
2226
                        0,
2227
                        0,
2228
                        false
2229
                    );
2230
                }
2231
2232
                if (!empty($id)) {
2233
                    api_item_property_update(
2234
                        $courseInfo,
2235
                        TOOL_DOCUMENT,
2236
                        $id,
2237
                        $visibility_command,
2238
                        api_get_user_id()
2239
                    );
2240
                }
2241
            }
2242
        }
2243
    }
2244
2245
    /**
2246
     * Get the document id of the directory certificate.
2247
     *
2248
     * @return int The document id of the directory certificate
2249
     *
2250
     * @todo move to certificate.lib.php
2251
     */
2252
    public static function get_document_id_of_directory_certificate()
2253
    {
2254
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
2255
        $course_id = api_get_course_int_id();
2256
        $sql = "SELECT id FROM $tbl_document
2257
                WHERE c_id = $course_id AND path='/certificates' ";
2258
        $rs = Database::query($sql);
2259
        $row = Database::fetch_array($rs);
2260
2261
        return $row['id'];
2262
    }
2263
2264
    /**
2265
     * Check if a directory given is for certificate.
2266
     *
2267
     * @todo move to certificate.lib.php
2268
     *
2269
     * @param string $dir path of directory
2270
     *
2271
     * @return bool true if is a certificate or false otherwise
2272
     */
2273
    public static function is_certificate_mode($dir)
2274
    {
2275
        // I'm in the certification module?
2276
        $is_certificate_mode = false;
2277
        $is_certificate_array = explode('/', $dir);
2278
        array_shift($is_certificate_array);
2279
        if (isset($is_certificate_array[0]) && $is_certificate_array[0] == 'certificates') {
2280
            $is_certificate_mode = true;
2281
        }
2282
2283
        return $is_certificate_mode || (isset($_GET['certificate']) && $_GET['certificate'] === 'true');
2284
    }
2285
2286
    /**
2287
     * Gets the list of included resources as a list of absolute or relative paths from a html file or string html
2288
     * This allows for a better SCORM export or replace urls inside content html from copy course
2289
     * The list will generally include pictures, flash objects, java applets, or any other
2290
     * stuff included in the source of the current item. The current item is expected
2291
     * to be an HTML file or string html. If it is not, then the function will return and empty list.
2292
     *
2293
     * @param    string  source html (content or path)
2294
     * @param    bool    is file or string html
2295
     * @param    string    type (one of the app tools) - optional (otherwise takes the current item's type)
2296
     * @param    int        level of recursivity we're in
2297
     *
2298
     * @return array List of file paths. An additional field containing 'local' or 'remote' helps determine
2299
     *               if the file should be copied into the zip or just linked
2300
     */
2301
    public static function get_resources_from_source_html(
2302
        $source_html,
2303
        $is_file = false,
2304
        $type = null,
2305
        $recursivity = 1
2306
    ) {
2307
        $max = 5;
2308
        $attributes = [];
2309
        $wanted_attributes = [
2310
            'src',
2311
            'url',
2312
            '@import',
2313
            'href',
2314
            'value',
2315
            'flashvars',
2316
            'poster',
2317
        ];
2318
        $explode_attributes = ['flashvars' => 'file'];
2319
        $abs_path = '';
2320
2321
        if ($recursivity > $max) {
2322
            return [];
2323
        }
2324
2325
        if (!isset($type)) {
2326
            $type = TOOL_DOCUMENT;
2327
        }
2328
2329
        if (!$is_file) {
2330
            $attributes = self::parse_HTML_attributes(
2331
                $source_html,
2332
                $wanted_attributes,
2333
                $explode_attributes
2334
            );
2335
        } else {
2336
            if (is_file($source_html)) {
2337
                $abs_path = $source_html;
2338
                //for now, read the whole file in one go (that's gonna be a problem when the file is too big)
2339
                $info = pathinfo($abs_path);
2340
                $ext = $info['extension'];
2341
                switch (strtolower($ext)) {
2342
                    case 'html':
2343
                    case 'htm':
2344
                    case 'shtml':
2345
                    case 'css':
2346
                        $file_content = file_get_contents($abs_path);
2347
                        // get an array of attributes from the HTML source
2348
                        $attributes = self::parse_HTML_attributes(
2349
                            $file_content,
2350
                            $wanted_attributes,
2351
                            $explode_attributes
2352
                        );
2353
                        break;
2354
                    default:
2355
                        break;
2356
                }
2357
            } else {
2358
                return false;
2359
            }
2360
        }
2361
2362
        $files_list = [];
2363
        switch ($type) {
2364
            case TOOL_DOCUMENT:
2365
            case TOOL_QUIZ:
2366
            case 'sco':
2367
                foreach ($wanted_attributes as $attr) {
2368
                    if (isset($attributes[$attr])) {
2369
                        //find which kind of path these are (local or remote)
2370
                        $sources = $attributes[$attr];
2371
                        foreach ($sources as $source) {
2372
                            //skip what is obviously not a resource
2373
                            if (strpos($source, '+this.')) {
2374
                                continue; //javascript code - will still work unaltered
2375
                            }
2376
                            if (strpos($source, '.') === false) {
2377
                                continue; //no dot, should not be an external file anyway
2378
                            }
2379
                            if (strpos($source, 'mailto:')) {
2380
                                continue; //mailto link
2381
                            }
2382
                            if (strpos($source, ';') && !strpos($source, '&amp;')) {
2383
                                continue; //avoid code - that should help
2384
                            }
2385
2386
                            if ($attr == 'value') {
2387
                                if (strpos($source, 'mp3file')) {
2388
                                    $files_list[] = [
2389
                                        substr($source, 0, strpos($source, '.swf') + 4),
2390
                                        'local',
2391
                                        'abs',
2392
                                    ];
2393
                                    $mp3file = substr($source, strpos($source, 'mp3file=') + 8);
2394
                                    if (substr($mp3file, 0, 1) == '/') {
2395
                                        $files_list[] = [$mp3file, 'local', 'abs'];
2396
                                    } else {
2397
                                        $files_list[] = [$mp3file, 'local', 'rel'];
2398
                                    }
2399
                                } elseif (strpos($source, 'flv=') === 0) {
2400
                                    $source = substr($source, 4);
2401
                                    if (strpos($source, '&') > 0) {
2402
                                        $source = substr($source, 0, strpos($source, '&'));
2403
                                    }
2404
                                    if (strpos($source, '://') > 0) {
2405
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2406
                                            //we found the current portal url
2407
                                            $files_list[] = [$source, 'local', 'url'];
2408
                                        } else {
2409
                                            //we didn't find any trace of current portal
2410
                                            $files_list[] = [$source, 'remote', 'url'];
2411
                                        }
2412
                                    } else {
2413
                                        $files_list[] = [$source, 'local', 'abs'];
2414
                                    }
2415
                                    /* skipping anything else to avoid two entries
2416
                                    (while the others can have sub-files in their url, flv's can't)*/
2417
                                    continue;
2418
                                }
2419
                            }
2420
                            if (strpos($source, '://') > 0) {
2421
                                //cut at '?' in a URL with params
2422
                                if (strpos($source, '?') > 0) {
2423
                                    $second_part = substr($source, strpos($source, '?'));
2424
                                    if (strpos($second_part, '://') > 0) {
2425
                                        //if the second part of the url contains a url too, treat the second one before cutting
2426
                                        $pos1 = strpos($second_part, '=');
2427
                                        $pos2 = strpos($second_part, '&');
2428
                                        $second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
2429
                                        if (strpos($second_part, api_get_path(WEB_PATH)) !== false) {
2430
                                            //we found the current portal url
2431
                                            $files_list[] = [$second_part, 'local', 'url'];
2432
                                            $in_files_list[] = self::get_resources_from_source_html(
2433
                                                $second_part,
2434
                                                true,
2435
                                                TOOL_DOCUMENT,
2436
                                                $recursivity + 1
2437
                                            );
2438
                                            if (count($in_files_list) > 0) {
2439
                                                $files_list = array_merge($files_list, $in_files_list);
2440
                                            }
2441
                                        } else {
2442
                                            //we didn't find any trace of current portal
2443
                                            $files_list[] = [$second_part, 'remote', 'url'];
2444
                                        }
2445
                                    } elseif (strpos($second_part, '=') > 0) {
2446
                                        if (substr($second_part, 0, 1) === '/') {
2447
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2448
                                            $files_list[] = [$second_part, 'local', 'abs'];
2449
                                            $in_files_list[] = self::get_resources_from_source_html(
2450
                                                $second_part,
2451
                                                true,
2452
                                                TOOL_DOCUMENT,
2453
                                                $recursivity + 1
2454
                                            );
2455
                                            if (count($in_files_list) > 0) {
2456
                                                $files_list = array_merge($files_list, $in_files_list);
2457
                                            }
2458
                                        } elseif (strstr($second_part, '..') === 0) {
2459
                                            //link is relative but going back in the hierarchy
2460
                                            $files_list[] = [$second_part, 'local', 'rel'];
2461
                                            //$dir = api_get_path(SYS_CODE_PATH);//dirname($abs_path);
2462
                                            //$new_abs_path = realpath($dir.'/'.$second_part);
2463
                                            $dir = '';
2464
                                            if (!empty($abs_path)) {
2465
                                                $dir = dirname($abs_path).'/';
2466
                                            }
2467
                                            $new_abs_path = realpath($dir.$second_part);
2468
                                            $in_files_list[] = self::get_resources_from_source_html(
2469
                                                $new_abs_path,
2470
                                                true,
2471
                                                TOOL_DOCUMENT,
2472
                                                $recursivity + 1
2473
                                            );
2474
                                            if (count($in_files_list) > 0) {
2475
                                                $files_list = array_merge($files_list, $in_files_list);
2476
                                            }
2477
                                        } else {
2478
                                            //no starting '/', making it relative to current document's path
2479
                                            if (substr($second_part, 0, 2) == './') {
2480
                                                $second_part = substr($second_part, 2);
2481
                                            }
2482
                                            $files_list[] = [$second_part, 'local', 'rel'];
2483
                                            $dir = '';
2484
                                            if (!empty($abs_path)) {
2485
                                                $dir = dirname($abs_path).'/';
2486
                                            }
2487
                                            $new_abs_path = realpath($dir.$second_part);
2488
                                            $in_files_list[] = self::get_resources_from_source_html(
2489
                                                $new_abs_path,
2490
                                                true,
2491
                                                TOOL_DOCUMENT,
2492
                                                $recursivity + 1
2493
                                            );
2494
                                            if (count($in_files_list) > 0) {
2495
                                                $files_list = array_merge($files_list, $in_files_list);
2496
                                            }
2497
                                        }
2498
                                    }
2499
                                    //leave that second part behind now
2500
                                    $source = substr($source, 0, strpos($source, '?'));
2501
                                    if (strpos($source, '://') > 0) {
2502
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2503
                                            //we found the current portal url
2504
                                            $files_list[] = [$source, 'local', 'url'];
2505
                                            $in_files_list[] = self::get_resources_from_source_html(
2506
                                                $source,
2507
                                                true,
2508
                                                TOOL_DOCUMENT,
2509
                                                $recursivity + 1
2510
                                            );
2511
                                            if (count($in_files_list) > 0) {
2512
                                                $files_list = array_merge($files_list, $in_files_list);
2513
                                            }
2514
                                        } else {
2515
                                            //we didn't find any trace of current portal
2516
                                            $files_list[] = [$source, 'remote', 'url'];
2517
                                        }
2518
                                    } else {
2519
                                        //no protocol found, make link local
2520
                                        if (substr($source, 0, 1) === '/') {
2521
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2522
                                            $files_list[] = [$source, 'local', 'abs'];
2523
                                            $in_files_list[] = self::get_resources_from_source_html(
2524
                                                $source,
2525
                                                true,
2526
                                                TOOL_DOCUMENT,
2527
                                                $recursivity + 1
2528
                                            );
2529
                                            if (count($in_files_list) > 0) {
2530
                                                $files_list = array_merge($files_list, $in_files_list);
2531
                                            }
2532
                                        } elseif (strstr($source, '..') === 0) {
2533
                                            //link is relative but going back in the hierarchy
2534
                                            $files_list[] = [$source, 'local', 'rel'];
2535
                                            $dir = '';
2536
                                            if (!empty($abs_path)) {
2537
                                                $dir = dirname($abs_path).'/';
2538
                                            }
2539
                                            $new_abs_path = realpath($dir.$source);
2540
                                            $in_files_list[] = self::get_resources_from_source_html(
2541
                                                $new_abs_path,
2542
                                                true,
2543
                                                TOOL_DOCUMENT,
2544
                                                $recursivity + 1
2545
                                            );
2546
                                            if (count($in_files_list) > 0) {
2547
                                                $files_list = array_merge($files_list, $in_files_list);
2548
                                            }
2549
                                        } else {
2550
                                            //no starting '/', making it relative to current document's path
2551
                                            if (substr($source, 0, 2) == './') {
2552
                                                $source = substr($source, 2);
2553
                                            }
2554
                                            $files_list[] = [$source, 'local', 'rel'];
2555
                                            $dir = '';
2556
                                            if (!empty($abs_path)) {
2557
                                                $dir = dirname($abs_path).'/';
2558
                                            }
2559
                                            $new_abs_path = realpath($dir.$source);
2560
                                            $in_files_list[] = self::get_resources_from_source_html(
2561
                                                $new_abs_path,
2562
                                                true,
2563
                                                TOOL_DOCUMENT,
2564
                                                $recursivity + 1
2565
                                            );
2566
                                            if (count($in_files_list) > 0) {
2567
                                                $files_list = array_merge($files_list, $in_files_list);
2568
                                            }
2569
                                        }
2570
                                    }
2571
                                }
2572
                                //found some protocol there
2573
                                if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2574
                                    //we found the current portal url
2575
                                    $files_list[] = [$source, 'local', 'url'];
2576
                                    $in_files_list[] = self::get_resources_from_source_html(
2577
                                        $source,
2578
                                        true,
2579
                                        TOOL_DOCUMENT,
2580
                                        $recursivity + 1
2581
                                    );
2582
                                    if (count($in_files_list) > 0) {
2583
                                        $files_list = array_merge($files_list, $in_files_list);
2584
                                    }
2585
                                } else {
2586
                                    //we didn't find any trace of current portal
2587
                                    $files_list[] = [$source, 'remote', 'url'];
2588
                                }
2589
                            } else {
2590
                                //no protocol found, make link local
2591
                                if (substr($source, 0, 1) === '/') {
2592
                                    //link starts with a /, making it absolute (relative to DocumentRoot)
2593
                                    $files_list[] = [$source, 'local', 'abs'];
2594
                                    $in_files_list[] = self::get_resources_from_source_html(
2595
                                        $source,
2596
                                        true,
2597
                                        TOOL_DOCUMENT,
2598
                                        $recursivity + 1
2599
                                    );
2600
                                    if (count($in_files_list) > 0) {
2601
                                        $files_list = array_merge($files_list, $in_files_list);
2602
                                    }
2603
                                } elseif (strpos($source, '..') === 0) {
2604
                                    //link is relative but going back in the hierarchy
2605
                                    $files_list[] = [$source, 'local', 'rel'];
2606
                                    $dir = '';
2607
                                    if (!empty($abs_path)) {
2608
                                        $dir = dirname($abs_path).'/';
2609
                                    }
2610
                                    $new_abs_path = realpath($dir.$source);
2611
                                    $in_files_list[] = self::get_resources_from_source_html(
2612
                                        $new_abs_path,
2613
                                        true,
2614
                                        TOOL_DOCUMENT,
2615
                                        $recursivity + 1
2616
                                    );
2617
                                    if (count($in_files_list) > 0) {
2618
                                        $files_list = array_merge($files_list, $in_files_list);
2619
                                    }
2620
                                } else {
2621
                                    //no starting '/', making it relative to current document's path
2622
                                    if (substr($source, 0, 2) == './') {
2623
                                        $source = substr($source, 2);
2624
                                    }
2625
                                    $files_list[] = [$source, 'local', 'rel'];
2626
                                    $dir = '';
2627
                                    if (!empty($abs_path)) {
2628
                                        $dir = dirname($abs_path).'/';
2629
                                    }
2630
                                    $new_abs_path = realpath($dir.$source);
2631
                                    $in_files_list[] = self::get_resources_from_source_html(
2632
                                        $new_abs_path,
2633
                                        true,
2634
                                        TOOL_DOCUMENT,
2635
                                        $recursivity + 1
2636
                                    );
2637
                                    if (count($in_files_list) > 0) {
2638
                                        $files_list = array_merge($files_list, $in_files_list);
2639
                                    }
2640
                                }
2641
                            }
2642
                        }
2643
                    }
2644
                }
2645
                break;
2646
            default: //ignore
2647
                break;
2648
        }
2649
2650
        $checked_files_list = [];
2651
        $checked_array_list = [];
2652
2653
        if (count($files_list) > 0) {
2654
            foreach ($files_list as $idx => $file) {
2655
                if (!empty($file[0])) {
2656
                    if (!in_array($file[0], $checked_files_list)) {
2657
                        $checked_files_list[] = $files_list[$idx][0];
2658
                        $checked_array_list[] = $files_list[$idx];
2659
                    }
2660
                }
2661
            }
2662
        }
2663
2664
        return $checked_array_list;
2665
    }
2666
2667
    /**
2668
     * Parses the HTML attributes given as string.
2669
     *
2670
     * @param string HTML attribute string
2671
     * @param array List of attributes that we want to get back
2672
     * @param array
2673
     *
2674
     * @return array An associative array of attributes
2675
     *
2676
     * @author Based on a function from the HTML_Common2 PEAR module     *
2677
     */
2678
    public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
2679
    {
2680
        $attributes = [];
2681
        $regs = [];
2682
        $reduced = false;
2683
        if (count($wanted) > 0) {
2684
            $reduced = true;
2685
        }
2686
        try {
2687
            //Find all occurences of something that looks like a URL
2688
            // The structure of this regexp is:
2689
            // (find protocol) then
2690
            // (optionally find some kind of space 1 or more times) then
2691
            // find (either an equal sign or a bracket) followed by an optional space
2692
            // followed by some text without quotes (between quotes itself or not)
2693
            // then possible closing brackets if we were in the opening bracket case
2694
            // OR something like @import()
2695
            $res = preg_match_all(
2696
                '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]*))'.
2697
                // '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]|[^\x00-\x7F])*)' . -> seems to be taking too much
2698
                // '/(((([A-Za-z_:])([^\x00-\x7F])*)' . -> takes only last letter of parameter name
2699
                '([ \n\t\r]+)?('.
2700
                // '(=([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+))' . -> doesn't restrict close enough to the url itself
2701
                '(=([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+))'.
2702
                '|'.
2703
                // '(\(([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)\))' . -> doesn't restrict close enough to the url itself
2704
                '(\(([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+)\))'.
2705
                '))'.
2706
                '|'.
2707
                // '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))?/', -> takes a lot (like 100's of thousands of empty possibilities)
2708
                '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))/',
2709
                $attrString,
2710
                $regs
2711
            );
2712
        } catch (Exception $e) {
2713
            error_log('Caught exception: '.$e->getMessage(), 0);
2714
        }
2715
        if ($res) {
2716
            for ($i = 0; $i < count($regs[1]); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2717
                $name = trim($regs[3][$i]);
2718
                $check = trim($regs[0][$i]);
2719
                $value = trim($regs[10][$i]);
2720
                if (empty($value) and !empty($regs[13][$i])) {
2721
                    $value = $regs[13][$i];
2722
                }
2723
                if (empty($name) && !empty($regs[16][$i])) {
2724
                    $name = '@import';
2725
                    $value = trim($regs[16][$i]);
2726
                }
2727
                if (!empty($name)) {
2728
                    if (!$reduced || in_array(strtolower($name), $wanted)) {
2729
                        if ($name == $check) {
2730
                            $attributes[strtolower($name)][] = strtolower($name);
2731
                        } else {
2732
                            if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) {
2733
                                $value = substr($value, 1, -1);
2734
                            }
2735
2736
                            if ($value == 'API.LMSGetValue(name') {
2737
                                $value = 'API.LMSGetValue(name)';
2738
                            }
2739
                            //Gets the xx.flv value from the string flashvars="width=320&height=240&autostart=false&file=xxx.flv&repeat=false"
2740
                            if (isset($explode_variables[$name])) {
2741
                                $value_modified = str_replace('&amp;', '&', $value);
2742
                                $value_array = explode('&', $value_modified);
2743
                                foreach ($value_array as $item) {
2744
                                    $itemParts = explode('=', $item);
2745
                                    $key = $itemParts[0];
2746
                                    $item_value = !empty($itemParts[1]) ? $itemParts[1] : '';
2747
                                    if ($key == $explode_variables[$name]) {
2748
                                        $attributes[strtolower($name)][] = $item_value;
2749
                                    }
2750
                                }
2751
                            }
2752
                            $attributes[strtolower($name)][] = $value;
2753
                        }
2754
                    }
2755
                }
2756
            }
2757
        }
2758
2759
        return $attributes;
2760
    }
2761
2762
    /**
2763
     * Replace urls inside content html from a copy course.
2764
     *
2765
     * @param string $content_html
2766
     * @param string $origin_course_code
2767
     * @param string $destination_course_directory
2768
     * @param string $origin_course_path_from_zip
2769
     * @param string $origin_course_info_path
2770
     *
2771
     * @return string new content html with replaced urls or return false if content is not a string
2772
     */
2773
    public static function replaceUrlWithNewCourseCode(
2774
        $content_html,
2775
        $origin_course_code,
2776
        $destination_course_directory,
2777
        $origin_course_path_from_zip = null,
2778
        $origin_course_info_path = null
2779
    ) {
2780
        if (empty($content_html)) {
2781
            return false;
2782
        }
2783
2784
        $orig_source_html = self::get_resources_from_source_html($content_html);
2785
        $orig_course_info = api_get_course_info($origin_course_code);
2786
2787
        // Course does not exist in the current DB probably this came from a zip file?
2788
        if (empty($orig_course_info)) {
2789
            if (!empty($origin_course_path_from_zip)) {
2790
                $orig_course_path = $origin_course_path_from_zip.'/';
2791
                $orig_course_info_path = $origin_course_info_path;
2792
            }
2793
        } else {
2794
            $orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
2795
            $orig_course_info_path = $orig_course_info['path'];
2796
        }
2797
2798
        $destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
2799
        $destination_course_info = api_get_course_info($destination_course_code);
2800
        $dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
2801
        $dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
2802
2803
        $user_id = api_get_user_id();
2804
2805
        if (!empty($orig_source_html)) {
2806
            foreach ($orig_source_html as $source) {
2807
                // Get information about source url
2808
                $real_orig_url = $source[0]; // url
2809
                $scope_url = $source[1]; // scope (local, remote)
2810
                $type_url = $source[2]; // type (rel, abs, url)
2811
2812
                // Get path and query from origin url
2813
                $orig_parse_url = parse_url($real_orig_url);
2814
                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
2815
                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
2816
2817
                // Replace origin course code by destination course code from origin url query
2818
                $dest_url_query = '';
2819
2820
                if (!empty($real_orig_query)) {
2821
                    $dest_url_query = '?'.$real_orig_query;
2822
                    if (strpos($dest_url_query, $origin_course_code) !== false) {
2823
                        $dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
2824
                    }
2825
                }
2826
2827
                if ($scope_url == 'local') {
2828
                    if ($type_url == 'abs' || $type_url == 'rel') {
2829
                        $document_file = strstr($real_orig_path, 'document');
2830
2831
                        if ($document_file && strpos($real_orig_path, $document_file) !== false) {
2832
                            $origin_filepath = $orig_course_path.$document_file;
2833
                            $destination_filepath = $dest_course_path.$document_file;
2834
2835
                            // copy origin file inside destination course
2836
                            if (file_exists($origin_filepath)) {
2837
                                $filepath_dir = dirname($destination_filepath);
2838
2839
                                if (!is_dir($filepath_dir)) {
2840
                                    $perm = api_get_permissions_for_new_directories();
2841
                                    $result = @mkdir($filepath_dir, $perm, true);
2842
                                    if ($result) {
2843
                                        $filepath_to_add = str_replace(
2844
                                            [$dest_course_path, 'document'],
2845
                                            '',
2846
                                            $filepath_dir
2847
                                        );
2848
2849
                                        //Add to item properties to the new folder
2850
                                        $doc_id = add_document(
2851
                                            $destination_course_info,
2852
                                            $filepath_to_add,
2853
                                            'folder',
2854
                                            0,
2855
                                            basename($filepath_to_add)
2856
                                        );
2857
                                        api_item_property_update(
2858
                                            $destination_course_info,
2859
                                            TOOL_DOCUMENT,
2860
                                            $doc_id,
2861
                                            'FolderCreated',
2862
                                            $user_id,
2863
                                            null,
2864
                                            null,
2865
                                            null,
2866
                                            null
2867
                                        );
2868
                                    }
2869
                                }
2870
2871
                                if (!file_exists($destination_filepath)) {
2872
                                    $result = @copy($origin_filepath, $destination_filepath);
2873
                                    if ($result) {
2874
                                        $filepath_to_add = str_replace(
2875
                                            [$dest_course_path, 'document'],
2876
                                            '',
2877
                                            $destination_filepath
2878
                                        );
2879
                                        $size = filesize($destination_filepath);
2880
2881
                                        // Add to item properties to the file
2882
                                        $doc_id = add_document(
2883
                                            $destination_course_info,
2884
                                            $filepath_to_add,
2885
                                            'file',
2886
                                            $size,
2887
                                            basename($filepath_to_add)
2888
                                        );
2889
                                        api_item_property_update(
2890
                                            $destination_course_info,
2891
                                            TOOL_DOCUMENT,
2892
                                            $doc_id,
2893
                                            'FolderCreated',
2894
                                            $user_id,
2895
                                            null,
2896
                                            null,
2897
                                            null,
2898
                                            null
2899
                                        );
2900
                                    }
2901
                                }
2902
                            }
2903
2904
                            // Replace origin course path by destination course path.
2905
                            if (strpos($content_html, $real_orig_url) !== false) {
2906
                                $url_course_path = str_replace(
2907
                                    $orig_course_info_path.'/'.$document_file,
2908
                                    '',
2909
                                    $real_orig_path
2910
                                );
2911
                                // See BT#7780
2912
                                $destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
2913
                                // If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
2914
                                if (strpos($real_orig_path, $origin_course_code) === false) {
2915
                                    $url_course_path = $real_orig_path;
2916
                                    $destination_url = $real_orig_path;
2917
                                }
2918
                                $content_html = str_replace($real_orig_url, $destination_url, $content_html);
2919
                            }
2920
                        }
2921
2922
                        // replace origin course code by destination course code  from origin url
2923
                        if (strpos($real_orig_url, '?') === 0) {
2924
                            $dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
2925
                            $content_html = str_replace($real_orig_url, $dest_url, $content_html);
2926
                        }
2927
                    }
2928
                }
2929
            }
2930
        }
2931
2932
        return $content_html;
2933
    }
2934
2935
    /**
2936
     * Export document to PDF.
2937
     *
2938
     * @param int    $document_id
2939
     * @param string $courseCode
2940
     * @param string $orientation
2941
     * @param bool   $showHeaderAndFooter
2942
     */
2943
    public static function export_to_pdf(
2944
        $document_id,
2945
        $courseCode,
2946
        $orientation = 'landscape',
2947
        $showHeaderAndFooter = true
2948
    ) {
2949
        $course_data = api_get_course_info($courseCode);
2950
        $document_data = self::get_document_data_by_id($document_id, $courseCode);
2951
        $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$document_data['path'];
2952
2953
        $pageFormat = 'A4';
2954
        $pdfOrientation = 'P';
2955
        if ($orientation === 'landscape') {
2956
            $pageFormat = 'A4-L';
2957
            $pdfOrientation = 'L';
2958
        }
2959
        $pdf = new PDF(
2960
            $pageFormat,
2961
            $pdfOrientation,
2962
            $showHeaderAndFooter ? [] : ['top' => 0, 'left' => 0, 'bottom' => 0, 'right' => 0]
2963
        );
2964
2965
        if (api_get_configuration_value('use_alternative_document_pdf_footer')) {
2966
            $view = new Template('', false, false, false, true, false, false);
2967
            $template = $view->get_template('export/alt_pdf_footer.tpl');
2968
            $pdf->set_custom_footer(['html' => $view->fetch($template)]);
2969
        }
2970
2971
        $pdf->html_to_pdf(
2972
            $file_path,
2973
            $document_data['title'],
2974
            $courseCode,
2975
            false,
2976
            $showHeaderAndFooter
2977
        );
2978
    }
2979
2980
    /**
2981
     * Uploads a document.
2982
     *
2983
     * @param array  $files                   the $_FILES variable
2984
     * @param string $path
2985
     * @param string $title
2986
     * @param string $comment
2987
     * @param int    $unzip                   unzip or not the file
2988
     * @param string $ifExists                overwrite, rename or warn (default)
2989
     * @param bool   $index_document          index document (search xapian module)
2990
     * @param bool   $show_output             print html messages
2991
     * @param string $fileKey
2992
     * @param bool   $treat_spaces_as_hyphens
2993
     * @param int    $userId                  Optional. User ID who upload file
2994
     * @param array  $courseInfo              Optional. Course info
2995
     * @param int    $sessionId               Optional. Session ID
2996
     * @param int    $groupId                 Optional. Group ID
2997
     *
2998
     * @return array|bool
2999
     */
3000
    public static function upload_document(
3001
        $files,
3002
        $path,
3003
        $title = '',
3004
        $comment = '',
3005
        $unzip = 0,
3006
        $ifExists = '',
3007
        $index_document = false,
3008
        $show_output = false,
3009
        $fileKey = 'file',
3010
        $treat_spaces_as_hyphens = true,
3011
        $userId = 0,
3012
        array $courseInfo = [],
3013
        $sessionId = 0,
3014
        $groupId = 0
3015
    ) {
3016
        $course_info = $courseInfo ?: api_get_course_info();
3017
        $sessionId = $sessionId ?: api_get_session_id();
3018
        $course_dir = $course_info['path'].'/document';
3019
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3020
        $base_work_dir = $sys_course_path.$course_dir;
3021
        $userId = $userId ?: api_get_user_id();
3022
        $groupId = $groupId ?: api_get_group_id();
3023
3024
        if (isset($files[$fileKey])) {
3025
            $uploadOk = process_uploaded_file($files[$fileKey], $show_output);
3026
            if ($uploadOk) {
3027
                $new_path = handle_uploaded_document(
3028
                    $course_info,
3029
                    $files[$fileKey],
3030
                    $base_work_dir,
3031
                    $path,
3032
                    $userId,
3033
                    $groupId,
3034
                    null,
3035
                    $unzip,
3036
                    $ifExists,
3037
                    $show_output,
3038
                    false,
3039
                    null,
3040
                    $sessionId,
3041
                    $treat_spaces_as_hyphens
3042
                );
3043
3044
                // When sending zip files
3045
                if ($new_path === true && $unzip == 1) {
3046
                    return [
3047
                        'title' => $files[$fileKey]['name'],
3048
                        'url' => '#',
3049
                    ];
3050
                }
3051
3052
                if ($new_path) {
3053
                    $documentId = self::get_document_id(
3054
                        $course_info,
3055
                        $new_path,
3056
                        $sessionId
3057
                    );
3058
3059
                    if (!empty($documentId)) {
3060
                        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3061
                        $params = [];
3062
3063
                        if (!empty($title)) {
3064
                            $params['title'] = $title;
3065
                        }
3066
3067
                        if (!empty($comment)) {
3068
                            $params['comment'] = trim($comment);
3069
                        }
3070
3071
                        Database::update(
3072
                            $table_document,
3073
                            $params,
3074
                            [
3075
                                'id = ? AND c_id = ? ' => [
3076
                                    $documentId,
3077
                                    $course_info['real_id'],
3078
                                ],
3079
                            ]
3080
                        );
3081
                    }
3082
3083
                    if ($index_document) {
3084
                        self::index_document(
3085
                            $documentId,
3086
                            $course_info['code'],
3087
                            null,
3088
                            $_POST['language'],
3089
                            $_REQUEST,
3090
                            $ifExists
3091
                        );
3092
                    }
3093
3094
                    if (!empty($documentId) && is_numeric($documentId)) {
3095
                        $documentData = self::get_document_data_by_id(
3096
                            $documentId,
3097
                            $course_info['code'],
3098
                            false,
3099
                            $sessionId
3100
                        );
3101
3102
                        return $documentData;
3103
                    }
3104
                }
3105
            }
3106
        }
3107
3108
        return false;
3109
    }
3110
3111
    /**
3112
     * Obtains the text inside the file with the right parser.
3113
     */
3114
    public static function get_text_content($doc_path, $doc_mime)
3115
    {
3116
        // TODO: review w$ compatibility
3117
        // Use usual exec output lines array to store stdout instead of a temp file
3118
        // because we need to store it at RAM anyway before index on ChamiloIndexer object
3119
        $ret_val = null;
3120
        switch ($doc_mime) {
3121
            case 'text/plain':
3122
                $handle = fopen($doc_path, 'r');
3123
                $output = [fread($handle, filesize($doc_path))];
3124
                fclose($handle);
3125
                break;
3126
            case 'application/pdf':
3127
                exec("pdftotext $doc_path -", $output, $ret_val);
3128
                break;
3129
            case 'application/postscript':
3130
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
3131
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
3132
                if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
3133
                    return false;
3134
                }
3135
                exec("pdftotext $temp_file -", $output, $ret_val);
3136
                unlink($temp_file);
3137
                break;
3138
            case 'application/msword':
3139
                exec("catdoc $doc_path", $output, $ret_val);
3140
                break;
3141
            case 'text/html':
3142
                exec("html2text $doc_path", $output, $ret_val);
3143
                break;
3144
            case 'text/rtf':
3145
                // Note: correct handling of code pages in unrtf
3146
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
3147
                exec("unrtf --text $doc_path", $output, $ret_val);
3148
                if ($ret_val == 127) { // command not found
3149
                    return false;
3150
                }
3151
                // Avoid index unrtf comments
3152
                if (is_array($output) && count($output) > 1) {
3153
                    $parsed_output = [];
3154
                    foreach ($output as &$line) {
3155
                        if (!preg_match('/^###/', $line, $matches)) {
3156
                            if (!empty($line)) {
3157
                                $parsed_output[] = $line;
3158
                            }
3159
                        }
3160
                    }
3161
                    $output = $parsed_output;
3162
                }
3163
                break;
3164
            case 'application/vnd.ms-powerpoint':
3165
                exec("catppt $doc_path", $output, $ret_val);
3166
                break;
3167
            case 'application/vnd.ms-excel':
3168
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
3169
                break;
3170
        }
3171
3172
        $content = '';
3173
        if (!is_null($ret_val)) {
3174
            if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
3175
                return false;
3176
            }
3177
        }
3178
        if (isset($output)) {
3179
            foreach ($output as &$line) {
3180
                $content .= $line."\n";
3181
            }
3182
3183
            return $content;
3184
        } else {
3185
            return false;
3186
        }
3187
    }
3188
3189
    /**
3190
     * Calculates the total size of all documents in a course.
3191
     *
3192
     * @author Bert vanderkimpen
3193
     *
3194
     * @param int $course_id
3195
     * @param int $group_id   (to calculate group document space)
3196
     * @param int $session_id
3197
     *
3198
     * @return int total size
3199
     */
3200
    public static function documents_total_space($course_id = null, $group_id = null, $session_id = null)
3201
    {
3202
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
3203
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
3204
3205
        $session_id = (int) $session_id;
3206
        $group_id = (int) $group_id;
3207
        $course_id = (int) $course_id;
3208
3209
        if (!$course_id) {
3210
            $course_id = api_get_course_int_id();
3211
        }
3212
3213
        $group_condition = '';
3214
        if ($group_id) {
3215
            $group_condition = " AND props.to_group_id='".$group_id."' ";
3216
        }
3217
3218
        $session_condition = '';
3219
        if ($session_id) {
3220
            $session_condition = " AND props.session_id='".$session_id."' ";
3221
        }
3222
3223
        $sql = "SELECT SUM(size)
3224
                FROM $TABLE_ITEMPROPERTY AS props
3225
                INNER JOIN $TABLE_DOCUMENT AS docs
3226
                ON (docs.id = props.ref AND props.c_id = docs.c_id)
3227
                WHERE
3228
                    props.c_id = $course_id AND
3229
                    docs.c_id = $course_id AND
3230
                    props.tool = '".TOOL_DOCUMENT."' AND
3231
                    props.visibility <> 2
3232
                    $group_condition
3233
                    $session_condition
3234
                ";
3235
        $result = Database::query($sql);
3236
3237
        if ($result && Database::num_rows($result) != 0) {
3238
            $row = Database::fetch_row($result);
3239
3240
            return (int) $row[0];
3241
        } else {
3242
            return 0;
3243
        }
3244
    }
3245
3246
    /**
3247
     * Display the document quota in a simple way.
3248
     *
3249
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
3250
     */
3251
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
3252
    {
3253
        $course_quota_m = round($course_quota / 1048576);
3254
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
3255
        $percentage = $already_consumed_space / $course_quota * 100;
3256
        $percentage = round($percentage, 1);
3257
        $message = get_lang('YouAreCurrentlyUsingXOfYourX');
3258
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
3259
3260
        return Display::div($message, ['id' => 'document_quota']);
3261
    }
3262
3263
    /**
3264
     * Checks if there is enough place to add a file on a directory
3265
     * on the base of a maximum directory size allowed.
3266
     *
3267
     * @author Bert Vanderkimpen
3268
     *
3269
     * @param int $file_size     size of the file in byte
3270
     * @param int $max_dir_space maximum size
3271
     *
3272
     * @return bool true if there is enough space, false otherwise
3273
     *
3274
     * @see enough_space() uses  documents_total_space() function
3275
     */
3276
    public static function enough_space($file_size, $max_dir_space)
3277
    {
3278
        if ($max_dir_space) {
3279
            $already_filled_space = self::documents_total_space();
3280
            if (($file_size + $already_filled_space) > $max_dir_space) {
3281
                return false;
3282
            }
3283
        }
3284
3285
        return true;
3286
    }
3287
3288
    /**
3289
     * @param array $params count, url, extension
3290
     *
3291
     * @return string
3292
     */
3293
    public static function generateAudioJavascript($params = [])
3294
    {
3295
        return '
3296
            $(\'audio.audio_preview\').mediaelementplayer({
3297
                features: [\'playpause\'],
3298
                audioWidth: 30,
3299
                audioHeight: 30,
3300
                success: function(mediaElement, originalNode, instance) {
3301
                }
3302
            });';
3303
    }
3304
3305
    /**
3306
     * Shows a play icon next to the document title in the document list.
3307
     *
3308
     * @param string $documentWebPath
3309
     * @param array  $documentInfo
3310
     *
3311
     * @return string
3312
     */
3313
    public static function generateAudioPreview($documentWebPath, $documentInfo)
3314
    {
3315
        $filePath = $documentWebPath.$documentInfo['path'];
3316
        $extension = $documentInfo['file_extension'];
3317
3318
        if (!in_array($extension, ['mp3', 'wav', 'ogg'])) {
3319
            return '';
3320
        }
3321
3322
        return '<span class="preview"> <audio class="audio_preview skip" src="'.$filePath.'" type="audio/'.$extension.'" > </audio></span>';
3323
    }
3324
3325
    /**
3326
     * @param string $file
3327
     * @param string $extension
3328
     *
3329
     * @return string
3330
     */
3331
    public static function generateMediaPreview($file, $extension)
3332
    {
3333
        $id = api_get_unique_id();
3334
        switch ($extension) {
3335
            case 'ogg':
3336
            case 'mp3':
3337
                $document_data['file_extension'] = $extension;
3338
                $html = '<div style="margin: 0; position: absolute; top: 50%; left: 35%;">';
3339
                $html .= '<audio id="'.$id.'" controls="controls" src="'.$file.'" type="audio/mp3" ></audio></div>';
3340
                break;
3341
            default:
3342
                $html = '<video id="'.$id.'" controls>';
3343
                $html .= '<source src="'.$file.'" >';
3344
                $html .= '</video>';
3345
                break;
3346
        }
3347
3348
        return $html;
3349
    }
3350
3351
    /**
3352
     * @param array  $course_info
3353
     * @param bool   $lp_id
3354
     * @param string $target
3355
     * @param int    $session_id
3356
     * @param bool   $add_move_button
3357
     * @param string $filter_by_folder
3358
     * @param string $overwrite_url
3359
     * @param bool   $showInvisibleFiles
3360
     * @param bool   $showOnlyFolders
3361
     * @param int    $folderId
3362
     * @param bool   $addCloseButton
3363
     * @param bool   $addAudioPreview
3364
     * @param array  $filterByExtension
3365
     *
3366
     * @return string
3367
     */
3368
    public static function get_document_preview(
3369
        $course_info,
3370
        $lp_id = false,
3371
        $target = '',
3372
        $session_id = 0,
3373
        $add_move_button = false,
3374
        $filter_by_folder = null,
3375
        $overwrite_url = '',
3376
        $showInvisibleFiles = false,
3377
        $showOnlyFolders = false,
3378
        $folderId = false,
3379
        $addCloseButton = true,
3380
        $addAudioPreview = false,
3381
        $filterByExtension = []
3382
    ) {
3383
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
3384
            return '';
3385
        }
3386
3387
        $user_id = api_get_user_id();
3388
        $userInfo = api_get_user_info();
3389
        $user_in_course = api_is_platform_admin();
3390
        if (!$user_in_course) {
3391
            if (CourseManager::is_course_teacher($user_id, $course_info['code'])) {
3392
                $user_in_course = true;
3393
            }
3394
        }
3395
3396
        // Condition for the session
3397
        $session_id = (int) $session_id;
3398
3399
        if (!$user_in_course) {
3400
            if (empty($session_id)) {
3401
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
3402
                    $user_in_course = true;
3403
                }
3404
                // Check if course is open then we can consider that the student is registered to the course
3405
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3406
                    $user_in_course = true;
3407
                }
3408
            } else {
3409
                $user_status = SessionManager::get_user_status_in_course_session(
3410
                    $user_id,
3411
                    $course_info['real_id'],
3412
                    $session_id
3413
                );
3414
                //is true if is an student, course session teacher or coach
3415
                if (in_array($user_status, ['0', '2', '6'])) {
3416
                    $user_in_course = true;
3417
                }
3418
            }
3419
        }
3420
3421
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3422
        $tbl_item_prop = Database::get_course_table(TABLE_ITEM_PROPERTY);
3423
        $condition_session = " AND (last.session_id = '$session_id' OR last.session_id = '0' OR last.session_id IS NULL)";
3424
3425
        $add_folder_filter = null;
3426
        if (!empty($filter_by_folder)) {
3427
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3428
        }
3429
3430
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3431
        $lp_visibility_condition = null;
3432
        if ($lp_id) {
3433
            if ($showInvisibleFiles) {
3434
                $lp_visibility_condition .= ' OR last.visibility = 0';
3435
            }
3436
        }
3437
3438
        $folderCondition = " AND docs.path LIKE '/%' ";
3439
        if (!api_is_allowed_to_edit()) {
3440
            $protectedFolders = self::getProtectedFolderFromStudent();
3441
            foreach ($protectedFolders as $folder) {
3442
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3443
            }
3444
        }
3445
3446
        $extensionConditionToString = '';
3447
        if (!empty($filterByExtension)) {
3448
            $extensionCondition = [];
3449
            foreach ($filterByExtension as $extension) {
3450
                $extensionCondition[] = " docs.path LIKE '%.$extension' ";
3451
            }
3452
            if (!empty($extensionCondition)) {
3453
                $extensionConditionToString .= " AND (".implode('OR', $extensionCondition).") ";
3454
            }
3455
        }
3456
3457
        $parentData = [];
3458
        if ($folderId !== false) {
3459
            $parentData = self::get_document_data_by_id(
3460
                $folderId,
3461
                $course_info['code'],
3462
                false,
3463
                $session_id
3464
            );
3465
            if (!empty($parentData)) {
3466
                $cleanedPath = $parentData['path'];
3467
                $num = substr_count($cleanedPath, '/');
3468
3469
                $notLikeCondition = '';
3470
                for ($i = 1; $i <= $num; $i++) {
3471
                    $repeat = str_repeat('/%', $i + 1);
3472
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3473
                }
3474
3475
                $folderId = (int) $folderId;
3476
                $folderCondition = " AND
3477
                    docs.id <> $folderId AND
3478
                    docs.path LIKE '".$cleanedPath."/%'
3479
                    $notLikeCondition
3480
                ";
3481
            } else {
3482
                $folderCondition = " AND docs.filetype = 'file' ";
3483
            }
3484
        }
3485
3486
        $levelCondition = '';
3487
        if ($folderId === false) {
3488
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3489
        }
3490
3491
        $sql = "SELECT DISTINCT last.visibility, docs.*
3492
                FROM $tbl_item_prop AS last
3493
                INNER JOIN $tbl_doc AS docs
3494
                ON (docs.id = last.ref AND docs.c_id = last.c_id)
3495
                WHERE
3496
                    docs.path NOT LIKE '%_DELETED_%' AND
3497
                    last.tool = '".TOOL_DOCUMENT."' $condition_session AND
3498
                    (last.visibility = '1' $lp_visibility_condition) AND
3499
                    last.visibility <> 2 AND
3500
                    docs.c_id = {$course_info['real_id']} AND
3501
                    last.c_id = {$course_info['real_id']}
3502
                    $folderCondition
3503
                    $levelCondition
3504
                    $extensionConditionToString
3505
                    $add_folder_filter
3506
                ORDER BY docs.filetype DESC, docs.title ASC";
3507
3508
        $res_doc = Database::query($sql);
3509
        $resources = Database::store_result($res_doc, 'ASSOC');
3510
3511
        $return = '';
3512
        if ($lp_id == false && $addCloseButton) {
3513
            if ($folderId === false) {
3514
                $return .= Display::div(
3515
                    Display::url(
3516
                        Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3517
                        ' javascript:void(0);',
3518
                        ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3519
                    ),
3520
                    ['style' => 'position:absolute;right:10px']
3521
                );
3522
            }
3523
        }
3524
3525
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3526
        $newResources = [];
3527
        if (!empty($resources) && $user_in_course) {
3528
            foreach ($resources as $resource) {
3529
                $is_visible = self::is_visible_by_id(
3530
                    $resource['id'],
3531
                    $course_info,
3532
                    $session_id,
3533
                    api_get_user_id()
3534
                );
3535
3536
                if ($showInvisibleFiles === false) {
3537
                    if (!$is_visible) {
3538
                        continue;
3539
                    }
3540
                }
3541
3542
                $newResources[] = $resource;
3543
            }
3544
        }
3545
3546
        $label = get_lang('Documents');
3547
3548
        $documents = [];
3549
        if ($folderId === false) {
3550
            $documents[$label] = [
3551
                'id' => 0,
3552
                'files' => $newResources,
3553
            ];
3554
        } else {
3555
            if (is_array($parentData)) {
3556
                $documents[$parentData['title']] = [
3557
                    'id' => (int) $folderId,
3558
                    'files' => $newResources,
3559
                ];
3560
            }
3561
        }
3562
3563
        $writeResult = self::write_resources_tree(
3564
            $userInfo,
3565
            $course_info,
3566
            $session_id,
3567
            $documents,
3568
            $lp_id,
3569
            $target,
3570
            $add_move_button,
3571
            $overwrite_url,
3572
            $folderId,
3573
            $addAudioPreview
3574
        );
3575
3576
        $return .= $writeResult;
3577
        $lpAjaxUrl = api_get_path(WEB_AJAX_PATH).'lp.ajax.php';
3578
        if ($lp_id === false) {
3579
            $url = $lpAjaxUrl.'?a=get_documents&lp_id=&cidReq='.$course_info['code'];
3580
            $return .= "<script>
3581
            $(function() {
3582
                $('.close_div').click(function() {
3583
                    var course_id = this.id.split('_')[2];
3584
                    var session_id = this.id.split('_')[3];
3585
                    $('#document_result_'+course_id+'_'+session_id).hide();
3586
                    $('.lp_resource').remove();
3587
                    $('.document_preview_container').html('');
3588
                });
3589
            });
3590
            </script>";
3591
        } else {
3592
            // For LPs
3593
            $url = $lpAjaxUrl.'?a=get_documents&lp_id='.$lp_id.'&'.api_get_cidreq();
3594
        }
3595
3596
        if (!empty($overwrite_url)) {
3597
            $url .= '&url='.urlencode(Security::remove_XSS($overwrite_url));
3598
        }
3599
3600
        if ($add_move_button) {
3601
            $url .= '&add_move_button=1';
3602
        }
3603
3604
        $return .= "<script>
3605
            function testResources(id, img) {
3606
                var numericId = id.split('_')[1];
3607
                var parentId = 'doc_id_'+numericId;
3608
                var tempId = 'temp_'+numericId;
3609
                var image = $('#'+img);
3610
3611
                if (image.hasClass('open')) {
3612
                    image.removeClass('open');
3613
                    image.attr('src', '".Display::returnIconPath('nolines_plus.gif')."');
3614
                    $('#'+id).show();
3615
                    $('#'+tempId).hide();
3616
                } else {
3617
                    image.addClass('open');
3618
                    image.attr('src', '".Display::returnIconPath('nolines_minus.gif')."');
3619
                    $('#'+id).hide();
3620
                    $('#'+tempId).show();
3621
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3622
                    if (tempDiv.length == 0) {
3623
                        $.ajax({
3624
                            type: 'GET',
3625
                            url:  '".$url."',
3626
                            data: 'folder_id='+numericId,
3627
                            success: function(data) {
3628
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3629
                            }
3630
                        });
3631
                    }
3632
                }
3633
            }
3634
            </script>";
3635
3636
        if (!$user_in_course) {
3637
            $return = '';
3638
        }
3639
3640
        return $return;
3641
    }
3642
3643
    /**
3644
     * Generate and return an HTML list of resources based on a given array.
3645
     * This list is used to show the course creator a list of available resources to choose from
3646
     * when creating a learning path.
3647
     *
3648
     * @param array  $userInfo        current user info
3649
     * @param array  $course_info
3650
     * @param int    $session_id
3651
     * @param array  $documents
3652
     * @param bool   $lp_id
3653
     * @param string $target
3654
     * @param bool   $add_move_button
3655
     * @param string $overwrite_url
3656
     * @param int    $folderId
3657
     * @param bool   $addAudioPreview
3658
     *
3659
     * @return string
3660
     */
3661
    public static function write_resources_tree(
3662
        $userInfo,
3663
        $course_info,
3664
        $session_id,
3665
        $documents,
3666
        $lp_id = false,
3667
        $target = '',
3668
        $add_move_button = false,
3669
        $overwrite_url = '',
3670
        $folderId = false,
3671
        $addAudioPreview = false
3672
    ) {
3673
        $return = '';
3674
        if (!empty($documents)) {
3675
            foreach ($documents as $key => $resource) {
3676
                if (isset($resource['id']) && is_int($resource['id'])) {
3677
                    $mainFolderResource = [
3678
                        'id' => $resource['id'],
3679
                        'title' => $key,
3680
                    ];
3681
3682
                    if ($folderId === false) {
3683
                        $return .= self::parseFolder($folderId, $mainFolderResource, $lp_id);
3684
                    }
3685
3686
                    if (isset($resource['files'])) {
3687
                        $return .= self::write_resources_tree(
3688
                            $userInfo,
3689
                            $course_info,
3690
                            $session_id,
3691
                            $resource['files'],
3692
                            $lp_id,
3693
                            $target,
3694
                            $add_move_button,
3695
                            $overwrite_url,
3696
                            null,
3697
                            $addAudioPreview
3698
                        );
3699
                    }
3700
                    $return .= '</div>';
3701
                    $return .= '</ul>';
3702
                } else {
3703
                    if ($resource['filetype'] === 'folder') {
3704
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3705
                    } else {
3706
                        $return .= self::parseFile(
3707
                            $userInfo,
3708
                            $course_info,
3709
                            $session_id,
3710
                            $resource,
3711
                            $lp_id,
3712
                            $add_move_button,
3713
                            $target,
3714
                            $overwrite_url,
3715
                            $addAudioPreview
3716
                        );
3717
                    }
3718
                }
3719
            }
3720
        }
3721
3722
        return $return;
3723
    }
3724
3725
    /**
3726
     * @param int   $doc_id
3727
     * @param array $courseInfo
3728
     * @param int   $sessionId
3729
     * @param int   $user_id
3730
     * @param int   $groupId               iid
3731
     * @param bool  $checkParentVisibility
3732
     *
3733
     * @return bool
3734
     */
3735
    public static function check_visibility_tree(
3736
        $doc_id,
3737
        $courseInfo,
3738
        $sessionId,
3739
        $user_id,
3740
        $groupId = 0,
3741
        $checkParentVisibility = true
3742
    ) {
3743
        if (empty($courseInfo)) {
3744
            return false;
3745
        }
3746
3747
        $courseCode = $courseInfo['code'];
3748
3749
        if (empty($courseCode)) {
3750
            return false;
3751
        }
3752
3753
        $document_data = self::get_document_data_by_id(
3754
            $doc_id,
3755
            $courseCode,
3756
            null,
3757
            $sessionId
3758
        );
3759
3760
        if ($sessionId != 0 && !$document_data) {
3761
            $document_data = self::get_document_data_by_id(
3762
                $doc_id,
3763
                $courseCode,
3764
                null,
3765
                0
3766
            );
3767
        }
3768
3769
        if (!empty($document_data)) {
3770
            // If admin or course teacher, allow anyway
3771
            if (api_is_platform_admin() || CourseManager::is_course_teacher($user_id, $courseCode)) {
3772
                return true;
3773
            }
3774
            if ($document_data['parent_id'] == false || empty($document_data['parent_id'])) {
3775
                if (!empty($groupId)) {
3776
                    return true;
3777
                }
3778
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3779
3780
                return $visible;
3781
            } else {
3782
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3783
3784
                if (!$visible) {
3785
                    return false;
3786
                } else {
3787
                    if ($checkParentVisibility && $doc_id != $document_data['parent_id']) {
3788
                        return self::check_visibility_tree(
3789
                            $document_data['parent_id'],
3790
                            $courseInfo,
3791
                            $sessionId,
3792
                            $user_id,
3793
                            $groupId
3794
                        );
3795
                    }
3796
3797
                    return true;
3798
                }
3799
            }
3800
        } else {
3801
            return false;
3802
        }
3803
    }
3804
3805
    /**
3806
     * Index a given document.
3807
     *
3808
     * @param   int     Document ID inside its corresponding course
3809
     * @param   string  Course code
3810
     * @param   int     Session ID (not used yet)
3811
     * @param   string  Language of document's content (defaults to course language)
3812
     * @param   array   Array of specific fields (['code'=>'value',...])
3813
     * @param   string  What to do if the file already exists (default or overwrite)
3814
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3815
     *
3816
     * @return bool Returns true on presumed success, false on failure
3817
     */
3818
    public static function index_document(
3819
        $docid,
3820
        $course_code,
3821
        $session_id = 0,
3822
        $lang = 'english',
3823
        $specific_fields_values = [],
3824
        $if_exists = '',
3825
        $simulation = false
3826
    ) {
3827
        if (api_get_setting('search_enabled') !== 'true') {
3828
            return false;
3829
        }
3830
        if (empty($docid) or $docid != intval($docid)) {
3831
            return false;
3832
        }
3833
        if (empty($session_id)) {
3834
            $session_id = api_get_session_id();
3835
        }
3836
        $course_info = api_get_course_info($course_code);
3837
        $course_dir = $course_info['path'].'/document';
3838
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3839
        $base_work_dir = $sys_course_path.$course_dir;
3840
3841
        $course_id = $course_info['real_id'];
3842
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3843
3844
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3845
        $result = Database::query($qry);
3846
        if (Database::num_rows($result) == 1) {
3847
            $row = Database::fetch_array($result);
3848
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3849
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3850
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3851
            $doc_mime = mime_content_type($doc_path);
3852
            $allowed_mime_types = self::file_get_mime_type(true);
3853
3854
            // mime_content_type does not detect correctly some formats that
3855
            // are going to be supported for index, so an extensions array is used for the moment
3856
            if (empty($doc_mime)) {
3857
                $allowed_extensions = [
3858
                    'doc',
3859
                    'docx',
3860
                    'ppt',
3861
                    'pptx',
3862
                    'pps',
3863
                    'ppsx',
3864
                    'xls',
3865
                    'xlsx',
3866
                    'odt',
3867
                    'odp',
3868
                    'ods',
3869
                    'pdf',
3870
                    'txt',
3871
                    'rtf',
3872
                    'msg',
3873
                    'csv',
3874
                    'html',
3875
                    'htm',
3876
                ];
3877
                $extensions = preg_split("/[\/\\.]/", $doc_path);
3878
                $doc_ext = strtolower($extensions[count($extensions) - 1]);
3879
                if (in_array($doc_ext, $allowed_extensions)) {
3880
                    switch ($doc_ext) {
3881
                        case 'ppt':
3882
                        case 'pps':
3883
                            $doc_mime = 'application/vnd.ms-powerpoint';
3884
                            break;
3885
                        case 'xls':
3886
                            $doc_mime = 'application/vnd.ms-excel';
3887
                            break;
3888
                    }
3889
                }
3890
            }
3891
3892
            //@todo move this nightmare in a search controller or something like that!!! J.M
3893
3894
            if (in_array($doc_mime, $allowed_mime_types)) {
3895
                $file_title = $row['title'];
3896
                $file_content = self::get_text_content($doc_path, $doc_mime);
3897
                $course_code = Database::escape_string($course_code);
3898
                $ic_slide = new IndexableChunk();
3899
                $ic_slide->addValue('title', $file_title);
3900
                $ic_slide->addCourseId($course_code);
3901
                $ic_slide->addToolId(TOOL_DOCUMENT);
3902
                $xapian_data = [
3903
                    SE_COURSE_ID => $course_code,
3904
                    SE_TOOL_ID => TOOL_DOCUMENT,
3905
                    SE_DATA => ['doc_id' => $docid],
3906
                    SE_USER => api_get_user_id(),
3907
                ];
3908
3909
                $ic_slide->xapian_data = serialize($xapian_data);
3910
                $di = new ChamiloIndexer();
3911
                $return = $di->connectDb(null, null, $lang);
3912
3913
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
3914
                $specific_fields = get_specific_field_list();
3915
3916
                // process different depending on what to do if file exists
3917
                /**
3918
                 * @TODO Find a way to really verify if the file had been
3919
                 * overwriten. Now all work is done at
3920
                 * handle_uploaded_document() and it's difficult to verify it
3921
                 */
3922
                if (!empty($if_exists) && $if_exists == 'overwrite') {
3923
                    // Overwrite the file on search engine
3924
                    // Actually, it consists on a delete of terms from db,
3925
                    // insert new ones, create a new search engine document,
3926
                    // and remove the old one
3927
                    // Get search_did
3928
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3929
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
3930
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
3931
3932
                    $res = Database::query($sql);
3933
3934
                    if (Database::num_rows($res) > 0) {
3935
                        $se_ref = Database::fetch_array($res);
3936
                        if (!$simulation) {
3937
                            $di->remove_document($se_ref['search_did']);
3938
                        }
3939
                        $all_specific_terms = '';
3940
                        foreach ($specific_fields as $specific_field) {
3941
                            if (!$simulation) {
3942
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
3943
                            }
3944
                            // Update search engine
3945
                            if (isset($specific_fields_values[$specific_field['code']])) {
3946
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
3947
                            } else { //if the specific field is not defined, force an empty one
3948
                                $sterms = '';
3949
                            }
3950
                            $all_specific_terms .= ' '.$sterms;
3951
                            $sterms = explode(',', $sterms);
3952
                            foreach ($sterms as $sterm) {
3953
                                $sterm = trim($sterm);
3954
                                if (!empty($sterm)) {
3955
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
3956
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
3957
                                    if (!$simulation) {
3958
                                        add_specific_field_value(
3959
                                            $specific_field['id'],
3960
                                            $course_code,
3961
                                            TOOL_DOCUMENT,
3962
                                            $docid,
3963
                                            $sterm
3964
                                        );
3965
                                    }
3966
                                }
3967
                            }
3968
                        }
3969
                        // Add terms also to content to make terms findable by probabilistic search
3970
                        $file_content = $all_specific_terms.' '.$file_content;
3971
3972
                        if (!$simulation) {
3973
                            $ic_slide->addValue('content', $file_content);
3974
                            $di->addChunk($ic_slide);
3975
                            // Index and return a new search engine document id
3976
                            $did = $di->index();
3977
3978
                            if ($did) {
3979
                                // update the search_did on db
3980
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3981
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
3982
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
3983
                                Database::query($sql);
3984
                            }
3985
                        }
3986
                    }
3987
                } else {
3988
                    // Add all terms
3989
                    $all_specific_terms = '';
3990
                    foreach ($specific_fields as $specific_field) {
3991
                        if (isset($specific_fields_values[$specific_field['code']])) {
3992
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
3993
                        } else { //if the specific field is not defined, force an empty one
3994
                            $sterms = '';
3995
                        }
3996
                        $all_specific_terms .= ' '.$sterms;
3997
                        if (!empty($sterms)) {
3998
                            $sterms = explode(',', $sterms);
3999
                            foreach ($sterms as $sterm) {
4000
                                if (!$simulation) {
4001
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
4002
                                    add_specific_field_value(
4003
                                        $specific_field['id'],
4004
                                        $course_code,
4005
                                        TOOL_DOCUMENT,
4006
                                        $docid,
4007
                                        $sterm
4008
                                    );
4009
                                }
4010
                            }
4011
                        }
4012
                    }
4013
                    // Add terms also to content to make terms findable by probabilistic search
4014
                    $file_content = $all_specific_terms.' '.$file_content;
4015
                    if (!$simulation) {
4016
                        $ic_slide->addValue('content', $file_content);
4017
                        $di->addChunk($ic_slide);
4018
                        // Index and return search engine document id
4019
                        $did = $di->index();
4020
                        if ($did) {
4021
                            // Save it to db
4022
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4023
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
4024
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
4025
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
4026
                            Database::query($sql);
4027
                        } else {
4028
                            return false;
4029
                        }
4030
                    }
4031
                }
4032
            } else {
4033
                return false;
4034
            }
4035
        }
4036
4037
        return true;
4038
    }
4039
4040
    /**
4041
     * @return array
4042
     */
4043
    public static function get_web_odf_extension_list()
4044
    {
4045
        return ['ods', 'odt', 'odp'];
4046
    }
4047
4048
    /**
4049
     * Set of extension allowed to use Jodconverter.
4050
     *
4051
     * @param $mode 'from'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'from' at position 0 could not be parsed: Unknown type name ''from'' at position 0 in 'from'.
Loading history...
4052
     *              'to'
4053
     *              'all'
4054
     * @param $format   'text'
4055
     *                  'spreadsheet'
4056
     *                  'presentation'
4057
     *                  'drawing'
4058
     *                  'all'
4059
     *
4060
     * @return array
4061
     */
4062
    public static function getJodconverterExtensionList($mode, $format)
4063
    {
4064
        $extensionList = [];
4065
        $extensionListFromText = [
4066
            'odt',
4067
            'sxw',
4068
            'rtf',
4069
            'doc',
4070
            'docx',
4071
            'wpd',
4072
            'txt',
4073
        ];
4074
        $extensionListToText = [
4075
            'pdf',
4076
            'odt',
4077
            'sxw',
4078
            'rtf',
4079
            'doc',
4080
            'docx',
4081
            'txt',
4082
        ];
4083
        $extensionListFromSpreadsheet = [
4084
            'ods',
4085
            'sxc',
4086
            'xls',
4087
            'xlsx',
4088
            'csv',
4089
            'tsv',
4090
        ];
4091
        $extensionListToSpreadsheet = [
4092
            'pdf',
4093
            'ods',
4094
            'sxc',
4095
            'xls',
4096
            'xlsx',
4097
            'csv',
4098
            'tsv',
4099
        ];
4100
        $extensionListFromPresentation = [
4101
            'odp',
4102
            'sxi',
4103
            'ppt',
4104
            'pptx',
4105
        ];
4106
        $extensionListToPresentation = [
4107
            'pdf',
4108
            'swf',
4109
            'odp',
4110
            'sxi',
4111
            'ppt',
4112
            'pptx',
4113
        ];
4114
        $extensionListFromDrawing = ['odg'];
4115
        $extensionListToDrawing = ['svg', 'swf'];
4116
4117
        if ($mode === 'from') {
4118
            if ($format === 'text') {
4119
                $extensionList = array_merge($extensionList, $extensionListFromText);
4120
            } elseif ($format === 'spreadsheet') {
4121
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4122
            } elseif ($format === 'presentation') {
4123
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4124
            } elseif ($format === 'drawing') {
4125
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4126
            } elseif ($format === 'all') {
4127
                $extensionList = array_merge($extensionList, $extensionListFromText);
4128
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4129
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4130
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4131
            }
4132
        } elseif ($mode === 'to') {
4133
            if ($format === 'text') {
4134
                $extensionList = array_merge($extensionList, $extensionListToText);
4135
            } elseif ($format === 'spreadsheet') {
4136
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4137
            } elseif ($format === 'presentation') {
4138
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4139
            } elseif ($format === 'drawing') {
4140
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4141
            } elseif ($format === 'all') {
4142
                $extensionList = array_merge($extensionList, $extensionListToText);
4143
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4144
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4145
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4146
            }
4147
        } elseif ($mode === 'all') {
4148
            if ($format === 'text') {
4149
                $extensionList = array_merge($extensionList, $extensionListFromText);
4150
                $extensionList = array_merge($extensionList, $extensionListToText);
4151
            } elseif ($format === 'spreadsheet') {
4152
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4153
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4154
            } elseif ($format === 'presentation') {
4155
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4156
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4157
            } elseif ($format === 'drawing') {
4158
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4159
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4160
            } elseif ($format === 'all') {
4161
                $extensionList = array_merge($extensionList, $extensionListFromText);
4162
                $extensionList = array_merge($extensionList, $extensionListToText);
4163
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4164
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4165
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4166
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4167
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4168
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4169
            }
4170
        }
4171
4172
        return $extensionList;
4173
    }
4174
4175
    /**
4176
     * Get Format type list by extension and mode.
4177
     *
4178
     * @param string $mode Mode to search format type list
4179
     *
4180
     * @example 'from'
4181
     * @example 'to'
4182
     *
4183
     * @param string $extension file extension to check file type
4184
     *
4185
     * @return array
4186
     */
4187
    public static function getFormatTypeListConvertor($mode = 'from', $extension)
4188
    {
4189
        $formatTypesList = [];
4190
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
4191
        foreach ($formatTypes as $formatType) {
4192
            if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
4193
                $formatTypesList[] = $formatType;
4194
            }
4195
        }
4196
4197
        return $formatTypesList;
4198
    }
4199
4200
    /**
4201
     * @param string $path
4202
     * @param bool   $is_certificate_mode
4203
     *
4204
     * @return bool
4205
     */
4206
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
4207
    {
4208
        $foldersToAvoid = [
4209
            '/HotPotatoes_files',
4210
            '/certificates',
4211
        ];
4212
        $systemFolder = api_get_course_setting('show_system_folders');
4213
4214
        if ($systemFolder == 1) {
4215
            $foldersToAvoid = [];
4216
        }
4217
4218
        if (basename($path) == 'css') {
4219
            return true;
4220
        }
4221
4222
        if ($is_certificate_mode == false) {
4223
            //Certificate results
4224
            if (strstr($path, 'certificates')) {
4225
                return true;
4226
            }
4227
        }
4228
4229
        // Admin setting for Hide/Show the folders of all users
4230
        if (api_get_setting('show_users_folders') == 'false') {
4231
            $foldersToAvoid[] = '/shared_folder';
4232
4233
            if (strstr($path, 'shared_folder_session_')) {
4234
                return true;
4235
            }
4236
        }
4237
4238
        // Admin setting for Hide/Show Default folders to all users
4239
        if (api_get_setting('show_default_folders') == 'false') {
4240
            $foldersToAvoid[] = '/images';
4241
            $foldersToAvoid[] = '/flash';
4242
            $foldersToAvoid[] = '/audio';
4243
            $foldersToAvoid[] = '/video';
4244
        }
4245
4246
        // Admin setting for Hide/Show chat history folder
4247
        if (api_get_setting('show_chat_folder') == 'false') {
4248
            $foldersToAvoid[] = '/chat_files';
4249
        }
4250
4251
        if (is_array($foldersToAvoid)) {
4252
            return in_array($path, $foldersToAvoid);
4253
        } else {
4254
            return false;
4255
        }
4256
    }
4257
4258
    /**
4259
     * @return array
4260
     */
4261
    public static function get_system_folders()
4262
    {
4263
        return [
4264
            '/certificates',
4265
            '/HotPotatoes_files',
4266
            '/chat_files',
4267
            '/images',
4268
            '/flash',
4269
            '/audio',
4270
            '/video',
4271
            '/shared_folder',
4272
            '/learning_path',
4273
        ];
4274
    }
4275
4276
    /**
4277
     * @return array
4278
     */
4279
    public static function getProtectedFolderFromStudent()
4280
    {
4281
        return [
4282
            '/certificates',
4283
            '/HotPotatoes_files',
4284
            '/chat_files',
4285
            '/shared_folder',
4286
            '/learning_path',
4287
        ];
4288
    }
4289
4290
    /**
4291
     * @param array $courseInfo
4292
     *
4293
     * @return string 'visible' or 'invisible' string
4294
     */
4295
    public static function getDocumentDefaultVisibility($courseInfo)
4296
    {
4297
        $settings = api_get_setting('tool_visible_by_default_at_creation');
4298
        $defaultVisibility = 'visible';
4299
4300
        if (isset($settings['documents'])) {
4301
            $portalDefaultVisibility = 'invisible';
4302
            if ($settings['documents'] == 'true') {
4303
                $portalDefaultVisibility = 'visible';
4304
            }
4305
4306
            $defaultVisibility = $portalDefaultVisibility;
4307
        }
4308
4309
        if (api_get_setting('documents_default_visibility_defined_in_course') == 'true') {
4310
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseInfo);
4311
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
4312
                $defaultVisibility = $courseVisibility;
4313
            }
4314
        }
4315
4316
        return $defaultVisibility;
4317
    }
4318
4319
    /**
4320
     * @param array  $courseInfo
4321
     * @param int    $id         doc id
4322
     * @param string $visibility visible/invisible
4323
     * @param int    $userId
4324
     */
4325
    public static function updateVisibilityFromAllSessions($courseInfo, $id, $visibility, $userId)
4326
    {
4327
        $sessionList = SessionManager::get_session_by_course($courseInfo['real_id']);
4328
4329
        if (!empty($sessionList)) {
4330
            foreach ($sessionList as $session) {
4331
                $sessionId = $session['id'];
4332
                api_item_property_update(
4333
                    $courseInfo,
4334
                    TOOL_DOCUMENT,
4335
                    $id,
4336
                    $visibility,
4337
                    $userId,
4338
                    null,
4339
                    null,
4340
                    null,
4341
                    null,
4342
                    $sessionId
4343
                );
4344
            }
4345
        }
4346
    }
4347
4348
    /**
4349
     * @param string $filePath
4350
     * @param string $path
4351
     * @param array  $courseInfo
4352
     * @param int    $sessionId
4353
     * @param string $whatIfFileExists overwrite|rename
4354
     * @param int    $userId
4355
     * @param int    $groupId
4356
     * @param int    $toUserId
4357
     * @param string $comment
4358
     *
4359
     * @return bool|path
4360
     */
4361
    public static function addFileToDocumentTool(
4362
        $filePath,
4363
        $path,
4364
        $courseInfo,
4365
        $sessionId,
4366
        $userId,
4367
        $whatIfFileExists = 'overwrite',
4368
        $groupId = null,
4369
        $toUserId = null,
4370
        $comment = null
4371
    ) {
4372
        if (!file_exists($filePath)) {
4373
            return false;
4374
        }
4375
4376
        $fileInfo = pathinfo($filePath);
4377
4378
        $file = [
4379
            'name' => $fileInfo['basename'],
4380
            'tmp_name' => $filePath,
4381
            'size' => filesize($filePath),
4382
            'from_file' => true,
4383
        ];
4384
4385
        $course_dir = $courseInfo['path'].'/document';
4386
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
4387
4388
        $filePath = handle_uploaded_document(
4389
            $courseInfo,
4390
            $file,
4391
            $baseWorkDir,
4392
            $path,
4393
            $userId,
4394
            $groupId,
4395
            $toUserId,
4396
            false,
4397
            $whatIfFileExists,
4398
            false,
4399
            false,
4400
            $comment,
4401
            $sessionId
4402
        );
4403
4404
        if ($filePath) {
4405
            return self::get_document_id(
4406
                $courseInfo,
4407
                $filePath,
4408
                $sessionId
4409
            );
4410
        }
4411
4412
        return false;
4413
    }
4414
4415
    /**
4416
     * Converts wav to mp3 file.
4417
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg.
4418
     *
4419
     * @param string $wavFile
4420
     * @param bool   $removeWavFileIfSuccess
4421
     *
4422
     * @return bool
4423
     */
4424
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
4425
    {
4426
        if (file_exists($wavFile)) {
4427
            try {
4428
                $ffmpeg = \FFMpeg\FFMpeg::create();
4429
                $video = $ffmpeg->open($wavFile);
4430
4431
                $mp3File = str_replace('wav', 'mp3', $wavFile);
4432
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
4433
                if ($result && $removeWavFileIfSuccess) {
4434
                    unlink($wavFile);
4435
                }
4436
4437
                if (file_exists($mp3File)) {
4438
                    return $mp3File;
4439
                }
4440
            } catch (Exception $e) {
4441
                error_log($e->getMessage());
4442
                error_log($e->getPrevious()->getMessage());
4443
            }
4444
        }
4445
4446
        return false;
4447
    }
4448
4449
    /**
4450
     * @param string $documentData     wav document information
4451
     * @param array  $courseInfo
4452
     * @param int    $sessionId
4453
     * @param int    $userId           user that adds the document
4454
     * @param string $whatIfFileExists
4455
     * @param bool   $deleteWavFile
4456
     *
4457
     * @return bool
4458
     */
4459
    public static function addAndConvertWavToMp3(
4460
        $documentData,
4461
        $courseInfo,
4462
        $sessionId,
4463
        $userId,
4464
        $whatIfFileExists = 'overwrite',
4465
        $deleteWavFile = false
4466
    ) {
4467
        if (empty($documentData)) {
4468
            return false;
4469
        }
4470
4471
        if (isset($documentData['absolute_path']) &&
4472
            file_exists($documentData['absolute_path'])
4473
        ) {
4474
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4475
4476
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4477
                $documentId = self::addFileToDocumentTool(
4478
                    $mp3FilePath,
4479
                    dirname($documentData['path']),
4480
                    $courseInfo,
4481
                    $sessionId,
4482
                    $userId,
4483
                    $whatIfFileExists,
4484
                    null,
4485
                    null,
4486
                    $documentData['comment']
4487
                );
4488
4489
                if (!empty($documentId)) {
4490
                    if ($deleteWavFile) {
4491
                        $coursePath = $courseInfo['directory'].'/document';
4492
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4493
                        self::delete_document(
4494
                            $courseInfo,
4495
                            null,
4496
                            $documentPath,
4497
                            $sessionId,
4498
                            $documentData['id']
4499
                        );
4500
                    }
4501
4502
                    return $documentId;
4503
                }
4504
            }
4505
        }
4506
4507
        return false;
4508
    }
4509
4510
    /**
4511
     * Sets.
4512
     *
4513
     * @param string $file         ($document_data['path'])
4514
     * @param string $file_url_sys
4515
     *
4516
     * @return string
4517
     */
4518
    public static function generateAudioTempFile($file, $file_url_sys)
4519
    {
4520
        //make temp audio
4521
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4522
        if (!file_exists($temp_folder)) {
4523
            @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);
4524
        }
4525
4526
        //make htaccess with allow from all, and file index.html into temp/audio
4527
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4528
        if (!file_exists($htaccess)) {
4529
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4530
            $fp = @fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4531
            if ($fp) {
4532
                fwrite($fp, $htaccess_content);
4533
                fclose($fp);
4534
            }
4535
        }
4536
4537
        //encript temp name file
4538
        $name_crip = sha1(uniqid()); //encript
4539
        $findext = explode(".", $file);
4540
        $extension = $findext[count($findext) - 1];
4541
        $file_crip = $name_crip.'.'.$extension;
4542
4543
        //copy file to temp/audio directory
4544
        $from_sys = $file_url_sys;
4545
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4546
4547
        if (file_exists($from_sys)) {
4548
            copy($from_sys, $to_sys);
4549
        }
4550
4551
        // get file from tmp directory
4552
        Session::write('temp_audio_nanogong', $to_sys);
4553
4554
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4555
    }
4556
4557
    /**
4558
     * Erase temp nanogong audio.
4559
     */
4560
    public static function removeGeneratedAudioTempFile()
4561
    {
4562
        $tempAudio = Session::read('temp_audio_nanogong');
4563
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4564
            unlink($tempAudio);
4565
            Session::erase('temp_audio_nanogong');
4566
        }
4567
    }
4568
4569
    /**
4570
     * Check if the path is used in this course.
4571
     *
4572
     * @param array  $courseInfo
4573
     * @param string $path
4574
     *
4575
     * @return array
4576
     */
4577
    public static function getDocumentByPathInCourse($courseInfo, $path)
4578
    {
4579
        $table = Database::get_course_table(TABLE_DOCUMENT);
4580
        $path = Database::escape_string($path);
4581
        $courseId = $courseInfo['real_id'];
4582
        if (empty($courseId)) {
4583
            return false;
4584
        }
4585
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4586
        $result = Database::query($sql);
4587
4588
        return Database::store_result($result, 'ASSOC');
4589
    }
4590
4591
    /**
4592
     * @param array $_course
4593
     *
4594
     * @return int
4595
     */
4596
    public static function createDefaultAudioFolder($_course)
4597
    {
4598
        if (!isset($_course['path'])) {
4599
            return false;
4600
        }
4601
4602
        $audioId = null;
4603
        $path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
4604
        if (!is_dir($path.'audio')) {
4605
            mkdir($path.'audio', api_get_permissions_for_new_directories());
4606
            $audioId = add_document($_course, '/audio', 'folder', 0, get_lang('Audio'));
4607
            api_item_property_update(
4608
                $_course,
4609
                TOOL_DOCUMENT,
4610
                $audioId,
4611
                'FolderCreated',
4612
                api_get_user_id(),
4613
                null,
4614
                null,
4615
                null,
4616
                null,
4617
                api_get_session_id()
4618
            );
4619
        }
4620
4621
        return $audioId;
4622
    }
4623
4624
    /**
4625
     * Generate a default certificate for a courses.
4626
     *
4627
     * @todo move to certificate lib
4628
     *
4629
     * @global string $css CSS directory
4630
     * @global string $img_dir image directory
4631
     * @global string $default_course_dir Course directory
4632
     * @global string $js JS directory
4633
     *
4634
     * @param array $courseData     The course info
4635
     * @param bool  $fromBaseCourse
4636
     * @param int   $sessionId
4637
     */
4638
    public static function generateDefaultCertificate(
4639
        $courseData,
4640
        $fromBaseCourse = false,
4641
        $sessionId = 0
4642
    ) {
4643
        if (empty($courseData)) {
4644
            return false;
4645
        }
4646
4647
        global $css, $img_dir, $default_course_dir, $js;
4648
        $codePath = api_get_path(REL_CODE_PATH);
4649
        $dir = '/certificates';
4650
        $comment = null;
4651
        $title = get_lang('DefaultCertificate');
4652
        $fileName = api_replace_dangerous_char($title);
4653
        $filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4654
4655
        if (!is_dir($filePath)) {
4656
            mkdir($filePath, api_get_permissions_for_new_directories());
4657
        }
4658
4659
        $fileFullPath = "$filePath/$fileName.html";
4660
        $fileType = 'file';
4661
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4662
4663
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4664
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4665
4666
        $fileContent = str_replace($search, $replace, $templateContent);
4667
        $saveFilePath = "$dir/$fileName.html";
4668
4669
        if ($fromBaseCourse) {
4670
            $defaultCertificateId = self::get_default_certificate_id(
4671
                $courseData['code'],
4672
                0
4673
            );
4674
            if (!empty($defaultCertificateId)) {
4675
                // We have a certificate from the course base
4676
                $documentData = self::get_document_data_by_id(
4677
                    $defaultCertificateId,
4678
                    $courseData['code'],
4679
                    false,
4680
                    0
4681
                );
4682
4683
                if ($documentData) {
4684
                    $fileContent = file_get_contents($documentData['absolute_path']);
4685
                }
4686
            }
4687
        }
4688
4689
        if (file_exists($fileFullPath) === false) {
4690
            $result = file_put_contents($fileFullPath, $fileContent);
4691
            if ($result) {
4692
                $fileSize = filesize($fileFullPath);
4693
4694
                $documentId = add_document(
4695
                    $courseData,
4696
                    $saveFilePath,
4697
                    $fileType,
4698
                    $fileSize,
4699
                    $title,
4700
                    $comment,
4701
                    0, //$readonly = 0,
4702
                    true, //$save_visibility = true,
4703
                    null, //$group_id = null,
4704
                    $sessionId
4705
                );
4706
4707
                api_item_property_update(
4708
                    $courseData,
4709
                    TOOL_DOCUMENT,
4710
                    $documentId,
4711
                    'DocumentAdded',
4712
                    api_get_user_id(),
4713
                    null,
4714
                    null,
4715
                    null,
4716
                    null,
4717
                    $sessionId
4718
                );
4719
4720
                $defaultCertificateId = self::get_default_certificate_id(
4721
                    $courseData['code'],
4722
                    $sessionId
4723
                );
4724
4725
                if (!isset($defaultCertificateId)) {
4726
                    self::attach_gradebook_certificate(
4727
                        $courseData['code'],
4728
                        $documentId,
4729
                        $sessionId
4730
                    );
4731
                }
4732
            }
4733
        }
4734
    }
4735
4736
    /**
4737
     * Update the document name.
4738
     *
4739
     * @param int    $documentId The document id
4740
     * @param string $newName    The new name
4741
     */
4742
    public static function renameDocument($documentId, $newName)
4743
    {
4744
        $documentId = intval($documentId);
4745
        $newName = Database::escape_string($newName);
4746
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4747
4748
        $values = [
4749
            'title' => $newName,
4750
        ];
4751
4752
        $whereConditions = [
4753
            'id = ?' => $documentId,
4754
        ];
4755
4756
        Database::update($docuentTable, $values, $whereConditions);
4757
    }
4758
4759
    /**
4760
     * Get folder/file suffix.
4761
     *
4762
     * @param array $courseInfo
4763
     * @param int   $sessionId
4764
     * @param int   $groupId
4765
     *
4766
     * @return string
4767
     */
4768
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4769
    {
4770
        // If no session or group, then no suffix.
4771
        if (empty($sessionId) && empty($groupId)) {
4772
            return '';
4773
        }
4774
4775
        return '__'.intval($sessionId).'__'.intval($groupId);
4776
    }
4777
4778
    /**
4779
     * Fix a document name adding session id and group id
4780
     * Turns picture.jpg -> picture__1__2.jpg
4781
     * Where 1 = session id and 2 group id
4782
     * Of session id and group id are empty then the function returns:
4783
     * picture.jpg ->  picture.jpg.
4784
     *
4785
     * @param string $name       folder or file name
4786
     * @param string $type       'folder' or 'file'
4787
     * @param array  $courseInfo
4788
     * @param int    $sessionId
4789
     * @param int    $groupId
4790
     *
4791
     * @return string
4792
     */
4793
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4794
    {
4795
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4796
4797
        switch ($type) {
4798
            case 'folder':
4799
                $name = $name.$suffix;
4800
                break;
4801
            case 'file':
4802
                $name = self::addSuffixToFileName($name, $suffix);
4803
                break;
4804
        }
4805
4806
        return $name;
4807
    }
4808
4809
    /**
4810
     * Add a suffix to a file Example:
4811
     * /folder/picture.jpg => to /folder/picture_this.jpg
4812
     * where "_this" is the suffix.
4813
     *
4814
     * @param string $name
4815
     * @param string $suffix
4816
     *
4817
     * @return string
4818
     */
4819
    public static function addSuffixToFileName($name, $suffix)
4820
    {
4821
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4822
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4823
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4824
4825
        if ($dir == '.') {
4826
            $dir = null;
4827
        }
4828
4829
        if (!empty($dir) && $dir != '/') {
4830
            $dir = $dir.'/';
4831
        }
4832
4833
        $name = $dir.$fileName.$suffix.'.'.$extension;
4834
4835
        return $name;
4836
    }
4837
4838
    /**
4839
     * Check if folder exist in the course base or in the session course.
4840
     *
4841
     * @param string $folder     Example: /folder/folder2
4842
     * @param array  $courseInfo
4843
     * @param int    $sessionId
4844
     * @param int    $groupId    group.id
4845
     *
4846
     * @return bool
4847
     */
4848
    public static function folderExists(
4849
        $folder,
4850
        $courseInfo,
4851
        $sessionId,
4852
        $groupId
4853
    ) {
4854
        $courseId = $courseInfo['real_id'];
4855
4856
        if (empty($courseId)) {
4857
            return false;
4858
        }
4859
4860
        $sessionId = (int) $sessionId;
4861
        $folderWithSuffix = self::fixDocumentName(
4862
            $folder,
4863
            'folder',
4864
            $courseInfo,
4865
            $sessionId,
4866
            $groupId
4867
        );
4868
4869
        $folder = Database::escape_string($folder);
4870
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4871
4872
        // Check if pathname already exists inside document table
4873
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4874
        $sql = "SELECT id, path FROM $tbl_document
4875
                WHERE
4876
                    filetype = 'folder' AND
4877
                    c_id = $courseId AND
4878
                    (path = '$folder' OR path = '$folderWithSuffix') AND
4879
                    (session_id = 0 OR session_id = $sessionId)
4880
        ";
4881
4882
        $rs = Database::query($sql);
4883
        if (Database::num_rows($rs)) {
4884
            return true;
4885
        }
4886
4887
        return false;
4888
    }
4889
4890
    /**
4891
     * Check if file exist in the course base or in the session course.
4892
     *
4893
     * @param string $fileName   Example: /folder/picture.jpg
4894
     * @param array  $courseInfo
4895
     * @param int    $sessionId
4896
     * @param int    $groupId
4897
     *
4898
     * @return bool
4899
     */
4900
    public static function documentExists(
4901
        $fileName,
4902
        $courseInfo,
4903
        $sessionId,
4904
        $groupId
4905
    ) {
4906
        $courseId = $courseInfo['real_id'];
4907
4908
        if (empty($courseId)) {
4909
            return false;
4910
        }
4911
4912
        $sessionId = (int) $sessionId;
4913
        $fileNameEscape = Database::escape_string($fileName);
4914
4915
        $fileNameWithSuffix = self::fixDocumentName(
4916
            $fileName,
4917
            'file',
4918
            $courseInfo,
4919
            $sessionId,
4920
            $groupId
4921
        );
4922
4923
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
4924
4925
        // Check if pathname already exists inside document table
4926
        $table = Database::get_course_table(TABLE_DOCUMENT);
4927
        $sql = "SELECT id, path FROM $table
4928
                WHERE
4929
                    filetype = 'file' AND
4930
                    c_id = $courseId AND
4931
                    (
4932
                        path = '".$fileNameEscape."' OR
4933
                        path = '$fileNameWithSuffix'
4934
                    ) AND
4935
                    (session_id = 0 OR session_id = $sessionId)
4936
        ";
4937
        $rs = Database::query($sql);
4938
        if (Database::num_rows($rs)) {
4939
            return true;
4940
        }
4941
4942
        return false;
4943
    }
4944
4945
    /**
4946
     * Undo the suffix applied to a file example:
4947
     * turns picture__1__1.jpg to picture.jpg.
4948
     *
4949
     * @param string $name
4950
     * @param int    $courseId
4951
     * @param int    $sessionId
4952
     * @param int    $groupId
4953
     *
4954
     * @return string
4955
     */
4956
    public static function undoFixDocumentName(
4957
        $name,
4958
        $courseId,
4959
        $sessionId,
4960
        $groupId
4961
    ) {
4962
        if (empty($sessionId) && empty($groupId)) {
4963
            return $name;
4964
        }
4965
4966
        $suffix = self::getDocumentSuffix(
4967
            ['real_id' => $courseId],
4968
            $sessionId,
4969
            $groupId
4970
        );
4971
4972
        $name = str_replace($suffix, '', $name);
4973
4974
        return $name;
4975
    }
4976
4977
    /**
4978
     * @param string $path
4979
     * @param string $name
4980
     * @param array  $courseInfo
4981
     * @param int    $sessionId
4982
     * @param int    $groupId
4983
     *
4984
     * @return string
4985
     */
4986
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
4987
    {
4988
        $counter = 1;
4989
        $filePath = $path.$name;
4990
        $uniqueName = $name;
4991
        while ($documentExists = self::documentExists(
4992
            $filePath,
4993
            $courseInfo,
4994
            $sessionId,
4995
            $groupId
4996
        )) {
4997
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
4998
            $filePath = $path.$uniqueName;
4999
            $counter++;
5000
        }
5001
5002
        return $uniqueName;
5003
    }
5004
5005
    /**
5006
     * Builds the form that enables the user to
5007
     * select a directory to browse/upload in.
5008
     *
5009
     * @param array    An array containing the folders we want to be able to select
5010
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
5011
     * @param string    Group directory, if empty, prevents documents to be uploaded
5012
     * (because group documents cannot be uploaded in root)
5013
     * @param bool    Whether to change the renderer (this will add a template <span>
5014
     * to the QuickForm object displaying the form)
5015
     *
5016
     * @return string html form
5017
     */
5018
    public static function build_directory_selector(
5019
        $folders,
5020
        $document_id,
5021
        $group_dir = '',
5022
        $change_renderer = false,
5023
        &$form = null,
5024
        $selectName = 'id',
5025
        $attributes = []
5026
    ) {
5027
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
5028
        $course_id = api_get_course_int_id();
5029
        $folder_titles = [];
5030
5031
        if (is_array($folders)) {
5032
            $escaped_folders = [];
5033
            foreach ($folders as $key => &$val) {
5034
                $escaped_folders[$key] = Database::escape_string($val);
5035
            }
5036
            $folder_sql = implode("','", $escaped_folders);
5037
5038
            $sql = "SELECT path, title
5039
                    FROM $doc_table
5040
                    WHERE
5041
                        filetype = 'folder' AND
5042
                        c_id = $course_id AND
5043
                        path IN ('".$folder_sql."')";
5044
            $res = Database::query($sql);
5045
            $folder_titles = [];
5046
            while ($obj = Database::fetch_object($res)) {
5047
                $folder_titles[$obj->path] = $obj->title;
5048
            }
5049
        }
5050
5051
        if (empty($form)) {
5052
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
5053
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
5054
        }
5055
        $form->addElement('hidden', 'cidReq', api_get_course_id());
5056
        $form->addElement('hidden', 'id_session', api_get_session_id());
5057
        $form->addElement('hidden', 'gidReq', api_get_group_id());
5058
5059
        $parent_select = $form->addSelect(
5060
            $selectName,
5061
            get_lang('CurrentDirectory'),
5062
            '',
5063
            $attributes
5064
        );
5065
5066
        // Group documents cannot be uploaded in the root
5067
        if (empty($group_dir)) {
5068
            $parent_select->addOption(get_lang('Documents'), '/');
5069
5070
            if (is_array($folders)) {
5071
                foreach ($folders as $folder_id => &$folder) {
5072
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5073
                    $path_parts = explode('/', $folder);
5074
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
5075
                    $counter = count($path_parts) - 2;
5076
                    if ($counter > 0) {
5077
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
5078
                    } else {
5079
                        $label = ' &mdash; '.$folder_titles[$folder];
5080
                    }
5081
                    $parent_select->addOption($label, $folder_id);
5082
                    if ($selected != '') {
5083
                        $parent_select->setSelected($folder_id);
5084
                    }
5085
                }
5086
            }
5087
        } else {
5088
            if (!empty($folders)) {
5089
                foreach ($folders as $folder_id => &$folder) {
5090
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5091
                    $label = $folder_titles[$folder];
5092
                    if ($folder == $group_dir) {
5093
                        $label = get_lang('Documents');
5094
                    } else {
5095
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
5096
                        $label = cut($label, 80);
5097
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
5098
                    }
5099
                    $parent_select->addOption($label, $folder_id);
5100
                    if ($selected != '') {
5101
                        $parent_select->setSelected($folder_id);
5102
                    }
5103
                }
5104
            }
5105
        }
5106
5107
        return $form->toHtml();
5108
    }
5109
5110
    /**
5111
     * Create a html hyperlink depending on if it's a folder or a file.
5112
     *
5113
     * @param string $documentWebPath
5114
     * @param array  $document_data
5115
     * @param bool   $show_as_icon      - if it is true, only a clickable icon will be shown
5116
     * @param int    $visibility        (1/0)
5117
     * @param int    $counter
5118
     * @param int    $size
5119
     * @param bool   $isAllowedToEdit
5120
     * @param bool   $isCertificateMode
5121
     *
5122
     * @return string url
5123
     */
5124
    public static function create_document_link(
5125
        $documentWebPath,
5126
        $document_data,
5127
        $show_as_icon = false,
5128
        $counter = null,
5129
        $visibility,
5130
        $size = 0,
5131
        $isAllowedToEdit = false,
5132
        $isCertificateMode = false
5133
    ) {
5134
        global $dbl_click_id;
5135
        $www = $documentWebPath;
5136
5137
        $sessionId = api_get_session_id();
5138
        $courseParams = api_get_cidreq();
5139
        $webODFList = self::get_web_odf_extension_list();
5140
5141
        // Get the title or the basename depending on what we're using
5142
        if ($document_data['title'] != '') {
5143
            $title = $document_data['title'];
5144
        } else {
5145
            $title = basename($document_data['path']);
5146
        }
5147
5148
        if (api_get_configuration_value('save_titles_as_html')) {
5149
            $title = strip_tags($title);
5150
        }
5151
5152
        $filetype = $document_data['filetype'];
5153
        $path = $document_data['path'];
5154
        $url_path = urlencode($document_data['path']);
5155
5156
        $basePageUrl = api_get_path(WEB_CODE_PATH).'document/';
5157
        $pageUrl = $basePageUrl.'document.php';
5158
5159
        // Add class="invisible" on invisible files
5160
        $visibility_class = $visibility == false ? ' class="muted"' : '';
5161
        $forcedownload_link = '';
5162
        $forcedownload_icon = '';
5163
        $prevent_multiple_click = '';
5164
        $force_download_html = '';
5165
5166
        if (!$show_as_icon) {
5167
            // Build download link (icon)
5168
            $forcedownload_link = $filetype == 'folder'
5169
                ? $pageUrl.'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id']
5170
                : $pageUrl.'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
5171
            // Folder download or file download?
5172
            $forcedownload_icon = $filetype == 'folder' ? 'save_pack.png' : 'save.png';
5173
            // Prevent multiple clicks on zipped folder download
5174
            $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; }\"" : '';
5175
        }
5176
5177
        $target = '_self';
5178
        $is_browser_viewable_file = false;
5179
5180
        if ($filetype == 'file') {
5181
            // Check the extension
5182
            $ext = explode('.', $path);
5183
            $ext = strtolower($ext[count($ext) - 1]);
5184
5185
            // HTML-files an some other types are shown in a frameset by default.
5186
            $is_browser_viewable_file = self::isBrowserViewable($ext);
5187
            if ($is_browser_viewable_file) {
5188
                if ($ext == 'pdf' || in_array($ext, $webODFList)) {
5189
                    $url = $pageUrl.'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
5190
                } else {
5191
                    $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5192
                }
5193
            } else {
5194
                // url-encode for problematic characters (we may not call them dangerous characters...)
5195
                //$path = str_replace('%2F', '/', $url_path).'?'.$courseParams;
5196
                $url = $www.str_replace('%2F', '/', $url_path).'?'.$courseParams;
5197
            }
5198
        } else {
5199
            $url = $pageUrl.'?'.$courseParams.'&id='.$document_data['id'];
5200
        }
5201
5202
        if ($isCertificateMode) {
5203
            $url .= '&certificate=true&selectcat='.(isset($_GET['selectcat']) ? $_GET['selectcat'] : '');
5204
        }
5205
5206
        // The little download icon
5207
        $tooltip_title = $title;
5208
        $tooltip_title_alt = $tooltip_title;
5209
5210
        if ($filetype == 'link') {
5211
            $tooltip_title_alt = $title;
5212
            $url = $document_data['comment'].'" target="_blank';
5213
        }
5214
5215
        if ($path == '/shared_folder') {
5216
            $tooltip_title_alt = get_lang('UserFolders');
5217
        } elseif (strstr($path, 'shared_folder_session_')) {
5218
            $tooltip_title_alt = get_lang('UserFolders').' ('.api_get_session_name(api_get_session_id()).')';
5219
        } elseif (strstr($tooltip_title, 'sf_user_')) {
5220
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
5221
            $tooltip_title_alt = get_lang('UserFolder').' '.$userinfo['complete_name'];
5222
        } elseif ($path == '/chat_files') {
5223
            $tooltip_title_alt = get_lang('ChatFiles');
5224
        } elseif ($path == '/learning_path') {
5225
            $tooltip_title_alt = get_lang('LearningPaths');
5226
        } elseif ($path == '/video') {
5227
            $tooltip_title_alt = get_lang('Video');
5228
        } elseif ($path == '/audio') {
5229
            $tooltip_title_alt = get_lang('Audio');
5230
        } elseif ($path == '/flash') {
5231
            $tooltip_title_alt = get_lang('Flash');
5232
        } elseif ($path == '/images') {
5233
            $tooltip_title_alt = get_lang('Images');
5234
        } elseif ($path == '/images/gallery') {
5235
            $tooltip_title_alt = get_lang('DefaultCourseImages');
5236
        }
5237
5238
        $copyToMyFiles = $open_in_new_window_link = '';
5239
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
5240
        $send_to = null;
5241
        $checkExtension = $path;
5242
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5243
        $document_data['file_extension'] = $extension;
5244
5245
        if (!$show_as_icon) {
5246
            if ($filetype == 'folder') {
5247
                if ($isAllowedToEdit ||
5248
                    api_is_platform_admin() ||
5249
                    api_get_setting('students_download_folders') == 'true'
5250
                ) {
5251
                    // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
5252
                    if (self::is_shared_folder($curdirpath, $sessionId)) {
5253
                        if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5254
                            preg_match('/shared_folder_session_'.$sessionId.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5255
                            $isAllowedToEdit || api_is_platform_admin()
5256
                        ) {
5257
                            $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5258
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5259
                        }
5260
                    } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
5261
                        $isAllowedToEdit ||
5262
                        api_is_platform_admin()
5263
                    ) {
5264
                        $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5265
                            Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5266
                    }
5267
                }
5268
            } else {
5269
                $force_download_html = $size == 0 ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
5270
                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5271
            }
5272
5273
            // Copy files to user's myfiles
5274
            if (api_get_setting('allow_my_files') === 'true' &&
5275
                api_get_setting('users_copy_files') === 'true' && api_is_anonymous() === false
5276
            ) {
5277
                $copy_myfiles_link = $filetype === 'file' ? $pageUrl.'?'.$courseParams.'&action=copytomyfiles&id='.$document_data['id'] : api_get_self().'?'.$courseParams;
5278
                if ($filetype === 'file') {
5279
                    $copyToMyFiles = '<a href="'.$copy_myfiles_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5280
                        Display::return_icon('briefcase.png', get_lang('CopyToMyFiles'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5281
5282
                    if (api_get_setting('allow_my_files') === 'false') {
5283
                        $copyToMyFiles = '';
5284
                    }
5285
                }
5286
            }
5287
5288
            $pdf_icon = '';
5289
            if (!$isAllowedToEdit &&
5290
                $filetype === 'file' &&
5291
                api_get_setting('students_export2pdf') == 'true' &&
5292
                in_array($extension, ['html', 'htm'])
5293
            ) {
5294
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.$pageUrl.'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&curdirpath='.$curdirpath.'">'.
5295
                    Display::return_icon('pdf.png', get_lang('Export2PDF'), [], ICON_SIZE_SMALL).'</a> ';
5296
            }
5297
5298
            if ($is_browser_viewable_file) {
5299
                $open_in_new_window_link = '<a href="'.$www.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
5300
                    Display::return_icon('open_in_new_window.png', get_lang('OpenInANewWindow'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5301
            }
5302
5303
            if ($filetype === 'file') {
5304
                // Sound preview
5305
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5306
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
5307
                    preg_match('/ogg$/i', urldecode($checkExtension))
5308
                ) {
5309
                    return '<span style="float:left" '.$visibility_class.'>'.
5310
                        $title.
5311
                        '</span>'.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5312
                } elseif (
5313
                    // Show preview
5314
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
5315
                    preg_match('/png$/i', urldecode($checkExtension)) ||
5316
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
5317
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
5318
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5319
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
5320
                    preg_match('/svg$/i', urldecode($checkExtension))
5321
                ) {
5322
                    // Simpler version of showinframesmin.php with no headers
5323
                    $url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
5324
                    $class = 'ajax';
5325
                    if ($visibility == false) {
5326
                        $class = 'ajax text-muted';
5327
                    }
5328
5329
                    return Display::url(
5330
                            $title,
5331
                            $url,
5332
                            [
5333
                                'class' => $class,
5334
                                'title' => $tooltip_title_alt,
5335
                                'data-title' => $title,
5336
                                'style' => 'float:left;',
5337
                            ]
5338
                        )
5339
                        .$force_download_html.$send_to.$copyToMyFiles
5340
                        .$open_in_new_window_link.$pdf_icon;
5341
                } else {
5342
                    // For a "PDF Download" of the file.
5343
                    $pdfPreview = null;
5344
                    if ($ext != 'pdf' && !in_array($ext, $webODFList)) {
5345
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5346
                    } else {
5347
                        $pdfPreview = Display::url(
5348
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
5349
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
5350
                            ['style' => 'float:right']
5351
                        );
5352
                    }
5353
                    // No plugin just the old and good showinframes.php page
5354
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
5355
                        $pdfPreview.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5356
                }
5357
            } else {
5358
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'.
5359
                    $force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5360
            }
5361
            // end copy files to users myfiles
5362
        } else {
5363
            // Icon column
5364
            if (preg_match('/shared_folder/', urldecode($checkExtension)) &&
5365
                preg_match('/shared_folder$/', urldecode($checkExtension)) == false &&
5366
                preg_match('/shared_folder_session_'.$sessionId.'$/', urldecode($url)) == false
5367
            ) {
5368
                if ($filetype == 'file') {
5369
                    //Sound preview
5370
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5371
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5372
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5373
                        return self::generateAudioPreview($documentWebPath, $document_data);
5374
                    } elseif (
5375
                        // Show preview
5376
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5377
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5378
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5379
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5380
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5381
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5382
                        preg_match('/svg$/i', urldecode($checkExtension))
5383
                    ) {
5384
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5385
5386
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5387
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5388
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5389
                            '</a>';
5390
                    } else {
5391
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5392
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5393
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5394
                            '</a>';
5395
                    }
5396
                } else {
5397
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5398
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5399
                        Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5400
                        '</a>';
5401
                }
5402
            } else {
5403
                if ($filetype === 'file') {
5404
                    // Sound preview with jplayer
5405
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5406
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5407
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5408
                        return self::generateAudioPreview($documentWebPath, $document_data);
5409
                    } elseif (
5410
                        //Show preview
5411
                        preg_match('/html$/i', urldecode($checkExtension)) ||
5412
                        preg_match('/htm$/i', urldecode($checkExtension)) ||
5413
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5414
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5415
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5416
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5417
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5418
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5419
                        preg_match('/svg$/i', urldecode($checkExtension))
5420
                    ) {
5421
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
5422
5423
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5424
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5425
                            '</a>';
5426
                    } else {
5427
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5428
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5429
                            '</a>';
5430
                    }
5431
                } else {
5432
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5433
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5434
                        '</a>';
5435
                }
5436
            }
5437
        }
5438
    }
5439
5440
    /**
5441
     * Builds an img html tag for the file type.
5442
     *
5443
     * @param string $type            (file/folder)
5444
     * @param string $path
5445
     * @param bool   $isAllowedToEdit
5446
     *
5447
     * @return string img html tag
5448
     */
5449
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
5450
    {
5451
        $basename = basename($path);
5452
        $sessionId = api_get_session_id();
5453
        if (is_null($isAllowedToEdit)) {
5454
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
5455
        }
5456
        $user_image = false;
5457
        if ($type == 'file') {
5458
            $icon = choose_image($basename);
5459
            $basename = substr(strrchr($basename, '.'), 1);
5460
        } elseif ($type == 'link') {
5461
            $icon = 'clouddoc.png';
5462
            $basename = get_lang('CloudFileLink');
5463
        } else {
5464
            if ($path == '/shared_folder') {
5465
                $icon = 'folder_users.png';
5466
                if ($isAllowedToEdit) {
5467
                    $basename = get_lang('HelpUsersFolder');
5468
                } else {
5469
                    $basename = get_lang('UserFolders');
5470
                }
5471
            } elseif (strstr($basename, 'sf_user_')) {
5472
                $userInfo = api_get_user_info(substr($basename, 8));
5473
                $icon = $userInfo['avatar_small'];
5474
                $basename = get_lang('UserFolder').' '.$userInfo['complete_name'];
5475
                $user_image = true;
5476
            } elseif (strstr($path, 'shared_folder_session_')) {
5477
                $sessionName = api_get_session_name($sessionId);
5478
                if ($isAllowedToEdit) {
5479
                    $basename = '***('.$sessionName.')*** '.get_lang('HelpUsersFolder');
5480
                } else {
5481
                    $basename = get_lang('UserFolders').' ('.$sessionName.')';
5482
                }
5483
                $icon = 'folder_users.png';
5484
            } else {
5485
                $icon = 'folder_document.png';
5486
5487
                if ($path == '/audio') {
5488
                    $icon = 'folder_audio.png';
5489
                    if ($isAllowedToEdit) {
5490
                        $basename = get_lang('HelpDefaultDirDocuments');
5491
                    } else {
5492
                        $basename = get_lang('Audio');
5493
                    }
5494
                } elseif ($path == '/flash') {
5495
                    $icon = 'folder_flash.png';
5496
                    if ($isAllowedToEdit) {
5497
                        $basename = get_lang('HelpDefaultDirDocuments');
5498
                    } else {
5499
                        $basename = get_lang('Flash');
5500
                    }
5501
                } elseif ($path == '/images') {
5502
                    $icon = 'folder_images.png';
5503
                    if ($isAllowedToEdit) {
5504
                        $basename = get_lang('HelpDefaultDirDocuments');
5505
                    } else {
5506
                        $basename = get_lang('Images');
5507
                    }
5508
                } elseif ($path == '/video') {
5509
                    $icon = 'folder_video.png';
5510
                    if ($isAllowedToEdit) {
5511
                        $basename = get_lang('HelpDefaultDirDocuments');
5512
                    } else {
5513
                        $basename = get_lang('Video');
5514
                    }
5515
                } elseif ($path == '/images/gallery') {
5516
                    $icon = 'folder_gallery.png';
5517
                    if ($isAllowedToEdit) {
5518
                        $basename = get_lang('HelpDefaultDirDocuments');
5519
                    } else {
5520
                        $basename = get_lang('Gallery');
5521
                    }
5522
                } elseif ($path == '/chat_files') {
5523
                    $icon = 'folder_chat.png';
5524
                    if ($isAllowedToEdit) {
5525
                        $basename = get_lang('HelpFolderChat');
5526
                    } else {
5527
                        $basename = get_lang('ChatFiles');
5528
                    }
5529
                } elseif ($path == '/learning_path') {
5530
                    $icon = 'folder_learningpath.png';
5531
                    if ($isAllowedToEdit) {
5532
                        $basename = get_lang('HelpFolderLearningPaths');
5533
                    } else {
5534
                        $basename = get_lang('LearningPaths');
5535
                    }
5536
                }
5537
            }
5538
        }
5539
5540
        if ($user_image) {
5541
            return Display::img($icon, $basename, [], false);
5542
        }
5543
5544
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5545
    }
5546
5547
    /**
5548
     * Creates the row of edit icons for a file/folder.
5549
     *
5550
     * @param array $document_data
5551
     * @param int   $id
5552
     * @param bool  $is_template
5553
     * @param int   $is_read_only
5554
     * @param int   $visibility    (1/0)
5555
     *
5556
     * @return string html img tags with hyperlinks
5557
     */
5558
    public static function build_edit_icons($document_data, $id, $is_template, $is_read_only = 0, $visibility)
5559
    {
5560
        $sessionId = api_get_session_id();
5561
        $courseParams = api_get_cidreq();
5562
        $document_id = $document_data['id'];
5563
        $type = $document_data['filetype'];
5564
        $is_read_only = $document_data['readonly'];
5565
        $path = $document_data['path'];
5566
5567
        if ($type == 'link') {
5568
            $parent_id = self::get_document_id(
5569
                api_get_course_info(),
5570
                rtrim($path, '/'),
5571
                0
5572
            );
5573
        } else {
5574
            $parent_id = self::get_document_id(
5575
                api_get_course_info(),
5576
                dirname($path),
5577
                0
5578
            );
5579
        }
5580
5581
        if (empty($parent_id) && !empty($sessionId)) {
5582
            $parent_id = self::get_document_id(
5583
                api_get_course_info(),
5584
                dirname($path),
5585
                $sessionId
5586
            );
5587
        }
5588
5589
        $curdirpath = dirname($document_data['path']);
5590
        $is_certificate_mode = self::is_certificate_mode($path);
5591
        $curdirpath = urlencode($curdirpath);
5592
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5593
        //@todo Implement remote support for converter
5594
        $usePpt2lp = api_get_setting('service_ppt2lp', 'active') == 'true' && api_get_setting('service_ppt2lp', 'host') == 'localhost';
5595
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5596
        $formatType = current($formatTypeList);
5597
5598
        // If document is read only *or* we're in a session and the document
5599
        // is from a non-session context, hide the edition capabilities
5600
        $modify_icons = [];
5601
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5602
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5603
        $modify_icons[] = self::getButtonVisibility(
5604
            $is_read_only,
5605
            $visibility,
5606
            $document_data,
5607
            $is_certificate_mode,
5608
            $parent_id
5609
        );
5610
        $modify_icons[] = self::getButtonDelete(
5611
            $is_read_only,
5612
            $document_data,
5613
            $is_certificate_mode,
5614
            $curdirpath,
5615
            $parent_id
5616
        );
5617
5618
        if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
5619
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5620
            // @TODO: add prompt to select a format target
5621
            if (!in_array($path, self::get_system_folders())) {
5622
                if ($usePpt2lp && $formatType) {
5623
                    $modify_icons[] = Display::url(
5624
                        Display::return_icon('convert.png', get_lang('Convert')),
5625
                        '#',
5626
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5627
                    );
5628
                }
5629
            }
5630
        }
5631
5632
        if ($type == 'file' && ($extension == 'html' || $extension == 'htm')) {
5633
            if ($is_template == 0) {
5634
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] != '/certificates') || !isset($_GET['curdirpath'])) {
5635
                    $modify_icons[] = Display::url(
5636
                        Display::return_icon('wizard.png', get_lang('AddAsTemplate')),
5637
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id"
5638
                    );
5639
                }
5640
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] == '/certificates') || $is_certificate_mode) {//allow attach certificate to course
5641
                    $visibility_icon_certificate = 'nocertificate';
5642
                    if (self::get_default_certificate_id(api_get_course_id()) == $id) {
5643
                        $visibility_icon_certificate = 'certificate';
5644
                        $certificate = get_lang('DefaultCertificate');
5645
                        $preview = get_lang('PreviewCertificate');
5646
                        $is_preview = true;
5647
                    } else {
5648
                        $is_preview = false;
5649
                        $certificate = get_lang('NoDefaultCertificate');
5650
                    }
5651
                    if (isset($_GET['selectcat'])) {
5652
                        $modify_icons[] = Display::url(
5653
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5654
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id"
5655
                        );
5656
                        if ($is_preview) {
5657
                            $modify_icons[] = Display::url(
5658
                                Display::return_icon('preview_view.png', $preview),
5659
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id"
5660
                            );
5661
                        }
5662
                    }
5663
                }
5664
            } else {
5665
                $modify_icons[] = Display::url(
5666
                    Display::return_icon('wizard_na.png', get_lang('RemoveAsTemplate')),
5667
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id"
5668
                );
5669
            }
5670
5671
            $modify_icons[] = Display::url(
5672
                Display::return_icon('pdf.png', get_lang('Export2PDF')),
5673
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath"
5674
            );
5675
        }
5676
        if ($type == 'file') {
5677
            $randomUploadName = md5(uniqid(mt_rand(), true));
5678
            $modify_icons[] = Display::url(
5679
                Display::return_icon('upload_file.png', get_lang('ReplaceFile')),
5680
                "#!",
5681
                [
5682
                    'data-id' => $randomUploadName,
5683
                    'class' => 'removeHiddenFile',
5684
                ]
5685
            );
5686
            $html = "
5687
            <div class='replaceIndividualFile upload_element_".$randomUploadName." hidden'>
5688
                <div class='form-group ' id='file_file'>
5689
                    <label class='col-sm-2 control-label' for='file_".$randomUploadName."'>
5690
                        ".get_lang('File')."
5691
                    </label>
5692
                    <div class='col-sm-8'>
5693
                        <input class='' name='file_".$randomUploadName."' style='width: 250px' type='file'>
5694
                    </div>
5695
                    <div class='col-sm-2'></div>
5696
                </div>
5697
                <div class='form-group '>
5698
                    <label class='col-sm-2 control-label' for='upload_".$randomUploadName."_submitDocument'>
5699
5700
                    </label>
5701
                    <div class='col-sm-8'>
5702
5703
                        <button class=' btn btn-primary ' id='upload_".$randomUploadName."_submitDocument'
5704
                        name='submitDocument'
5705
                                type='submit'>
5706
                                <em class='fa fa-paper-plane'></em> ".get_lang('ReplaceFile')."
5707
                        </button>
5708
                    </div>
5709
                    <div class='col-sm-2'></div>
5710
                </div>
5711
                <input class='currentFile' name='currentFile' type='hidden' >
5712
5713
                <input id='upload_".$randomUploadName."__qf__upload_".$randomUploadName."' name='_qf__upload_".$randomUploadName."'
5714
                       type='hidden'
5715
                       value=''>
5716
                <input id='upload_".$randomUploadName."_id' name='id_$randomUploadName' type='hidden' value='$id'>
5717
                <input id='upload_".$randomUploadName."_MAX_FILE_SIZE' name='MAX_FILE_SIZE' type='hidden' value='".ini_get('upload_max_filesize')."'>
5718
            </div>
5719
            ";
5720
            $modify_icons[] = $html;
5721
        }
5722
5723
        return implode(PHP_EOL, $modify_icons);
5724
    }
5725
5726
    /**
5727
     * @param $folders
5728
     * @param $curdirpath
5729
     * @param $move_file
5730
     * @param string $group_dir
5731
     *
5732
     * @return string
5733
     */
5734
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5735
    {
5736
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5737
5738
        // Form title
5739
        $form->addHidden('move_file', $move_file);
5740
5741
        $options = [];
5742
5743
        // Group documents cannot be uploaded in the root
5744
        if ($group_dir == '') {
5745
            if ($curdirpath != '/') {
5746
                $options['/'] = get_lang('Documents');
5747
            }
5748
5749
            if (is_array($folders)) {
5750
                foreach ($folders as &$folder) {
5751
                    // Hide some folders
5752
                    if ($folder == '/HotPotatoes_files' ||
5753
                        $folder == '/certificates' ||
5754
                        basename($folder) == 'css'
5755
                    ) {
5756
                        continue;
5757
                    }
5758
                    // Admin setting for Hide/Show the folders of all users
5759
                    if (api_get_setting('show_users_folders') == 'false' &&
5760
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5761
                    ) {
5762
                        continue;
5763
                    }
5764
5765
                    // Admin setting for Hide/Show Default folders to all users
5766
                    if (api_get_setting('show_default_folders') == 'false' &&
5767
                        (
5768
                            $folder == '/images' ||
5769
                            $folder == '/flash' ||
5770
                            $folder == '/audio' ||
5771
                            $folder == '/video' ||
5772
                            strstr($folder, '/images/gallery') ||
5773
                            $folder == '/video/flv'
5774
                        )
5775
                    ) {
5776
                        continue;
5777
                    }
5778
5779
                    // Admin setting for Hide/Show chat history folder
5780
                    if (api_get_setting('show_chat_folder') == 'false' &&
5781
                        $folder == '/chat_files') {
5782
                        continue;
5783
                    }
5784
5785
                    // You cannot move a file to:
5786
                    // 1. current directory
5787
                    // 2. inside the folder you want to move
5788
                    // 3. inside a subfolder of the folder you want to move
5789
                    if (($curdirpath != $folder) &&
5790
                        ($folder != $move_file) &&
5791
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5792
                    ) {
5793
                        // If document title is used, we have to display titles instead of real paths...
5794
                        $path_displayed = self::get_titles_of_path($folder);
5795
                        if (empty($path_displayed)) {
5796
                            $path_displayed = get_lang('Untitled');
5797
                        }
5798
                        $options[$folder] = $path_displayed;
5799
                    }
5800
                }
5801
            }
5802
        } else {
5803
            foreach ($folders as $folder) {
5804
                if (($curdirpath != $folder) &&
5805
                    ($folder != $move_file) &&
5806
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5807
                ) {
5808
                    // Cannot copy dir into his own subdir
5809
                    $path_displayed = self::get_titles_of_path($folder);
5810
                    $display_folder = substr($path_displayed, strlen($group_dir));
5811
                    $display_folder = $display_folder == '' ? get_lang('Documents') : $display_folder;
5812
                    $options[$folder] = $display_folder;
5813
                }
5814
            }
5815
        }
5816
        $form->addElement('select', 'move_to', get_lang('MoveTo'), $options);
5817
        $form->addButtonNext(get_lang('MoveElement'), 'move_file_submit');
5818
5819
        return $form->returnForm();
5820
    }
5821
5822
    /**
5823
     * Gets the path translated with title of docs and folders.
5824
     *
5825
     * @param string $path the real path
5826
     *
5827
     * @return the path which should be displayed
5828
     */
5829
    public static function get_titles_of_path($path)
5830
    {
5831
        global $tmp_folders_titles;
5832
        $course_id = api_get_course_int_id();
5833
        $nb_slashes = substr_count($path, '/');
5834
        $current_slash_pos = 0;
5835
        $path_displayed = '';
5836
        for ($i = 0; $i < $nb_slashes; $i++) {
5837
            // For each folder of the path, retrieve title.
5838
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
5839
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
5840
5841
            if (empty($tmp_path)) {
5842
                // If empty, then we are in the final part of the path
5843
                $tmp_path = $path;
5844
            }
5845
5846
            if (!empty($tmp_folders_titles[$tmp_path])) {
5847
                // If this path has soon been stored here we don't need a new query
5848
                $path_displayed .= $tmp_folders_titles[$tmp_path];
5849
            } else {
5850
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
5851
                        WHERE c_id = '.$course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
5852
                $rs = Database::query($sql);
5853
                $tmp_title = '/'.Database::result($rs, 0, 0);
5854
                $path_displayed .= $tmp_title;
5855
                $tmp_folders_titles[$tmp_path] = $tmp_title;
5856
            }
5857
        }
5858
5859
        return $path_displayed;
5860
    }
5861
5862
    /**
5863
     * Creates form that asks for the directory name.
5864
     *
5865
     * @return string html-output text for the form
5866
     */
5867
    public static function create_dir_form($dirId)
5868
    {
5869
        global $document_id;
5870
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
5871
        $form->addElement('hidden', 'create_dir', 1);
5872
        $form->addElement('hidden', 'dir_id', intval($document_id));
5873
        $form->addElement('hidden', 'id', intval($dirId));
5874
        $form->addElement('header', get_lang('CreateDir'));
5875
        $form->addText('dirname', get_lang('NewDir'), ['autofocus' => 'autofocus']);
5876
        $form->addButtonCreate(get_lang('CreateFolder'));
5877
5878
        return $form->returnForm();
5879
    }
5880
5881
    /**
5882
     * Checks whether the user is in shared folder.
5883
     *
5884
     * @param string $curdirpath
5885
     * @param int    $sessionId
5886
     *
5887
     * @return bool Return true when user is into shared folder
5888
     */
5889
    public static function is_shared_folder($curdirpath, $sessionId)
5890
    {
5891
        $clean_curdirpath = Security::remove_XSS($curdirpath);
5892
        if ($clean_curdirpath == '/shared_folder') {
5893
            return true;
5894
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$sessionId) {
5895
            return true;
5896
        }
5897
5898
        return false;
5899
    }
5900
5901
    /**
5902
     * Checks whether the user is into any user shared folder.
5903
     *
5904
     * @param string $path
5905
     * @param int    $sessionId
5906
     *
5907
     * @return bool Return true when user is in any user shared folder
5908
     */
5909
    public static function is_any_user_shared_folder($path, $sessionId)
5910
    {
5911
        $clean_path = Security::remove_XSS($path);
5912
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
5913
            return true;
5914
        } elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
5915
            return true;
5916
        }
5917
5918
        return false;
5919
    }
5920
5921
    /**
5922
     * Create users shared folder for course.
5923
     *
5924
     * @param int $userId
5925
     * @param int $sessionId
5926
     */
5927
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
5928
    {
5929
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
5930
        $userInfo = api_get_user_info($userId);
5931
5932
        if (!$sessionId) {
5933
            //Create shared folder. Necessary for recycled courses.
5934
            if (!file_exists($documentDirectory.'/shared_folder')) {
5935
                create_unexisting_directory(
5936
                    $courseInfo,
5937
                    $userId,
5938
                    0,
5939
                    0,
5940
                    0,
5941
                    $documentDirectory,
5942
                    '/shared_folder',
5943
                    get_lang('UserFolders'),
5944
                    0,
5945
                    false,
5946
                    false
5947
                );
5948
            }
5949
            // Create dynamic user shared folder
5950
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
5951
                create_unexisting_directory(
5952
                    $courseInfo,
5953
                    $userId,
5954
                    0,
5955
                    0,
5956
                    0,
5957
                    $documentDirectory,
5958
                    '/shared_folder/sf_user_'.$userId,
5959
                    $userInfo['complete_name'],
5960
                    1,
5961
                    false,
5962
                    false
5963
                );
5964
            }
5965
5966
            return;
5967
        }
5968
5969
        // Create shared folder session.
5970
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
5971
            create_unexisting_directory(
5972
                $courseInfo,
5973
                api_get_user_id(),
5974
                $sessionId,
5975
                0,
5976
                0,
5977
                $documentDirectory,
5978
                '/shared_folder_session_'.$sessionId,
5979
                get_lang('UserFolders').' ('.api_get_session_name($sessionId).')',
5980
                0,
5981
                false,
5982
                false
5983
            );
5984
        }
5985
        //Create dynamic user shared folder into a shared folder session
5986
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
5987
            create_unexisting_directory(
5988
                $courseInfo,
5989
                $userId,
5990
                $sessionId,
5991
                0,
5992
                0,
5993
                $documentDirectory,
5994
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
5995
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
5996
                1,
5997
                false,
5998
                false
5999
            );
6000
        }
6001
    }
6002
6003
    /**
6004
     * Checks whether the user is into his shared folder or into a subfolder.
6005
     *
6006
     * @param int    $user_id
6007
     * @param string $path
6008
     * @param int    $sessionId
6009
     *
6010
     * @return bool Return true when user is in his user shared folder or into a subfolder
6011
     */
6012
    public static function is_my_shared_folder($user_id, $path, $sessionId)
6013
    {
6014
        $clean_path = Security::remove_XSS($path).'/';
6015
        //for security does not remove the last slash
6016
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
6017
        //for security does not remove the last slash
6018
        $main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
6019
6020
        if (preg_match($main_user_shared_folder, $clean_path)) {
6021
            return true;
6022
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
6023
            return true;
6024
        } else {
6025
            return false;
6026
        }
6027
    }
6028
6029
    public static function isBasicCourseFolder($path, $sessionId)
6030
    {
6031
        $cleanPath = Security::remove_XSS($path);
6032
        $basicCourseFolder = '/basic-course-documents__'.$sessionId.'__0';
6033
6034
        return $cleanPath == $basicCourseFolder;
6035
    }
6036
6037
    /**
6038
     * Check if the file name or folder searched exist.
6039
     *
6040
     * @return bool Return true when exist
6041
     */
6042
    public static function searchKeyword($name, $keyword)
6043
    {
6044
        if (api_strripos($name, $keyword) !== false) {
6045
            return true;
6046
        }
6047
6048
        return false;
6049
    }
6050
6051
    /**
6052
     * Checks whether a document can be previewed by using the browser.
6053
     *
6054
     * @param string $file_extension the filename extension of the document (it must be in lower case)
6055
     *
6056
     * @return bool returns TRUE or FALSE
6057
     */
6058
    public static function isBrowserViewable($file_extension)
6059
    {
6060
        static $allowed_extensions = [
6061
            'htm', 'html', 'xhtml',
6062
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
6063
            'pdf', 'svg', 'swf',
6064
            'txt', 'log',
6065
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
6066
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar',
6067
        ];
6068
6069
        /*
6070
          //TODO: make a admin switch to strict mode
6071
          1. global default $allowed_extensions
6072
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
6073
          return true;
6074
          }
6075
          2. check native support
6076
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
6077
         */
6078
6079
        if (!($result = in_array($file_extension, $allowed_extensions))) {
6080
            // Assignment + a logical check.
6081
            return false;
6082
        }
6083
6084
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
6085
        if ($file_extension == "pdf") {
6086
            return api_browser_support('pdf');
6087
        } elseif ($file_extension == "mp3") {
6088
            return api_browser_support('mp3');
6089
        } elseif ($file_extension == "mp4") {
6090
            return api_browser_support('mp4');
6091
        } elseif ($file_extension == "ogg" || $file_extension == "ogx" || $file_extension == "ogv" || $file_extension == "oga") {
6092
            return api_browser_support('ogg');
6093
        } elseif ($file_extension == "svg") {
6094
            return api_browser_support('svg');
6095
        } elseif ($file_extension == "mpg" || $file_extension == "mpeg") {
6096
            return api_browser_support('mpg');
6097
        } elseif ($file_extension == "mov") {
6098
            return api_browser_support('mov');
6099
        } elseif ($file_extension == "wav") {
6100
            return api_browser_support('wav');
6101
        } elseif ($file_extension == "mid" || $file_extension == "kar") {
6102
            return api_browser_support('mid');
6103
        } elseif ($file_extension == "avi") {
6104
            return api_browser_support('avi');
6105
        } elseif ($file_extension == "wma") {
6106
            return api_browser_support('wma');
6107
        } elseif ($file_extension == "wmv") {
6108
            return api_browser_support('wmv');
6109
        } elseif ($file_extension == "tif" || $file_extension == "tiff") {
6110
            return api_browser_support('tif');
6111
        } elseif ($file_extension == "mov") {
6112
            return api_browser_support('mov');
6113
        } elseif ($file_extension == "au") {
6114
            return api_browser_support('au');
6115
        } elseif ($file_extension == "webm") {
6116
            return api_browser_support('webm');
6117
        }
6118
6119
        return $result;
6120
    }
6121
6122
    /**
6123
     * @param array $courseInfo
6124
     * @param int   $sessionId
6125
     *
6126
     * @return array
6127
     */
6128
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
6129
    {
6130
        $table = Database::get_course_table(TABLE_DOCUMENT);
6131
        $courseId = $courseInfo['real_id'];
6132
        $sessionCondition = api_get_session_condition($sessionId);
6133
        $sql = "SELECT * FROM $table
6134
                WHERE
6135
                  path LIKE '%DELETED%' AND
6136
                  c_id = $courseId
6137
                  $sessionCondition
6138
                ORDER BY path
6139
        ";
6140
6141
        $result = Database::query($sql);
6142
        $files = [];
6143
        while ($document = Database::fetch_array($result, 'ASSOC')) {
6144
            $files[] = $document;
6145
        }
6146
6147
        return $files;
6148
    }
6149
6150
    /**
6151
     * @param int   $id
6152
     * @param array $courseInfo
6153
     * @param int   $sessionId
6154
     *
6155
     * @return array
6156
     */
6157
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
6158
    {
6159
        if (empty($courseInfo)) {
6160
            return false;
6161
        }
6162
6163
        $table = Database::get_course_table(TABLE_DOCUMENT);
6164
        $courseId = $courseInfo['real_id'];
6165
        $id = (int) $id;
6166
        $sessionCondition = api_get_session_condition($sessionId);
6167
        $sql = "SELECT * FROM $table
6168
                WHERE
6169
                  path LIKE '%DELETED%' AND
6170
                  id = $id AND
6171
                  c_id = $courseId
6172
                  $sessionCondition
6173
                LIMIT 1
6174
        ";
6175
        $result = Database::query($sql);
6176
        if (Database::num_rows($result)) {
6177
            $result = Database::fetch_array($result, 'ASSOC');
6178
6179
            return $result;
6180
        }
6181
6182
        return [];
6183
    }
6184
6185
    /**
6186
     * @param int   $id
6187
     * @param array $courseInfo
6188
     * @param int   $sessionId
6189
     *
6190
     * @return bool
6191
     */
6192
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
6193
    {
6194
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6195
        if (!empty($document)) {
6196
            $path = $document['path'];
6197
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6198
            my_delete($coursePath.$path);
6199
            // Hard delete.
6200
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
6201
6202
            return true;
6203
        }
6204
6205
        return false;
6206
    }
6207
6208
    /**
6209
     * @param array $courseInfo
6210
     * @param int   $sessionId
6211
     */
6212
    public static function purgeDocuments($courseInfo, $sessionId)
6213
    {
6214
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6215
        foreach ($files as $file) {
6216
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
6217
        }
6218
    }
6219
6220
    /**
6221
     * @param int   $id
6222
     * @param array $courseInfo
6223
     * @param int   $sessionId
6224
     */
6225
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
6226
    {
6227
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6228
        if (!empty($document)) {
6229
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6230
6231
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
6232
                self::file_send_for_download($coursePath.$document['path'], true);
6233
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
6234
            }
6235
        }
6236
    }
6237
6238
    /**
6239
     * @param array $courseInfo
6240
     * @param int   $sessionId
6241
     *
6242
     * @return bool
6243
     */
6244
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
6245
    {
6246
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6247
6248
        if (empty($files)) {
6249
            return false;
6250
        }
6251
6252
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
6253
6254
        // Creating a ZIP file.
6255
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().'.zip';
6256
        $zip = new PclZip($tempZipFile);
6257
        foreach ($files as $file) {
6258
            $zip->add(
6259
                $coursePath.$file['path'],
6260
                PCLZIP_OPT_REMOVE_PATH,
6261
                $coursePath
6262
            );
6263
        }
6264
6265
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
6266
            self::file_send_for_download($tempZipFile, true);
6267
            @unlink($tempZipFile);
6268
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
6269
        }
6270
    }
6271
6272
    /**
6273
     * Delete documents from a session in a course.
6274
     *
6275
     * @param array $courseInfo
6276
     * @param int   $sessionId
6277
     *
6278
     * @return bool
6279
     */
6280
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
6281
    {
6282
        if (empty($courseInfo)) {
6283
            return false;
6284
        }
6285
6286
        if (empty($sessionId)) {
6287
            return false;
6288
        }
6289
6290
        $itemPropertyTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
6291
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
6292
6293
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
6294
        $courseId = $courseInfo['real_id'];
6295
6296
        // get invisible folders
6297
        $sql = "SELECT DISTINCT d.id, path
6298
                FROM $itemPropertyTable i
6299
                INNER JOIN $documentTable d
6300
                ON (i.c_id = d.c_id)
6301
                WHERE
6302
                    d.id = i.ref AND
6303
                    i.tool = '".TOOL_DOCUMENT."'
6304
                    $conditionSession AND
6305
                    i.c_id = $courseId AND
6306
                    d.c_id = $courseId ";
6307
6308
        $result = Database::query($sql);
6309
        $documents = Database::store_result($result, 'ASSOC');
6310
        if ($documents) {
6311
            $course_dir = $courseInfo['directory'].'/document';
6312
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
6313
            $base_work_dir = $sys_course_path.$course_dir;
6314
6315
            foreach ($documents as $document) {
6316
                $documentId = $document['id'];
6317
                self::delete_document(
6318
                    $courseInfo,
6319
                    null,
6320
                    $base_work_dir,
6321
                    $sessionId,
6322
                    $documentId
6323
                );
6324
            }
6325
        }
6326
6327
        $sql = "DELETE FROM $documentTable
6328
                WHERE c_id = $courseId AND session_id = $sessionId";
6329
        Database::query($sql);
6330
6331
        $sql = "DELETE FROM $itemPropertyTable
6332
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
6333
        Database::query($sql);
6334
    }
6335
6336
    /**
6337
     * Update the file or directory path in the document db document table.
6338
     *
6339
     * @author - Hugues Peeters <[email protected]>
6340
     *
6341
     * @param string $action   - action type require : 'delete' or 'update'
6342
     * @param string $old_path - old path info stored to change
6343
     * @param string $new_path - new path info to substitute
6344
     *
6345
     * @desc Update the file or directory path in the document db document table
6346
     */
6347
    public static function updateDbInfo($action, $old_path, $new_path = '')
6348
    {
6349
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
6350
        $course_id = api_get_course_int_id();
6351
        $old_path = Database::escape_string($old_path);
6352
        switch ($action) {
6353
            case 'delete':
6354
                $query = "DELETE FROM $dbTable
6355
                          WHERE
6356
                            c_id = $course_id AND
6357
                            (
6358
                                path LIKE BINARY '".$old_path."' OR
6359
                                path LIKE BINARY '".$old_path."/%'
6360
                            )";
6361
                Database::query($query);
6362
                break;
6363
            case 'update':
6364
                if ($new_path[0] == '.') {
6365
                    $new_path = substr($new_path, 1);
6366
                }
6367
                $new_path = str_replace('//', '/', $new_path);
6368
6369
                // Attempt to update	- tested & working for root	dir
6370
                $new_path = Database::escape_string($new_path);
6371
                $query = "UPDATE $dbTable SET
6372
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
6373
                          WHERE
6374
                                c_id = $course_id AND
6375
                                (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
6376
                Database::query($query);
6377
                break;
6378
        }
6379
    }
6380
6381
    /**
6382
     * This function calculates the resized width and resized heigt
6383
     * according to the source and target widths
6384
     * and heights, height so that no distortions occur
6385
     * parameters.
6386
     *
6387
     * @param $image = the absolute path to the image
0 ignored issues
show
Documentation Bug introduced by
The doc comment = at position 0 could not be parsed: Unknown type name '=' at position 0 in =.
Loading history...
6388
     * @param $target_width = how large do you want your resized image
6389
     * @param $target_height = how large do you want your resized image
6390
     * @param $slideshow (default=0) =
6391
     *      indicates weither we are generating images for a slideshow or not,
6392
     *		this overrides the $_SESSION["image_resizing"] a bit so that a thumbnail
6393
     *	    view is also possible when you choose not to resize the source images
6394
     *
6395
     * @return array
6396
     */
6397
    public static function resizeImageSlideShow(
6398
        $image,
6399
        $target_width,
6400
        $target_height,
6401
        $slideshow = 0
6402
    ) {
6403
        // Modifications by Ivan Tcholakov, 04-MAY-2009.
6404
        $result = [];
6405
        $imageResize = Session::read('image_resizing');
6406
        if ($imageResize == 'resizing' || $slideshow == 1) {
6407
            $new_sizes = api_resize_image($image, $target_width, $target_height);
6408
            $result[] = $new_sizes['height'];
6409
            $result[] = $new_sizes['width'];
6410
        } else {
6411
            $size = api_getimagesize($image);
6412
            $result[] = $size['height'];
6413
            $result[] = $size['width'];
6414
        }
6415
6416
        return $result;
6417
    }
6418
6419
    /**
6420
     * Calculates the total size of a directory by adding the sizes (that
6421
     * are stored in the database) of all files & folders in this directory.
6422
     *
6423
     * @param string $path
6424
     * @param bool   $can_see_invisible
6425
     *
6426
     * @return int Total size
6427
     */
6428
    public static function getTotalFolderSize($path, $can_see_invisible = false)
6429
    {
6430
        $table_itemproperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6431
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
6432
        $tool_document = TOOL_DOCUMENT;
6433
6434
        $course_id = api_get_course_int_id();
6435
        $session_id = api_get_session_id();
6436
        $session_condition = api_get_session_condition(
6437
            $session_id,
6438
            true,
6439
            true,
6440
            'props.session_id'
6441
        );
6442
6443
        if (empty($course_id)) {
6444
            return 0;
6445
        }
6446
6447
        $path = Database::escape_string($path);
6448
        $visibility_rule = ' props.visibility '.($can_see_invisible ? '<> 2' : '= 1');
6449
6450
        $sql = "SELECT SUM(table1.size) FROM (
6451
                SELECT props.ref, size
6452
                FROM $table_itemproperty AS props
6453
                INNER JOIN $table_document AS docs
6454
                ON (docs.id = props.ref AND docs.c_id = props.c_id)
6455
                WHERE
6456
                    docs.c_id = $course_id AND
6457
                    docs.path LIKE '$path/%' AND
6458
                    props.c_id = $course_id AND
6459
                    props.tool = '$tool_document' AND
6460
                    $visibility_rule
6461
                    $session_condition
6462
                GROUP BY ref
6463
            ) as table1";
6464
6465
        $result = Database::query($sql);
6466
        if ($result && Database::num_rows($result) != 0) {
6467
            $row = Database::fetch_row($result);
6468
6469
            return $row[0] == null ? 0 : $row[0];
6470
        } else {
6471
            return 0;
6472
        }
6473
    }
6474
6475
    /**
6476
     * Adds a cloud link to the database.
6477
     *
6478
     * @author - Aquilino Blanco Cores <[email protected]>
6479
     *
6480
     * @param array  $_course
6481
     * @param string $path
6482
     * @param string $url
6483
     * @param string $name
6484
     *
6485
     * @return int id of document or 0 if already exists or there was a problem creating it
6486
     */
6487
    public static function addCloudLink($_course, $path, $url, $name)
6488
    {
6489
        $file_path = $path;
6490
        if (!self::cloudLinkExists($_course, $path, $url)) {
6491
            $doc_id = add_document($_course, $file_path, 'link', 0, $name, $url);
6492
            if ($doc_id) {
6493
                // Update document item_property
6494
                api_item_property_update(
6495
                    $_course,
6496
                    TOOL_DOCUMENT,
6497
                    $doc_id,
6498
                    'DocumentAdded',
6499
                    api_get_user_id(),
6500
                    api_get_group_id(),
6501
                    api_get_user_id(),
6502
                    null,
6503
                    null,
6504
                    api_get_session_id()
6505
                );
6506
            }
6507
6508
            // If the file is in a folder, we need to update all parent folders
6509
            item_property_update_on_folder($_course, $file_path, api_get_user_id());
6510
6511
            return $doc_id;
6512
        } else {
6513
            return 0;
6514
        }
6515
    }
6516
6517
    /**
6518
     * Deletes a cloud link from the database.
6519
     *
6520
     * @author - Aquilino Blanco Cores <[email protected]>
6521
     *
6522
     * @param array  $courseInfo
6523
     * @param string $documentId
6524
     *
6525
     * @return bool true if success / false if an error occurred
6526
     */
6527
    public static function deleteCloudLink($courseInfo, $documentId)
6528
    {
6529
        if (empty($documentId) || empty($courseInfo)) {
6530
            return false;
6531
        }
6532
6533
        $documentId = (int) $documentId;
6534
        $fileDeletedFromDb = false;
6535
        if (!empty($documentId)) {
6536
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
6537
            // checking
6538
            $table = Database::get_course_table(TABLE_DOCUMENT);
6539
            $courseId = $courseInfo['real_id'];
6540
            echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
6541
            $result = Database::query($sql);
6542
            $exists = Database::num_rows($result) > 0;
6543
            $fileDeletedFromDb = !$exists;
6544
        }
6545
6546
        return $fileDeletedFromDb;
6547
    }
6548
6549
    /**
6550
     * Gets the id of a cloud link with a given path.
6551
     *
6552
     * @author - Aquilino Blanco Cores <[email protected]>
6553
     *
6554
     * @param array  $courseInfo
6555
     * @param string $path
6556
     * @param string $url
6557
     *
6558
     * @return int link's id / false if no link found
6559
     */
6560
    public static function getCloudLinkId($courseInfo, $path, $url)
6561
    {
6562
        $table = Database::get_course_table(TABLE_DOCUMENT);
6563
6564
        if (empty($courseInfo)) {
6565
            return false;
6566
        }
6567
6568
        $courseId = (int) $courseInfo['real_id'];
6569
        $path = Database::escape_string($path);
6570
6571
        if (substr($path, -1) != '/') {
6572
            // Add final slash to path if not present
6573
            $path .= '/';
6574
        }
6575
6576
        if (!empty($courseId) && !empty($path)) {
6577
            $sql = "SELECT id FROM $table
6578
                    WHERE
6579
                        c_id = $courseId AND
6580
                        path LIKE BINARY '$path' AND
6581
                        comment = '$url' AND
6582
                        filetype = 'link'
6583
                    LIMIT 1";
6584
            $result = Database::query($sql);
6585
            if ($result && Database::num_rows($result)) {
6586
                $row = Database::fetch_array($result);
6587
6588
                return intval($row[0]);
6589
            }
6590
        }
6591
6592
        return false;
6593
    }
6594
6595
    /**
6596
     * Checks if a cloud link exists.
6597
     *
6598
     * @author - Aquilino Blanco Cores <[email protected]>
6599
     *
6600
     * @param array  $courseInfo
6601
     * @param string $path
6602
     * @param string $url
6603
     *
6604
     * @return bool true if it exists false in other case
6605
     */
6606
    public static function cloudLinkExists($courseInfo, $path, $url)
6607
    {
6608
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6609
6610
        return $exists;
6611
    }
6612
6613
    /**
6614
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications.
6615
     *
6616
     * @author Aquilino Blanco Cores <[email protected]>
6617
     *
6618
     * @return string the well formed URLs regular expressions string
6619
     */
6620
    public static function getWellFormedUrlRegex()
6621
    {
6622
        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';
6623
    }
6624
6625
    /**
6626
     * Gets the files hosting sites' whitelist.
6627
     *
6628
     * @author Aquilino Blanco Cores <[email protected]>
6629
     *
6630
     * @return array the sites list
6631
     */
6632
    public static function getFileHostingWhiteList()
6633
    {
6634
        return [
6635
            'asuswebstorage.com',
6636
            'box.com',
6637
            'dropbox.com',
6638
            'dropboxusercontent.com',
6639
            'docs.google.com',
6640
            'drive.google.com',
6641
            'fileserve.com',
6642
            'icloud.com',
6643
            'livefilestore.com', // OneDrive
6644
            'mediafire.com',
6645
            'mega.nz',
6646
            'onedrive.live.com',
6647
            'scribd.com',
6648
            'slideshare.net',
6649
            'sharepoint.com',
6650
            'wetransfer.com',
6651
        ];
6652
    }
6653
6654
    /**
6655
     * @param int $userId
6656
     *
6657
     * @return array Example [ 0 => ['code' => 'ABC', 'directory' => 'ABC0', 'path' => '/images/gallery/test.png', 'code_path' => 'ABC:/images/gallery/test.png'], 1 => ...]
6658
     */
6659
    public static function getAllDocumentsCreatedByUser($userId)
6660
    {
6661
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6662
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
6663
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
6664
        $userId = (int) $userId;
6665
6666
        $sql = "SELECT DISTINCT c.code, c.directory, docs.path
6667
                FROM $tblItemProperty AS last
6668
                INNER JOIN $tblDocument AS docs
6669
                ON (
6670
                    docs.id = last.ref AND
6671
                    docs.c_id = last.c_id AND
6672
                    docs.filetype <> 'folder'
6673
                )
6674
                INNER JOIN $tblCourse as c
6675
                ON (
6676
                    docs.c_id = c.id
6677
                )
6678
                WHERE
6679
                    last.tool = '".TOOL_DOCUMENT."' AND
6680
                    last.insert_user_id = $userId AND
6681
                    docs.path NOT LIKE '%_DELETED_%'
6682
                ORDER BY c.directory, docs.path
6683
                ";
6684
        $result = Database::query($sql);
6685
6686
        $list = [];
6687
        if (Database::num_rows($result) != 0) {
6688
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6689
                $row['code_path'] = $row['code'].':'.$row['path'];
6690
                $list[] = $row;
6691
            }
6692
        }
6693
6694
        return $list;
6695
    }
6696
6697
    /**
6698
     * Writes the content of a sent file to an existing one in the system, backing up the previous one.
6699
     *
6700
     * @param array  $_course
6701
     * @param string $path          Path stored in the database
6702
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
6703
     * @param int    $sessionId     The ID of the session, if any
6704
     * @param int    $documentId    The document id, if available
6705
     * @param int    $groupId       iid
6706
     * @param file   $file          $_FILES content
6707
     *
6708
     * @return bool true/false
6709
     */
6710
    public static function writeContentIntoDocument(
6711
        $_course,
6712
        $path = null,
6713
        $base_work_dir = null,
6714
        $sessionId = null,
6715
        $documentId = null,
6716
        $groupId = 0,
6717
        $file
6718
    ) {
6719
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
6720
6721
        $documentId = (int) $documentId;
6722
        $groupId = (int) $groupId;
6723
        if (empty($groupId)) {
6724
            $groupId = api_get_group_id();
6725
        }
6726
6727
        $sessionId = (int) $sessionId;
6728
        if (empty($sessionId)) {
6729
            $sessionId = api_get_session_id();
6730
        }
6731
6732
        $course_id = $_course['real_id'];
6733
6734
        if (empty($course_id)) {
6735
            return false;
6736
        }
6737
6738
        if (empty($base_work_dir)) {
6739
            return false;
6740
        }
6741
6742
        if (isset($file) && $file['error'] == 4) {
6743
            //no file
6744
            return false;
6745
        }
6746
6747
        if (empty($documentId)) {
6748
            $documentId = self::get_document_id($_course, $path, $sessionId);
6749
            $docInfo = self::get_document_data_by_id(
6750
                $documentId,
6751
                $_course['code'],
6752
                false,
6753
                $sessionId
6754
            );
6755
            $path = $docInfo['path'];
6756
        } else {
6757
            $docInfo = self::get_document_data_by_id(
6758
                $documentId,
6759
                $_course['code'],
6760
                false,
6761
                $sessionId
6762
            );
6763
            if (empty($docInfo)) {
6764
                return false;
6765
            }
6766
            $path = $docInfo['path'];
6767
        }
6768
6769
        if (empty($path) || empty($docInfo) || empty($documentId)) {
6770
            return false;
6771
        }
6772
6773
        $itemInfo = api_get_item_property_info(
6774
            $_course['real_id'],
6775
            TOOL_DOCUMENT,
6776
            $documentId,
6777
            $sessionId,
6778
            $groupId
6779
        );
6780
6781
        if (empty($itemInfo)) {
6782
            return false;
6783
        }
6784
6785
        // Filtering by group.
6786
        if ($itemInfo['to_group_id'] != $groupId) {
6787
            return false;
6788
        }
6789
        $now = new DateTime();
6790
        $now = $now->format('Y_m_d__H_i_s_');
6791
6792
        $document_exists_in_disk = file_exists($base_work_dir.$path);
6793
        $new_path = $path.'_REPLACED_DATE_'.$now.'_ID_'.$documentId;
6794
6795
        $fileMoved = false;
6796
        $file_renamed_from_disk = false;
6797
6798
        $originalMime = self::file_get_mime_type($base_work_dir.$path);
6799
        $newMime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file['tmp_name']);
6800
        if ($originalMime != $newMime) {
6801
            Display::addFlash(Display::return_message(get_lang('FileError'), 'warning'));
6802
6803
            return false;
6804
        }
6805
6806
        if ($document_exists_in_disk) {
6807
            // Move old file to xxx_REPLACED_DATE_#date_ID_#id (soft delete)
6808
            if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
6809
                if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
6810
                    $file_renamed_from_disk = true;
6811
                } else {
6812
                    // Couldn't rename - file permissions problem?
6813
                    error_log(
6814
                        __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '
6815
                        .$base_work_dir.$new_path.'. This is probably due to file permissions',
6816
                        0
6817
                    );
6818
                }
6819
            }
6820
6821
            if (move_uploaded_file($file['tmp_name'], $base_work_dir.$path)) {
6822
                //update file size into db
6823
                $size = filesize($base_work_dir.$path);
6824
                $sql = "UPDATE $TABLE_DOCUMENT
6825
                                SET size = '".$size."'
6826
                                WHERE
6827
                                    c_id = $course_id AND
6828
                                    session_id = $sessionId AND
6829
                                    id = ".$documentId;
6830
                Database::query($sql);
6831
                $fileMoved = true;
6832
            }
6833
        }
6834
        // Checking inconsistency
6835
        if ($fileMoved &&
6836
            $file_renamed_from_disk
6837
        ) {
6838
            return true;
6839
        } else {
6840
            //Something went wrong
6841
            //The file or directory isn't there anymore (on the filesystem)
6842
            // This means it has been removed externally. To prevent a
6843
            // blocking error from happening, we drop the related items from the
6844
            // item_property and the document table.
6845
            error_log(
6846
                __FILE__.' '.__LINE__.': System inconsistency detected. '.
6847
                'The file or directory '.$base_work_dir.$path.
6848
                ' seems to have been removed from the filesystem independently from '.
6849
                'the web platform. To restore consistency, the elements using the same '.
6850
                'path will be removed from the database',
6851
                0
6852
            );
6853
6854
            return false;
6855
        }
6856
    }
6857
6858
    /**
6859
     * Parse file information into a link.
6860
     *
6861
     * @param array  $userInfo        Current user info
6862
     * @param array  $courseInfo
6863
     * @param int    $session_id
6864
     * @param array  $resource
6865
     * @param int    $lp_id
6866
     * @param bool   $add_move_button
6867
     * @param string $target
6868
     * @param string $overwrite_url
6869
     * @param bool   $addAudioPreview
6870
     *
6871
     * @return string|null
6872
     */
6873
    private static function parseFile(
6874
        $userInfo,
6875
        $courseInfo,
6876
        $session_id,
6877
        $resource,
6878
        $lp_id,
6879
        $add_move_button,
6880
        $target,
6881
        $overwrite_url,
6882
        $addAudioPreview = false
6883
    ) {
6884
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
6885
        $web_code_path = api_get_path(WEB_CODE_PATH);
6886
6887
        $documentId = $resource['id'];
6888
        $path = $resource['path'];
6889
6890
        if (empty($path)) {
6891
            $num = 0;
6892
        } else {
6893
            $num = substr_count($path, '/') - 1;
6894
        }
6895
6896
        // It's a file.
6897
        $icon = choose_image($path);
6898
        $position = strrpos($icon, '.');
6899
        $icon = substr($icon, 0, $position).'_small.gif';
6900
        $my_file_title = $resource['title'];
6901
        $visibility = $resource['visibility'];
6902
6903
        // If title is empty we try to use the path
6904
        if (empty($my_file_title)) {
6905
            $my_file_title = basename($path);
6906
        }
6907
6908
        if (api_get_configuration_value('save_titles_as_html')) {
6909
            $my_file_title = strip_tags($my_file_title);
6910
        }
6911
6912
        // Show the "image name" not the filename of the image.
6913
        if ($lp_id) {
6914
            // LP URL
6915
            $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;
6916
        } else {
6917
            // Direct document URL
6918
            $url = $web_code_path.'document/document.php?cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&id='.$documentId;
6919
        }
6920
6921
        if (!empty($overwrite_url)) {
6922
            $overwrite_url = Security::remove_XSS($overwrite_url);
6923
            $url = $overwrite_url.'&cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&document_id='.$documentId;
6924
        }
6925
6926
        $img = Display::returnIconPath($icon);
6927
        if (!file_exists($img_sys_path.$icon)) {
6928
            $img = Display::returnIconPath('default_small.gif');
6929
        }
6930
6931
        $icon = '<img alt="" src="'.$img.'" title="" />';
6932
6933
        $preview = '';
6934
        if ($addAudioPreview) {
6935
            $path = api_get_path(WEB_COURSE_PATH).$courseInfo['directory'].'/document';
6936
            $resource['file_extension'] = pathinfo($resource['path'], PATHINFO_EXTENSION);
6937
            $preview = self::generateAudioPreview($path, $resource);
6938
            if (!empty($preview)) {
6939
                $preview .= '&nbsp;';
6940
                $icon = '';
6941
            }
6942
        }
6943
6944
        $link = Display::url(
6945
            $icon.'&nbsp;'.
6946
            $my_file_title,
6947
            $url,
6948
            ['target' => $target, 'class' => 'moved']
6949
        );
6950
6951
        $directUrl = $web_code_path.'document/document.php?cidReq='.$courseInfo['code'].'&id_session='.$session_id.'&id='.$documentId;
6952
        $link .= '&nbsp;'.Display::url(
6953
                Display::return_icon('preview_view.png', get_lang('Preview')),
6954
                $directUrl,
6955
                ['target' => '_blank']
6956
            );
6957
6958
        $visibilityClass = null;
6959
        if ($visibility == 0) {
6960
            $visibilityClass = ' text-muted ';
6961
        }
6962
        $return = null;
6963
6964
        if ($lp_id == false) {
6965
            $return .= '<li class="doc_resource '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6966
        } else {
6967
            $return .= '<li class="doc_resource lp_resource_element '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6968
        }
6969
6970
        $return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
6971
        $return .= $preview;
6972
6973
        if ($add_move_button) {
6974
            $return .= '<a class="moved" href="#">';
6975
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
6976
            $return .= '</a> ';
6977
        }
6978
6979
        $return .= $link;
6980
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
6981
        $return .= $sessionStar;
6982
6983
        $return .= '</div></li>';
6984
6985
        return $return;
6986
    }
6987
6988
    /**
6989
     * @param int   $folderId
6990
     * @param array $resource
6991
     * @param int   $lp_id
6992
     *
6993
     * @return string|null
6994
     */
6995
    private static function parseFolder($folderId, $resource, $lp_id)
6996
    {
6997
        $title = isset($resource['title']) ? $resource['title'] : null;
6998
        $path = isset($resource['path']) ? $resource['path'] : null;
6999
7000
        if (empty($path)) {
7001
            $num = 0;
7002
        } else {
7003
            $num = substr_count($path, '/');
7004
        }
7005
7006
        // It's a folder.
7007
        //hide some folders
7008
        if (in_array(
7009
            $path,
7010
            ['shared_folder', 'chat_files', 'HotPotatoes_files', 'css', 'certificates']
7011
        )) {
7012
            return null;
7013
        } elseif (preg_match('/_groupdocs/', $path)) {
7014
            return null;
7015
        } elseif (preg_match('/sf_user_/', $path)) {
7016
            return null;
7017
        } elseif (preg_match('/shared_folder_session_/', $path)) {
7018
            return null;
7019
        }
7020
7021
        //$onclick = '';
7022
        // if in LP, hidden folder are displayed in grey
7023
        $folder_class_hidden = '';
7024
        if ($lp_id) {
7025
            if (isset($resource['visible']) && $resource['visible'] == 0) {
7026
                $folder_class_hidden = ' doc_folder_hidden'; // in base.css
7027
            }
7028
        }
7029
        $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
7030
        $return = null;
7031
7032
        if (empty($path)) {
7033
            $return = '<ul class="lp_resource">';
7034
        }
7035
7036
        $return .= '<li class="doc_folder'.$folder_class_hidden.'" id="doc_id_'.$resource['id'].'"  style="margin-left:'.($num * 18).'px; ">';
7037
7038
        $image = Display::returnIconPath('nolines_plus.gif');
7039
        if (empty($path)) {
7040
            $image = Display::returnIconPath('nolines_minus.gif');
7041
        }
7042
        $return .= '<img style="cursor: pointer;" src="'.$image.'" align="absmiddle" id="img_'.$resource['id'].'" '.$onclick.'>';
7043
        $return .= Display::return_icon('lp_folder.gif').'&nbsp;';
7044
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.$title.'</span>';
7045
        $return .= '</li>';
7046
7047
        if (empty($path)) {
7048
            if ($folderId == false) {
7049
                $return .= '<div id="res_'.$resource['id'].'" >';
7050
            } else {
7051
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
7052
            }
7053
        }
7054
7055
        return $return;
7056
    }
7057
7058
    /**
7059
     * Get the button to edit document.
7060
     *
7061
     * @param bool   $isReadOnly
7062
     * @param string $extension
7063
     * @param bool   $isCertificateMode
7064
     *
7065
     * @return string
7066
     */
7067
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
7068
    {
7069
        $extension = strtolower($extension);
7070
        $iconEn = Display::return_icon('edit.png', get_lang('Modify'));
7071
        $iconDis = Display::return_icon('edit_na.png', get_lang('Modify'));
7072
        $courseParams = api_get_cidreq();
7073
        $webOdfExtensionList = self::get_web_odf_extension_list();
7074
        $path = $documentData['path'];
7075
        $document_id = $documentData['id'];
7076
7077
        if ($isReadOnly) {
7078
            if (!api_is_course_admin() && !api_is_platform_admin()) {
7079
                return $iconDis;
7080
            }
7081
7082
            if (
7083
                $extension == 'svg' && api_browser_support('svg') &&
7084
                api_get_setting('enabled_support_svg') == 'true'
7085
            ) {
7086
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
7087
            }
7088
7089
            if (
7090
                in_array($extension, $webOdfExtensionList) &&
7091
                api_get_configuration_value('enabled_support_odf') === true
7092
            ) {
7093
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
7094
            }
7095
7096
            if (
7097
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
7098
                api_get_setting('enabled_support_pixlr') == 'true'
7099
            ) {
7100
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
7101
            }
7102
7103
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
7104
        }
7105
7106
        if (in_array($path, self::get_system_folders())) {
7107
            return $iconDis;
7108
        }
7109
7110
        if ($isCertificateMode) {
7111
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id&curdirpath=/certificates");
7112
        }
7113
7114
        $sessionId = api_get_session_id();
7115
7116
        if ($sessionId && $documentData['session_id'] != $sessionId) {
7117
            return $iconDis;
7118
        }
7119
7120
        if (
7121
            $extension == 'svg' && api_browser_support('svg') &&
7122
            api_get_setting('enabled_support_svg') == 'true'
7123
        ) {
7124
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
7125
        }
7126
7127
        if (
7128
            in_array($extension, $webOdfExtensionList) &&
7129
            api_get_configuration_value('enabled_support_odf') === true
7130
        ) {
7131
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
7132
        }
7133
7134
        if (
7135
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
7136
            api_get_setting('enabled_support_pixlr') == 'true'
7137
        ) {
7138
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
7139
        }
7140
7141
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
7142
    }
7143
7144
    /**
7145
     * Get the button to move document.
7146
     *
7147
     * @param bool $isReadOnly
7148
     * @param bool $isCertificateMode
7149
     * @param int  $parentId
7150
     *
7151
     * @return string
7152
     */
7153
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
7154
    {
7155
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
7156
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
7157
7158
        if ($isReadOnly) {
7159
            return $iconDis;
7160
        }
7161
7162
        $path = $documentData['path'];
7163
        $document_id = $documentData['id'];
7164
        $sessionId = api_get_session_id();
7165
        $courseParams = api_get_cidreq();
7166
7167
        if ($isCertificateMode || in_array($path, self::get_system_folders())) {
7168
            return $iconDis;
7169
        }
7170
7171
        if ($sessionId) {
7172
            if ($documentData['session_id'] != $sessionId) {
7173
                return $iconDis;
7174
            }
7175
        }
7176
7177
        $urlMoveParams = http_build_query(['id' => $parentId, 'move' => $document_id]);
7178
7179
        return Display::url(
7180
            $iconEn,
7181
            api_get_self()."?$courseParams&$urlMoveParams"
7182
        );
7183
    }
7184
7185
    /**
7186
     * Get the button to set visibility to document.
7187
     *
7188
     * @param bool $isReadOnly
7189
     * @param int  $visibility
7190
     * @param bool $isCertificateMode
7191
     * @param int  $parentId
7192
     *
7193
     * @return string|null
7194
     */
7195
    private static function getButtonVisibility(
7196
        $isReadOnly,
7197
        $visibility,
7198
        array $documentData,
7199
        $isCertificateMode,
7200
        $parentId
7201
    ) {
7202
        $visibility_icon = $visibility == 0 ? 'invisible' : 'visible';
7203
        $visibility_command = $visibility == 0 ? 'set_visible' : 'set_invisible';
7204
        $courseParams = api_get_cidreq();
7205
7206
        if ($isReadOnly) {
7207
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
7208
                return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
7209
            }
7210
7211
            return null;
7212
        }
7213
7214
        if ($isCertificateMode) {
7215
            return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
7216
        }
7217
7218
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
7219
            $tip_visibility = $visibility_icon == 'invisible' ? get_lang('Show') : get_lang('Hide');
7220
7221
            return Display::url(
7222
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
7223
                api_get_self()."?$courseParams&id=$parentId&$visibility_command={$documentData['id']}"
7224
            );
7225
        }
7226
7227
        return null;
7228
    }
7229
7230
    /**
7231
     * GEt the button to delete a document.
7232
     *
7233
     * @param bool   $isReadOnly
7234
     * @param bool   $isCertificateMode
7235
     * @param string $curDirPath
7236
     * @param int    $parentId
7237
     *
7238
     * @return string
7239
     */
7240
    private static function getButtonDelete(
7241
        $isReadOnly,
7242
        array $documentData,
7243
        $isCertificateMode,
7244
        $curDirPath,
7245
        $parentId
7246
    ) {
7247
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
7248
        $iconDis = Display::return_icon('delete_na.png', get_lang('ThisFolderCannotBeDeleted'));
7249
        $path = $documentData['path'];
7250
        $id = $documentData['id'];
7251
        $courseParams = api_get_cidreq();
7252
7253
        if ($isReadOnly) {
7254
            return $iconDis;
7255
        }
7256
7257
        if (in_array($path, self::get_system_folders())) {
7258
            return $iconDis;
7259
        }
7260
7261
        $titleToShow = addslashes(basename($documentData['title']));
7262
        $urlDeleteParams = http_build_query([
7263
            'curdirpath' => $curDirPath,
7264
            'action' => 'delete_item',
7265
            'id' => $parentId,
7266
            'deleteid' => $documentData['id'],
7267
        ]);
7268
7269
        $btn = Display::url(
7270
            $iconEn,
7271
            '#',
7272
            [
7273
                'data-item-title' => $titleToShow,
7274
                'data-href' => api_get_self()."?$courseParams&$urlDeleteParams",
7275
                'data-toggle' => 'modal',
7276
                'data-target' => '#confirm-delete',
7277
            ]
7278
        );
7279
7280
        if (
7281
            isset($_GET['curdirpath']) &&
7282
            $_GET['curdirpath'] == '/certificates' &&
7283
            self::get_default_certificate_id(api_get_course_id()) == $id
7284
        ) {
7285
            return $btn;
7286
        }
7287
7288
        if ($isCertificateMode) {
7289
            return $btn;
7290
        }
7291
7292
        $sessionId = api_get_session_id();
7293
7294
        if ($sessionId) {
7295
            if ($documentData['session_id'] != $sessionId) {
7296
                return $iconDis;
7297
            }
7298
        }
7299
7300
        return $btn;
7301
    }
7302
}
7303