Passed
Push — 1.11.x ( de6608...72b397 )
by Julito
10:54
created

DocumentManager::get_document_id()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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