Passed
Push — 1.11.x ( fb88d1...66a1d2 )
by Angel Fernando Quiroz
08:07
created

DocumentManager::getButtonMove()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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