Passed
Push — 1.11.x ( 8d5b2d...895588 )
by Julito
13:20
created

DocumentManager::getDocumentByPathInCourse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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