Passed
Push — 1.11.x ( 1e579c...d546f9 )
by Julito
10:31
created

DocumentManager::deleteDocumentsFromSession()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 54
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 35
nc 4
nop 2
dl 0
loc 54
rs 9.0488
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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

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

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