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