Completed
Push — master ( 506c79...7daaad )
by Julito
12:09
created

DocumentManager::build_edit_icons()   F

Complexity

Conditions 22
Paths 512

Size

Total Lines 120
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 80
nc 512
nop 4
dl 0
loc 120
rs 0.6777
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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 Chamilo\CoreBundle\Entity\ResourceFile;
5
use Chamilo\CoreBundle\Entity\ResourceLink;
6
use Chamilo\CoreBundle\Entity\User;
7
use Chamilo\CoreBundle\Framework\Container;
8
use Chamilo\CourseBundle\Entity\CDocument;
9
use Chamilo\CourseBundle\Entity\CGroup;
10
use ChamiloSession as Session;
11
use Symfony\Component\HttpFoundation\File\UploadedFile;
12
13
/**
14
 *  Class DocumentManager
15
 *  This is the document library for Chamilo.
16
 *  It is / will be used to provide a service layer to all document-using tools.
17
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
18
 *  Include/require it in your code to use its functionality.
19
 */
20
class DocumentManager
21
{
22
    /**
23
     * Construct.
24
     */
25
    private function __construct()
26
    {
27
    }
28
29
    /**
30
     * @param string $course_code
31
     *
32
     * @return int the document folder quota for the current course in bytes
33
     *             or the default quota
34
     */
35
    public static function get_course_quota($course_code = null)
36
    {
37
        if (empty($course_code)) {
38
            $course_info = api_get_course_info();
39
        } else {
40
            $course_info = api_get_course_info($course_code);
41
        }
42
43
        $course_quota = null;
44
        if (empty($course_info)) {
45
            return DEFAULT_DOCUMENT_QUOTA;
46
        } else {
47
            $course_quota = $course_info['disk_quota'];
48
        }
49
        if (is_null($course_quota) || empty($course_quota)) {
50
            // Course table entry for quota was null, then use default value
51
            $course_quota = DEFAULT_DOCUMENT_QUOTA;
52
        }
53
54
        return $course_quota;
55
    }
56
57
    /**
58
     * Get the content type of a file by checking the extension
59
     * We could use mime_content_type() with php-versions > 4.3,
60
     * but this doesn't work as it should on Windows installations.
61
     *
62
     * @param string $filename or boolean TRUE to return complete array
63
     *
64
     * @author ? first version
65
     * @author Bert Vanderkimpen
66
     *
67
     * @return string
68
     */
69
    public static function file_get_mime_type($filename)
70
    {
71
        // All MIME types in an array (from 1.6, this is the authorative source)
72
        // Please, keep this alphabetical if you add something to this list!
73
        $mimeTypes = [
74
            'ai' => 'application/postscript',
75
            'aif' => 'audio/x-aiff',
76
            'aifc' => 'audio/x-aiff',
77
            'aiff' => 'audio/x-aiff',
78
            'asf' => 'video/x-ms-asf',
79
            'asc' => 'text/plain',
80
            'au' => 'audio/basic',
81
            'avi' => 'video/x-msvideo',
82
            'bcpio' => 'application/x-bcpio',
83
            'bin' => 'application/octet-stream',
84
            'bmp' => 'image/bmp',
85
            'cdf' => 'application/x-netcdf',
86
            'class' => 'application/octet-stream',
87
            'cpio' => 'application/x-cpio',
88
            'cpt' => 'application/mac-compactpro',
89
            'csh' => 'application/x-csh',
90
            'css' => 'text/css',
91
            'dcr' => 'application/x-director',
92
            'dir' => 'application/x-director',
93
            'djv' => 'image/vnd.djvu',
94
            'djvu' => 'image/vnd.djvu',
95
            'dll' => 'application/octet-stream',
96
            'dmg' => 'application/x-diskcopy',
97
            'dms' => 'application/octet-stream',
98
            'doc' => 'application/msword',
99
            'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
100
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
101
            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
102
            'dvi' => 'application/x-dvi',
103
            'dwg' => 'application/vnd.dwg',
104
            'dwf' => 'application/vnd.dwf',
105
            'dxf' => 'application/vnd.dxf',
106
            'dxr' => 'application/x-director',
107
            'eps' => 'application/postscript',
108
            'epub' => 'application/epub+zip',
109
            'etx' => 'text/x-setext',
110
            'exe' => 'application/octet-stream',
111
            'ez' => 'application/andrew-inset',
112
            'flv' => 'video/flv',
113
            'gif' => 'image/gif',
114
            'gtar' => 'application/x-gtar',
115
            'gz' => 'application/x-gzip',
116
            'hdf' => 'application/x-hdf',
117
            'hqx' => 'application/mac-binhex40',
118
            'htm' => 'text/html',
119
            'html' => 'text/html',
120
            'ice' => 'x-conference-xcooltalk',
121
            'ief' => 'image/ief',
122
            'iges' => 'model/iges',
123
            'igs' => 'model/iges',
124
            'jar' => 'application/java-archiver',
125
            'jpe' => 'image/jpeg',
126
            'jpeg' => 'image/jpeg',
127
            'jpg' => 'image/jpeg',
128
            'js' => 'application/x-javascript',
129
            'kar' => 'audio/midi',
130
            'lam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
131
            'latex' => 'application/x-latex',
132
            'lha' => 'application/octet-stream',
133
            'log' => 'text/plain',
134
            'lzh' => 'application/octet-stream',
135
            'm1a' => 'audio/mpeg',
136
            'm2a' => 'audio/mpeg',
137
            'm3u' => 'audio/x-mpegurl',
138
            'man' => 'application/x-troff-man',
139
            'me' => 'application/x-troff-me',
140
            'mesh' => 'model/mesh',
141
            'mid' => 'audio/midi',
142
            'midi' => 'audio/midi',
143
            'mov' => 'video/quicktime',
144
            'movie' => 'video/x-sgi-movie',
145
            'mp2' => 'audio/mpeg',
146
            'mp3' => 'audio/mpeg',
147
            'mp4' => 'video/mp4',
148
            'mpa' => 'audio/mpeg',
149
            'mpe' => 'video/mpeg',
150
            'mpeg' => 'video/mpeg',
151
            'mpg' => 'video/mpeg',
152
            'mpga' => 'audio/mpeg',
153
            'ms' => 'application/x-troff-ms',
154
            'msh' => 'model/mesh',
155
            'mxu' => 'video/vnd.mpegurl',
156
            'nc' => 'application/x-netcdf',
157
            'oda' => 'application/oda',
158
            'oga' => 'audio/ogg',
159
            'ogg' => 'application/ogg',
160
            'ogx' => 'application/ogg',
161
            'ogv' => 'video/ogg',
162
            'pbm' => 'image/x-portable-bitmap',
163
            'pct' => 'image/pict',
164
            'pdb' => 'chemical/x-pdb',
165
            'pdf' => 'application/pdf',
166
            'pgm' => 'image/x-portable-graymap',
167
            'pgn' => 'application/x-chess-pgn',
168
            'pict' => 'image/pict',
169
            'png' => 'image/png',
170
            'pnm' => 'image/x-portable-anymap',
171
            'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
172
            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
173
            'pps' => 'application/vnd.ms-powerpoint',
174
            'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
175
            'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
176
            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
177
            'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
178
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
179
            'ppm' => 'image/x-portable-pixmap',
180
            'ppt' => 'application/vnd.ms-powerpoint',
181
            'ps' => 'application/postscript',
182
            'qt' => 'video/quicktime',
183
            'ra' => 'audio/x-realaudio',
184
            'ram' => 'audio/x-pn-realaudio',
185
            'rar' => 'image/x-rar-compressed',
186
            'ras' => 'image/x-cmu-raster',
187
            'rgb' => 'image/x-rgb',
188
            'rm' => 'audio/x-pn-realaudio',
189
            'roff' => 'application/x-troff',
190
            'rpm' => 'audio/x-pn-realaudio-plugin',
191
            'rtf' => 'text/rtf',
192
            'rtx' => 'text/richtext',
193
            'sgm' => 'text/sgml',
194
            'sgml' => 'text/sgml',
195
            'sh' => 'application/x-sh',
196
            'shar' => 'application/x-shar',
197
            'silo' => 'model/mesh',
198
            'sib' => 'application/X-Sibelius-Score',
199
            'sit' => 'application/x-stuffit',
200
            'skd' => 'application/x-koan',
201
            'skm' => 'application/x-koan',
202
            'skp' => 'application/x-koan',
203
            'skt' => 'application/x-koan',
204
            'smi' => 'application/smil',
205
            'smil' => 'application/smil',
206
            'snd' => 'audio/basic',
207
            'so' => 'application/octet-stream',
208
            'spl' => 'application/x-futuresplash',
209
            'src' => 'application/x-wais-source',
210
            'sv4cpio' => 'application/x-sv4cpio',
211
            'sv4crc' => 'application/x-sv4crc',
212
            'svf' => 'application/vnd.svf',
213
            'svg' => 'image/svg+xml',
214
            //'svgz' => 'image/svg+xml',
215
            'swf' => 'application/x-shockwave-flash',
216
            'sxc' => 'application/vnd.sun.xml.calc',
217
            'sxi' => 'application/vnd.sun.xml.impress',
218
            'sxw' => 'application/vnd.sun.xml.writer',
219
            't' => 'application/x-troff',
220
            'tar' => 'application/x-tar',
221
            'tcl' => 'application/x-tcl',
222
            'tex' => 'application/x-tex',
223
            'texi' => 'application/x-texinfo',
224
            'texinfo' => 'application/x-texinfo',
225
            'tga' => 'image/x-targa',
226
            'tif' => 'image/tif',
227
            'tiff' => 'image/tiff',
228
            'tr' => 'application/x-troff',
229
            'tsv' => 'text/tab-seperated-values',
230
            'txt' => 'text/plain',
231
            'ustar' => 'application/x-ustar',
232
            'vcd' => 'application/x-cdlink',
233
            'vrml' => 'model/vrml',
234
            'wav' => 'audio/x-wav',
235
            'wbmp' => 'image/vnd.wap.wbmp',
236
            'wbxml' => 'application/vnd.wap.wbxml',
237
            'webp' => 'image/webp',
238
            'wml' => 'text/vnd.wap.wml',
239
            'wmlc' => 'application/vnd.wap.wmlc',
240
            'wmls' => 'text/vnd.wap.wmlscript',
241
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
242
            'wma' => 'audio/x-ms-wma',
243
            'wmv' => 'video/x-ms-wmv',
244
            'wrl' => 'model/vrml',
245
            'xbm' => 'image/x-xbitmap',
246
            'xht' => 'application/xhtml+xml',
247
            'xhtml' => 'application/xhtml+xml',
248
            'xls' => 'application/vnd.ms-excel',
249
            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
250
            'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
251
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
252
            'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
253
            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
254
            'xml' => 'text/xml',
255
            'xpm' => 'image/x-xpixmap',
256
            'xsl' => 'text/xml',
257
            'xwd' => 'image/x-windowdump',
258
            'xyz' => 'chemical/x-xyz',
259
            'zip' => 'application/zip',
260
        ];
261
262
        if (true === $filename) {
263
            return $mimeTypes;
264
        }
265
266
        // Get the extension of the file
267
        $extension = explode('.', $filename);
268
269
        // $filename will be an array if a . was found
270
        if (is_array($extension)) {
271
            $extension = strtolower($extension[count($extension) - 1]);
272
        } else {
273
            //file without extension
274
            $extension = 'empty';
275
        }
276
277
        //if the extension is found, return the content type
278
        if (isset($mimeTypes[$extension])) {
279
            return $mimeTypes[$extension];
280
        }
281
282
        return 'application/octet-stream';
283
    }
284
285
    /**
286
     * This function smart streams a file to the client using HTTP headers.
287
     *
288
     * @param string $fullFilename The full path of the file to be sent
289
     * @param string $filename     The name of the file as shown to the client
290
     * @param string $contentType  The MIME type of the file
291
     *
292
     * @return bool false if file doesn't exist, true if stream succeeded
293
     */
294
    public static function smartReadFile($fullFilename, $filename, $contentType = 'application/octet-stream')
295
    {
296
        if (!file_exists($fullFilename)) {
297
            header("HTTP/1.1 404 Not Found");
298
299
            return false;
300
        }
301
302
        $size = filesize($fullFilename);
303
        $time = date('r', filemtime($fullFilename));
304
305
        $fm = @fopen($fullFilename, 'rb');
306
        if (!$fm) {
0 ignored issues
show
introduced by
$fm is of type false|resource, thus it always evaluated to false.
Loading history...
307
            header("HTTP/1.1 505 Internal server error");
308
309
            return false;
310
        }
311
312
        $begin = 0;
313
        $end = $size - 1;
314
315
        if (isset($_SERVER['HTTP_RANGE'])) {
316
            if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
317
                $begin = intval($matches[1]);
318
                if (!empty($matches[2])) {
319
                    $end = intval($matches[2]);
320
                }
321
            }
322
        }
323
324
        if (isset($_SERVER['HTTP_RANGE'])) {
325
            header('HTTP/1.1 206 Partial Content');
326
        } else {
327
            header('HTTP/1.1 200 OK');
328
        }
329
330
        header("Content-Type: $contentType");
331
        header('Cache-Control: public, must-revalidate, max-age=0');
332
        header('Pragma: no-cache');
333
        header('Accept-Ranges: bytes');
334
        header('Content-Length:'.(($end - $begin) + 1));
335
        if (isset($_SERVER['HTTP_RANGE'])) {
336
            header("Content-Range: bytes $begin-$end/$size");
337
        }
338
        header("Content-Disposition: inline; filename=$filename");
339
        header("Content-Transfer-Encoding: binary");
340
        header("Last-Modified: $time");
341
342
        $cur = $begin;
343
        fseek($fm, $begin, 0);
344
345
        while (!feof($fm) && $cur <= $end && (0 == connection_status())) {
346
            echo fread($fm, min(1024 * 16, ($end - $cur) + 1));
347
            $cur += 1024 * 16;
348
        }
349
    }
350
351
    /**
352
     * This function streams a file to the client.
353
     *
354
     * @param string $full_file_name
355
     * @param bool   $forced              Whether to force the browser to download the file
356
     * @param string $name
357
     * @param bool   $fixLinksHttpToHttps change file content from http to https
358
     * @param array  $extraHeaders        Additional headers to be sent
359
     *
360
     * @return false if file doesn't exist, true if stream succeeded
361
     */
362
    public static function file_send_for_download(
363
        $full_file_name,
364
        $forced = false,
365
        $name = '',
366
        $fixLinksHttpToHttps = false,
367
        $extraHeaders = []
368
    ) {
369
        session_write_close(); //we do not need write access to session anymore
370
        if (!is_file($full_file_name)) {
371
            return false;
372
        }
373
        $filename = '' == $name ? basename($full_file_name) : api_replace_dangerous_char($name);
374
        $len = filesize($full_file_name);
375
        // Fixing error when file name contains a ","
376
        $filename = str_replace(',', '', $filename);
377
        $sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
378
379
        // Allows chrome to make videos and audios seekable
380
        header('Accept-Ranges: bytes');
381
        if (!empty($extraHeaders)) {
382
            foreach ($extraHeaders as $name => $value) {
383
                //TODO: add restrictions to allowed headers?
384
                header($name.': '.$value);
385
            }
386
        }
387
388
        if ($forced) {
389
            // Force the browser to save the file instead of opening it
390
            if (isset($sendFileHeaders) &&
391
                !empty($sendFileHeaders)) {
392
                header("X-Sendfile: $filename");
393
            }
394
395
            header('Content-type: application/octet-stream');
396
            header('Content-length: '.$len);
397
            if (preg_match("/MSIE 5.5/", $_SERVER['HTTP_USER_AGENT'])) {
398
                header('Content-Disposition: filename= '.$filename);
399
            } else {
400
                header('Content-Disposition: attachment; filename= '.$filename);
401
            }
402
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
403
                header('Pragma: ');
404
                header('Cache-Control: ');
405
                header('Cache-Control: public'); // IE cannot download from sessions without a cache
406
            }
407
            header('Content-Description: '.$filename);
408
            header('Content-Transfer-Encoding: binary');
409
410
            if (function_exists('ob_end_clean') && ob_get_length()) {
411
                // Use ob_end_clean() to avoid weird buffering situations
412
                // where file is sent broken/incomplete for download
413
                ob_end_clean();
414
            }
415
416
            $res = fopen($full_file_name, 'r');
417
            fpassthru($res);
418
419
            return true;
420
        } else {
421
            // no forced download, just let the browser decide what to do according to the mimetype
422
            $lpFixedEncoding = 'true' === api_get_setting('lp.fixed_encoding');
423
424
            // Commented to let courses content to be cached in order to improve performance:
425
            //header('Expires: Wed, 01 Jan 1990 00:00:00 GMT');
426
            //header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
427
428
            // Commented to avoid double caching declaration when playing with IE and HTTPS
429
            //header('Cache-Control: no-cache, must-revalidate');
430
            //header('Pragma: no-cache');
431
432
            $contentType = self::file_get_mime_type($filename);
433
434
            switch ($contentType) {
435
                case 'text/html':
436
                    if (isset($lpFixedEncoding) && 'true' === $lpFixedEncoding) {
437
                        $contentType .= '; charset=UTF-8';
438
                    } else {
439
                        $encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
440
                        if (!empty($encoding)) {
441
                            $contentType .= '; charset='.$encoding;
442
                        }
443
                    }
444
                    break;
445
                case 'text/plain':
446
                    if (isset($lpFixedEncoding) && 'true' === $lpFixedEncoding) {
447
                        $contentType .= '; charset=UTF-8';
448
                    } else {
449
                        $encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
450
                        if (!empty($encoding)) {
451
                            $contentType .= '; charset='.$encoding;
452
                        }
453
                    }
454
                    break;
455
                case 'video/mp4':
456
                case 'audio/mpeg':
457
                case 'audio/mp4':
458
                case 'audio/ogg':
459
                case 'audio/webm':
460
                case 'audio/wav':
461
                case 'video/ogg':
462
                case 'video/webm':
463
                    self::smartReadFile($full_file_name, $filename, $contentType);
464
                    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...
465
                case 'application/vnd.dwg':
466
                case 'application/vnd.dwf':
467
                    header('Content-type: application/octet-stream');
468
                    break;
469
            }
470
471
            header('Content-type: '.$contentType);
472
            header('Content-Length: '.$len);
473
            $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
474
475
            if (strpos($userAgent, 'msie')) {
476
                header('Content-Disposition: ; filename= '.$filename);
477
            } else {
478
                //header('Content-Disposition: inline');
479
                header('Content-Disposition: inline;');
480
            }
481
482
            if ($fixLinksHttpToHttps) {
483
                $content = file_get_contents($full_file_name);
484
                $content = str_replace(
485
                    ['http%3A%2F%2F', 'http://'],
486
                    ['https%3A%2F%2F', 'https://'],
487
                    $content
488
                );
489
                echo $content;
490
            } else {
491
                if (function_exists('ob_end_clean') && ob_get_length()) {
492
                    // Use ob_end_clean() to avoid weird buffering situations
493
                    // where file is sent broken/incomplete for download
494
                    ob_end_clean();
495
                }
496
497
                readfile($full_file_name);
498
            }
499
500
            return true;
501
        }
502
    }
503
504
    /**
505
     * Session folder filters.
506
     *
507
     * @param string $path
508
     * @param int    $sessionId
509
     *
510
     * @return string|null
511
     */
512
    public static function getSessionFolderFilters($path, $sessionId)
513
    {
514
        $sessionId = (int) $sessionId;
515
        $condition = null;
516
517
        if (!empty($sessionId)) {
518
            // Chat folder filter
519
            if ('/chat_files' == $path) {
520
                $condition .= " AND (docs.session_id = '$sessionId') ";
521
            }
522
            // share_folder filter
523
            $condition .= " AND docs.path != '/shared_folder' ";
524
        }
525
526
        return $condition;
527
    }
528
529
    /**
530
     * Fetches all document data for the given user/group.
531
     *
532
     * @param array  $courseInfo
533
     * @param string $path
534
     * @param int    $toGroupId       iid
535
     * @param int    $toUserId
536
     * @param bool   $canSeeInvisible
537
     * @param bool   $search
538
     * @param int    $sessionId
539
     *
540
     * @return array with all document data
541
     */
542
    public static function getAllDocumentData(
543
        $courseInfo,
544
        $path = '/',
545
        $toGroupId = 0,
546
        $toUserId = null,
547
        $canSeeInvisible = false,
548
        $search = false,
549
        $sessionId = 0,
550
        User $currentUser = null
551
    ) {
552
        if (empty($courseInfo)) {
553
            return [];
554
        }
555
556
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
557
        $currentUser = $currentUser ?: api_get_current_user();
558
559
        // Escape underscores in the path so they don't act as a wildcard
560
        $originalPath = $path;
561
        $path = str_replace('_', '\_', $path);
562
563
        // The given path will not end with a slash, unless it's the root '/'
564
        // so no root -> add slash
565
        $addedSlash = '/' == $path ? '' : '/';
566
567
        $sharedCondition = null;
568
        if ('/shared_folder' == $originalPath) {
569
            $students = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
570
            if (!empty($students)) {
571
                $conditionList = [];
572
                foreach ($students as $studentInfo) {
573
                    $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
574
                }
575
                $sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
576
            }
577
        }
578
579
        $sql = "SELECT
580
                    docs.id,
581
                    docs.filetype,
582
                    docs.path,
583
                    docs.title,
584
                    docs.comment,
585
                    docs.size,
586
                    docs.readonly,
587
                    docs.session_id,
588
                    creator_id,
589
                    visibility,
590
                    n.updated_at,
591
                    n.created_at,
592
                    n.creator_id
593
                FROM resource_node AS n
594
                INNER JOIN $tblDocument AS docs
595
                ON (docs.resource_node_id = n.id)
596
                INNER JOIN resource_link l
597
                ON (l.resource_node_id = n.id)
598
                WHERE
599
                    docs.c_id = {$courseInfo['real_id']} AND
600
                    docs.path LIKE '".Database::escape_string($path.$addedSlash.'%')."' AND
601
                    docs.path NOT LIKE '".Database::escape_string($path.$addedSlash.'%/%')."' AND
602
                    docs.path NOT LIKE '%_DELETED_%' AND
603
                    l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
604
                    $sharedCondition
605
                ";
606
        //$userGroupFilter AND
607
        //$conditionSession
608
        $result = Database::query($sql);
609
610
        $documentData = [];
611
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
612
        $isCoach = api_is_coach();
613
        if (false !== $result && 0 != Database::num_rows($result)) {
614
            $rows = [];
615
            $hideInvisibleDocuments = api_get_configuration_value('hide_invisible_course_documents_in_sessions');
616
            while ($row = Database::fetch_array($result, 'ASSOC')) {
617
                if (isset($rows[$row['id']])) {
618
                    continue;
619
                }
620
621
                // If we are in session and hide_invisible_course_documents_in_sessions is enabled
622
                // Then we avoid the documents that have visibility in session but that they come from a base course
623
                /*if ($hideInvisibleDocuments && $sessionId) {
624
                    if ($row['item_property_session_id'] == $sessionId && empty($row['session_id'])) {
625
                        continue;
626
                    }
627
                }*/
628
629
                if (self::isBasicCourseFolder($row['path'], $sessionId)) {
630
                    $basicCourseDocumentsContent = self::getAllDocumentData(
631
                        $courseInfo,
632
                        $row['path']
633
                    );
634
635
                    if (empty($basicCourseDocumentsContent)) {
636
                        continue;
637
                    }
638
                }
639
640
                $rows[$row['id']] = $row;
641
            }
642
643
            // If we are in session and hide_invisible_course_documents_in_sessions is enabled
644
            // Or if we are students
645
            // Then don't list the invisible or deleted documents
646
            if (($sessionId && $hideInvisibleDocuments) || (!$isCoach && !$isAllowedToEdit)) {
647
                $rows = array_filter($rows, function ($row) {
648
                    if (in_array(
649
                        $row['visibility'],
650
                        [
651
                            ResourceLink::VISIBILITY_DELETED,
652
                            ResourceLink::VISIBILITY_DRAFT,
653
                        ]
654
                    )) {
655
                        return false;
656
                    }
657
658
                    return true;
659
                });
660
            }
661
662
            foreach ($rows as $row) {
663
                if ('file' == $row['filetype'] &&
664
                    'html' == pathinfo($row['path'], PATHINFO_EXTENSION)
665
                ) {
666
                    // Templates management
667
                    $tblTemplate = Database::get_main_table(TABLE_MAIN_TEMPLATES);
668
                    $sql = "SELECT id FROM $tblTemplate
669
                            WHERE
670
                                c_id = '".$courseInfo['real_id']."' AND
671
                                user_id = '".$currentUser->getId()."' AND
672
                                ref_doc = '".$row['id']."'";
673
                    $templateResult = Database::query($sql);
674
                    $row['is_template'] = (Database::num_rows($templateResult) > 0) ? 1 : 0;
675
                }
676
                $row['basename'] = basename($row['path']);
677
                // Just filling $document_data.
678
                $documentData[$row['id']] = $row;
679
            }
680
681
            // Only for the student we filter the results see BT#1652
682
            if (!$isCoach && !$isAllowedToEdit) {
683
                // Checking parents visibility.
684
                $finalDocumentData = [];
685
                foreach ($documentData as $row) {
686
                    $isVisible = self::check_visibility_tree(
687
                        $row['id'],
688
                        $courseInfo,
689
                        $sessionId,
690
                        $currentUser->getId(),
691
                        $toGroupId
692
                    );
693
                    if ($isVisible) {
694
                        $finalDocumentData[$row['id']] = $row;
695
                    }
696
                }
697
            } else {
698
                $finalDocumentData = $documentData;
699
            }
700
701
            return $finalDocumentData;
702
        }
703
704
        return [];
705
    }
706
707
    /**
708
     * Gets the paths of all folders in a course
709
     * can show all folders (except for the deleted ones) or only visible ones.
710
     *
711
     * @param array  $courseInfo
712
     * @param int    $groupIid          iid
713
     * @param bool   $can_see_invisible
714
     * @param bool   $getInvisibleList
715
     * @param string $path              current path
716
     *
717
     * @return array with paths
718
     */
719
    public static function get_all_document_folders(
720
        $courseInfo,
721
        $groupIid = 0,
722
        $can_see_invisible = false,
723
        $getInvisibleList = false,
724
        $path = ''
725
    ) {
726
        if (empty($courseInfo)) {
727
            return [];
728
        }
729
730
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
731
        $groupIid = (int) $groupIid;
732
        $courseId = $courseInfo['real_id'];
733
        $sessionId = api_get_session_id();
734
735
        $folders = [];
736
        $students = CourseManager::get_user_list_from_course_code(
737
            $courseInfo['code'],
738
            api_get_session_id()
739
        );
740
741
        $conditionList = [];
742
        if (!empty($students)) {
743
            foreach ($students as $studentId => $studentInfo) {
744
                $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
745
            }
746
        }
747
748
        $groupCondition = " l.group_id = $groupIid";
749
        if (empty($groupIid)) {
750
            $groupCondition = ' (l.group_id = 0 OR l.group_id IS NULL)';
751
        }
752
753
        $show_users_condition = '';
754
        if ('false' === api_get_setting('show_users_folders')) {
755
            $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
756
        }
757
758
        if ($can_see_invisible) {
759
            $sessionId = $sessionId ?: api_get_session_id();
760
            $condition_session = " AND (l.session_id = '$sessionId' OR (l.session_id = '0' OR l.session_id IS NULL) )";
761
            $condition_session .= self::getSessionFolderFilters($path, $sessionId);
762
763
            $sql = "SELECT DISTINCT docs.id, docs.path
764
                    FROM resource_node AS n
765
                    INNER JOIN $TABLE_DOCUMENT  AS docs
766
                    ON (docs.resource_node_id = n.id)
767
                    INNER JOIN resource_link l
768
                    ON (l.resource_node_id = n.id)
769
                    WHERE
770
                        docs.c_id = $courseId AND
771
                        docs.filetype = 'folder' AND
772
                        $groupCondition AND
773
                        docs.path NOT LIKE '%shared_folder%' AND
774
                        docs.path NOT LIKE '%_DELETED_%' AND
775
                        l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
776
                        $condition_session ";
777
778
            if (0 != $groupIid) {
779
                $sql .= " AND docs.path NOT LIKE '%shared_folder%' ";
780
            } else {
781
                $sql .= $show_users_condition;
782
            }
783
784
            $result = Database::query($sql);
785
            if ($result && 0 != Database::num_rows($result)) {
786
                while ($row = Database::fetch_array($result, 'ASSOC')) {
787
                    if (self::is_folder_to_avoid($row['path'])) {
788
                        continue;
789
                    }
790
791
                    if (false !== strpos($row['path'], '/shared_folder/')) {
792
                        if (!in_array($row['path'], $conditionList)) {
793
                            continue;
794
                        }
795
                    }
796
797
                    $folders[$row['id']] = $row['path'];
798
                }
799
800
                if (!empty($folders)) {
801
                    natsort($folders);
802
                }
803
804
                return $folders;
805
            } else {
806
                return false;
807
            }
808
        } else {
809
            // No invisible folders
810
            // Condition for the session
811
            $condition_session = api_get_session_condition(
812
                $sessionId,
813
                true,
814
                false,
815
                'docs.session_id'
816
            );
817
818
            $visibilityCondition = 'l.visibility = 1';
819
            $fileType = "docs.filetype = 'folder' AND";
820
            if ($getInvisibleList) {
821
                $visibilityCondition = 'l.visibility = 0';
822
                $fileType = '';
823
            }
824
825
            //get visible folders
826
            $sql = "SELECT DISTINCT docs.id, docs.path
827
                    FROM resource_node AS n
828
                    INNER JOIN $TABLE_DOCUMENT  AS docs
829
                    ON (docs.resource_node_id = n.id)
830
                    INNER JOIN resource_link l
831
                    ON (l.resource_node_id = n.id)
832
                    WHERE
833
                        $fileType
834
                        $groupCondition AND
835
                        $visibilityCondition
836
                        $show_users_condition
837
                        $condition_session AND
838
                        docs.c_id = $courseId ";
839
            $result = Database::query($sql);
840
            $visibleFolders = [];
841
            while ($row = Database::fetch_array($result, 'ASSOC')) {
842
                $visibleFolders[$row['id']] = $row['path'];
843
            }
844
845
            if ($getInvisibleList) {
846
                return $visibleFolders;
847
            }
848
849
            // get invisible folders
850
            $sql = "SELECT DISTINCT docs.id, docs.path
851
                    FROM resource_node AS n
852
                    INNER JOIN $TABLE_DOCUMENT  AS docs
853
                    ON (docs.resource_node_id = n.id)
854
                    INNER JOIN resource_link l
855
                    ON (l.resource_node_id = n.id)
856
                    WHERE
857
                        docs.filetype = 'folder' AND
858
                        $groupCondition AND
859
                        l.visibility IN ('".ResourceLink::VISIBILITY_PENDING."')
860
                        $condition_session AND
861
                        docs.c_id = $courseId ";
862
            $result = Database::query($sql);
863
            $invisibleFolders = [];
864
            while ($row = Database::fetch_array($result, 'ASSOC')) {
865
                //get visible folders in the invisible ones -> they are invisible too
866
                $sql = "SELECT DISTINCT docs.id, docs.path
867
                        FROM resource_node AS n
868
                        INNER JOIN $TABLE_DOCUMENT  AS docs
869
                        ON (docs.resource_node_id = n.id)
870
                        INNER JOIN resource_link l
871
                        ON (l.resource_node_id = n.id)
872
                        WHERE
873
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
874
                            docs.filetype = 'folder' AND
875
                            $groupCondition AND
876
                            l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
877
                            $condition_session AND
878
                            docs.c_id = $courseId ";
879
                $folder_in_invisible_result = Database::query($sql);
880
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
881
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
882
                }
883
            }
884
885
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
886
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
887
                $folders = array_diff($visibleFolders, $invisibleFolders);
888
                natsort($folders);
889
890
                return $folders;
891
            }
892
893
            if (is_array($visibleFolders)) {
894
                natsort($visibleFolders);
895
896
                return $visibleFolders;
897
            }
898
899
            // no visible folders found
900
            return false;
901
        }
902
    }
903
904
    /**
905
     * This check if a document has the readonly property checked, then see if the user
906
     * is the owner of this file, if all this is true then return true.
907
     *
908
     * @param array  $_course
909
     * @param int    $user_id     id of the current user
910
     * @param string $file        path stored in the database (if not defined, $documentId must be used)
911
     * @param int    $document_id in case you don't have the file path ,
912
     *                            insert the id of the file here and leave $file in blank ''
913
     * @param bool   $to_delete
914
     * @param int    $sessionId
915
     *
916
     * @return bool true/false
917
     * */
918
    public static function check_readonly(
919
        $_course,
920
        $user_id,
921
        $document_id = 0,
922
        $to_delete = false,
923
        $sessionId = null
924
    ) {
925
        $sessionId = (int) $sessionId;
926
        if (empty($sessionId)) {
927
            $sessionId = api_get_session_id();
928
        }
929
        $document_id = (int) $document_id;
930
931
        $repo = Container::getDocumentRepository();
932
        /** @var CDocument $document */
933
        $document = $repo->find($document_id);
934
935
        if (null === $document) {
936
            return false;
937
        }
938
939
        $readOnly = $document->getReadonly();
940
941
        return $readOnly;
942
    }
943
944
    /**
945
     * This check if a document is a folder or not.
946
     *
947
     * @param array $_course
948
     * @param int   $id      document id
949
     *
950
     * @return bool true/false
951
     * */
952
    public static function isFolder($_course, $id)
953
    {
954
        $table = Database::get_course_table(TABLE_DOCUMENT);
955
        if (empty($_course)) {
956
            return false;
957
        }
958
        $course_id = $_course['real_id'];
959
        $id = (int) $id;
960
        $sql = "SELECT filetype FROM $table
961
                WHERE c_id = $course_id AND id= $id";
962
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
963
964
        return 'folder' === $result['filetype'];
965
    }
966
967
    /**
968
     * @param int   $document_id
969
     * @param array $course_info
970
     * @param int   $session_id
971
     * @param bool  $remove_content_from_db
972
     */
973
    public static function deleteDocumentFromDb(
974
        $document_id,
975
        $course_info = [],
976
        $session_id = 0,
977
        $remove_content_from_db = false
978
    ) {
979
        // Deleting from the DB
980
        $user_id = api_get_user_id();
981
        $document_id = (int) $document_id;
982
983
        if (empty($course_info)) {
984
            $course_info = api_get_course_info();
985
        }
986
987
        if (empty($session_id)) {
988
            $session_id = api_get_session_id();
989
        }
990
991
        self::delete_document_from_search_engine($course_info['code'], $document_id);
992
        self::unsetDocumentAsTemplate($document_id, $course_info['real_id'], $user_id);
993
994
        // Hard DB delete
995
        if ($remove_content_from_db) {
996
            $repo = Container::getDocumentRepository();
997
            /** @var CDocument $document */
998
            $document = $repo->find($document_id);
999
            $repo->softDelete($document);
1000
1001
            return true;
1002
        }
1003
    }
1004
1005
    /**
1006
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
1007
     * Files/folders that are inside a deleted folder get visibility 2.
1008
     *
1009
     * @param array  $_course
1010
     * @param string $path          Path stored in the database
1011
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
1012
     * @param int    $sessionId     The ID of the session, if any
1013
     * @param int    $documentId    The document id, if available
1014
     * @param int    $groupId       iid
1015
     *
1016
     * @return bool true/false
1017
     *
1018
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1019
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1020
     */
1021
    public static function delete_document(
1022
        $_course,
1023
        $path = null,
1024
        $base_work_dir = null,
1025
        $sessionId = null,
1026
        $documentId = null,
1027
        $groupId = 0
1028
    ) {
1029
        $groupId = (int) $groupId;
1030
        if (empty($groupId)) {
1031
            $groupId = api_get_group_id();
1032
        }
1033
1034
        $sessionId = (int) $sessionId;
1035
        if (empty($sessionId)) {
1036
            $sessionId = api_get_session_id();
1037
        }
1038
1039
        $course_id = $_course['real_id'];
1040
1041
        if (empty($course_id)) {
1042
            return false;
1043
        }
1044
1045
        if (empty($documentId)) {
1046
            $documentId = self::get_document_id($_course, $path, $sessionId);
1047
            $docInfo = self::get_document_data_by_id(
1048
                $documentId,
1049
                $_course['code'],
1050
                false,
1051
                $sessionId
1052
            );
1053
            $path = $docInfo['path'];
1054
        } else {
1055
            $docInfo = self::get_document_data_by_id(
1056
                $documentId,
1057
                $_course['code'],
1058
                false,
1059
                $sessionId
1060
            );
1061
            if (empty($docInfo)) {
1062
                return false;
1063
            }
1064
            $path = $docInfo['path'];
1065
        }
1066
1067
        $documentId = (int) $documentId;
1068
1069
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1070
            return false;
1071
        }
1072
1073
        $repo = Container::getDocumentRepository();
1074
        $document = $repo->find($docInfo['iid']);
1075
        if ($document) {
1076
            $repo->hardDelete($document);
1077
1078
            return true;
1079
        }
1080
1081
        return false;
1082
    }
1083
1084
    /**
1085
     * Removes documents from search engine database.
1086
     *
1087
     * @param string $course_id   Course code
1088
     * @param int    $document_id Document id to delete
1089
     */
1090
    public static function delete_document_from_search_engine($course_id, $document_id)
1091
    {
1092
        // remove from search engine if enabled
1093
        if ('true' === api_get_setting('search_enabled')) {
1094
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1095
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1096
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1097
            $res = Database::query($sql);
1098
            if (Database::num_rows($res) > 0) {
1099
                $row2 = Database::fetch_array($res);
1100
                $di = new ChamiloIndexer();
1101
                $di->remove_document($row2['search_did']);
1102
            }
1103
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1104
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1105
            Database::query($sql);
1106
1107
            // remove terms from db
1108
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1109
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1110
        }
1111
    }
1112
1113
    /**
1114
     * Gets the id of a document with a given path.
1115
     *
1116
     * @param array  $courseInfo
1117
     * @param string $path
1118
     * @param int    $sessionId
1119
     *
1120
     * @return int id of document / false if no doc found
1121
     */
1122
    public static function get_document_id($courseInfo, $path, $sessionId = 0)
1123
    {
1124
        $table = Database::get_course_table(TABLE_DOCUMENT);
1125
        $courseId = $courseInfo['real_id'];
1126
1127
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
1128
        $sessionCondition = api_get_session_condition($sessionId, true);
1129
1130
        $path = Database::escape_string($path);
1131
        if (!empty($courseId) && !empty($path)) {
1132
            $sql = "SELECT id FROM $table
1133
                    WHERE
1134
                        c_id = $courseId AND
1135
                        path LIKE BINARY '$path'
1136
                        $sessionCondition
1137
                    LIMIT 1";
1138
1139
            $result = Database::query($sql);
1140
            if (Database::num_rows($result)) {
1141
                $row = Database::fetch_array($result);
1142
1143
                return (int) $row['id'];
1144
            }
1145
        }
1146
1147
        return false;
1148
    }
1149
1150
    /**
1151
     * Gets the document data with a given id.
1152
     *
1153
     * @param int    $id            Document Id (id field in c_document table)
1154
     * @param string $course_code   Course code
1155
     * @param bool   $load_parents  load folder parents
1156
     * @param int    $session_id    The session ID,
1157
     *                              0 if requires context *out of* session, and null to use global context
1158
     * @param bool   $ignoreDeleted
1159
     *
1160
     * @return array document content
1161
     */
1162
    public static function get_document_data_by_id(
1163
        $id,
1164
        $course_code,
1165
        $load_parents = false,
1166
        $session_id = null,
1167
        $ignoreDeleted = false
1168
    ) {
1169
        $course_info = api_get_course_info($course_code);
1170
        $course_id = $course_info['real_id'];
1171
1172
        if (empty($course_info)) {
1173
            return false;
1174
        }
1175
1176
        $session_id = empty($session_id) ? api_get_session_id() : (int) $session_id;
1177
        $groupId = api_get_group_id();
1178
1179
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
1180
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1181
        $id = (int) $id;
1182
        $sessionCondition = api_get_session_condition($session_id, true, true);
1183
1184
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1185
                WHERE c_id = $course_id $sessionCondition AND id = $id";
1186
1187
        if ($ignoreDeleted) {
1188
            $sql .= " AND path NOT LIKE '%_DELETED_%' ";
1189
        }
1190
1191
        $result = Database::query($sql);
1192
        $courseParam = '&cidReq='.$course_code.'&id='.$id.'&id_session='.$session_id.'&gidReq='.$groupId;
1193
        if ($result && 1 == Database::num_rows($result)) {
1194
            $row = Database::fetch_array($result, 'ASSOC');
1195
            //@todo need to clarify the name of the URLs not nice right now
1196
            $url_path = urlencode($row['path']);
1197
            $path = str_replace('%2F', '/', $url_path);
1198
            $pathinfo = pathinfo($row['path']);
1199
1200
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.$courseParam;
1201
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?id='.$id.$courseParam;
1202
            //$row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1203
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1204
            //$row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1205
            $row['direct_url'] = $www.$path;
1206
            $row['basename'] = basename($row['path']);
1207
1208
            if ('.' == dirname($row['path'])) {
1209
                $row['parent_id'] = '0';
1210
            } else {
1211
                $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), $session_id);
1212
                if (empty($row['parent_id'])) {
1213
                    // Try one more with session id = 0
1214
                    $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), 0);
1215
                }
1216
            }
1217
            $parents = [];
1218
1219
            //Use to generate parents (needed for the breadcrumb)
1220
            //@todo sorry but this for is here because there's not a parent_id in the document table so we parsed the path!!
1221
            if ($load_parents) {
1222
                $dir_array = explode('/', $row['path']);
1223
                $dir_array = array_filter($dir_array);
1224
                $array_len = count($dir_array) + 1;
1225
                $real_dir = '';
1226
1227
                for ($i = 1; $i < $array_len; $i++) {
1228
                    $real_dir .= '/'.(isset($dir_array[$i]) ? $dir_array[$i] : '');
1229
                    $parent_id = self::get_document_id($course_info, $real_dir);
1230
                    if (0 != $session_id && empty($parent_id)) {
1231
                        $parent_id = self::get_document_id($course_info, $real_dir, 0);
1232
                    }
1233
                    if (!empty($parent_id)) {
1234
                        $sub_document_data = self::get_document_data_by_id(
1235
                            $parent_id,
1236
                            $course_code,
1237
                            false,
1238
                            $session_id
1239
                        );
1240
                        if (0 != $session_id and !$sub_document_data) {
1241
                            $sub_document_data = self::get_document_data_by_id(
1242
                                $parent_id,
1243
                                $course_code,
1244
                                false,
1245
                                0
1246
                            );
1247
                        }
1248
                        //@todo add visibility here
1249
                        $parents[] = $sub_document_data;
1250
                    }
1251
                }
1252
            }
1253
            $row['parents'] = $parents;
1254
1255
            return $row;
1256
        }
1257
1258
        return false;
1259
    }
1260
1261
    /**
1262
     * Allow to set a specific document as a new template for CKeditor
1263
     * for a particular user in a particular course.
1264
     *
1265
     * @param string $title
1266
     * @param string $description
1267
     * @param int    $document_id_for_template the document id
1268
     * @param int    $courseId
1269
     * @param int    $user_id
1270
     * @param string $image
1271
     *
1272
     * @return bool
1273
     */
1274
    public static function setDocumentAsTemplate(
1275
        $title,
1276
        $description,
1277
        $document_id_for_template,
1278
        $courseId,
1279
        $user_id,
1280
        $image
1281
    ) {
1282
        // Database table definition
1283
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1284
        $params = [
1285
            'title' => $title,
1286
            'description' => $description,
1287
            'c_id' => $courseId,
1288
            'user_id' => $user_id,
1289
            'ref_doc' => $document_id_for_template,
1290
            'image' => $image,
1291
        ];
1292
        Database::insert($table_template, $params);
1293
1294
        return true;
1295
    }
1296
1297
    /**
1298
     * Unset a document as template.
1299
     *
1300
     * @param int $document_id
1301
     * @param int $courseId
1302
     * @param int $user_id
1303
     */
1304
    public static function unsetDocumentAsTemplate(
1305
        $document_id,
1306
        $courseId,
1307
        $user_id
1308
    ) {
1309
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1310
        $courseId = (int) $courseId;
1311
        $user_id = (int) $user_id;
1312
        $document_id = (int) $document_id;
1313
1314
        $sql = 'SELECT id FROM '.$table_template.'
1315
                WHERE
1316
                    c_id = "'.$courseId.'" AND
1317
                    user_id = "'.$user_id.'" AND
1318
                    ref_doc = "'.$document_id.'"';
1319
        $result = Database::query($sql);
1320
        $template_id = Database::result($result, 0, 0);
1321
1322
        my_delete(api_get_path(SYS_CODE_PATH).'upload/template_thumbnails/'.$template_id.'.jpg');
1323
1324
        $sql = 'DELETE FROM '.$table_template.'
1325
                WHERE
1326
                    c_id ="'.$courseId.'" AND
1327
                    user_id="'.$user_id.'" AND
1328
                    ref_doc="'.$document_id.'"';
1329
1330
        Database::query($sql);
1331
    }
1332
1333
    /**
1334
     * Check document visibility.
1335
     *
1336
     * @param string $doc_path the relative complete path of the document
1337
     * @param array  $course   the _course array info of the document's course
1338
     * @param int
1339
     * @param string
1340
     *
1341
     * @return bool
1342
     */
1343
    public static function is_visible(
1344
        $doc_path,
1345
        $course,
1346
        $session_id = 0,
1347
        $file_type = 'file'
1348
    ) {
1349
        $docTable = Database::get_course_table(TABLE_DOCUMENT);
1350
1351
        $course_id = $course['real_id'];
1352
        // note the extra / at the end of doc_path to match every path in
1353
        // the document table that is part of the document path
1354
        $session_id = (int) $session_id;
1355
        $condition = " AND d.session_id IN  ('$session_id', '0') ";
1356
        // The " d.filetype='file' " let the user see a file even if the folder is hidden see #2198
1357
1358
        /*
1359
          When using hotpotatoes files, a new html files are generated
1360
          in the hotpotatoes folder to display the test.
1361
          The genuine html file is copied to math4.htm(user_id).t.html
1362
          Images files are not copied, and keep same name.
1363
          To check the html file visibility, we don't have to check file math4.htm(user_id).t.html but file math4.htm
1364
          In this case, we have to remove (user_id).t.html to check the visibility of the file
1365
          For images, we just check the path of the image file.
1366
1367
          Exemple of hotpotatoes folder :
1368
          A.jpg
1369
          maths4-consigne.jpg
1370
          maths4.htm
1371
          maths4.htm1.t.html
1372
          maths4.htm52.t.html
1373
          maths4.htm654.t.html
1374
          omega.jpg
1375
          theta.jpg
1376
         */
1377
1378
        if (strpos($doc_path, 'HotPotatoes_files') && preg_match("/\.t\.html$/", $doc_path)) {
1379
            $doc_path = substr($doc_path, 0, strlen($doc_path) - 7 - strlen(api_get_user_id()));
1380
        }
1381
1382
        if (!in_array($file_type, ['file', 'folder'])) {
1383
            $file_type = 'file';
1384
        }
1385
        $doc_path = Database::escape_string($doc_path).'/';
1386
1387
        $sql = "SELECT iid
1388
                FROM $docTable d
1389
        		WHERE
1390
        		    d.c_id  = $course_id AND
1391
        		    $condition AND
1392
        			filetype = '$file_type' AND
1393
        			locate(concat(path,'/'), '$doc_path')=1
1394
                ";
1395
1396
        $result = Database::query($sql);
1397
        $is_visible = false;
1398
        if (Database::num_rows($result) > 0) {
1399
            $row = Database::fetch_array($result, 'ASSOC');
1400
1401
            $em = Database::getManager();
1402
1403
            $repo = $em->getRepository('ChamiloCourseBundle:CDocument');
1404
            /** @var \Chamilo\CourseBundle\Entity\CDocument $document */
1405
            $document = $repo->find($row['iid']);
1406
            if (ResourceLink::VISIBILITY_PUBLISHED === $document->getVisibility()) {
1407
                $is_visible = api_is_allowed_in_course() || api_is_platform_admin();
1408
            }
1409
        }
1410
1411
        /* improved protection of documents viewable directly through the url:
1412
            incorporates the same protections of the course at the url of
1413
            documents:
1414
            access allowed for the whole world Open, access allowed for
1415
            users registered on the platform Private access, document accessible
1416
            only to course members (see the Users list), Completely closed;
1417
            the document is only accessible to the course admin and
1418
            teaching assistants.*/
1419
        //return $_SESSION ['is_allowed_in_course'] || api_is_platform_admin();
1420
        return $is_visible;
1421
    }
1422
1423
    /**
1424
     * Return true if user can see a file.
1425
     *
1426
     * @param   int     document id
1427
     * @param   array   course info
1428
     * @param   int
1429
     * @param   int
1430
     * @param bool
1431
     *
1432
     * @return bool
1433
     */
1434
    public static function is_visible_by_id(
1435
        $doc_id,
1436
        $course_info,
1437
        $sessionId,
1438
        $user_id,
1439
        $admins_can_see_everything = true,
1440
        $userIsSubscribed = null
1441
    ) {
1442
        $user_in_course = false;
1443
1444
        //1. Checking the course array
1445
        if (empty($course_info)) {
1446
            $course_info = api_get_course_info();
1447
            if (empty($course_info)) {
1448
                return false;
1449
            }
1450
        }
1451
1452
        $doc_id = (int) $doc_id;
1453
        $sessionId = (int) $sessionId;
1454
        // 2. Course and Session visibility are handle in local.inc.php/global.inc.php
1455
        // 3. Checking if user exist in course/session
1456
        if (0 == $sessionId) {
1457
            if (is_null($userIsSubscribed)) {
1458
                $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
1459
                    $user_id,
1460
                    $course_info['code']
1461
                );
1462
            }
1463
1464
            if (true === $userIsSubscribed || api_is_platform_admin()) {
1465
                $user_in_course = true;
1466
            }
1467
1468
            // Check if course is open then we can consider that the student is registered to the course
1469
            if (isset($course_info) &&
1470
                in_array(
1471
                    $course_info['visibility'],
1472
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
1473
                )
1474
            ) {
1475
                $user_in_course = true;
1476
            }
1477
        } else {
1478
            $user_status = SessionManager::get_user_status_in_course_session(
1479
                $user_id,
1480
                $course_info['real_id'],
1481
                $sessionId
1482
            );
1483
1484
            if (in_array($user_status, ['0', '2', '6'])) {
1485
                //is true if is an student, course session teacher or coach
1486
                $user_in_course = true;
1487
            }
1488
1489
            if (api_is_platform_admin()) {
1490
                $user_in_course = true;
1491
            }
1492
        }
1493
1494
        $em = Database::getManager();
1495
1496
        // 4. Checking document visibility (i'm repeating the code in order to be more clear when reading ) - jm
1497
        if ($user_in_course) {
1498
            $repo = $em->getRepository('ChamiloCourseBundle:CDocument');
1499
            /** @var CDocument $document */
1500
            $document = $repo->find($doc_id);
1501
            $link = $document->getCourseSessionResourceLink($course_info['entity']);
1502
            if ($link && ResourceLink::VISIBILITY_PUBLISHED == $link->getVisibility()) {
1503
                return true;
1504
            }
1505
1506
            return false;
1507
        } elseif ($admins_can_see_everything && api_is_platform_admin()) {
1508
            return true;
1509
        }
1510
1511
        return false;
1512
    }
1513
1514
    /**
1515
     * Allow attach a certificate to a course.
1516
     *
1517
     * @todo move to certificate.lib.php
1518
     *
1519
     * @param int $courseId
1520
     * @param int $document_id
1521
     * @param int $sessionId
1522
     */
1523
    public static function attach_gradebook_certificate($courseId, $document_id, $sessionId = 0)
1524
    {
1525
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1526
        $sessionId = intval($sessionId);
1527
        $courseId = (int) $courseId;
1528
        if (empty($sessionId)) {
1529
            $sessionId = api_get_session_id();
1530
        }
1531
1532
        if (empty($sessionId)) {
1533
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1534
        } elseif ($sessionId > 0) {
1535
            $sql_session = 'AND session_id='.$sessionId;
1536
        } else {
1537
            $sql_session = '';
1538
        }
1539
        $sql = 'UPDATE '.$tbl_category.' SET document_id="'.intval($document_id).'"
1540
                WHERE c_id ="'.$courseId.'" '.$sql_session;
1541
        Database::query($sql);
1542
    }
1543
1544
    /**
1545
     * get the document id of default certificate.
1546
     *
1547
     * @todo move to certificate.lib.php
1548
     *
1549
     * @param int $courseId
1550
     * @param int $session_id
1551
     *
1552
     * @return int The default certificate id
1553
     */
1554
    public static function get_default_certificate_id($courseId, $session_id = 0)
1555
    {
1556
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1557
        $session_id = (int) $session_id;
1558
        $courseId = (int) $courseId;
1559
        if (empty($session_id)) {
1560
            $session_id = api_get_session_id();
1561
        }
1562
1563
        if (empty($session_id)) {
1564
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1565
        } elseif ($session_id > 0) {
1566
            $sql_session = 'AND session_id='.$session_id;
1567
        } else {
1568
            $sql_session = '';
1569
        }
1570
1571
        $sql = 'SELECT document_id FROM '.$tbl_category.'
1572
                WHERE c_id ="'.$courseId.'" '.$sql_session;
1573
1574
        $rs = Database::query($sql);
1575
        $num = Database::num_rows($rs);
1576
        if (0 == $num) {
1577
            return null;
1578
        }
1579
        $row = Database::fetch_array($rs);
1580
1581
        return $row['document_id'];
1582
    }
1583
1584
    /**
1585
     * Allow replace user info in file html.
1586
     *
1587
     * @param int   $user_id
1588
     * @param array $courseInfo
1589
     * @param int   $sessionId
1590
     * @param bool  $is_preview
1591
     *
1592
     * @return array
1593
     */
1594
    public static function replace_user_info_into_html(
1595
        $user_id,
1596
        $courseInfo,
1597
        $sessionId,
1598
        $is_preview = false
1599
    ) {
1600
        $user_id = intval($user_id);
1601
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1602
        $course_id = $courseInfo['real_id'];
1603
1604
        $document_id = self::get_default_certificate_id(
1605
            $course_id,
1606
            $sessionId
1607
        );
1608
1609
        $my_content_html = null;
1610
        if ($document_id) {
1611
            $sql = "SELECT path FROM $tbl_document
1612
                    WHERE c_id = $course_id AND id = $document_id";
1613
            $rs = Database::query($sql);
1614
            $new_content = '';
1615
            $all_user_info = [];
1616
            if (Database::num_rows($rs)) {
1617
                $row = Database::fetch_array($rs);
1618
                $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'.$row['path'];
1619
                if (is_file($filepath)) {
1620
                    $my_content_html = file_get_contents($filepath);
1621
                }
1622
                $all_user_info = self::get_all_info_to_certificate(
1623
                    $user_id,
1624
                    $courseInfo,
1625
                    $is_preview
1626
                );
1627
1628
                $info_to_be_replaced_in_content_html = $all_user_info[0];
1629
                $info_to_replace_in_content_html = $all_user_info[1];
1630
                $new_content = str_replace(
1631
                    $info_to_be_replaced_in_content_html,
1632
                    $info_to_replace_in_content_html,
1633
                    $my_content_html
1634
                );
1635
            }
1636
1637
            return [
1638
                'content' => $new_content,
1639
                'variables' => $all_user_info,
1640
            ];
1641
        }
1642
1643
        return [];
1644
    }
1645
1646
    /**
1647
     * Return all content to replace and all content to be replace.
1648
     *
1649
     * @param int  $user_id
1650
     * @param int  $course_id
1651
     * @param bool $is_preview
1652
     *
1653
     * @return array
1654
     */
1655
    public static function get_all_info_to_certificate($user_id, $course_id, $sessionId, $is_preview = false)
1656
    {
1657
        $info_list = [];
1658
        $user_id = (int) $user_id;
1659
        $course_info = api_get_course_info($course_id);
1660
        $sessionId = (int) $sessionId;
1661
1662
        // Portal info
1663
        $organization_name = api_get_setting('Institution');
1664
        $portal_name = api_get_setting('siteName');
1665
1666
        // Extra user data information
1667
        $extra_user_info_data = UserManager::get_extra_user_data(
1668
            $user_id,
1669
            false,
1670
            false,
1671
            false,
1672
            true
1673
        );
1674
1675
        // get extra fields
1676
        $extraField = new ExtraField('user');
1677
        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
1678
1679
        // Student information
1680
        $user_info = api_get_user_info($user_id);
1681
        $first_name = $user_info['firstname'];
1682
        $last_name = $user_info['lastname'];
1683
        $username = $user_info['username'];
1684
        $official_code = $user_info['official_code'];
1685
1686
        // Teacher information
1687
        $info_teacher_id = UserManager::get_user_id_of_course_admin_or_session_admin($course_info);
1688
        $teacher_info = api_get_user_info($info_teacher_id);
1689
        $teacher_first_name = $teacher_info['firstname'];
1690
        $teacher_last_name = $teacher_info['lastname'];
1691
1692
        // info gradebook certificate
1693
        $info_grade_certificate = UserManager::get_info_gradebook_certificate($courseCode, $sessionId, $user_id);
1694
        $date_long_certificate = '';
1695
        $date_certificate = '';
1696
        $url = '';
1697
        if ($info_grade_certificate) {
1698
            $date_certificate = $info_grade_certificate['created_at'];
1699
            $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$info_grade_certificate['id'];
1700
        }
1701
        $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1702
        if (!empty($date_certificate)) {
1703
            $date_long_certificate = api_convert_and_format_date($date_certificate);
1704
            $date_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_LONG_NO_DAY);
1705
        }
1706
1707
        if ($is_preview) {
1708
            $date_long_certificate = api_convert_and_format_date(api_get_utc_datetime());
1709
            $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1710
        }
1711
1712
        $externalStyleFile = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/certificate.css';
1713
        $externalStyle = '';
1714
        if (is_file($externalStyleFile)) {
1715
            $externalStyle = file_get_contents($externalStyleFile);
1716
        }
1717
        $timeInCourse = Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $sessionId);
1718
        $timeInCourse = api_time_to_hms($timeInCourse, ':', false, true);
1719
1720
        $timeInCourseInAllSessions = 0;
1721
        $sessions = SessionManager::get_session_by_course($course_info['real_id']);
1722
1723
        if (!empty($sessions)) {
1724
            foreach ($sessions as $session) {
1725
                $timeInCourseInAllSessions += Tracking::get_time_spent_on_the_course($user_id, $course_info['real_id'], $session['id']);
1726
            }
1727
        }
1728
        $timeInCourseInAllSessions = api_time_to_hms($timeInCourseInAllSessions, ':', false, true);
1729
1730
        $first = Tracking::get_first_connection_date_on_the_course($user_id, $course_info['real_id'], $sessionId, false);
1731
        $first = substr($first, 0, 10);
1732
        $last = Tracking::get_last_connection_date_on_the_course($user_id, $course_info, $sessionId, false);
1733
        $last = substr($last, 0, 10);
1734
1735
        if ($first === $last) {
1736
            $startDateAndEndDate = get_lang('From').' '.$first;
1737
        } else {
1738
            $startDateAndEndDate = sprintf(
1739
                get_lang('FromDateXToDateY'),
1740
                $first,
1741
                $last
1742
            );
1743
        }
1744
        $courseDescription = new CourseDescription();
1745
        $description = $courseDescription->get_data_by_description_type(2, $course_info['real_id'], $sessionId);
1746
        $courseObjectives = '';
1747
        if ($description) {
1748
            $courseObjectives = $description['description_content'];
1749
        }
1750
1751
        // Replace content
1752
        $info_to_replace_in_content_html = [
1753
            $first_name,
1754
            $last_name,
1755
            $username,
1756
            $organization_name,
1757
            $portal_name,
1758
            $teacher_first_name,
1759
            $teacher_last_name,
1760
            $official_code,
1761
            $date_long_certificate,
1762
            $date_no_time,
1763
            $course_info['code'],
1764
            $course_info['name'],
1765
            isset($info_grade_certificate['grade']) ? $info_grade_certificate['grade'] : '',
1766
            $url,
1767
            '<a href="'.$url.'" target="_blank">'.get_lang('Online link to certificate').'</a>',
1768
            '((certificate_barcode))',
1769
            $externalStyle,
1770
            $timeInCourse,
1771
            $timeInCourseInAllSessions,
1772
            $startDateAndEndDate,
1773
            $courseObjectives,
1774
        ];
1775
1776
        $tags = [
1777
            '((user_firstname))',
1778
            '((user_lastname))',
1779
            '((user_username))',
1780
            '((gradebook_institution))',
1781
            '((gradebook_sitename))',
1782
            '((teacher_firstname))',
1783
            '((teacher_lastname))',
1784
            '((official_code))',
1785
            '((date_certificate))',
1786
            '((date_certificate_no_time))',
1787
            '((course_code))',
1788
            '((course_title))',
1789
            '((gradebook_grade))',
1790
            '((certificate_link))',
1791
            '((certificate_link_html))',
1792
            '((certificate_barcode))',
1793
            '((external_style))',
1794
            '((time_in_course))',
1795
            '((time_in_course_in_all_sessions))',
1796
            '((start_date_and_end_date))',
1797
            '((course_objectives))',
1798
        ];
1799
1800
        if (!empty($extraFields)) {
1801
            foreach ($extraFields as $extraField) {
1802
                $valueExtra = isset($extra_user_info_data[$extraField['variable']]) ? $extra_user_info_data[$extraField['variable']] : '';
1803
                $tags[] = '(('.strtolower($extraField['variable']).'))';
1804
                $info_to_replace_in_content_html[] = $valueExtra;
1805
            }
1806
        }
1807
1808
        $info_list[] = $tags;
1809
        $info_list[] = $info_to_replace_in_content_html;
1810
1811
        return $info_list;
1812
    }
1813
1814
    /**
1815
     * Remove default certificate.
1816
     *
1817
     * @param int $course_id              The course code
1818
     * @param int $default_certificate_id The document id of the default certificate
1819
     */
1820
    public static function remove_attach_certificate($course_id, $default_certificate_id)
1821
    {
1822
        if (empty($default_certificate_id)) {
1823
            return false;
1824
        }
1825
1826
        $default_certificate = self::get_default_certificate_id($course_id);
1827
        if ((int) $default_certificate == (int) $default_certificate_id) {
1828
            $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1829
            $session_id = api_get_session_id();
1830
            if (0 == $session_id || is_null($session_id)) {
1831
                $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
1832
            } elseif ($session_id > 0) {
1833
                $sql_session = 'AND session_id='.intval($session_id);
1834
            } else {
1835
                $sql_session = '';
1836
            }
1837
1838
            $sql = 'UPDATE '.$tbl_category.' SET document_id = null
1839
                    WHERE
1840
                        c_id = "'.Database::escape_string($course_id).'" AND
1841
                        document_id="'.$default_certificate_id.'" '.$sql_session;
1842
            Database::query($sql);
1843
        }
1844
    }
1845
1846
    /**
1847
     * Create directory certificate.
1848
     *
1849
     * @param array $courseInfo
1850
     */
1851
    public static function create_directory_certificate_in_course($courseInfo)
1852
    {
1853
        if (!empty($courseInfo)) {
1854
            $dir_name = '/certificates';
1855
            $post_dir_name = get_lang('Certificates');
1856
            $id = self::get_document_id_of_directory_certificate();
1857
            if (empty($id)) {
1858
                create_unexisting_directory(
1859
                    $courseInfo,
1860
                    api_get_user_id(),
1861
                    api_get_session_id(),
1862
                    0,
1863
                    0,
1864
                    '',
1865
                    $dir_name,
1866
                    $post_dir_name,
1867
                    null,
1868
                    false,
1869
                    false
1870
                );
1871
            }
1872
        }
1873
    }
1874
1875
    /**
1876
     * Get the document id of the directory certificate.
1877
     *
1878
     * @return int The document id of the directory certificate
1879
     *
1880
     * @todo move to certificate.lib.php
1881
     */
1882
    public static function get_document_id_of_directory_certificate()
1883
    {
1884
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1885
        $course_id = api_get_course_int_id();
1886
        $sql = "SELECT id FROM $tbl_document
1887
                WHERE c_id = $course_id AND path='/certificates' ";
1888
        $rs = Database::query($sql);
1889
        $row = Database::fetch_array($rs);
1890
1891
        return $row['id'];
1892
    }
1893
1894
    /**
1895
     * Check if a directory given is for certificate.
1896
     *
1897
     * @todo move to certificate.lib.php
1898
     *
1899
     * @param string $dir path of directory
1900
     *
1901
     * @return bool true if is a certificate or false otherwise
1902
     */
1903
    public static function is_certificate_mode($dir)
1904
    {
1905
        // I'm in the certification module?
1906
        $is_certificate_mode = false;
1907
        $is_certificate_array = explode('/', $dir);
1908
        array_shift($is_certificate_array);
1909
        if (isset($is_certificate_array[0]) && 'certificates' == $is_certificate_array[0]) {
1910
            $is_certificate_mode = true;
1911
        }
1912
1913
        return $is_certificate_mode || (isset($_GET['certificate']) && 'true' === $_GET['certificate']);
1914
    }
1915
1916
    /**
1917
     * Gets the list of included resources as a list of absolute or relative paths from a html file or string html
1918
     * This allows for a better SCORM export or replace urls inside content html from copy course
1919
     * The list will generally include pictures, flash objects, java applets, or any other
1920
     * stuff included in the source of the current item. The current item is expected
1921
     * to be an HTML file or string html. If it is not, then the function will return and empty list.
1922
     *
1923
     * @param    string  source html (content or path)
1924
     * @param    bool    is file or string html
1925
     * @param    string    type (one of the app tools) - optional (otherwise takes the current item's type)
1926
     * @param    int        level of recursivity we're in
1927
     *
1928
     * @return array List of file paths. An additional field containing 'local' or 'remote' helps determine
1929
     *               if the file should be copied into the zip or just linked
1930
     */
1931
    public static function get_resources_from_source_html(
1932
        $source_html,
1933
        $is_file = false,
1934
        $type = null,
1935
        $recursivity = 1
1936
    ) {
1937
        $max = 5;
1938
        $attributes = [];
1939
        $wanted_attributes = [
1940
            'src',
1941
            'url',
1942
            '@import',
1943
            'href',
1944
            'value',
1945
            'flashvars',
1946
            'poster',
1947
        ];
1948
        $explode_attributes = ['flashvars' => 'file'];
1949
        $abs_path = '';
1950
1951
        if ($recursivity > $max) {
1952
            return [];
1953
        }
1954
1955
        if (!isset($type)) {
1956
            $type = TOOL_DOCUMENT;
1957
        }
1958
1959
        if (!$is_file) {
1960
            $attributes = self::parse_HTML_attributes(
1961
                $source_html,
1962
                $wanted_attributes,
1963
                $explode_attributes
1964
            );
1965
        } else {
1966
            if (is_file($source_html)) {
1967
                $abs_path = $source_html;
1968
                //for now, read the whole file in one go (that's gonna be a problem when the file is too big)
1969
                $info = pathinfo($abs_path);
1970
                $ext = $info['extension'];
1971
                switch (strtolower($ext)) {
1972
                    case 'html':
1973
                    case 'htm':
1974
                    case 'shtml':
1975
                    case 'css':
1976
                        $file_content = file_get_contents($abs_path);
1977
                        // get an array of attributes from the HTML source
1978
                        $attributes = self::parse_HTML_attributes(
1979
                            $file_content,
1980
                            $wanted_attributes,
1981
                            $explode_attributes
1982
                        );
1983
                        break;
1984
                    default:
1985
                        break;
1986
                }
1987
            } else {
1988
                return [];
1989
            }
1990
        }
1991
1992
        $files_list = [];
1993
        switch ($type) {
1994
            case TOOL_DOCUMENT:
1995
            case TOOL_QUIZ:
1996
            case 'sco':
1997
                foreach ($wanted_attributes as $attr) {
1998
                    if (isset($attributes[$attr])) {
1999
                        //find which kind of path these are (local or remote)
2000
                        $sources = $attributes[$attr];
2001
                        foreach ($sources as $source) {
2002
                            //skip what is obviously not a resource
2003
                            if (strpos($source, '+this.')) {
2004
                                continue; //javascript code - will still work unaltered
2005
                            }
2006
                            if (false === strpos($source, '.')) {
2007
                                continue; //no dot, should not be an external file anyway
2008
                            }
2009
                            if (strpos($source, 'mailto:')) {
2010
                                continue; //mailto link
2011
                            }
2012
                            if (strpos($source, ';') && !strpos($source, '&amp;')) {
2013
                                continue; //avoid code - that should help
2014
                            }
2015
2016
                            if ('value' == $attr) {
2017
                                if (strpos($source, 'mp3file')) {
2018
                                    $files_list[] = [
2019
                                        substr($source, 0, strpos($source, '.swf') + 4),
2020
                                        'local',
2021
                                        'abs',
2022
                                    ];
2023
                                    $mp3file = substr($source, strpos($source, 'mp3file=') + 8);
2024
                                    if ('/' == substr($mp3file, 0, 1)) {
2025
                                        $files_list[] = [$mp3file, 'local', 'abs'];
2026
                                    } else {
2027
                                        $files_list[] = [$mp3file, 'local', 'rel'];
2028
                                    }
2029
                                } elseif (0 === strpos($source, 'flv=')) {
2030
                                    $source = substr($source, 4);
2031
                                    if (strpos($source, '&') > 0) {
2032
                                        $source = substr($source, 0, strpos($source, '&'));
2033
                                    }
2034
                                    if (strpos($source, '://') > 0) {
2035
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
2036
                                            //we found the current portal url
2037
                                            $files_list[] = [$source, 'local', 'url'];
2038
                                        } else {
2039
                                            //we didn't find any trace of current portal
2040
                                            $files_list[] = [$source, 'remote', 'url'];
2041
                                        }
2042
                                    } else {
2043
                                        $files_list[] = [$source, 'local', 'abs'];
2044
                                    }
2045
                                    /* skipping anything else to avoid two entries
2046
                                    (while the others can have sub-files in their url, flv's can't)*/
2047
                                    continue;
2048
                                }
2049
                            }
2050
                            if (strpos($source, '://') > 0) {
2051
                                //cut at '?' in a URL with params
2052
                                if (strpos($source, '?') > 0) {
2053
                                    $second_part = substr($source, strpos($source, '?'));
2054
                                    if (strpos($second_part, '://') > 0) {
2055
                                        //if the second part of the url contains a url too, treat the second one before cutting
2056
                                        $pos1 = strpos($second_part, '=');
2057
                                        $pos2 = strpos($second_part, '&');
2058
                                        $second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
2059
                                        if (false !== strpos($second_part, api_get_path(WEB_PATH))) {
2060
                                            //we found the current portal url
2061
                                            $files_list[] = [$second_part, 'local', 'url'];
2062
                                            $in_files_list[] = self::get_resources_from_source_html(
2063
                                                $second_part,
2064
                                                true,
2065
                                                TOOL_DOCUMENT,
2066
                                                $recursivity + 1
2067
                                            );
2068
                                            if (count($in_files_list) > 0) {
2069
                                                $files_list = array_merge($files_list, $in_files_list);
2070
                                            }
2071
                                        } else {
2072
                                            //we didn't find any trace of current portal
2073
                                            $files_list[] = [$second_part, 'remote', 'url'];
2074
                                        }
2075
                                    } elseif (strpos($second_part, '=') > 0) {
2076
                                        if ('/' === substr($second_part, 0, 1)) {
2077
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2078
                                            $files_list[] = [$second_part, 'local', 'abs'];
2079
                                            $in_files_list[] = self::get_resources_from_source_html(
2080
                                                $second_part,
2081
                                                true,
2082
                                                TOOL_DOCUMENT,
2083
                                                $recursivity + 1
2084
                                            );
2085
                                            if (count($in_files_list) > 0) {
2086
                                                $files_list = array_merge($files_list, $in_files_list);
2087
                                            }
2088
                                        } elseif (0 === strstr($second_part, '..')) {
2089
                                            //link is relative but going back in the hierarchy
2090
                                            $files_list[] = [$second_part, 'local', 'rel'];
2091
                                            //$dir = api_get_path(SYS_CODE_PATH);//dirname($abs_path);
2092
                                            //$new_abs_path = realpath($dir.'/'.$second_part);
2093
                                            $dir = '';
2094
                                            if (!empty($abs_path)) {
2095
                                                $dir = dirname($abs_path).'/';
2096
                                            }
2097
                                            $new_abs_path = realpath($dir.$second_part);
2098
                                            $in_files_list[] = self::get_resources_from_source_html(
2099
                                                $new_abs_path,
2100
                                                true,
2101
                                                TOOL_DOCUMENT,
2102
                                                $recursivity + 1
2103
                                            );
2104
                                            if (count($in_files_list) > 0) {
2105
                                                $files_list = array_merge($files_list, $in_files_list);
2106
                                            }
2107
                                        } else {
2108
                                            //no starting '/', making it relative to current document's path
2109
                                            if ('./' == substr($second_part, 0, 2)) {
2110
                                                $second_part = substr($second_part, 2);
2111
                                            }
2112
                                            $files_list[] = [$second_part, 'local', 'rel'];
2113
                                            $dir = '';
2114
                                            if (!empty($abs_path)) {
2115
                                                $dir = dirname($abs_path).'/';
2116
                                            }
2117
                                            $new_abs_path = realpath($dir.$second_part);
2118
                                            $in_files_list[] = self::get_resources_from_source_html(
2119
                                                $new_abs_path,
2120
                                                true,
2121
                                                TOOL_DOCUMENT,
2122
                                                $recursivity + 1
2123
                                            );
2124
                                            if (count($in_files_list) > 0) {
2125
                                                $files_list = array_merge($files_list, $in_files_list);
2126
                                            }
2127
                                        }
2128
                                    }
2129
                                    //leave that second part behind now
2130
                                    $source = substr($source, 0, strpos($source, '?'));
2131
                                    if (strpos($source, '://') > 0) {
2132
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
2133
                                            //we found the current portal url
2134
                                            $files_list[] = [$source, 'local', 'url'];
2135
                                            $in_files_list[] = self::get_resources_from_source_html(
2136
                                                $source,
2137
                                                true,
2138
                                                TOOL_DOCUMENT,
2139
                                                $recursivity + 1
2140
                                            );
2141
                                            if (count($in_files_list) > 0) {
2142
                                                $files_list = array_merge($files_list, $in_files_list);
2143
                                            }
2144
                                        } else {
2145
                                            //we didn't find any trace of current portal
2146
                                            $files_list[] = [$source, 'remote', 'url'];
2147
                                        }
2148
                                    } else {
2149
                                        //no protocol found, make link local
2150
                                        if ('/' === substr($source, 0, 1)) {
2151
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2152
                                            $files_list[] = [$source, 'local', 'abs'];
2153
                                            $in_files_list[] = self::get_resources_from_source_html(
2154
                                                $source,
2155
                                                true,
2156
                                                TOOL_DOCUMENT,
2157
                                                $recursivity + 1
2158
                                            );
2159
                                            if (count($in_files_list) > 0) {
2160
                                                $files_list = array_merge($files_list, $in_files_list);
2161
                                            }
2162
                                        } elseif (0 === strstr($source, '..')) {
2163
                                            //link is relative but going back in the hierarchy
2164
                                            $files_list[] = [$source, 'local', 'rel'];
2165
                                            $dir = '';
2166
                                            if (!empty($abs_path)) {
2167
                                                $dir = dirname($abs_path).'/';
2168
                                            }
2169
                                            $new_abs_path = realpath($dir.$source);
2170
                                            $in_files_list[] = self::get_resources_from_source_html(
2171
                                                $new_abs_path,
2172
                                                true,
2173
                                                TOOL_DOCUMENT,
2174
                                                $recursivity + 1
2175
                                            );
2176
                                            if (count($in_files_list) > 0) {
2177
                                                $files_list = array_merge($files_list, $in_files_list);
2178
                                            }
2179
                                        } else {
2180
                                            //no starting '/', making it relative to current document's path
2181
                                            if ('./' == substr($source, 0, 2)) {
2182
                                                $source = substr($source, 2);
2183
                                            }
2184
                                            $files_list[] = [$source, 'local', 'rel'];
2185
                                            $dir = '';
2186
                                            if (!empty($abs_path)) {
2187
                                                $dir = dirname($abs_path).'/';
2188
                                            }
2189
                                            $new_abs_path = realpath($dir.$source);
2190
                                            $in_files_list[] = self::get_resources_from_source_html(
2191
                                                $new_abs_path,
2192
                                                true,
2193
                                                TOOL_DOCUMENT,
2194
                                                $recursivity + 1
2195
                                            );
2196
                                            if (count($in_files_list) > 0) {
2197
                                                $files_list = array_merge($files_list, $in_files_list);
2198
                                            }
2199
                                        }
2200
                                    }
2201
                                }
2202
                                //found some protocol there
2203
                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
2204
                                    //we found the current portal url
2205
                                    $files_list[] = [$source, 'local', 'url'];
2206
                                    $in_files_list[] = self::get_resources_from_source_html(
2207
                                        $source,
2208
                                        true,
2209
                                        TOOL_DOCUMENT,
2210
                                        $recursivity + 1
2211
                                    );
2212
                                    if (count($in_files_list) > 0) {
2213
                                        $files_list = array_merge($files_list, $in_files_list);
2214
                                    }
2215
                                } else {
2216
                                    //we didn't find any trace of current portal
2217
                                    $files_list[] = [$source, 'remote', 'url'];
2218
                                }
2219
                            } else {
2220
                                //no protocol found, make link local
2221
                                if ('/' === substr($source, 0, 1)) {
2222
                                    //link starts with a /, making it absolute (relative to DocumentRoot)
2223
                                    $files_list[] = [$source, 'local', 'abs'];
2224
                                    $in_files_list[] = self::get_resources_from_source_html(
2225
                                        $source,
2226
                                        true,
2227
                                        TOOL_DOCUMENT,
2228
                                        $recursivity + 1
2229
                                    );
2230
                                    if (count($in_files_list) > 0) {
2231
                                        $files_list = array_merge($files_list, $in_files_list);
2232
                                    }
2233
                                } elseif (0 === strpos($source, '..')) {
2234
                                    //link is relative but going back in the hierarchy
2235
                                    $files_list[] = [$source, 'local', 'rel'];
2236
                                    $dir = '';
2237
                                    if (!empty($abs_path)) {
2238
                                        $dir = dirname($abs_path).'/';
2239
                                    }
2240
                                    $new_abs_path = realpath($dir.$source);
2241
                                    $in_files_list[] = self::get_resources_from_source_html(
2242
                                        $new_abs_path,
2243
                                        true,
2244
                                        TOOL_DOCUMENT,
2245
                                        $recursivity + 1
2246
                                    );
2247
                                    if (count($in_files_list) > 0) {
2248
                                        $files_list = array_merge($files_list, $in_files_list);
2249
                                    }
2250
                                } else {
2251
                                    //no starting '/', making it relative to current document's path
2252
                                    if ('./' == substr($source, 0, 2)) {
2253
                                        $source = substr($source, 2);
2254
                                    }
2255
                                    $files_list[] = [$source, 'local', 'rel'];
2256
                                    $dir = '';
2257
                                    if (!empty($abs_path)) {
2258
                                        $dir = dirname($abs_path).'/';
2259
                                    }
2260
                                    $new_abs_path = realpath($dir.$source);
2261
                                    $in_files_list[] = self::get_resources_from_source_html(
2262
                                        $new_abs_path,
2263
                                        true,
2264
                                        TOOL_DOCUMENT,
2265
                                        $recursivity + 1
2266
                                    );
2267
                                    if (count($in_files_list) > 0) {
2268
                                        $files_list = array_merge($files_list, $in_files_list);
2269
                                    }
2270
                                }
2271
                            }
2272
                        }
2273
                    }
2274
                }
2275
                break;
2276
            default: //ignore
2277
                break;
2278
        }
2279
2280
        $checked_files_list = [];
2281
        $checked_array_list = [];
2282
2283
        if (count($files_list) > 0) {
2284
            foreach ($files_list as $idx => $file) {
2285
                if (!empty($file[0])) {
2286
                    if (!in_array($file[0], $checked_files_list)) {
2287
                        $checked_files_list[] = $files_list[$idx][0];
2288
                        $checked_array_list[] = $files_list[$idx];
2289
                    }
2290
                }
2291
            }
2292
        }
2293
2294
        return $checked_array_list;
2295
    }
2296
2297
    /**
2298
     * Parses the HTML attributes given as string.
2299
     *
2300
     * @param string HTML attribute string
2301
     * @param array List of attributes that we want to get back
2302
     * @param array
2303
     *
2304
     * @return array An associative array of attributes
2305
     *
2306
     * @author Based on a function from the HTML_Common2 PEAR module     *
2307
     */
2308
    public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
2309
    {
2310
        $attributes = [];
2311
        $regs = [];
2312
        $reduced = false;
2313
        if (count($wanted) > 0) {
2314
            $reduced = true;
2315
        }
2316
        try {
2317
            //Find all occurences of something that looks like a URL
2318
            // The structure of this regexp is:
2319
            // (find protocol) then
2320
            // (optionally find some kind of space 1 or more times) then
2321
            // find (either an equal sign or a bracket) followed by an optional space
2322
            // followed by some text without quotes (between quotes itself or not)
2323
            // then possible closing brackets if we were in the opening bracket case
2324
            // OR something like @import()
2325
            $res = preg_match_all(
2326
                '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]*))'.
2327
                // '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]|[^\x00-\x7F])*)' . -> seems to be taking too much
2328
                // '/(((([A-Za-z_:])([^\x00-\x7F])*)' . -> takes only last letter of parameter name
2329
                '([ \n\t\r]+)?('.
2330
                // '(=([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+))' . -> doesn't restrict close enough to the url itself
2331
                '(=([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+))'.
2332
                '|'.
2333
                // '(\(([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)\))' . -> doesn't restrict close enough to the url itself
2334
                '(\(([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+)\))'.
2335
                '))'.
2336
                '|'.
2337
                // '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))?/', -> takes a lot (like 100's of thousands of empty possibilities)
2338
                '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))/',
2339
                $attrString,
2340
                $regs
2341
            );
2342
        } catch (Exception $e) {
2343
            error_log('Caught exception: '.$e->getMessage(), 0);
2344
        }
2345
        if ($res) {
2346
            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...
2347
                $name = trim($regs[3][$i]);
2348
                $check = trim($regs[0][$i]);
2349
                $value = trim($regs[10][$i]);
2350
                if (empty($value) and !empty($regs[13][$i])) {
2351
                    $value = $regs[13][$i];
2352
                }
2353
                if (empty($name) && !empty($regs[16][$i])) {
2354
                    $name = '@import';
2355
                    $value = trim($regs[16][$i]);
2356
                }
2357
                if (!empty($name)) {
2358
                    if (!$reduced || in_array(strtolower($name), $wanted)) {
2359
                        if ($name == $check) {
2360
                            $attributes[strtolower($name)][] = strtolower($name);
2361
                        } else {
2362
                            if (!empty($value) && ('\'' == $value[0] || '"' == $value[0])) {
2363
                                $value = substr($value, 1, -1);
2364
                            }
2365
2366
                            if ('API.LMSGetValue(name' == $value) {
2367
                                $value = 'API.LMSGetValue(name)';
2368
                            }
2369
                            //Gets the xx.flv value from the string flashvars="width=320&height=240&autostart=false&file=xxx.flv&repeat=false"
2370
                            if (isset($explode_variables[$name])) {
2371
                                $value_modified = str_replace('&amp;', '&', $value);
2372
                                $value_array = explode('&', $value_modified);
2373
                                foreach ($value_array as $item) {
2374
                                    $itemParts = explode('=', $item);
2375
                                    $key = $itemParts[0];
2376
                                    $item_value = !empty($itemParts[1]) ? $itemParts[1] : '';
2377
                                    if ($key == $explode_variables[$name]) {
2378
                                        $attributes[strtolower($name)][] = $item_value;
2379
                                    }
2380
                                }
2381
                            }
2382
                            $attributes[strtolower($name)][] = $value;
2383
                        }
2384
                    }
2385
                }
2386
            }
2387
        }
2388
2389
        return $attributes;
2390
    }
2391
2392
    /**
2393
     * Replace urls inside content html from a copy course.
2394
     *
2395
     * @param string $content_html
2396
     * @param string $origin_course_code
2397
     * @param string $destination_course_directory
2398
     * @param string $origin_course_path_from_zip
2399
     * @param string $origin_course_info_path
2400
     *
2401
     * @return string new content html with replaced urls or return false if content is not a string
2402
     */
2403
    public static function replaceUrlWithNewCourseCode(
2404
        $content_html,
2405
        $origin_course_code,
2406
        $destination_course_directory,
2407
        $origin_course_path_from_zip = null,
2408
        $origin_course_info_path = null
2409
    ) {
2410
        if (empty($content_html)) {
2411
            return false;
2412
        }
2413
2414
        $orig_source_html = self::get_resources_from_source_html($content_html);
2415
        $orig_course_info = api_get_course_info($origin_course_code);
2416
2417
        // Course does not exist in the current DB probably this came from a zip file?
2418
        if (empty($orig_course_info)) {
2419
            if (!empty($origin_course_path_from_zip)) {
2420
                $orig_course_path = $origin_course_path_from_zip.'/';
2421
                $orig_course_info_path = $origin_course_info_path;
2422
            }
2423
        } else {
2424
            $orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
2425
            $orig_course_info_path = $orig_course_info['path'];
2426
        }
2427
2428
        $destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
2429
        $destination_course_info = api_get_course_info($destination_course_code);
2430
        $dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
2431
        $dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
2432
2433
        $user_id = api_get_user_id();
2434
2435
        if (!empty($orig_source_html)) {
2436
            foreach ($orig_source_html as $source) {
2437
                // Get information about source url
2438
                $real_orig_url = $source[0]; // url
2439
                $scope_url = $source[1]; // scope (local, remote)
2440
                $type_url = $source[2]; // type (rel, abs, url)
2441
2442
                // Get path and query from origin url
2443
                $orig_parse_url = parse_url($real_orig_url);
2444
                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
2445
                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
2446
2447
                // Replace origin course code by destination course code from origin url query
2448
                $dest_url_query = '';
2449
2450
                if (!empty($real_orig_query)) {
2451
                    $dest_url_query = '?'.$real_orig_query;
2452
                    if (false !== strpos($dest_url_query, $origin_course_code)) {
2453
                        $dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
2454
                    }
2455
                }
2456
2457
                if ('local' == $scope_url) {
2458
                    if ('abs' == $type_url || 'rel' == $type_url) {
2459
                        $document_file = strstr($real_orig_path, 'document');
2460
2461
                        if (false !== strpos($real_orig_path, $document_file)) {
2462
                            $origin_filepath = $orig_course_path.$document_file;
2463
                            $destination_filepath = $dest_course_path.$document_file;
2464
2465
                            // copy origin file inside destination course
2466
                            if (file_exists($origin_filepath)) {
2467
                                $filepath_dir = dirname($destination_filepath);
2468
2469
                                if (!is_dir($filepath_dir)) {
2470
                                    $perm = api_get_permissions_for_new_directories();
2471
                                    $result = @mkdir($filepath_dir, $perm, true);
2472
                                    if ($result) {
2473
                                        $filepath_to_add = str_replace(
2474
                                            [$dest_course_path, 'document'],
2475
                                            '',
2476
                                            $filepath_dir
2477
                                        );
2478
2479
                                        // Add to item properties to the new folder
2480
                                        self::addDocument(
2481
                                            $destination_course_info,
2482
                                            $filepath_to_add,
2483
                                            'folder',
2484
                                            0,
2485
                                            basename($filepath_to_add)
2486
                                        );
2487
                                    }
2488
                                }
2489
2490
                                if (!file_exists($destination_filepath)) {
2491
                                    $result = @copy($origin_filepath, $destination_filepath);
2492
                                    if ($result) {
2493
                                        $filepath_to_add = str_replace(
2494
                                            [$dest_course_path, 'document'],
2495
                                            '',
2496
                                            $destination_filepath
2497
                                        );
2498
                                        $size = filesize($destination_filepath);
2499
2500
                                        // Add to item properties to the file
2501
                                        self::addDocument(
2502
                                            $destination_course_info,
2503
                                            $filepath_to_add,
2504
                                            'file',
2505
                                            $size,
2506
                                            basename($filepath_to_add)
2507
                                        );
2508
                                    }
2509
                                }
2510
                            }
2511
2512
                            // Replace origin course path by destination course path.
2513
                            if (false !== strpos($content_html, $real_orig_url)) {
2514
                                $url_course_path = str_replace(
2515
                                    $orig_course_info_path.'/'.$document_file,
2516
                                    '',
2517
                                    $real_orig_path
2518
                                );
2519
                                // See BT#7780
2520
                                $destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
2521
                                // If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
2522
                                if (false === strpos($real_orig_path, $origin_course_code)) {
2523
                                    $url_course_path = $real_orig_path;
2524
                                    $destination_url = $real_orig_path;
2525
                                }
2526
                                $content_html = str_replace($real_orig_url, $destination_url, $content_html);
2527
                            }
2528
                        }
2529
2530
                        // replace origin course code by destination course code  from origin url
2531
                        if (0 === strpos($real_orig_url, '?')) {
2532
                            $dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
2533
                            $content_html = str_replace($real_orig_url, $dest_url, $content_html);
2534
                        }
2535
                    }
2536
                }
2537
            }
2538
        }
2539
2540
        return $content_html;
2541
    }
2542
2543
    /**
2544
     * Export document to PDF.
2545
     *
2546
     * @param int    $documentId
2547
     * @param string $courseCode
2548
     * @param string $orientation
2549
     * @param bool   $showHeaderAndFooter
2550
     */
2551
    public static function export_to_pdf(
2552
        $documentId,
2553
        $courseCode,
2554
        $orientation = 'landscape',
2555
        $showHeaderAndFooter = true
2556
    ) {
2557
        $repo = Container::getDocumentRepository();
2558
        $document = $repo->find($documentId);
2559
2560
        if (empty($document)) {
2561
            return false;
2562
        }
2563
2564
        $filePath = $repo->getDocumentPath($documentId);
2565
2566
        if (empty($filePath)) {
2567
            return false;
2568
        }
2569
2570
        $title = $document->getTitle();
2571
        //$filePath = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$document_data['path'];
2572
        $pageFormat = 'A4';
2573
        $pdfOrientation = 'P';
2574
        if ('landscape' === $orientation) {
2575
            $pageFormat = 'A4-L';
2576
            $pdfOrientation = 'L';
2577
        }
2578
2579
        $pdf = new PDF(
2580
            $pageFormat,
2581
            $pdfOrientation,
2582
            $showHeaderAndFooter ? [] : ['top' => 0, 'left' => 0, 'bottom' => 0, 'right' => 0]
2583
        );
2584
2585
        if (api_get_configuration_value('use_alternative_document_pdf_footer')) {
2586
            $view = new Template('', false, false, false, true, false, false);
2587
            $template = $view->get_template('export/alt_pdf_footer.tpl');
2588
2589
            $pdf->set_custom_footer([
2590
                'html' => $view->fetch($template),
2591
            ]);
2592
        }
2593
2594
        $pdf->html_to_pdf(
2595
            $filePath,
2596
            $title,
2597
            $courseCode,
2598
            false,
2599
            $showHeaderAndFooter
2600
        );
2601
        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...
2602
    }
2603
2604
    /**
2605
     * Uploads a document.
2606
     *
2607
     * @param array  $files                   the $_FILES variable
2608
     * @param string $path
2609
     * @param string $title
2610
     * @param string $comment
2611
     * @param int    $unzip                   unzip or not the file
2612
     * @param string $ifExists                overwrite, rename or warn (default)
2613
     * @param bool   $index_document          index document (search xapian module)
2614
     * @param bool   $show_output             print html messages
2615
     * @param string $fileKey
2616
     * @param bool   $treat_spaces_as_hyphens
2617
     * @param int    $parentId
2618
     * @param string $content
2619
     *
2620
     * @return CDocument|false
2621
     */
2622
    public static function upload_document(
2623
        $files,
2624
        $path,
2625
        $title = '',
2626
        $comment = '',
2627
        $unzip = 0,
2628
        $ifExists = '',
2629
        $index_document = false,
2630
        $show_output = false,
2631
        $fileKey = 'file',
2632
        $treat_spaces_as_hyphens = true,
2633
        $parentId = 0,
2634
        $content = null
2635
    ) {
2636
        $course_info = api_get_course_info();
2637
        $sessionId = api_get_session_id();
2638
        $course_dir = $course_info['path'].'/document';
2639
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
2640
        $base_work_dir = $sys_course_path.$course_dir;
2641
2642
        if (isset($files[$fileKey])) {
2643
            $uploadOk = process_uploaded_file($files[$fileKey], $show_output);
2644
2645
            if ($uploadOk) {
2646
                $document = handle_uploaded_document(
2647
                    $course_info,
2648
                    $files[$fileKey],
2649
                    $base_work_dir,
2650
                    $path,
2651
                    api_get_user_id(),
2652
                    api_get_group_id(),
2653
                    null,
2654
                    $unzip,
2655
                    $ifExists,
2656
                    $show_output,
2657
                    false,
2658
                    null,
2659
                    $sessionId,
2660
                    $treat_spaces_as_hyphens,
2661
                    $fileKey,
2662
                    $parentId,
2663
                    $content
2664
                );
2665
2666
                // Showing message when sending zip files
2667
                if ($document && 1 == $unzip) {
2668
                    if ($show_output) {
2669
                        echo Display::return_message(
2670
                            get_lang('File upload succeeded!').'<br />',
2671
                            'confirm',
2672
                            false
2673
                        );
2674
                    }
2675
2676
                    return $document;
2677
                }
2678
2679
                if ($document) {
2680
                    if ($index_document) {
2681
                        self::index_document(
2682
                            $document->getIid(),
2683
                            $course_info['code'],
2684
                            null,
2685
                            $_POST['language'] ?? '',
2686
                            $_REQUEST,
2687
                            $ifExists
2688
                        );
2689
                    }
2690
2691
                    return $document;
2692
                }
2693
            }
2694
        }
2695
2696
        return false;
2697
    }
2698
2699
    /**
2700
     * Obtains the text inside the file with the right parser.
2701
     */
2702
    public static function get_text_content($doc_path, $doc_mime)
2703
    {
2704
        // TODO: review w$ compatibility
2705
        // Use usual exec output lines array to store stdout instead of a temp file
2706
        // because we need to store it at RAM anyway before index on ChamiloIndexer object
2707
        $ret_val = null;
2708
        switch ($doc_mime) {
2709
            case 'text/plain':
2710
                $handle = fopen($doc_path, 'r');
2711
                $output = [fread($handle, filesize($doc_path))];
2712
                fclose($handle);
2713
                break;
2714
            case 'application/pdf':
2715
                exec("pdftotext $doc_path -", $output, $ret_val);
2716
                break;
2717
            case 'application/postscript':
2718
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
2719
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
2720
                if (0 !== $ret_val) { // shell fail, probably 127 (command not found)
2721
                    return false;
2722
                }
2723
                exec("pdftotext $temp_file -", $output, $ret_val);
2724
                unlink($temp_file);
2725
                break;
2726
            case 'application/msword':
2727
                exec("catdoc $doc_path", $output, $ret_val);
2728
                break;
2729
            case 'text/html':
2730
                exec("html2text $doc_path", $output, $ret_val);
2731
                break;
2732
            case 'text/rtf':
2733
                // Note: correct handling of code pages in unrtf
2734
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
2735
                exec("unrtf --text $doc_path", $output, $ret_val);
2736
                if (127 == $ret_val) { // command not found
2737
                    return false;
2738
                }
2739
                // Avoid index unrtf comments
2740
                if (is_array($output) && count($output) > 1) {
2741
                    $parsed_output = [];
2742
                    foreach ($output as &$line) {
2743
                        if (!preg_match('/^###/', $line, $matches)) {
2744
                            if (!empty($line)) {
2745
                                $parsed_output[] = $line;
2746
                            }
2747
                        }
2748
                    }
2749
                    $output = $parsed_output;
2750
                }
2751
                break;
2752
            case 'application/vnd.ms-powerpoint':
2753
                exec("catppt $doc_path", $output, $ret_val);
2754
                break;
2755
            case 'application/vnd.ms-excel':
2756
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
2757
                break;
2758
        }
2759
2760
        $content = '';
2761
        if (!is_null($ret_val)) {
2762
            if (0 !== $ret_val) { // shell fail, probably 127 (command not found)
2763
                return false;
2764
            }
2765
        }
2766
        if (isset($output)) {
2767
            foreach ($output as &$line) {
2768
                $content .= $line."\n";
2769
            }
2770
2771
            return $content;
2772
        } else {
2773
            return false;
2774
        }
2775
    }
2776
2777
    /**
2778
     * Display the document quota in a simple way.
2779
     *
2780
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
2781
     */
2782
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
2783
    {
2784
        $course_quota_m = round($course_quota / 1048576);
2785
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
2786
        $percentage = $already_consumed_space / $course_quota * 100;
2787
        $percentage = round($percentage, 1);
2788
        $message = get_lang('You are currently using %s MB (%s) of your %s MB.');
2789
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
2790
2791
        return Display::div($message, ['id' => 'document_quota', 'class' => 'card-quota']);
2792
    }
2793
2794
    /**
2795
     * Checks if there is enough place to add a file on a directory
2796
     * on the base of a maximum directory size allowed.
2797
     *
2798
     * @author Bert Vanderkimpen
2799
     *
2800
     * @param int $file_size     size of the file in byte
2801
     * @param int $max_dir_space maximum size
2802
     *
2803
     * @return bool true if there is enough space, false otherwise
2804
     */
2805
    public static function enough_space($file_size, $max_dir_space)
2806
    {
2807
        if ($max_dir_space) {
2808
            $repo = Container::getDocumentRepository();
2809
            $total = $repo->getTotalSpace(api_get_course_int_id());
2810
2811
            if (($file_size + $total) > $max_dir_space) {
2812
                return false;
2813
            }
2814
        }
2815
2816
        return true;
2817
    }
2818
2819
    /**
2820
     * @param array $params count, url, extension
2821
     *
2822
     * @return string
2823
     */
2824
    public static function generateAudioJavascript($params = [])
2825
    {
2826
        $js = '
2827
            $(\'audio.audio_preview\').mediaelementplayer({
2828
                features: [\'playpause\'],
2829
                audioWidth: 30,
2830
                audioHeight: 30,
2831
                success: function(mediaElement, originalNode, instance) {
2832
                }
2833
            });';
2834
2835
        return $js;
2836
    }
2837
2838
    /**
2839
     * Shows a play icon next to the document title in the document list.
2840
     *
2841
     * @param string $documentWebPath
2842
     * @param array  $documentInfo
2843
     *
2844
     * @return string
2845
     */
2846
    public static function generateAudioPreview($documentWebPath, $documentInfo)
2847
    {
2848
        $filePath = $documentWebPath.$documentInfo['path'];
2849
        $extension = $documentInfo['file_extension'];
2850
        $html = '<span class="preview"> <audio class="audio_preview skip" src="'.$filePath.'" type="audio/'.$extension.'" > </audio></span>';
2851
2852
        return $html;
2853
    }
2854
2855
    /**
2856
     * @param string $file
2857
     * @param string $extension
2858
     *
2859
     * @return string
2860
     */
2861
    public static function generateMediaPreview($file, $extension)
2862
    {
2863
        $id = api_get_unique_id();
2864
        switch ($extension) {
2865
            case 'ogg':
2866
            case 'mp3':
2867
                $document_data['file_extension'] = $extension;
2868
                $html = '<div style="margin: 0; position: absolute; top: 50%; left: 35%;">';
2869
                $html .= '<audio id="'.$id.'" controls="controls" src="'.$file.'" type="audio/mp3" ></audio></div>';
2870
                break;
2871
            default:
2872
                $html = '<video id="'.$id.'" controls>';
2873
                $html .= '<source src="'.$file.'" >';
2874
                $html .= '</video>';
2875
                break;
2876
        }
2877
2878
        return $html;
2879
    }
2880
2881
    /**
2882
     * @param array  $course_info
2883
     * @param bool   $lp_id
2884
     * @param string $target
2885
     * @param int    $session_id
2886
     * @param bool   $add_move_button
2887
     * @param string $filter_by_folder
2888
     * @param string $overwrite_url
2889
     * @param bool   $showInvisibleFiles
2890
     * @param bool   $showOnlyFolders
2891
     * @param int    $folderId
2892
     * @param bool   $addCloseButton
2893
     * @param bool   $addAudioPreview
2894
     * @param array  $filterByExtension
2895
     *
2896
     * @return string
2897
     */
2898
    public static function get_document_preview(
2899
        $course_info,
2900
        $lp_id = false,
2901
        $target = '',
2902
        $session_id = 0,
2903
        $add_move_button = false,
2904
        $filter_by_folder = null,
2905
        $overwrite_url = '',
2906
        $showInvisibleFiles = false,
2907
        $showOnlyFolders = false,
2908
        $folderId = false,
2909
        $addCloseButton = true,
2910
        $addAudioPreview = false,
2911
        $filterByExtension = []
2912
    ) {
2913
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
2914
            return '';
2915
        }
2916
2917
        $repo = Container::getDocumentRepository();
2918
        $nodeRepository = $repo->getResourceNodeRepository();
2919
        $move = get_lang('Move');
2920
        $icon = Display::return_icon('move_everywhere.png', $move);
2921
        $folderIcon = Display::return_icon('lp_folder.png');
2922
2923
        $options = [
2924
            'decorate' => true,
2925
            'rootOpen' => '<ul class="lp_resource">',
2926
            'rootClose' => '</ul>',
2927
            'childOpen' => '<li class="doc_resource lp_resource_element ">',
2928
            'childClose' => '</li>',
2929
            'nodeDecorator' => function ($node) use ($icon, $folderIcon) {
2930
                $link = '<div class="item_data">';
2931
                $slug = $node['slug'];
2932
                $extension = pathinfo($slug, PATHINFO_EXTENSION);
2933
                $folder = $folderIcon;
2934
                if (empty($node['__children']) && !empty($extension)) {
2935
                    $link .= '<a class="moved ui-sortable-handle" href="#">';
2936
                    $link .= $icon;
2937
                    $link .= '</a>';
2938
                    $folder = '';
2939
                }
2940
2941
                $link .= '<a data_id="'.$node['id'].'" data_type="document" class="moved ui-sortable-handle link_with_id">';
2942
                $link .= $folder.'&nbsp;'.$node['slug'];
2943
                $link .= '</a>';
2944
2945
                $link .= '</div>';
2946
2947
                return $link;
2948
            },
2949
        ];
2950
2951
        $type = $repo->getResourceType();
2952
        $em = $repo->getEntityManager();
2953
        $query = $em
2954
            ->createQueryBuilder()
2955
            ->select('node')
2956
            ->from('ChamiloCoreBundle:ResourceNode', 'node')
2957
            ->innerJoin('node.resourceType', 'type')
2958
            ->innerJoin('node.resourceLinks', 'links')
2959
            //->innerJoin('node.resourceFile', 'file')
2960
            ->where('type = :type')
2961
            ->andWhere('links.course = :course')
2962
            /*  ->where('node.parent = :parent') */
2963
            ->setParameters(['type' => $type, 'course' => $course_info['entity']])
2964
            ->orderBy('node.parent', 'ASC')
2965
            ->getQuery()
2966
        ;
2967
2968
        /*$em->getConfiguration()->addCustomHydrationMode('tree', 'Gedmo\Tree\Hydrator\ORM\TreeObjectHydrator');
2969
        $tree = $query->getQuery()
2970
            ->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
2971
            ->getResult('tree');
2972
        */
2973
2974
        /*foreach ($tree as $a) {
2975
            var_dump(get_class($a));
2976
        }*/
2977
2978
        return $nodeRepository->buildTree($query->getArrayResult(), $options);
2979
2980
        return $tree;
0 ignored issues
show
Unused Code introduced by
return $tree is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2981
        $nodeRepository->buildTree($query->getResult(), $options);
2982
2983
        $user_id = api_get_user_id();
2984
        $userInfo = api_get_user_info();
2985
        $user_in_course = api_is_platform_admin();
2986
        if (!$user_in_course) {
2987
            if (CourseManager::is_course_teacher($user_id, $course_info['code'])) {
2988
                $user_in_course = true;
2989
            }
2990
        }
2991
2992
        // Condition for the session
2993
        $session_id = (int) $session_id;
2994
2995
        if (!$user_in_course) {
2996
            if (empty($session_id)) {
2997
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
2998
                    $user_in_course = true;
2999
                }
3000
                // Check if course is open then we can consider that the student is registered to the course
3001
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3002
                    $user_in_course = true;
3003
                }
3004
            } else {
3005
                $user_status = SessionManager::get_user_status_in_course_session(
3006
                    $user_id,
3007
                    $course_info['real_id'],
3008
                    $session_id
3009
                );
3010
                //is true if is an student, course session teacher or coach
3011
                if (in_array($user_status, ['0', '2', '6'])) {
3012
                    $user_in_course = true;
3013
                }
3014
            }
3015
        }
3016
3017
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3018
3019
        $add_folder_filter = null;
3020
        if (!empty($filter_by_folder)) {
3021
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3022
        }
3023
3024
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3025
        $lp_visibility_condition = null;
3026
        if ($lp_id) {
3027
            if ($showInvisibleFiles) {
3028
                $lp_visibility_condition .= ' OR l.visibility = 0';
3029
            }
3030
        }
3031
3032
        $folderCondition = " AND docs.path LIKE '/%' ";
3033
        if (!api_is_allowed_to_edit()) {
3034
            $protectedFolders = self::getProtectedFolderFromStudent();
3035
            foreach ($protectedFolders as $folder) {
3036
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3037
            }
3038
        }
3039
3040
        $parentData = [];
3041
        if (false !== $folderId) {
3042
            $parentData = self::get_document_data_by_id(
3043
                $folderId,
3044
                $course_info['code'],
3045
                false,
3046
                $session_id
3047
            );
3048
            if (!empty($parentData)) {
3049
                $cleanedPath = $parentData['path'];
3050
                $num = substr_count($cleanedPath, '/');
3051
3052
                $notLikeCondition = '';
3053
                for ($i = 1; $i <= $num; $i++) {
3054
                    $repeat = str_repeat('/%', $i + 1);
3055
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3056
                }
3057
3058
                $folderId = (int) $folderId;
3059
                $folderCondition = " AND
3060
                    docs.id <> $folderId AND
3061
                    docs.path LIKE '".$cleanedPath."/%'
3062
                    $notLikeCondition
3063
                ";
3064
            } else {
3065
                $folderCondition = " AND docs.filetype = 'file' ";
3066
            }
3067
        }
3068
3069
        $levelCondition = '';
3070
        if (false === $folderId) {
3071
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3072
        }
3073
3074
        $sql = "SELECT DISTINCT l.visibility, docs.*
3075
                FROM resource_node AS n
3076
                INNER JOIN $tbl_doc AS docs
3077
                ON (docs.resource_node_id = n.id)
3078
                INNER JOIN resource_link l
3079
                ON (l.resource_node_id = n.id)
3080
                WHERE
3081
                    l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
3082
                    $folderCondition
3083
                    $levelCondition
3084
                    $add_folder_filter
3085
                ORDER BY docs.filetype DESC, docs.title ASC";
3086
3087
        $res_doc = Database::query($sql);
3088
        $resources = Database::store_result($res_doc, 'ASSOC');
3089
3090
        $return = '';
3091
        if (false == $lp_id && $addCloseButton) {
3092
            if (false === $folderId) {
3093
                $return .= Display::div(
3094
                    Display::url(
3095
                        Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3096
                        ' javascript:void(0);',
3097
                        ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3098
                    ),
3099
                    ['style' => 'position:absolute;right:10px']
3100
                );
3101
            }
3102
        }
3103
3104
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3105
        $newResources = [];
3106
        if (!empty($resources) && $user_in_course) {
3107
            foreach ($resources as $resource) {
3108
                $is_visible = self::is_visible_by_id(
3109
                    $resource['id'],
3110
                    $course_info,
3111
                    $session_id,
3112
                    api_get_user_id()
3113
                );
3114
3115
                if (false === $showInvisibleFiles) {
3116
                    if (!$is_visible) {
3117
                        continue;
3118
                    }
3119
                }
3120
3121
                $newResources[] = $resource;
3122
            }
3123
        }
3124
3125
        $label = get_lang('Documents');
3126
3127
        $documents = [];
3128
        if (false === $folderId) {
3129
            $documents[$label] = [
3130
                'id' => 0,
3131
                'files' => $newResources,
3132
            ];
3133
        } else {
3134
            if (is_array($parentData)) {
3135
                $documents[$parentData['title']] = [
3136
                    'id' => (int) $folderId,
3137
                    'files' => $newResources,
3138
                ];
3139
            }
3140
        }
3141
3142
        $writeResult = self::write_resources_tree(
3143
            $userInfo,
3144
            $course_info,
3145
            $session_id,
3146
            $documents,
3147
            $lp_id,
3148
            $target,
3149
            $add_move_button,
3150
            $overwrite_url,
3151
            $folderId
3152
        );
3153
3154
        $return .= $writeResult;
3155
        $lpAjaxUrl = api_get_path(WEB_AJAX_PATH).'lp.ajax.php';
3156
        if (false === $lp_id) {
3157
            $url = $lpAjaxUrl.'?a=get_documents&lp_id=&cidReq='.$course_info['code'];
3158
            $return .= "<script>
3159
            $(function() {
3160
                $('.close_div').click(function() {
3161
                    var course_id = this.id.split('_')[2];
3162
                    var session_id = this.id.split('_')[3];
3163
                    $('#document_result_'+course_id+'_'+session_id).hide();
3164
                    $('.lp_resource').remove();
3165
                    $('.document_preview_container').html('');
3166
                });
3167
            });
3168
            </script>";
3169
        } else {
3170
            // For LPs
3171
            $url = $lpAjaxUrl.'?a=get_documents&lp_id='.$lp_id.'&'.api_get_cidreq();
3172
        }
3173
3174
        if (!empty($overwrite_url)) {
3175
            $url .= '&url='.Security::remove_XSS($overwrite_url);
3176
        }
3177
3178
        if ($add_move_button) {
3179
            $url .= '&add_move_button=1';
3180
        }
3181
3182
        $return .= "<script>
3183
            function testResources(id, img) {
3184
                var numericId = id.split('_')[1];
3185
                var parentId = 'doc_id_'+numericId;
3186
                var tempId = 'temp_'+numericId;
3187
                var image = $('#'+img);
3188
3189
                if (image.hasClass('open')) {
3190
                    image.removeClass('open');
3191
                    image.attr('src', '".Display::returnIconPath('nolines_plus.gif')."');
3192
                    $('#'+id).show();
3193
                    $('#'+tempId).hide();
3194
                } else {
3195
                    image.addClass('open');
3196
                    image.attr('src', '".Display::returnIconPath('nolines_minus.gif')."');
3197
                    $('#'+id).hide();
3198
                    $('#'+tempId).show();
3199
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3200
                    if (tempDiv.length == 0) {
3201
                        $.ajax({
3202
                            type: 'GET',
3203
                            async: false,
3204
                            url:  '".$url."',
3205
                            data: 'folder_id='+numericId,
3206
                            success: function(data) {
3207
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3208
                            }
3209
                        });
3210
                    }
3211
                }
3212
            }
3213
            </script>";
3214
3215
        if (!$user_in_course) {
3216
            $return = '';
3217
        }
3218
3219
        return $return;
3220
    }
3221
3222
    /**
3223
     * Generate and return an HTML list of resources based on a given array.
3224
     * This list is used to show the course creator a list of available resources to choose from
3225
     * when creating a learning path.
3226
     *
3227
     * @param array  $userInfo        current user info
3228
     * @param array  $course_info
3229
     * @param int    $session_id
3230
     * @param array  $documents
3231
     * @param bool   $lp_id
3232
     * @param string $target
3233
     * @param bool   $add_move_button
3234
     * @param string $overwrite_url
3235
     * @param int    $folderId
3236
     *
3237
     * @return string
3238
     */
3239
    public static function write_resources_tree(
3240
        $userInfo,
3241
        $course_info,
3242
        $session_id,
3243
        $documents,
3244
        $lp_id = false,
3245
        $target = '',
3246
        $add_move_button = false,
3247
        $overwrite_url = '',
3248
        $folderId = false,
3249
        $addAudioPreview = false
3250
    ) {
3251
        $return = '';
3252
        if (!empty($documents)) {
3253
            foreach ($documents as $key => $resource) {
3254
                if (isset($resource['id']) && is_int($resource['id'])) {
3255
                    $mainFolderResource = [
3256
                        'id' => $resource['id'],
3257
                        'title' => $key,
3258
                    ];
3259
3260
                    if (false === $folderId) {
3261
                        $return .= self::parseFolder($folderId, $mainFolderResource, $lp_id);
3262
                    }
3263
3264
                    if (isset($resource['files'])) {
3265
                        $return .= self::write_resources_tree(
3266
                            $userInfo,
3267
                            $course_info,
3268
                            $session_id,
3269
                            $resource['files'],
3270
                            $lp_id,
3271
                            $target,
3272
                            $add_move_button,
3273
                            $overwrite_url,
3274
                            null,
3275
                            $addAudioPreview
3276
                        );
3277
                    }
3278
                    $return .= '</div>';
3279
                    $return .= '</ul>';
3280
                } else {
3281
                    if ('folder' === $resource['filetype']) {
3282
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3283
                    } else {
3284
                        $return .= self::parseFile(
3285
                            $userInfo,
3286
                            $course_info,
3287
                            $session_id,
3288
                            $resource,
3289
                            $lp_id,
3290
                            $add_move_button,
3291
                            $target,
3292
                            $overwrite_url,
3293
                            $addAudioPreview
3294
                        );
3295
                    }
3296
                }
3297
            }
3298
        }
3299
3300
        return $return;
3301
    }
3302
3303
    /**
3304
     * @param int   $doc_id
3305
     * @param array $courseInfo
3306
     * @param int   $sessionId
3307
     * @param int   $user_id
3308
     * @param int   $groupId               iid
3309
     * @param bool  $checkParentVisibility
3310
     *
3311
     * @return bool
3312
     */
3313
    public static function check_visibility_tree(
3314
        $doc_id,
3315
        $courseInfo,
3316
        $sessionId,
3317
        $user_id,
3318
        $groupId = 0,
3319
        $checkParentVisibility = true
3320
    ) {
3321
        if (empty($courseInfo)) {
3322
            return false;
3323
        }
3324
3325
        $courseCode = $courseInfo['code'];
3326
3327
        if (empty($courseCode)) {
3328
            return false;
3329
        }
3330
3331
        $document_data = self::get_document_data_by_id(
3332
            $doc_id,
3333
            $courseCode,
3334
            null,
3335
            $sessionId
3336
        );
3337
3338
        if (0 != $sessionId && !$document_data) {
3339
            $document_data = self::get_document_data_by_id(
3340
                $doc_id,
3341
                $courseCode,
3342
                null,
3343
                0
3344
            );
3345
        }
3346
3347
        if (!empty($document_data)) {
3348
            // If admin or course teacher, allow anyway
3349
            if (api_is_platform_admin() || CourseManager::is_course_teacher($user_id, $courseCode)) {
3350
                return true;
3351
            }
3352
3353
            if (false == $document_data['parent_id'] || empty($document_data['parent_id'])) {
3354
                if (!empty($groupId)) {
3355
                    return true;
3356
                }
3357
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3358
3359
                return $visible;
3360
            } else {
3361
                $visible = self::is_visible_by_id($doc_id, $courseInfo, $sessionId, $user_id);
3362
3363
                if (!$visible) {
3364
                    return false;
3365
                } else {
3366
                    if ($checkParentVisibility) {
3367
                        return self::check_visibility_tree(
3368
                            $document_data['parent_id'],
3369
                            $courseInfo,
3370
                            $sessionId,
3371
                            $user_id,
3372
                            $groupId
3373
                        );
3374
                    }
3375
3376
                    return true;
3377
                }
3378
            }
3379
        } else {
3380
            return false;
3381
        }
3382
    }
3383
3384
    /**
3385
     * Index a given document.
3386
     *
3387
     * @param   int     Document ID inside its corresponding course
3388
     * @param   string  Course code
3389
     * @param   int     Session ID (not used yet)
3390
     * @param   string  Language of document's content (defaults to course language)
3391
     * @param   array   Array of specific fields (['code'=>'value',...])
3392
     * @param   string  What to do if the file already exists (default or overwrite)
3393
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3394
     *
3395
     * @return bool Returns true on presumed success, false on failure
3396
     */
3397
    public static function index_document(
3398
        $docid,
3399
        $course_code,
3400
        $session_id = 0,
3401
        $lang = 'english',
3402
        $specific_fields_values = [],
3403
        $if_exists = '',
3404
        $simulation = false
3405
    ) {
3406
        if ('true' !== api_get_setting('search_enabled')) {
3407
            return false;
3408
        }
3409
        if (empty($docid) or $docid != intval($docid)) {
3410
            return false;
3411
        }
3412
        if (empty($session_id)) {
3413
            $session_id = api_get_session_id();
3414
        }
3415
        $course_info = api_get_course_info($course_code);
3416
        $course_dir = $course_info['path'].'/document';
3417
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3418
        $base_work_dir = $sys_course_path.$course_dir;
3419
3420
        $course_id = $course_info['real_id'];
3421
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3422
3423
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3424
        $result = Database::query($qry);
3425
        if (1 == Database::num_rows($result)) {
3426
            $row = Database::fetch_array($result);
3427
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3428
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3429
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3430
            $doc_mime = mime_content_type($doc_path);
3431
            $allowed_mime_types = self::file_get_mime_type(true);
3432
3433
            // mime_content_type does not detect correctly some formats that
3434
            // are going to be supported for index, so an extensions array is used for the moment
3435
            if (empty($doc_mime)) {
3436
                $allowed_extensions = [
3437
                    'doc',
3438
                    'docx',
3439
                    'ppt',
3440
                    'pptx',
3441
                    'pps',
3442
                    'ppsx',
3443
                    'xls',
3444
                    'xlsx',
3445
                    'odt',
3446
                    'odp',
3447
                    'ods',
3448
                    'pdf',
3449
                    'txt',
3450
                    'rtf',
3451
                    'msg',
3452
                    'csv',
3453
                    'html',
3454
                    'htm',
3455
                ];
3456
                $extensions = preg_split("/[\/\\.]/", $doc_path);
3457
                $doc_ext = strtolower($extensions[count($extensions) - 1]);
3458
                if (in_array($doc_ext, $allowed_extensions)) {
3459
                    switch ($doc_ext) {
3460
                        case 'ppt':
3461
                        case 'pps':
3462
                            $doc_mime = 'application/vnd.ms-powerpoint';
3463
                            break;
3464
                        case 'xls':
3465
                            $doc_mime = 'application/vnd.ms-excel';
3466
                            break;
3467
                    }
3468
                }
3469
            }
3470
3471
            //@todo move this nightmare in a search controller or something like that!!! J.M
3472
3473
            if (in_array($doc_mime, $allowed_mime_types)) {
3474
                $file_title = $row['title'];
3475
                $file_content = self::get_text_content($doc_path, $doc_mime);
3476
                $course_code = Database::escape_string($course_code);
3477
                $ic_slide = new IndexableChunk();
3478
                $ic_slide->addValue('title', $file_title);
3479
                $ic_slide->addCourseId($course_code);
3480
                $ic_slide->addToolId(TOOL_DOCUMENT);
3481
                $xapian_data = [
3482
                    SE_COURSE_ID => $course_code,
3483
                    SE_TOOL_ID => TOOL_DOCUMENT,
3484
                    SE_DATA => ['doc_id' => $docid],
3485
                    SE_USER => api_get_user_id(),
3486
                ];
3487
3488
                $ic_slide->xapian_data = serialize($xapian_data);
3489
                $di = new ChamiloIndexer();
3490
                $return = $di->connectDb(null, null, $lang);
3491
3492
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
3493
                $specific_fields = get_specific_field_list();
3494
3495
                // process different depending on what to do if file exists
3496
                /**
3497
                 * @TODO Find a way to really verify if the file had been
3498
                 * overwriten. Now all work is done at
3499
                 * handle_uploaded_document() and it's difficult to verify it
3500
                 */
3501
                if (!empty($if_exists) && 'overwrite' == $if_exists) {
3502
                    // Overwrite the file on search engine
3503
                    // Actually, it consists on a delete of terms from db,
3504
                    // insert new ones, create a new search engine document,
3505
                    // and remove the old one
3506
                    // Get search_did
3507
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3508
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
3509
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
3510
3511
                    $res = Database::query($sql);
3512
3513
                    if (Database::num_rows($res) > 0) {
3514
                        $se_ref = Database::fetch_array($res);
3515
                        if (!$simulation) {
3516
                            $di->remove_document($se_ref['search_did']);
3517
                        }
3518
                        $all_specific_terms = '';
3519
                        foreach ($specific_fields as $specific_field) {
3520
                            if (!$simulation) {
3521
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
3522
                            }
3523
                            // Update search engine
3524
                            if (isset($specific_fields_values[$specific_field['code']])) {
3525
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
3526
                            } else { //if the specific field is not defined, force an empty one
3527
                                $sterms = '';
3528
                            }
3529
                            $all_specific_terms .= ' '.$sterms;
3530
                            $sterms = explode(',', $sterms);
3531
                            foreach ($sterms as $sterm) {
3532
                                $sterm = trim($sterm);
3533
                                if (!empty($sterm)) {
3534
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
3535
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
3536
                                    if (!$simulation) {
3537
                                        add_specific_field_value(
3538
                                            $specific_field['id'],
3539
                                            $course_code,
3540
                                            TOOL_DOCUMENT,
3541
                                            $docid,
3542
                                            $sterm
3543
                                        );
3544
                                    }
3545
                                }
3546
                            }
3547
                        }
3548
                        // Add terms also to content to make terms findable by probabilistic search
3549
                        $file_content = $all_specific_terms.' '.$file_content;
3550
3551
                        if (!$simulation) {
3552
                            $ic_slide->addValue('content', $file_content);
3553
                            $di->addChunk($ic_slide);
3554
                            // Index and return a new search engine document id
3555
                            $did = $di->index();
3556
3557
                            if ($did) {
3558
                                // update the search_did on db
3559
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3560
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
3561
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
3562
                                Database::query($sql);
3563
                            }
3564
                        }
3565
                    }
3566
                } else {
3567
                    // Add all terms
3568
                    $all_specific_terms = '';
3569
                    foreach ($specific_fields as $specific_field) {
3570
                        if (isset($specific_fields_values[$specific_field['code']])) {
3571
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
3572
                        } else { //if the specific field is not defined, force an empty one
3573
                            $sterms = '';
3574
                        }
3575
                        $all_specific_terms .= ' '.$sterms;
3576
                        if (!empty($sterms)) {
3577
                            $sterms = explode(',', $sterms);
3578
                            foreach ($sterms as $sterm) {
3579
                                if (!$simulation) {
3580
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
3581
                                    add_specific_field_value(
3582
                                        $specific_field['id'],
3583
                                        $course_code,
3584
                                        TOOL_DOCUMENT,
3585
                                        $docid,
3586
                                        $sterm
3587
                                    );
3588
                                }
3589
                            }
3590
                        }
3591
                    }
3592
                    // Add terms also to content to make terms findable by probabilistic search
3593
                    $file_content = $all_specific_terms.' '.$file_content;
3594
                    if (!$simulation) {
3595
                        $ic_slide->addValue('content', $file_content);
3596
                        $di->addChunk($ic_slide);
3597
                        // Index and return search engine document id
3598
                        $did = $di->index();
3599
                        if ($did) {
3600
                            // Save it to db
3601
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3602
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
3603
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
3604
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
3605
                            Database::query($sql);
3606
                        } else {
3607
                            return false;
3608
                        }
3609
                    }
3610
                }
3611
            } else {
3612
                return false;
3613
            }
3614
        }
3615
3616
        return true;
3617
    }
3618
3619
    /**
3620
     * @return array
3621
     */
3622
    public static function get_web_odf_extension_list()
3623
    {
3624
        return ['ods', 'odt', 'odp'];
3625
    }
3626
3627
    /**
3628
     * Set of extension allowed to use Jodconverter.
3629
     *
3630
     * @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...
3631
     *              'to'
3632
     *              'all'
3633
     * @param $format   'text'
3634
     *                  'spreadsheet'
3635
     *                  'presentation'
3636
     *                  'drawing'
3637
     *                  'all'
3638
     *
3639
     * @return array
3640
     */
3641
    public static function getJodconverterExtensionList($mode, $format)
3642
    {
3643
        $extensionList = [];
3644
        $extensionListFromText = [
3645
            'odt',
3646
            'sxw',
3647
            'rtf',
3648
            'doc',
3649
            'docx',
3650
            'wpd',
3651
            'txt',
3652
        ];
3653
        $extensionListToText = [
3654
            'pdf',
3655
            'odt',
3656
            'sxw',
3657
            'rtf',
3658
            'doc',
3659
            'docx',
3660
            'txt',
3661
        ];
3662
        $extensionListFromSpreadsheet = [
3663
            'ods',
3664
            'sxc',
3665
            'xls',
3666
            'xlsx',
3667
            'csv',
3668
            'tsv',
3669
        ];
3670
        $extensionListToSpreadsheet = [
3671
            'pdf',
3672
            'ods',
3673
            'sxc',
3674
            'xls',
3675
            'xlsx',
3676
            'csv',
3677
            'tsv',
3678
        ];
3679
        $extensionListFromPresentation = [
3680
            'odp',
3681
            'sxi',
3682
            'ppt',
3683
            'pptx',
3684
        ];
3685
        $extensionListToPresentation = [
3686
            'pdf',
3687
            'swf',
3688
            'odp',
3689
            'sxi',
3690
            'ppt',
3691
            'pptx',
3692
        ];
3693
        $extensionListFromDrawing = ['odg'];
3694
        $extensionListToDrawing = ['svg', 'swf'];
3695
3696
        if ('from' === $mode) {
3697
            if ('text' === $format) {
3698
                $extensionList = array_merge($extensionList, $extensionListFromText);
3699
            } elseif ('spreadsheet' === $format) {
3700
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3701
            } elseif ('presentation' === $format) {
3702
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3703
            } elseif ('drawing' === $format) {
3704
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3705
            } elseif ('all' === $format) {
3706
                $extensionList = array_merge($extensionList, $extensionListFromText);
3707
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3708
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3709
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3710
            }
3711
        } elseif ('to' === $mode) {
3712
            if ('text' === $format) {
3713
                $extensionList = array_merge($extensionList, $extensionListToText);
3714
            } elseif ('spreadsheet' === $format) {
3715
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3716
            } elseif ('presentation' === $format) {
3717
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3718
            } elseif ('drawing' === $format) {
3719
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3720
            } elseif ('all' === $format) {
3721
                $extensionList = array_merge($extensionList, $extensionListToText);
3722
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3723
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3724
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3725
            }
3726
        } elseif ('all' === $mode) {
3727
            if ('text' === $format) {
3728
                $extensionList = array_merge($extensionList, $extensionListFromText);
3729
                $extensionList = array_merge($extensionList, $extensionListToText);
3730
            } elseif ('spreadsheet' === $format) {
3731
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3732
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3733
            } elseif ('presentation' === $format) {
3734
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3735
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3736
            } elseif ('drawing' === $format) {
3737
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3738
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3739
            } elseif ('all' === $format) {
3740
                $extensionList = array_merge($extensionList, $extensionListFromText);
3741
                $extensionList = array_merge($extensionList, $extensionListToText);
3742
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3743
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3744
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3745
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3746
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3747
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3748
            }
3749
        }
3750
3751
        return $extensionList;
3752
    }
3753
3754
    /**
3755
     * Get Format type list by extension and mode.
3756
     *
3757
     * @param string $mode Mode to search format type list
3758
     *
3759
     * @example 'from'
3760
     * @example 'to'
3761
     *
3762
     * @param string $extension file extension to check file type
3763
     *
3764
     * @return array
3765
     */
3766
    public static function getFormatTypeListConvertor($mode = 'from', $extension)
3767
    {
3768
        $formatTypesList = [];
3769
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
3770
        foreach ($formatTypes as $formatType) {
3771
            if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
3772
                $formatTypesList[] = $formatType;
3773
            }
3774
        }
3775
3776
        return $formatTypesList;
3777
    }
3778
3779
    /**
3780
     * @param string $path
3781
     * @param bool   $is_certificate_mode
3782
     *
3783
     * @return bool
3784
     */
3785
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
3786
    {
3787
        $foldersToAvoid = [
3788
            '/HotPotatoes_files',
3789
            '/certificates',
3790
        ];
3791
        $systemFolder = api_get_course_setting('show_system_folders');
3792
3793
        if (1 == $systemFolder) {
3794
            $foldersToAvoid = [];
3795
        }
3796
3797
        if ('css' == basename($path)) {
3798
            return true;
3799
        }
3800
3801
        if (false == $is_certificate_mode) {
3802
            //Certificate results
3803
            if (strstr($path, 'certificates')) {
3804
                return true;
3805
            }
3806
        }
3807
3808
        // Admin setting for Hide/Show the folders of all users
3809
        if ('false' == api_get_setting('show_users_folders')) {
3810
            $foldersToAvoid[] = '/shared_folder';
3811
3812
            if (strstr($path, 'shared_folder_session_')) {
3813
                return true;
3814
            }
3815
        }
3816
3817
        // Admin setting for Hide/Show Default folders to all users
3818
        if ('false' == api_get_setting('show_default_folders')) {
3819
            $foldersToAvoid[] = '/images';
3820
            $foldersToAvoid[] = '/flash';
3821
            $foldersToAvoid[] = '/audio';
3822
            $foldersToAvoid[] = '/video';
3823
        }
3824
3825
        // Admin setting for Hide/Show chat history folder
3826
        if ('false' == api_get_setting('show_chat_folder')) {
3827
            $foldersToAvoid[] = '/chat_files';
3828
        }
3829
3830
        if (is_array($foldersToAvoid)) {
3831
            return in_array($path, $foldersToAvoid);
3832
        } else {
3833
            return false;
3834
        }
3835
    }
3836
3837
    /**
3838
     * @return array
3839
     */
3840
    public static function get_system_folders()
3841
    {
3842
        return [
3843
            '/certificates',
3844
            '/HotPotatoes_files',
3845
            '/chat_files',
3846
            '/images',
3847
            '/flash',
3848
            '/audio',
3849
            '/video',
3850
            '/shared_folder',
3851
            '/learning_path',
3852
        ];
3853
    }
3854
3855
    /**
3856
     * @return array
3857
     */
3858
    public static function getProtectedFolderFromStudent()
3859
    {
3860
        return [
3861
            '/certificates',
3862
            '/HotPotatoes_files',
3863
            '/chat_files',
3864
            '/shared_folder',
3865
            '/learning_path',
3866
        ];
3867
    }
3868
3869
    /**
3870
     * @param string $courseCode
3871
     *
3872
     * @return string 'visible' or 'invisible' string
3873
     */
3874
    public static function getDocumentDefaultVisibility($courseCode)
3875
    {
3876
        $settings = api_get_setting('tool_visible_by_default_at_creation');
3877
        $defaultVisibility = 'visible';
3878
3879
        if (isset($settings['documents'])) {
3880
            $portalDefaultVisibility = 'invisible';
3881
            if ('true' == $settings['documents']) {
3882
                $portalDefaultVisibility = 'visible';
3883
            }
3884
3885
            $defaultVisibility = $portalDefaultVisibility;
3886
        }
3887
3888
        if ('true' == api_get_setting('documents_default_visibility_defined_in_course')) {
3889
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseCode);
3890
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
3891
                $defaultVisibility = $courseVisibility;
3892
            }
3893
        }
3894
3895
        return $defaultVisibility;
3896
    }
3897
3898
    /**
3899
     * @param string $filePath
3900
     * @param string $path
3901
     * @param array  $courseInfo
3902
     * @param int    $sessionId
3903
     * @param string $whatIfFileExists overwrite|rename
3904
     * @param int    $userId
3905
     * @param int    $groupId
3906
     * @param int    $toUserId
3907
     * @param string $comment
3908
     *
3909
     * @return bool|path
3910
     */
3911
    public static function addFileToDocumentTool(
3912
        $filePath,
3913
        $path,
3914
        $courseInfo,
3915
        $sessionId,
3916
        $userId,
3917
        $whatIfFileExists = 'overwrite',
3918
        $groupId = null,
3919
        $toUserId = null,
3920
        $comment = null
3921
    ) {
3922
        if (!file_exists($filePath)) {
3923
            return false;
3924
        }
3925
3926
        $fileInfo = pathinfo($filePath);
3927
3928
        $file = [
3929
            'name' => $fileInfo['basename'],
3930
            'tmp_name' => $filePath,
3931
            'size' => filesize($filePath),
3932
            'from_file' => true,
3933
        ];
3934
3935
        $course_dir = $courseInfo['path'].'/document';
3936
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
3937
3938
        $filePath = handle_uploaded_document(
3939
            $courseInfo,
3940
            $file,
3941
            $baseWorkDir,
3942
            $path,
3943
            $userId,
3944
            $groupId,
3945
            $toUserId,
3946
            false,
3947
            $whatIfFileExists,
3948
            false,
3949
            false,
3950
            $comment,
3951
            $sessionId
3952
        );
3953
3954
        if ($filePath) {
3955
            return self::get_document_id(
3956
                $courseInfo,
3957
                $filePath,
3958
                $sessionId
3959
            );
3960
        }
3961
3962
        return false;
3963
    }
3964
3965
    /**
3966
     * Converts wav to mp3 file.
3967
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg.
3968
     *
3969
     * @param string $wavFile
3970
     * @param bool   $removeWavFileIfSuccess
3971
     *
3972
     * @return bool
3973
     */
3974
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
3975
    {
3976
        if (file_exists($wavFile)) {
3977
            try {
3978
                $ffmpeg = \FFMpeg\FFMpeg::create();
3979
                $video = $ffmpeg->open($wavFile);
3980
3981
                $mp3File = str_replace('wav', 'mp3', $wavFile);
3982
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
3983
                if ($result && $removeWavFileIfSuccess) {
3984
                    unlink($wavFile);
3985
                }
3986
3987
                if (file_exists($mp3File)) {
3988
                    return $mp3File;
3989
                }
3990
            } catch (Exception $e) {
3991
                error_log($e->getMessage());
3992
                error_log($e->getPrevious()->getMessage());
3993
            }
3994
        }
3995
3996
        return false;
3997
    }
3998
3999
    /**
4000
     * @param string $documentData     wav document information
4001
     * @param array  $courseInfo
4002
     * @param int    $sessionId
4003
     * @param int    $userId           user that adds the document
4004
     * @param string $whatIfFileExists
4005
     * @param bool   $deleteWavFile
4006
     *
4007
     * @return bool
4008
     */
4009
    public static function addAndConvertWavToMp3(
4010
        $documentData,
4011
        $courseInfo,
4012
        $sessionId,
4013
        $userId,
4014
        $whatIfFileExists = 'overwrite',
4015
        $deleteWavFile = false
4016
    ) {
4017
        if (empty($documentData)) {
4018
            return false;
4019
        }
4020
4021
        if (isset($documentData['absolute_path']) &&
4022
            file_exists($documentData['absolute_path'])
4023
        ) {
4024
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4025
4026
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4027
                $documentId = self::addFileToDocumentTool(
4028
                    $mp3FilePath,
4029
                    dirname($documentData['path']),
4030
                    $courseInfo,
4031
                    $sessionId,
4032
                    $userId,
4033
                    $whatIfFileExists,
4034
                    null,
4035
                    null,
4036
                    $documentData['comment']
4037
                );
4038
4039
                if (!empty($documentId)) {
4040
                    if ($deleteWavFile) {
4041
                        $coursePath = $courseInfo['directory'].'/document';
4042
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4043
                        self::delete_document(
4044
                            $courseInfo,
4045
                            null,
4046
                            $documentPath,
4047
                            $sessionId,
4048
                            $documentData['id']
4049
                        );
4050
                    }
4051
4052
                    return $documentId;
4053
                }
4054
            }
4055
        }
4056
4057
        return false;
4058
    }
4059
4060
    /**
4061
     * Sets.
4062
     *
4063
     * @param string $file         ($document_data['path'])
4064
     * @param string $file_url_sys
4065
     *
4066
     * @return string
4067
     */
4068
    public static function generateAudioTempFile($file, $file_url_sys)
4069
    {
4070
        //make temp audio
4071
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4072
        if (!file_exists($temp_folder)) {
4073
            @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);
4074
        }
4075
4076
        //make htaccess with allow from all, and file index.html into temp/audio
4077
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4078
        if (!file_exists($htaccess)) {
4079
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4080
            $fp = @fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4081
            if ($fp) {
4082
                fwrite($fp, $htaccess_content);
4083
                fclose($fp);
4084
            }
4085
        }
4086
4087
        //encript temp name file
4088
        $name_crip = sha1(uniqid()); //encript
4089
        $findext = explode(".", $file);
4090
        $extension = $findext[count($findext) - 1];
4091
        $file_crip = $name_crip.'.'.$extension;
4092
4093
        //copy file to temp/audio directory
4094
        $from_sys = $file_url_sys;
4095
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4096
4097
        if (file_exists($from_sys)) {
4098
            copy($from_sys, $to_sys);
4099
        }
4100
4101
        // get file from tmp directory
4102
        Session::write('temp_audio_nanogong', $to_sys);
4103
4104
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4105
    }
4106
4107
    /**
4108
     * Erase temp nanogong audio.
4109
     */
4110
    public static function removeGeneratedAudioTempFile()
4111
    {
4112
        $tempAudio = Session::read('temp_audio_nanogong');
4113
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4114
            unlink($tempAudio);
4115
            Session::erase('temp_audio_nanogong');
4116
        }
4117
    }
4118
4119
    /**
4120
     * Check if the path is used in this course.
4121
     *
4122
     * @param array  $courseInfo
4123
     * @param string $path
4124
     *
4125
     * @return array
4126
     */
4127
    public static function getDocumentByPathInCourse($courseInfo, $path)
4128
    {
4129
        $table = Database::get_course_table(TABLE_DOCUMENT);
4130
        $path = Database::escape_string($path);
4131
        $courseId = $courseInfo['real_id'];
4132
        if (empty($courseId)) {
4133
            return false;
4134
        }
4135
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4136
        $result = Database::query($sql);
4137
4138
        return Database::store_result($result, 'ASSOC');
4139
    }
4140
4141
    /**
4142
     * @param array $_course
4143
     *
4144
     * @return int
4145
     */
4146
    public static function createDefaultAudioFolder($_course)
4147
    {
4148
        if (!isset($_course['path'])) {
4149
            return false;
4150
        }
4151
4152
        self::addDocument($_course, '/audio', 'folder', 0, 'Audio');
4153
    }
4154
4155
    /**
4156
     * Generate a default certificate for a courses.
4157
     *
4158
     * @todo move to certificate lib
4159
     *
4160
     * @global string $css CSS directory
4161
     * @global string $img_dir image directory
4162
     * @global string $default_course_dir Course directory
4163
     * @global string $js JS directory
4164
     *
4165
     * @param array $courseData     The course info
4166
     * @param bool  $fromBaseCourse
4167
     * @param int   $sessionId
4168
     */
4169
    public static function generateDefaultCertificate(
4170
        $courseData,
4171
        $fromBaseCourse = false,
4172
        $sessionId = 0
4173
    ) {
4174
        if (empty($courseData)) {
4175
            return false;
4176
        }
4177
4178
        global $css, $img_dir, $default_course_dir, $js;
4179
        $codePath = api_get_path(REL_CODE_PATH);
4180
        $dir = '/certificates';
4181
        $comment = null;
4182
        $title = get_lang('Default certificate');
4183
        $fileName = api_replace_dangerous_char($title);
4184
        //$filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4185
        /*if (!is_dir($filePath)) {
4186
            mkdir($filePath, api_get_permissions_for_new_directories());
4187
        }*/
4188
4189
        //$fileFullPath = "$filePath/$fileName.html";
4190
        $fileType = 'file';
4191
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4192
4193
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4194
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4195
4196
        $fileContent = str_replace($search, $replace, $templateContent);
4197
        $saveFilePath = "$dir/$fileName.html";
4198
4199
        if ($fromBaseCourse) {
4200
            $defaultCertificateId = self::get_default_certificate_id($courseData['real_id'], 0);
4201
            if (!empty($defaultCertificateId)) {
4202
                // We have a certificate from the course base
4203
                $documentData = self::get_document_data_by_id(
4204
                    $defaultCertificateId,
4205
                    $courseData['code'],
4206
                    false,
4207
                    0
4208
                );
4209
4210
                if ($documentData) {
4211
                    $fileContent = file_get_contents($documentData['absolute_path']);
4212
                }
4213
            }
4214
        }
4215
4216
        $document = self::addDocument(
4217
            $courseData,
4218
            $saveFilePath,
4219
            $fileType,
4220
            '',
4221
            $title,
4222
            $comment,
4223
            0, //$readonly = 0,
4224
            true, //$save_visibility = true,
4225
            null, //$group_id = null,
4226
            $sessionId,
4227
            0,
4228
            false,
4229
            $fileContent
4230
        );
4231
4232
        $defaultCertificateId = self::get_default_certificate_id($courseData['real_id'], $sessionId);
4233
4234
        if (!isset($defaultCertificateId)) {
4235
            self::attach_gradebook_certificate(
4236
                $courseData['real_id'],
4237
                $document->getIid(),
4238
                $sessionId
4239
            );
4240
        }
4241
    }
4242
4243
    /**
4244
     * Update the document name.
4245
     *
4246
     * @param int    $documentId The document id
4247
     * @param string $newName    The new name
4248
     */
4249
    public static function renameDocument($documentId, $newName)
4250
    {
4251
        $documentId = intval($documentId);
4252
        $newName = Database::escape_string($newName);
4253
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4254
4255
        $values = [
4256
            'title' => $newName,
4257
        ];
4258
4259
        $whereConditions = [
4260
            'id = ?' => $documentId,
4261
        ];
4262
4263
        Database::update($docuentTable, $values, $whereConditions);
4264
    }
4265
4266
    /**
4267
     * Get folder/file suffix.
4268
     *
4269
     * @param array $courseInfo
4270
     * @param int   $sessionId
4271
     * @param int   $groupId
4272
     *
4273
     * @return string
4274
     */
4275
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4276
    {
4277
        // If no session or group, then no suffix.
4278
        if (empty($sessionId) && empty($groupId)) {
4279
            return '';
4280
        }
4281
4282
        return '__'.(int) $sessionId.'__'.(int) $groupId;
4283
    }
4284
4285
    /**
4286
     * Fix a document name adding session id and group id
4287
     * Turns picture.jpg -> picture__1__2.jpg
4288
     * Where 1 = session id and 2 group id
4289
     * Of session id and group id are empty then the function returns:
4290
     * picture.jpg ->  picture.jpg.
4291
     *
4292
     * @param string $name       folder or file name
4293
     * @param string $type       'folder' or 'file'
4294
     * @param array  $courseInfo
4295
     * @param int    $sessionId
4296
     * @param int    $groupId
4297
     *
4298
     * @return string
4299
     */
4300
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4301
    {
4302
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4303
4304
        switch ($type) {
4305
            case 'folder':
4306
                $name = $name.$suffix;
4307
                break;
4308
            case 'file':
4309
                $name = self::addSuffixToFileName($name, $suffix);
4310
                break;
4311
        }
4312
4313
        return $name;
4314
    }
4315
4316
    /**
4317
     * Add a suffix to a file Example:
4318
     * /folder/picture.jpg => to /folder/picture_this.jpg
4319
     * where "_this" is the suffix.
4320
     *
4321
     * @param string $name
4322
     * @param string $suffix
4323
     *
4324
     * @return string
4325
     */
4326
    public static function addSuffixToFileName($name, $suffix)
4327
    {
4328
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4329
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4330
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4331
4332
        if ('.' == $dir) {
4333
            $dir = null;
4334
        }
4335
4336
        if (!empty($dir) && '/' != $dir) {
4337
            $dir = $dir.'/';
4338
        }
4339
4340
        $name = $dir.$fileName.$suffix.'.'.$extension;
4341
4342
        return $name;
4343
    }
4344
4345
    /**
4346
     * Check if folder exist in the course base or in the session course.
4347
     *
4348
     * @param string $folder     Example: /folder/folder2
4349
     * @param array  $courseInfo
4350
     * @param int    $sessionId
4351
     * @param int    $groupId    group.id
4352
     *
4353
     * @return bool
4354
     */
4355
    public static function folderExists(
4356
        $folder,
4357
        $courseInfo,
4358
        $sessionId,
4359
        $groupId
4360
    ) {
4361
        $courseId = $courseInfo['real_id'];
4362
4363
        if (empty($courseId)) {
4364
            return false;
4365
        }
4366
4367
        $sessionId = (int) $sessionId;
4368
        $folderWithSuffix = self::fixDocumentName(
4369
            $folder,
4370
            'folder',
4371
            $courseInfo,
4372
            $sessionId,
4373
            $groupId
4374
        );
4375
4376
        $folder = Database::escape_string($folder);
4377
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4378
4379
        // Check if pathname already exists inside document table
4380
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4381
        $sql = "SELECT id, path FROM $tbl_document
4382
                WHERE
4383
                    filetype = 'folder' AND
4384
                    c_id = $courseId AND
4385
                    (path = '$folder' OR path = '$folderWithSuffix') AND
4386
                    (session_id = 0 OR session_id IS NULL OR session_id = $sessionId)
4387
        ";
4388
4389
        $rs = Database::query($sql);
4390
        if (Database::num_rows($rs)) {
4391
            return true;
4392
        }
4393
4394
        return false;
4395
    }
4396
4397
    /**
4398
     * Check if file exist in the course base or in the session course.
4399
     *
4400
     * @param string $fileName   Example: /folder/picture.jpg
4401
     * @param array  $courseInfo
4402
     * @param int    $sessionId
4403
     * @param int    $groupId
4404
     *
4405
     * @return bool
4406
     */
4407
    public static function documentExists(
4408
        $fileName,
4409
        $courseInfo,
4410
        $sessionId,
4411
        $groupId
4412
    ) {
4413
        $courseId = $courseInfo['real_id'];
4414
4415
        if (empty($courseId)) {
4416
            return false;
4417
        }
4418
4419
        $sessionId = (int) $sessionId;
4420
        $fileNameEscape = Database::escape_string($fileName);
4421
4422
        $fileNameWithSuffix = self::fixDocumentName(
4423
            $fileName,
4424
            'file',
4425
            $courseInfo,
4426
            $sessionId,
4427
            $groupId
4428
        );
4429
4430
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
4431
4432
        // Check if pathname already exists inside document table
4433
        $table = Database::get_course_table(TABLE_DOCUMENT);
4434
        $sql = "SELECT id, path FROM $table
4435
                WHERE
4436
                    filetype = 'file' AND
4437
                    c_id = $courseId AND
4438
                    (
4439
                        path = '".$fileNameEscape."' OR
4440
                        path = '$fileNameWithSuffix'
4441
                    ) AND
4442
                    (session_id = 0 OR session_id = $sessionId)
4443
        ";
4444
        $rs = Database::query($sql);
4445
        if (Database::num_rows($rs)) {
4446
            return true;
4447
        }
4448
4449
        return false;
4450
    }
4451
4452
    /**
4453
     * Undo the suffix applied to a file example:
4454
     * turns picture__1__1.jpg to picture.jpg.
4455
     *
4456
     * @param string $name
4457
     * @param int    $courseId
4458
     * @param int    $sessionId
4459
     * @param int    $groupId
4460
     *
4461
     * @return string
4462
     */
4463
    public static function undoFixDocumentName(
4464
        $name,
4465
        $courseId,
4466
        $sessionId,
4467
        $groupId
4468
    ) {
4469
        if (empty($sessionId) && empty($groupId)) {
4470
            return $name;
4471
        }
4472
4473
        $suffix = self::getDocumentSuffix(
4474
            ['real_id' => $courseId],
4475
            $sessionId,
4476
            $groupId
4477
        );
4478
4479
        $name = str_replace($suffix, '', $name);
4480
4481
        return $name;
4482
    }
4483
4484
    /**
4485
     * @param string $path
4486
     * @param string $name
4487
     * @param array  $courseInfo
4488
     * @param int    $sessionId
4489
     * @param int    $groupId
4490
     *
4491
     * @return string
4492
     */
4493
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
4494
    {
4495
        $counter = 1;
4496
        $filePath = $path.$name;
4497
        $uniqueName = $name;
4498
        while ($documentExists = self::documentExists(
4499
            $filePath,
4500
            $courseInfo,
4501
            $sessionId,
4502
            $groupId
4503
        )) {
4504
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
4505
            $filePath = $path.$uniqueName;
4506
            $counter++;
4507
        }
4508
4509
        return $uniqueName;
4510
    }
4511
4512
    /**
4513
     * Builds the form that enables the user to
4514
     * select a directory to browse/upload in.
4515
     *
4516
     * @param array    An array containing the folders we want to be able to select
4517
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
4518
     * @param string    Group directory, if empty, prevents documents to be uploaded
4519
     * (because group documents cannot be uploaded in root)
4520
     * @param bool    Whether to change the renderer (this will add a template <span>
4521
     * to the QuickForm object displaying the form)
4522
     *
4523
     * @return string html form
4524
     */
4525
    public static function build_directory_selector(
4526
        $folders,
4527
        $document_id,
4528
        $group_dir = '',
4529
        $change_renderer = false,
4530
        &$form = null,
4531
        $selectName = 'id'
4532
    ) {
4533
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
4534
        $course_id = api_get_course_int_id();
4535
        $folder_titles = [];
4536
4537
        if (is_array($folders)) {
4538
            $escaped_folders = [];
4539
            foreach ($folders as $key => &$val) {
4540
                $escaped_folders[$key] = Database::escape_string($val);
4541
            }
4542
            $folder_sql = implode("','", $escaped_folders);
4543
4544
            $sql = "SELECT path, title
4545
                    FROM $doc_table
4546
                    WHERE
4547
                        filetype = 'folder' AND
4548
                        c_id = $course_id AND
4549
                        path IN ('".$folder_sql."')";
4550
            $res = Database::query($sql);
4551
            $folder_titles = [];
4552
            while ($obj = Database::fetch_object($res)) {
4553
                $folder_titles[$obj->path] = $obj->title;
4554
            }
4555
        }
4556
4557
        $attributes = [];
4558
        if (empty($form)) {
4559
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
4560
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
4561
        }
4562
        $form->addElement('hidden', 'cidReq', api_get_course_id());
4563
        $form->addElement('hidden', 'id_session', api_get_session_id());
4564
        $form->addElement('hidden', 'gidReq', api_get_group_id());
4565
4566
        $parent_select = $form->addSelect(
4567
            $selectName,
4568
            get_lang('Current folder'),
4569
            '',
4570
            $attributes
4571
        );
4572
4573
        // Group documents cannot be uploaded in the root
4574
        if (empty($group_dir)) {
4575
            $parent_select->addOption(get_lang('Documents'), '/');
4576
4577
            if (is_array($folders)) {
4578
                foreach ($folders as $folder_id => &$folder) {
4579
                    if (!isset($folder_titles[$folder])) {
4580
                        continue;
4581
                    }
4582
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4583
                    $path_parts = explode('/', $folder);
4584
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
4585
                    $counter = count($path_parts) - 2;
4586
                    if ($counter > 0) {
4587
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
4588
                    } else {
4589
                        $label = ' &mdash; '.$folder_titles[$folder];
4590
                    }
4591
                    $parent_select->addOption($label, $folder_id);
4592
                    if ('' != $selected) {
4593
                        $parent_select->setSelected($folder_id);
4594
                    }
4595
                }
4596
            }
4597
        } else {
4598
            if (!empty($folders)) {
4599
                foreach ($folders as $folder_id => &$folder) {
4600
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4601
                    $label = $folder_titles[$folder];
4602
                    if ($folder == $group_dir) {
4603
                        $label = get_lang('Documents');
4604
                    } else {
4605
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
4606
                        $label = cut($label, 80);
4607
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
4608
                    }
4609
                    $parent_select->addOption($label, $folder_id);
4610
                    if ('' != $selected) {
4611
                        $parent_select->setSelected($folder_id);
4612
                    }
4613
                }
4614
            }
4615
        }
4616
4617
        return $form->toHtml();
4618
    }
4619
4620
    /**
4621
     * Create a html hyperlink depending on if it's a folder or a file.
4622
     *
4623
     * @param string $documentWebPath
4624
     * @param array  $document_data
4625
     * @param bool   $show_as_icon      - if it is true, only a clickable icon will be shown
4626
     * @param int    $visibility        (1/0)
4627
     * @param int    $size
4628
     * @param bool   $isAllowedToEdit
4629
     * @param bool   $isCertificateMode
4630
     * @param bool   $addToEditor
4631
     * @param string $editorUrl
4632
     *
4633
     * @return string url
4634
     */
4635
    public static function create_document_link(
4636
        $documentWebPath,
4637
        $document_data,
4638
        $show_as_icon = false,
4639
        $visibility,
4640
        $size = 0,
4641
        $isAllowedToEdit = false,
4642
        $isCertificateMode = false,
4643
        $addToEditor = false,
4644
        $editorUrl = ''
4645
    ) {
4646
        global $dbl_click_id;
4647
4648
        $sessionId = api_get_session_id();
4649
        $courseParams = api_get_cidreq();
4650
        $courseCode = api_get_course_id();
4651
        $webODFList = self::get_web_odf_extension_list();
4652
4653
        // Get the title or the basename depending on what we're using
4654
        if ('' != $document_data['title']) {
4655
            $title = $document_data['title'];
4656
        } else {
4657
            $title = basename($document_data['path']);
4658
        }
4659
4660
        $isAdmin = api_is_platform_admin();
4661
4662
        $filetype = $document_data['filetype'];
4663
        $path = $document_data['path'];
4664
        $url_path = urlencode($document_data['path']);
4665
4666
        $basePageUrl = api_get_path(WEB_CODE_PATH).'document/';
4667
        $pageUrl = $basePageUrl.'document.php';
4668
4669
        // Add class="invisible" on invisible files
4670
        $classAddToEditor = '';
4671
        if ($addToEditor) {
4672
            $classAddToEditor = 'select_to_ckeditor';
4673
        }
4674
        $visibility_class = false === $visibility ? ' class="muted"' : ' class="'.$classAddToEditor.'" ';
4675
4676
        $forcedownload_link = '';
4677
        $forcedownload_icon = '';
4678
        $prevent_multiple_click = '';
4679
        $force_download_html = '';
4680
4681
        if (!$show_as_icon) {
4682
            // Build download link (icon)
4683
            $forcedownload_link = 'folder' === $filetype
4684
                ? $pageUrl.'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id']
4685
                : $pageUrl.'?'.$courseParams.'&action=download&id='.$document_data['id'];
4686
            // Folder download or file download?
4687
            $forcedownload_icon = 'folder' === $filetype ? 'save_pack.png' : 'save.png';
4688
            // Prevent multiple clicks on zipped folder download
4689
            $prevent_multiple_click = 'folder' === $filetype ? " 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; }\"" : '';
4690
        }
4691
4692
        $target = '_self';
4693
        $is_browser_viewable_file = false;
4694
4695
        if ('file' === $filetype) {
4696
            // Check the extension
4697
            $ext = explode('.', $path);
4698
            $ext = strtolower($ext[count($ext) - 1]);
4699
4700
            // HTML-files an some other types are shown in a frameset by default.
4701
            $is_browser_viewable_file = self::isBrowserViewable($ext);
4702
            if ($is_browser_viewable_file) {
4703
                if ('pdf' == $ext || in_array($ext, $webODFList)) {
4704
                    $url = $pageUrl.'?'.$courseParams.'&action=download&amp;id='.$document_data['id'];
4705
                } else {
4706
                    $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4707
                }
4708
            } else {
4709
                $url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4710
            }
4711
        } else {
4712
            $url = $pageUrl.'?'.$courseParams.'&id='.$document_data['id'];
4713
        }
4714
4715
        if ($isCertificateMode) {
4716
            $url .= '&certificate=true&selectcat='.(isset($_GET['selectcat']) ? $_GET['selectcat'] : '');
4717
        }
4718
4719
        // The little download icon
4720
        $tooltip_title = $title;
4721
        $tooltip_title_alt = $tooltip_title;
4722
4723
        if ('link' == $filetype) {
4724
            $tooltip_title_alt = $title;
4725
            $url = $document_data['comment'].'" target="_blank';
4726
        }
4727
4728
        if ('/shared_folder' === $path) {
4729
            $tooltip_title_alt = get_lang('Folders of users');
4730
        } elseif (strstr($path, 'shared_folder_session_')) {
4731
            $tooltip_title_alt = get_lang('Folders of users').' ('.api_get_session_name($sessionId).')';
4732
        } elseif (strstr($tooltip_title, 'sf_user_')) {
4733
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
4734
            $tooltip_title_alt = get_lang('User folder').' '.$userinfo['complete_name'];
4735
        } elseif ('/chat_files' == $path) {
4736
            $tooltip_title_alt = get_lang('Chat conversations history');
4737
        } elseif ('/learning_path' == $path) {
4738
            $tooltip_title_alt = get_lang('Learning paths');
4739
        } elseif ('/video' == $path) {
4740
            $tooltip_title_alt = get_lang('Video');
4741
        } elseif ('/audio' == $path) {
4742
            $tooltip_title_alt = get_lang('Audio');
4743
        } elseif ('/flash' == $path) {
4744
            $tooltip_title_alt = get_lang('Flash');
4745
        } elseif ('/images' == $path) {
4746
            $tooltip_title_alt = get_lang('Images');
4747
        } elseif ('/images/gallery' == $path) {
4748
            $tooltip_title_alt = get_lang('Gallery');
4749
        }
4750
4751
        $copyToMyFiles = $open_in_new_window_link = '';
4752
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
4753
        $send_to = null;
4754
        $checkExtension = $path;
4755
        $extension = pathinfo($path, PATHINFO_EXTENSION);
4756
        $document_data['file_extension'] = $extension;
4757
4758
        if (!$show_as_icon) {
4759
            if ('folder' === $filetype) {
4760
                if ($isAllowedToEdit ||
4761
                    $isAdmin ||
4762
                    'true' == api_get_setting('students_download_folders')
4763
                ) {
4764
                    // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
4765
                    if (self::is_shared_folder($curdirpath, $sessionId)) {
4766
                        if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
4767
                            preg_match('/shared_folder_session_'.$sessionId.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
4768
                            $isAllowedToEdit || $isAdmin
4769
                        ) {
4770
                            $force_download_html = (0 == $size) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
4771
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4772
                        }
4773
                    } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
4774
                        $isAllowedToEdit ||
4775
                        $isAdmin
4776
                    ) {
4777
                        $force_download_html = (0 == $size) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
4778
                            Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4779
                    }
4780
                }
4781
            } else {
4782
                $force_download_html = 0 == $size ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
4783
                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
4784
            }
4785
4786
            $pdf_icon = '';
4787
            if (!$isAllowedToEdit &&
4788
                'true' == api_get_setting('students_export2pdf') &&
4789
                'file' === $filetype &&
4790
                in_array($extension, ['html', 'htm'])
4791
            ) {
4792
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.$pageUrl.'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&curdirpath='.$curdirpath.'">'.
4793
                    Display::return_icon('pdf.png', get_lang('Export to PDF format'), [], ICON_SIZE_SMALL).'</a> ';
4794
            }
4795
4796
            if ($is_browser_viewable_file) {
4797
                $open_in_new_window_link = '<a href="'.$documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
4798
                    Display::return_icon('open_in_new_window.png', get_lang('Open in a new window'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
4799
            }
4800
4801
            if ($addToEditor) {
4802
                $force_download_html = '';
4803
                $open_in_new_window_link = '';
4804
                $send_to = '';
4805
                $pdf_icon = '';
4806
                if ('folder' === $filetype) {
4807
                    $url = $editorUrl.'/'.$document_data['id'].'/?'.api_get_cidreq();
4808
                } else {
4809
                    $url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4810
                }
4811
            }
4812
4813
            if ('file' === $filetype) {
4814
                // Sound preview
4815
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
4816
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
4817
                    preg_match('/ogg$/i', urldecode($checkExtension))
4818
                ) {
4819
                    return '<span style="float:left" '.$visibility_class.'>'.
4820
                    $title.
4821
                    '</span>'.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4822
                } elseif (
4823
                    // Show preview
4824
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
4825
                    preg_match('/png$/i', urldecode($checkExtension)) ||
4826
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
4827
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
4828
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
4829
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
4830
                    preg_match('/svg$/i', urldecode($checkExtension))
4831
                ) {
4832
                    // Simpler version of showinframesmin.php with no headers
4833
                    //$url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
4834
                    $class = 'ajax ';
4835
                    //$url = $documentWebPath.str_replace('%2F', '/', $url_path).'?'.$courseParams;
4836
                    $url_path = str_replace('%2F', '/', $url_path);
4837
                    $url = api_get_path(WEB_PUBLIC_PATH)."courses/$courseCode/document$url_path?type=show";
4838
                    if ($addToEditor) {
4839
                        $class = $classAddToEditor;
4840
                        $url = $documentWebPath.$url_path;
4841
                    }
4842
4843
                    if (false == $visibility) {
4844
                        $class = ' ajax text-muted ';
4845
                        if ($addToEditor) {
4846
                            $class = ' text-muted not_select_to_ckeditor';
4847
                        }
4848
                    }
4849
4850
                    return Display::url(
4851
                        $title,
4852
                        $url,
4853
                        [
4854
                            'class' => $class,
4855
                            'title' => $tooltip_title_alt,
4856
                            'data-title' => $title,
4857
                            'style' => 'float:left;',
4858
                        ]
4859
                    )
4860
                    .$force_download_html.$send_to.$copyToMyFiles
4861
                    .$open_in_new_window_link.$pdf_icon;
4862
                } else {
4863
                    // For a "PDF Download" of the file.
4864
                    $pdfPreview = null;
4865
                    if ('pdf' != $ext && !in_array($ext, $webODFList)) {
4866
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4867
                    } else {
4868
                        $pdfPreview = Display::url(
4869
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
4870
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
4871
                            ['style' => 'float:right']
4872
                        );
4873
                    }
4874
                    // No plugin just the old and good showinframes.php page
4875
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
4876
                    $pdfPreview.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4877
                }
4878
            } else {
4879
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'.
4880
                $force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
4881
            }
4882
        } else {
4883
            $urlDecoded = urldecode($checkExtension);
4884
            // Icon column
4885
            if (preg_match('/shared_folder/', $urlDecoded) &&
4886
                false == preg_match('/shared_folder$/', $urlDecoded) &&
4887
                false == preg_match('/shared_folder_session_'.$sessionId.'$/', urldecode($url))
4888
            ) {
4889
                if ('file' === $filetype) {
4890
                    // Sound preview
4891
                    if (preg_match('/mp3$/i', $urlDecoded) ||
4892
                        preg_match('/wav$/i', $urlDecoded) ||
4893
                        preg_match('/ogg$/i', $urlDecoded)
4894
                    ) {
4895
                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
4896
4897
                        return $soundPreview;
4898
                    } elseif (
4899
                        // Show preview
4900
                        preg_match('/swf$/i', $urlDecoded) ||
4901
                        preg_match('/png$/i', $urlDecoded) ||
4902
                        preg_match('/gif$/i', $urlDecoded) ||
4903
                        preg_match('/jpg$/i', $urlDecoded) ||
4904
                        preg_match('/jpeg$/i', $urlDecoded) ||
4905
                        preg_match('/bmp$/i', $urlDecoded) ||
4906
                        preg_match('/svg$/i', $urlDecoded)
4907
                    ) {
4908
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
4909
4910
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4911
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4912
                            Display::return_icon('shared.png', get_lang('Resource shared'), []).
4913
                        '</a>';
4914
                    } else {
4915
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4916
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4917
                            Display::return_icon('shared.png', get_lang('Resource shared'), []).
4918
                        '</a>';
4919
                    }
4920
                } else {
4921
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
4922
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4923
                        Display::return_icon('shared.png', get_lang('Resource shared'), []).
4924
                    '</a>';
4925
                }
4926
            } else {
4927
                if ('file' === $filetype) {
4928
                    // Sound preview with jplayer
4929
                    if (preg_match('/mp3$/i', $urlDecoded) ||
4930
                        (preg_match('/wav$/i', $urlDecoded)) ||
4931
                        preg_match('/ogg$/i', $urlDecoded)) {
4932
                        $soundPreview = self::generateAudioPreview($documentWebPath, $document_data);
4933
4934
                        return $soundPreview;
4935
                    } elseif (
4936
                        //Show preview
4937
                        preg_match('/html$/i', $urlDecoded) ||
4938
                        preg_match('/htm$/i', $urlDecoded) ||
4939
                        preg_match('/swf$/i', $urlDecoded) ||
4940
                        preg_match('/png$/i', $urlDecoded) ||
4941
                        preg_match('/gif$/i', $urlDecoded) ||
4942
                        preg_match('/jpg$/i', $urlDecoded) ||
4943
                        preg_match('/jpeg$/i', $urlDecoded) ||
4944
                        preg_match('/bmp$/i', $urlDecoded) ||
4945
                        preg_match('/svg$/i', $urlDecoded)
4946
                    ) {
4947
                        $url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
4948
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4949
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4950
                        '</a>';
4951
                    } else {
4952
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
4953
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4954
                        '</a>';
4955
                    }
4956
                } else {
4957
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
4958
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
4959
                    '</a>';
4960
                }
4961
            }
4962
        }
4963
    }
4964
4965
    /**
4966
     * Builds an img html tag for the file type.
4967
     *
4968
     * @param string $type            (file/folder)
4969
     * @param string $path
4970
     * @param bool   $isAllowedToEdit
4971
     *
4972
     * @return string img html tag
4973
     */
4974
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
4975
    {
4976
        $basename = basename($path);
4977
        $sessionId = api_get_session_id();
4978
        if (is_null($isAllowedToEdit)) {
4979
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
4980
        }
4981
        $user_image = false;
4982
        if ('file' == $type) {
4983
            $icon = choose_image($basename);
4984
            $basename = substr(strrchr($basename, '.'), 1);
4985
        } elseif ('link' == $type) {
4986
            $icon = 'clouddoc.png';
4987
            $basename = get_lang('Cloud file link');
4988
        } else {
4989
            if ('/shared_folder' == $path) {
4990
                $icon = 'folder_users.png';
4991
                if ($isAllowedToEdit) {
4992
                    $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
4993
The users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.');
4994
                } else {
4995
                    $basename = get_lang('Folders of users');
4996
                }
4997
            } elseif (strstr($basename, 'sf_user_')) {
4998
                $userInfo = api_get_user_info(substr($basename, 8));
4999
                $icon = $userInfo['avatar_small'];
5000
                $basename = get_lang('User folder').' '.$userInfo['complete_name'];
5001
                $user_image = true;
5002
            } elseif (strstr($path, 'shared_folder_session_')) {
5003
                $sessionName = api_get_session_name($sessionId);
5004
                if ($isAllowedToEdit) {
5005
                    $basename = '***('.$sessionName.')*** '.get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5006
The users folder contains a folder for each user who has accessed it through the documents tool, or when any file has been sent in the course through the online editor. If neither circumstances has occurred, then no user folder will have been created. In the case of groups, files that are sent through the editor will be added in the folder of each group, which is only accessible by students from this group.');
5007
                } else {
5008
                    $basename = get_lang('Folders of users').' ('.$sessionName.')';
5009
                }
5010
                $icon = 'folder_users.png';
5011
            } else {
5012
                $icon = 'folder_document.png';
5013
5014
                if ('/audio' == $path) {
5015
                    $icon = 'folder_audio.png';
5016
                    if ($isAllowedToEdit) {
5017
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5018
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5019
                    } else {
5020
                        $basename = get_lang('Audio');
5021
                    }
5022
                } elseif ('/flash' == $path) {
5023
                    $icon = 'folder_flash.png';
5024
                    if ($isAllowedToEdit) {
5025
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5026
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5027
                    } else {
5028
                        $basename = get_lang('Flash');
5029
                    }
5030
                } elseif ('/images' == $path) {
5031
                    $icon = 'folder_images.png';
5032
                    if ($isAllowedToEdit) {
5033
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5034
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5035
                    } else {
5036
                        $basename = get_lang('Images');
5037
                    }
5038
                } elseif ('/video' == $path) {
5039
                    $icon = 'folder_video.png';
5040
                    if ($isAllowedToEdit) {
5041
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5042
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5043
                    } else {
5044
                        $basename = get_lang('Video');
5045
                    }
5046
                } elseif ('/images/gallery' == $path) {
5047
                    $icon = 'folder_gallery.png';
5048
                    if ($isAllowedToEdit) {
5049
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5050
This folder contains the default archives. You can clear files or add new ones, but if a file is hidden when it is inserted in a web document, the students will not be able to see it in this document. When inserting a file in a web document, first make sure it is visible. The folders can remain hidden.');
5051
                    } else {
5052
                        $basename = get_lang('Gallery');
5053
                    }
5054
                } elseif ('/chat_files' == $path) {
5055
                    $icon = 'folder_chat.png';
5056
                    if ($isAllowedToEdit) {
5057
                        $basename = get_lang('INFORMATION VISIBLE TO THE TEACHER ONLY:
5058
This folder contains all sessions that have been opened in the chat. Although the chat sessions can often be trivial, others can be really interesting and worthy of being incorporated as an additional work document. To do this without changing the visibility of this folder, make the file visible and link it from where you deem appropriate. It is not recommended to make this folder visible to all.');
5059
                    } else {
5060
                        $basename = get_lang('Chat conversations history');
5061
                    }
5062
                } elseif ('/learning_path' == $path) {
5063
                    $icon = 'folder_learningpath.png';
5064
                    if ($isAllowedToEdit) {
5065
                        $basename = get_lang('HelpFolderLearning paths');
5066
                    } else {
5067
                        $basename = get_lang('Learning paths');
5068
                    }
5069
                }
5070
            }
5071
        }
5072
5073
        if ($user_image) {
5074
            return Display::img($icon, $basename, [], false);
5075
        }
5076
5077
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5078
    }
5079
5080
    /**
5081
     * Creates the row of edit icons for a file/folder.
5082
     *
5083
     * @param array $document_data
5084
     * @param int   $id
5085
     * @param bool  $is_template
5086
     * @param int   $visibility    (1/0)
5087
     *
5088
     * @return string html img tags with hyperlinks
5089
     */
5090
    public static function build_edit_icons($document_data, $id, $is_template, $visibility)
5091
    {
5092
        $sessionId = api_get_session_id();
5093
        $courseParams = api_get_cidreq();
5094
        $document_id = $document_data['id'];
5095
        $type = $document_data['filetype'];
5096
        $is_read_only = $document_data['readonly'];
5097
        $path = $document_data['path'];
5098
5099
        if ('link' == $type) {
5100
            $parent_id = self::get_document_id(
5101
                api_get_course_info(),
5102
                rtrim($path, '/'),
5103
                0
5104
            );
5105
        } else {
5106
            $parent_id = self::get_document_id(
5107
                api_get_course_info(),
5108
                dirname($path),
5109
                0
5110
            );
5111
        }
5112
5113
        if (empty($parent_id) && !empty($sessionId)) {
5114
            $parent_id = self::get_document_id(
5115
                api_get_course_info(),
5116
                dirname($path),
5117
                $sessionId
5118
            );
5119
        }
5120
5121
        $curdirpath = dirname($document_data['path']);
5122
        $is_certificate_mode = self::is_certificate_mode($path);
5123
        $curdirpath = urlencode($curdirpath);
5124
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5125
        //@todo Implement remote support for converter
5126
        $usePpt2lp = 'true' == api_get_setting('service_ppt2lp', 'active') && 'localhost' == api_get_setting('service_ppt2lp', 'host');
5127
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5128
        $formatType = current($formatTypeList);
5129
5130
        // If document is read only *or* we're in a session and the document
5131
        // is from a non-session context, hide the edition capabilities
5132
        $modify_icons = [];
5133
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5134
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5135
        $modify_icons[] = self::getButtonVisibility(
5136
            $is_read_only,
5137
            $visibility,
5138
            $document_data,
5139
            $is_certificate_mode,
5140
            $parent_id
5141
        );
5142
        $modify_icons[] = self::getButtonDelete(
5143
            $is_read_only,
5144
            $document_data,
5145
            $is_certificate_mode,
5146
            $curdirpath,
5147
            $parent_id
5148
        );
5149
5150
        if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
5151
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5152
            // @TODO: add prompt to select a format target
5153
            if (!in_array($path, self::get_system_folders())) {
5154
                if ($usePpt2lp && $formatType) {
5155
                    $modify_icons[] = Display::url(
5156
                        Display::return_icon('convert.png', get_lang('Convert')),
5157
                        '#',
5158
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5159
                    );
5160
                }
5161
            }
5162
        }
5163
5164
        if ('file' == $type && ('html' == $extension || 'htm' == $extension)) {
5165
            if (0 == $is_template) {
5166
                if ((isset($_GET['curdirpath']) && '/certificates' != $_GET['curdirpath']) || !isset($_GET['curdirpath'])) {
5167
                    $modify_icons[] = Display::url(
5168
                        Display::return_icon('wizard.png', get_lang('Add as a template')),
5169
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id"
5170
                    );
5171
                }
5172
                if ((isset($_GET['curdirpath']) && '/certificates' == $_GET['curdirpath']) || $is_certificate_mode) {//allow attach certificate to course
5173
                    $visibility_icon_certificate = 'nocertificate';
5174
                    if (self::get_default_certificate_id(api_get_course_int_id()) == $id) {
5175
                        $visibility_icon_certificate = 'certificate';
5176
                        $certificate = get_lang('Default certificate');
5177
                        $preview = get_lang('Preview certificate');
5178
                        $is_preview = true;
5179
                    } else {
5180
                        $is_preview = false;
5181
                        $certificate = get_lang('NoDefault certificate');
5182
                    }
5183
                    if (isset($_GET['selectcat'])) {
5184
                        $modify_icons[] = Display::url(
5185
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5186
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id"
5187
                        );
5188
                        if ($is_preview) {
5189
                            $modify_icons[] = Display::url(
5190
                                Display::return_icon('preview_view.png', $preview),
5191
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id"
5192
                            );
5193
                        }
5194
                    }
5195
                }
5196
            } else {
5197
                $modify_icons[] = Display::url(
5198
                    Display::return_icon('wizard_na.png', get_lang('Remove template')),
5199
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id"
5200
                );
5201
            }
5202
5203
            $modify_icons[] = Display::url(
5204
                Display::return_icon('pdf.png', get_lang('Export to PDF format')),
5205
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath"
5206
            );
5207
        }
5208
5209
        return implode(PHP_EOL, $modify_icons);
5210
    }
5211
5212
    /**
5213
     * @param $folders
5214
     * @param $curdirpath
5215
     * @param $move_file
5216
     * @param string $group_dir
5217
     *
5218
     * @return string
5219
     */
5220
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5221
    {
5222
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5223
5224
        // Form title
5225
        $form->addHidden('move_file', $move_file);
5226
5227
        $options = [];
5228
5229
        // Group documents cannot be uploaded in the root
5230
        if ('' == $group_dir) {
5231
            if ('/' != $curdirpath) {
5232
                $options['/'] = get_lang('Documents');
5233
            }
5234
5235
            if (is_array($folders)) {
5236
                foreach ($folders as &$folder) {
5237
                    // Hide some folders
5238
                    if ('/HotPotatoes_files' == $folder ||
5239
                        '/certificates' == $folder ||
5240
                        'css' == basename($folder)
5241
                    ) {
5242
                        continue;
5243
                    }
5244
                    // Admin setting for Hide/Show the folders of all users
5245
                    if ('false' == api_get_setting('show_users_folders') &&
5246
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5247
                    ) {
5248
                        continue;
5249
                    }
5250
5251
                    // Admin setting for Hide/Show Default folders to all users
5252
                    if ('false' == api_get_setting('show_default_folders') &&
5253
                        (
5254
                            '/images' == $folder ||
5255
                            '/flash' == $folder ||
5256
                            '/audio' == $folder ||
5257
                            '/video' == $folder ||
5258
                            strstr($folder, '/images/gallery') ||
5259
                            '/video/flv' == $folder
5260
                        )
5261
                    ) {
5262
                        continue;
5263
                    }
5264
5265
                    // Admin setting for Hide/Show chat history folder
5266
                    if ('false' == api_get_setting('show_chat_folder') &&
5267
                        '/chat_files' == $folder) {
5268
                        continue;
5269
                    }
5270
5271
                    // You cannot move a file to:
5272
                    // 1. current directory
5273
                    // 2. inside the folder you want to move
5274
                    // 3. inside a subfolder of the folder you want to move
5275
                    if (($curdirpath != $folder) &&
5276
                        ($folder != $move_file) &&
5277
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5278
                    ) {
5279
                        // If document title is used, we have to display titles instead of real paths...
5280
                        $path_displayed = self::get_titles_of_path($folder);
5281
                        if (empty($path_displayed)) {
5282
                            $path_displayed = get_lang('Untitled');
5283
                        }
5284
                        $options[$folder] = $path_displayed;
5285
                    }
5286
                }
5287
            }
5288
        } else {
5289
            foreach ($folders as $folder) {
5290
                if (($curdirpath != $folder) &&
5291
                    ($folder != $move_file) &&
5292
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5293
                ) {
5294
                    // Cannot copy dir into his own subdir
5295
                    $path_displayed = self::get_titles_of_path($folder);
5296
                    $display_folder = substr($path_displayed, strlen($group_dir));
5297
                    $display_folder = '' == $display_folder ? get_lang('Documents') : $display_folder;
5298
                    $options[$folder] = $display_folder;
5299
                }
5300
            }
5301
        }
5302
        $form->addElement('select', 'move_to', get_lang('Move to'), $options);
5303
        $form->addButtonNext(get_lang('Move element'), 'move_file_submit');
5304
5305
        return $form->returnForm();
5306
    }
5307
5308
    /**
5309
     * Gets the path translated with title of docs and folders.
5310
     *
5311
     * @param string $path the real path
5312
     *
5313
     * @return the path which should be displayed
5314
     */
5315
    public static function get_titles_of_path($path)
5316
    {
5317
        global $tmp_folders_titles;
5318
        $course_id = api_get_course_int_id();
5319
        $nb_slashes = substr_count($path, '/');
5320
        $current_slash_pos = 0;
5321
        $path_displayed = '';
5322
        for ($i = 0; $i < $nb_slashes; $i++) {
5323
            // For each folder of the path, retrieve title.
5324
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
5325
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
5326
5327
            if (empty($tmp_path)) {
5328
                // If empty, then we are in the final part of the path
5329
                $tmp_path = $path;
5330
            }
5331
5332
            if (!empty($tmp_folders_titles[$tmp_path])) {
5333
                // If this path has soon been stored here we don't need a new query
5334
                $path_displayed .= $tmp_folders_titles[$tmp_path];
5335
            } else {
5336
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
5337
                        WHERE c_id = '.$course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
5338
                $rs = Database::query($sql);
5339
                $tmp_title = '/'.Database::result($rs, 0, 0);
5340
                $path_displayed .= $tmp_title;
5341
                $tmp_folders_titles[$tmp_path] = $tmp_title;
5342
            }
5343
        }
5344
5345
        return $path_displayed;
5346
    }
5347
5348
    /**
5349
     * Creates form that asks for the directory name.
5350
     *
5351
     * @return string html-output text for the form
5352
     */
5353
    public static function create_dir_form($dirId)
5354
    {
5355
        global $document_id;
5356
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
5357
        $form->addElement('hidden', 'create_dir', 1);
5358
        $form->addElement('hidden', 'dir_id', intval($document_id));
5359
        $form->addElement('hidden', 'id', intval($dirId));
5360
        $form->addElement('header', get_lang('Create folder'));
5361
        $form->addText('dirname', get_lang('Name of the new folder'), ['autofocus' => 'autofocus']);
5362
        $form->addButtonCreate(get_lang('Create the folder'));
5363
5364
        return $form->returnForm();
5365
    }
5366
5367
    /**
5368
     * Checks whether the user is in shared folder.
5369
     *
5370
     * @param string $curdirpath
5371
     * @param int    $sessionId
5372
     *
5373
     * @return bool Return true when user is into shared folder
5374
     */
5375
    public static function is_shared_folder($curdirpath, $sessionId)
5376
    {
5377
        $clean_curdirpath = Security::remove_XSS($curdirpath);
5378
        if ('/shared_folder' == $clean_curdirpath) {
5379
            return true;
5380
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$sessionId) {
5381
            return true;
5382
        } else {
5383
            return false;
5384
        }
5385
    }
5386
5387
    /**
5388
     * Checks whether the user is into any user shared folder.
5389
     *
5390
     * @param string $path
5391
     * @param int    $sessionId
5392
     *
5393
     * @return bool Return true when user is in any user shared folder
5394
     */
5395
    public static function is_any_user_shared_folder($path, $sessionId)
5396
    {
5397
        $clean_path = Security::remove_XSS($path);
5398
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
5399
            return true;
5400
        } elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
5401
            return true;
5402
        } else {
5403
            return false;
5404
        }
5405
    }
5406
5407
    /**
5408
     * Create users shared folder for course.
5409
     *
5410
     * @param int $userId
5411
     * @param int $sessionId
5412
     */
5413
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
5414
    {
5415
        return false;
5416
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
0 ignored issues
show
Unused Code introduced by
$documentDirectory = api...rectory'] . '/document' is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5417
        $userInfo = api_get_user_info($userId);
5418
5419
        if (!$sessionId) {
5420
            //Create shared folder. Necessary for recycled courses.
5421
            if (!file_exists($documentDirectory.'/shared_folder')) {
5422
                create_unexisting_directory(
5423
                    $courseInfo,
5424
                    $userId,
5425
                    0,
5426
                    0,
5427
                    0,
5428
                    $documentDirectory,
5429
                    '/shared_folder',
5430
                    get_lang('Folders of users'),
5431
                    0,
5432
                    false,
5433
                    false
5434
                );
5435
            }
5436
            // Create dynamic user shared folder
5437
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
5438
                create_unexisting_directory(
5439
                    $courseInfo,
5440
                    $userId,
5441
                    0,
5442
                    0,
5443
                    0,
5444
                    $documentDirectory,
5445
                    '/shared_folder/sf_user_'.$userId,
5446
                    $userInfo['complete_name'],
5447
                    1,
5448
                    false,
5449
                    false
5450
                );
5451
            }
5452
5453
            return;
5454
        }
5455
5456
        // Create shared folder session.
5457
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
5458
            create_unexisting_directory(
5459
                $courseInfo,
5460
                api_get_user_id(),
5461
                $sessionId,
5462
                0,
5463
                0,
5464
                $documentDirectory,
5465
                '/shared_folder_session_'.$sessionId,
5466
                get_lang('Folders of users').' ('.api_get_session_name($sessionId).')',
5467
                0,
5468
                false,
5469
                false
5470
            );
5471
        }
5472
        //Create dynamic user shared folder into a shared folder session
5473
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
5474
            create_unexisting_directory(
5475
                $courseInfo,
5476
                $userId,
5477
                $sessionId,
5478
                0,
5479
                0,
5480
                $documentDirectory,
5481
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
5482
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
5483
                1,
5484
                false,
5485
                false
5486
            );
5487
        }
5488
    }
5489
5490
    /**
5491
     * Checks whether the user is into his shared folder or into a subfolder.
5492
     *
5493
     * @param int    $user_id
5494
     * @param string $path
5495
     * @param int    $sessionId
5496
     *
5497
     * @return bool Return true when user is in his user shared folder or into a subfolder
5498
     */
5499
    public static function is_my_shared_folder($user_id, $path, $sessionId)
5500
    {
5501
        $clean_path = Security::remove_XSS($path).'/';
5502
        //for security does not remove the last slash
5503
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
5504
        //for security does not remove the last slash
5505
        $main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
5506
5507
        if (preg_match($main_user_shared_folder, $clean_path)) {
5508
            return true;
5509
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
5510
            return true;
5511
        } else {
5512
            return false;
5513
        }
5514
    }
5515
5516
    public static function isBasicCourseFolder($path, $sessionId)
5517
    {
5518
        $cleanPath = Security::remove_XSS($path);
5519
        $basicCourseFolder = '/basic-course-documents__'.$sessionId.'__0';
5520
5521
        return $cleanPath == $basicCourseFolder;
5522
    }
5523
5524
    /**
5525
     * Check if the file name or folder searched exist.
5526
     *
5527
     * @return bool Return true when exist
5528
     */
5529
    public static function search_keyword($document_name, $keyword)
5530
    {
5531
        if (false !== api_strripos($document_name, $keyword)) {
5532
            return true;
5533
        } else {
5534
            return false;
5535
        }
5536
    }
5537
5538
    /**
5539
     * Checks whether a document can be previewed by using the browser.
5540
     *
5541
     * @param string $file_extension the filename extension of the document (it must be in lower case)
5542
     *
5543
     * @return bool returns TRUE or FALSE
5544
     */
5545
    public static function isBrowserViewable($file_extension)
5546
    {
5547
        static $allowed_extensions = [
5548
            'htm', 'html', 'xhtml',
5549
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
5550
            'pdf', 'svg', 'swf',
5551
            'txt', 'log',
5552
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
5553
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar',
5554
        ];
5555
5556
        /*
5557
          //TODO: make a admin switch to strict mode
5558
          1. global default $allowed_extensions
5559
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
5560
          return true;
5561
          }
5562
          2. check native support
5563
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
5564
         */
5565
5566
        if (!($result = in_array($file_extension, $allowed_extensions))) {
5567
            // Assignment + a logical check.
5568
            return false;
5569
        }
5570
5571
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
5572
        if ("pdf" == $file_extension) {
5573
            return api_browser_support('pdf');
5574
        } elseif ("mp3" == $file_extension) {
5575
            return api_browser_support('mp3');
5576
        } elseif ("mp4" == $file_extension) {
5577
            return api_browser_support('mp4');
5578
        } elseif ("ogg" == $file_extension || "ogx" == $file_extension || "ogv" == $file_extension || "oga" == $file_extension) {
5579
            return api_browser_support('ogg');
5580
        } elseif ("svg" == $file_extension) {
5581
            return api_browser_support('svg');
5582
        } elseif ("mpg" == $file_extension || "mpeg" == $file_extension) {
5583
            return api_browser_support('mpg');
5584
        } elseif ("mov" == $file_extension) {
5585
            return api_browser_support('mov');
5586
        } elseif ("wav" == $file_extension) {
5587
            return api_browser_support('wav');
5588
        } elseif ("mid" == $file_extension || "kar" == $file_extension) {
5589
            return api_browser_support('mid');
5590
        } elseif ("avi" == $file_extension) {
5591
            return api_browser_support('avi');
5592
        } elseif ("wma" == $file_extension) {
5593
            return api_browser_support('wma');
5594
        } elseif ("wmv" == $file_extension) {
5595
            return api_browser_support('wmv');
5596
        } elseif ("tif" == $file_extension || "tiff" == $file_extension) {
5597
            return api_browser_support('tif');
5598
        } elseif ("mov" == $file_extension) {
5599
            return api_browser_support('mov');
5600
        } elseif ("au" == $file_extension) {
5601
            return api_browser_support('au');
5602
        } elseif ("webm" == $file_extension) {
5603
            return api_browser_support('webm');
5604
        }
5605
5606
        return $result;
5607
    }
5608
5609
    /**
5610
     * @param array $courseInfo
5611
     * @param int   $sessionId
5612
     *
5613
     * @return array
5614
     */
5615
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
5616
    {
5617
        $table = Database::get_course_table(TABLE_DOCUMENT);
5618
        $courseId = $courseInfo['real_id'];
5619
        $sessionCondition = api_get_session_condition($sessionId);
5620
        $sql = "SELECT * FROM $table
5621
                WHERE
5622
                  path LIKE '%DELETED%' AND
5623
                  c_id = $courseId
5624
                  $sessionCondition
5625
                ORDER BY path
5626
        ";
5627
5628
        $result = Database::query($sql);
5629
        $files = [];
5630
        while ($document = Database::fetch_array($result, 'ASSOC')) {
5631
            $files[] = $document;
5632
        }
5633
5634
        return $files;
5635
    }
5636
5637
    /**
5638
     * @param int   $id
5639
     * @param array $courseInfo
5640
     * @param int   $sessionId
5641
     *
5642
     * @return array
5643
     */
5644
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
5645
    {
5646
        if (empty($courseInfo)) {
5647
            return false;
5648
        }
5649
5650
        $table = Database::get_course_table(TABLE_DOCUMENT);
5651
        $courseId = $courseInfo['real_id'];
5652
        $sessionCondition = api_get_session_condition($sessionId);
5653
        $sql = "SELECT * FROM $table
5654
                WHERE
5655
                  path LIKE '%DELETED%' AND
5656
                  id = $id AND
5657
                  c_id = $courseId
5658
                  $sessionCondition
5659
                LIMIT 1
5660
        ";
5661
        $result = Database::query($sql);
5662
        if (Database::num_rows($result)) {
5663
            $result = Database::fetch_array($result, 'ASSOC');
5664
5665
            return $result;
5666
        }
5667
5668
        return [];
5669
    }
5670
5671
    /**
5672
     * @param int   $id
5673
     * @param array $courseInfo
5674
     * @param int   $sessionId
5675
     *
5676
     * @return bool
5677
     */
5678
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
5679
    {
5680
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
5681
        if (!empty($document)) {
5682
            $path = $document['path'];
5683
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
5684
            my_delete($coursePath.$path);
5685
            // Hard delete.
5686
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
5687
5688
            return true;
5689
        }
5690
5691
        return false;
5692
    }
5693
5694
    /**
5695
     * @param array $courseInfo
5696
     * @param int   $sessionId
5697
     */
5698
    public static function purgeDocuments($courseInfo, $sessionId)
5699
    {
5700
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
5701
        foreach ($files as $file) {
5702
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
5703
        }
5704
    }
5705
5706
    /**
5707
     * @param int   $id
5708
     * @param array $courseInfo
5709
     * @param int   $sessionId
5710
     */
5711
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
5712
    {
5713
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
5714
        if (!empty($document)) {
5715
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
5716
5717
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
5718
                self::file_send_for_download($coursePath.$document['path']);
5719
                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...
5720
            }
5721
        }
5722
    }
5723
5724
    /**
5725
     * @param array $courseInfo
5726
     * @param int   $sessionId
5727
     *
5728
     * @return bool
5729
     */
5730
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
5731
    {
5732
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
5733
5734
        if (empty($files)) {
5735
            return false;
5736
        }
5737
5738
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
5739
5740
        // Creating a ZIP file.
5741
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
5742
        $zip = new PclZip($tempZipFile);
5743
        foreach ($files as $file) {
5744
            $zip->add(
5745
                $coursePath.$file['path'],
5746
                PCLZIP_OPT_REMOVE_PATH,
5747
                $coursePath
5748
            );
5749
        }
5750
5751
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
5752
            self::file_send_for_download($tempZipFile, true);
5753
            @unlink($tempZipFile);
5754
            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...
5755
        }
5756
    }
5757
5758
    /**
5759
     * Delete documents from a session in a course.
5760
     *
5761
     * @param array $courseInfo
5762
     * @param int   $sessionId
5763
     *
5764
     * @return bool
5765
     */
5766
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
5767
    {
5768
        if (empty($courseInfo)) {
5769
            return false;
5770
        }
5771
5772
        if (empty($sessionId)) {
5773
            return false;
5774
        }
5775
5776
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
5777
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
5778
        $courseId = $courseInfo['real_id'];
5779
5780
        // get invisible folders
5781
        $sql = "SELECT DISTINCT d.id, path
5782
                FROM $documentTable d
5783
                WHERE
5784
                    $conditionSession AND
5785
                    d.c_id = $courseId ";
5786
5787
        $result = Database::query($sql);
5788
        $documents = Database::store_result($result, 'ASSOC');
5789
        if ($documents) {
5790
            foreach ($documents as $document) {
5791
                $documentId = $document['id'];
5792
                self::delete_document(
5793
                    $courseInfo,
5794
                    null,
5795
                    null,
5796
                    $sessionId,
5797
                    $documentId
5798
                );
5799
            }
5800
        }
5801
5802
        /*
5803
        $sql = "DELETE FROM $documentTable
5804
                WHERE c_id = $courseId AND session_id = $sessionId";
5805
        Database::query($sql);
5806
5807
        $sql = "DELETE FROM $itemPropertyTable
5808
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
5809
        Database::query($sql);*/
5810
    }
5811
5812
    /**
5813
     * Update the file or directory path in the document db document table.
5814
     *
5815
     * @author - Hugues Peeters <[email protected]>
5816
     *
5817
     * @param string $action   - action type require : 'delete' or 'update'
5818
     * @param string $old_path - old path info stored to change
5819
     * @param string $new_path - new path info to substitute
5820
     *
5821
     * @desc Update the file or directory path in the document db document table
5822
     */
5823
    public static function updateDbInfo($action, $old_path, $new_path = '')
5824
    {
5825
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
5826
        $course_id = api_get_course_int_id();
5827
        $old_path = Database::escape_string($old_path);
5828
        switch ($action) {
5829
            case 'delete':
5830
                $query = "DELETE FROM $dbTable
5831
                          WHERE
5832
                            c_id = $course_id AND
5833
                            (
5834
                                path LIKE BINARY '".$old_path."' OR
5835
                                path LIKE BINARY '".$old_path."/%'
5836
                            )";
5837
                Database::query($query);
5838
                break;
5839
            case 'update':
5840
                if ('.' == $new_path[0]) {
5841
                    $new_path = substr($new_path, 1);
5842
                }
5843
                $new_path = str_replace('//', '/', $new_path);
5844
5845
                // Attempt to update	- tested & working for root	dir
5846
                $new_path = Database::escape_string($new_path);
5847
                $query = "UPDATE $dbTable SET
5848
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
5849
                          WHERE
5850
                                c_id = $course_id AND
5851
                                (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
5852
                Database::query($query);
5853
                break;
5854
        }
5855
    }
5856
5857
    /**
5858
     * This function calculates the resized width and resized heigt
5859
     * according to the source and target widths
5860
     * and heights, height so that no distortions occur
5861
     * parameters.
5862
     *
5863
     * @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...
5864
     * @param $target_width = how large do you want your resized image
5865
     * @param $target_height = how large do you want your resized image
5866
     * @param $slideshow (default=0) =
5867
     *      indicates weither we are generating images for a slideshow or not,
5868
     *		this overrides the $_SESSION["image_resizing"] a bit so that a thumbnail
5869
     *	    view is also possible when you choose not to resize the source images
5870
     *
5871
     * @return array
5872
     */
5873
    public static function resizeImageSlideShow(
5874
        $image,
5875
        $target_width,
5876
        $target_height,
5877
        $slideshow = 0
5878
    ) {
5879
        // Modifications by Ivan Tcholakov, 04-MAY-2009.
5880
        $result = [];
5881
        $imageResize = Session::read('image_resizing');
5882
        if ('resizing' == $imageResize || 1 == $slideshow) {
5883
            $new_sizes = api_resize_image($image, $target_width, $target_height);
5884
            $result[] = $new_sizes['height'];
5885
            $result[] = $new_sizes['width'];
5886
        } else {
5887
            $size = api_getimagesize($image);
5888
            $result[] = $size['height'];
5889
            $result[] = $size['width'];
5890
        }
5891
5892
        return $result;
5893
    }
5894
5895
    /**
5896
     * Adds a cloud link to the database.
5897
     *
5898
     * @author - Aquilino Blanco Cores <[email protected]>
5899
     *
5900
     * @param array  $_course
5901
     * @param string $path
5902
     * @param string $url
5903
     * @param string $name
5904
     *
5905
     * @return int id of document or 0 if already exists or there was a problem creating it
5906
     */
5907
    public static function addCloudLink($_course, $path, $url, $name)
5908
    {
5909
        $file_path = $path;
5910
        if (!self::cloudLinkExists($_course, $path, $url)) {
5911
            $doc = self::addDocument($_course, $file_path, 'link', 0, $name, $url);
5912
5913
            return $doc->getIid();
5914
        } else {
5915
            return 0;
5916
        }
5917
    }
5918
5919
    /**
5920
     * Deletes a cloud link from the database.
5921
     *
5922
     * @author - Aquilino Blanco Cores <[email protected]>
5923
     *
5924
     * @param array  $courseInfo
5925
     * @param string $documentId
5926
     *
5927
     * @return bool true if success / false if an error occurred
5928
     */
5929
    public static function deleteCloudLink($courseInfo, $documentId)
5930
    {
5931
        if (empty($documentId) || empty($courseInfo)) {
5932
            return false;
5933
        }
5934
5935
        $documentId = (int) $documentId;
5936
        $fileDeletedFromDb = false;
5937
        if (!empty($documentId)) {
5938
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
5939
            // checking
5940
            $table = Database::get_course_table(TABLE_DOCUMENT);
5941
            $courseId = $courseInfo['real_id'];
5942
            echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
5943
            $result = Database::query($sql);
5944
            $exists = Database::num_rows($result) > 0;
5945
            $fileDeletedFromDb = !$exists;
5946
        }
5947
5948
        return $fileDeletedFromDb;
5949
    }
5950
5951
    /**
5952
     * Gets the id of a cloud link with a given path.
5953
     *
5954
     * @author - Aquilino Blanco Cores <[email protected]>
5955
     *
5956
     * @param array  $courseInfo
5957
     * @param string $path
5958
     * @param string $url
5959
     *
5960
     * @return int link's id / false if no link found
5961
     */
5962
    public static function getCloudLinkId($courseInfo, $path, $url)
5963
    {
5964
        $table = Database::get_course_table(TABLE_DOCUMENT);
5965
5966
        if (empty($courseInfo)) {
5967
            return false;
5968
        }
5969
5970
        $courseId = (int) $courseInfo['real_id'];
5971
        $path = Database::escape_string($path);
5972
5973
        if ('/' != substr($path, -1)) {
5974
            // Add final slash to path if not present
5975
            $path .= '/';
5976
        }
5977
5978
        if (!empty($courseId) && !empty($path)) {
5979
            $sql = "SELECT id FROM $table
5980
                    WHERE
5981
                        c_id = $courseId AND
5982
                        path LIKE BINARY '$path' AND
5983
                        comment = '$url' AND
5984
                        filetype = 'link'
5985
                    LIMIT 1";
5986
            $result = Database::query($sql);
5987
            if ($result && Database::num_rows($result)) {
5988
                $row = Database::fetch_array($result);
5989
5990
                return (int) $row[0];
5991
            }
5992
        }
5993
5994
        return false;
5995
    }
5996
5997
    /**
5998
     * Checks if a cloud link exists.
5999
     *
6000
     * @author - Aquilino Blanco Cores <[email protected]>
6001
     *
6002
     * @param array  $courseInfo
6003
     * @param string $path
6004
     * @param string $url
6005
     *
6006
     * @return bool true if it exists false in other case
6007
     */
6008
    public static function cloudLinkExists($courseInfo, $path, $url)
6009
    {
6010
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6011
6012
        return $exists;
6013
    }
6014
6015
    /**
6016
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications.
6017
     *
6018
     * @author Aquilino Blanco Cores <[email protected]>
6019
     *
6020
     * @return string the well formed URLs regular expressions string
6021
     */
6022
    public static function getWellFormedUrlRegex()
6023
    {
6024
        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';
6025
    }
6026
6027
    /**
6028
     * Gets the files hosting sites' whitelist.
6029
     *
6030
     * @author Aquilino Blanco Cores <[email protected]>
6031
     *
6032
     * @return array the sites list
6033
     */
6034
    public static function getFileHostingWhiteList()
6035
    {
6036
        return [
6037
            'asuswebstorage.com',
6038
            'dropbox.com',
6039
            'dropboxusercontent.com',
6040
            'fileserve.com',
6041
            'drive.google.com',
6042
            'docs.google.com',
6043
            'icloud.com',
6044
            'mediafire.com',
6045
            'mega.nz',
6046
            'onedrive.live.com',
6047
            'slideshare.net',
6048
            'scribd.com',
6049
            'wetransfer.com',
6050
            'box.com',
6051
            'livefilestore.com', // OneDrive
6052
        ];
6053
    }
6054
6055
    /**
6056
     * @param int $userId
6057
     *
6058
     * @return array Example [ 0 => ['code' => 'ABC', 'directory' => 'ABC0', 'path' => '/images/gallery/test.png', 'code_path' => 'ABC:/images/gallery/test.png'], 1 => ...]
6059
     */
6060
    public static function getAllDocumentsCreatedByUser($userId)
6061
    {
6062
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6063
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
6064
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
6065
        $userId = (int) $userId;
6066
6067
        $sql = "SELECT DISTINCT c.code, c.directory, docs.path
6068
                FROM $tblItemProperty AS last
6069
                INNER JOIN $tblDocument AS docs
6070
                ON (
6071
                    docs.id = last.ref AND
6072
                    docs.c_id = last.c_id AND
6073
                    docs.filetype <> 'folder'
6074
                )
6075
                INNER JOIN $tblCourse as c
6076
                ON (
6077
                    docs.c_id = c.id
6078
                )
6079
                WHERE
6080
                    last.tool = '".TOOL_DOCUMENT."' AND
6081
                    last.insert_user_id = $userId AND
6082
                    docs.path NOT LIKE '%_DELETED_%'
6083
                ORDER BY c.directory, docs.path
6084
                ";
6085
        $result = Database::query($sql);
6086
6087
        $list = [];
6088
        if (0 != Database::num_rows($result)) {
6089
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6090
                $row['code_path'] = $row['code'].':'.$row['path'];
6091
                $list[] = $row;
6092
            }
6093
        }
6094
6095
        return $list;
6096
    }
6097
6098
    /**
6099
     * @param string              $realPath
6100
     * @param string|UploadedFile $content
6101
     * @param int                 $visibility
6102
     * @param CGroup              $group
6103
     *
6104
     * @return CDocument
6105
     */
6106
    public static function addFileToDocument(CDocument $document, $realPath, $content, $visibility, $group)
6107
    {
6108
        $fileType = $document->getFiletype();
6109
        $resourceNode = $document->getResourceNode();
6110
6111
        if (!$resourceNode) {
6112
            return false;
6113
        }
6114
6115
        $em = Database::getManager();
6116
        $title = $document->getTitle();
6117
6118
        // Only create a ResourceFile if there's a file involved
6119
        if ('file' === $fileType) {
6120
            $resourceFile = $resourceNode->getResourceFile();
6121
            if (empty($resourceFile)) {
6122
                $resourceFile = new ResourceFile();
6123
            }
6124
6125
            if ($content instanceof UploadedFile) {
6126
                $resourceFile->setFile($content);
6127
                error_log('UploadedFile');
6128
            } else {
6129
                // $path points to a file in the directory
6130
                if (file_exists($realPath) && !is_dir($realPath)) {
6131
                    error_log('file_exists');
6132
                    $file = new UploadedFile($realPath, $title, null, null, true);
6133
                    $resourceFile->setFile($file);
6134
                } else {
6135
                    // We get the content and create a file
6136
                    error_log('From content');
6137
                    $handle = tmpfile();
6138
                    fwrite($handle, $content);
6139
                    $meta = stream_get_meta_data($handle);
6140
                    $file = new UploadedFile($meta['uri'], $title, null, null, true);
6141
                    $resourceFile->setFile($file);
6142
                }
6143
            }
6144
6145
            $resourceFile->setName($title);
6146
            $em->persist($resourceFile);
6147
            $resourceNode->setResourceFile($resourceFile);
6148
            $em->persist($resourceNode);
6149
        }
6150
        $em->persist($document);
6151
        $em->flush();
6152
6153
        $documentId = $document->getIid();
6154
6155
        if ($documentId) {
6156
            $table = Database::get_course_table(TABLE_DOCUMENT);
6157
            $sql = "UPDATE $table SET id = iid WHERE iid = $documentId";
6158
            Database::query($sql);
6159
6160
            return $document;
6161
        }
6162
6163
        return false;
6164
    }
6165
6166
    /**
6167
     * Adds a new document to the database.
6168
     *
6169
     * @param array  $courseInfo
6170
     * @param string $path
6171
     * @param string $fileType
6172
     * @param int    $fileSize
6173
     * @param string $title
6174
     * @param string $comment
6175
     * @param int    $readonly
6176
     * @param int    $visibility       see ResourceLink constants
6177
     * @param int    $groupId          group.id
6178
     * @param int    $sessionId        Session ID, if any
6179
     * @param int    $userId           creator user id
6180
     * @param bool   $sendNotification
6181
     * @param string $content
6182
     * @param int    $parentId
6183
     * @param string $realPath
6184
     *
6185
     * @return CDocument|false
6186
     */
6187
    public static function addDocument(
6188
        $courseInfo,
6189
        $path,
6190
        $fileType,
6191
        $fileSize,
6192
        $title,
6193
        $comment = null,
6194
        $readonly = 0,
6195
        $visibility = null,
6196
        $groupId = 0,
6197
        $sessionId = 0,
6198
        $userId = 0,
6199
        $sendNotification = true,
6200
        $content = '',
6201
        $parentId = 0,
6202
        $realPath = ''
6203
    ) {
6204
        $userId = empty($userId) ? api_get_user_id() : $userId;
6205
        if (empty($userId)) {
6206
            return false;
6207
        }
6208
6209
        $userEntity = api_get_user_entity($userId);
6210
        if (empty($userEntity)) {
6211
            return false;
6212
        }
6213
6214
        $courseEntity = api_get_course_entity($courseInfo['real_id']);
6215
        if (empty($courseEntity)) {
6216
            return false;
6217
        }
6218
6219
        $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
6220
        $session = api_get_session_entity($sessionId);
6221
        $group = api_get_group_entity($groupId);
6222
        $readonly = (int) $readonly;
6223
        $fileSize = (int) $fileSize;
6224
        $documentRepo = Container::getDocumentRepository();
6225
6226
        $parentNode = $courseEntity;
6227
        if (!empty($parentId)) {
6228
            $parent = $documentRepo->find($parentId);
6229
            if ($parent) {
6230
                $parentNode = $parent;
6231
            }
6232
        }
6233
6234
        $criteria = ['path' => $path, 'course' => $courseEntity];
6235
        $document = $documentRepo->findOneBy($criteria);
6236
6237
        // Document already exists
6238
        if ($document) {
6239
            return false;
6240
        }
6241
6242
        // is updated using the title
6243
        $document = new CDocument();
6244
        $document
6245
            ->setCourse($courseEntity)
6246
            ->setFiletype($fileType)
6247
            ->setSize($fileSize)
6248
            ->setTitle($title)
6249
            ->setPath($path)
6250
            ->setComment($comment)
6251
            ->setReadonly($readonly)
6252
            ->setSession($session)
6253
            ->setParent($courseEntity)
6254
            ->addCourseLink($courseEntity, $session, $group)
6255
        ;
6256
6257
        $em = $documentRepo->getEntityManager();
6258
        $em->persist($document);
6259
6260
        $document = self::addFileToDocument($document, $realPath, $content, $visibility, $group);
6261
6262
        if ($document) {
6263
            $allowNotification = api_get_configuration_value('send_notification_when_document_added');
6264
            if ($sendNotification && $allowNotification) {
6265
                $courseTitle = $courseInfo['title'];
6266
                if (!empty($sessionId)) {
6267
                    $sessionInfo = api_get_session_info($sessionId);
6268
                    $courseTitle .= ' ( '.$sessionInfo['name'].') ';
6269
                }
6270
6271
                $url = api_get_path(WEB_CODE_PATH).
6272
                    'document/showinframes.php?cidReq='.$courseInfo['code'].'&id_session='.$sessionId.'&id='.$document->getIid();
6273
                $link = Display::url(basename($title), $url, ['target' => '_blank']);
6274
                $userInfo = api_get_user_info($userId);
6275
6276
                $message = sprintf(
6277
                    get_lang('A new document %s has been added to the document tool in your course %s by %s.'),
6278
                    $link,
6279
                    $courseTitle,
6280
                    $userInfo['complete_name']
6281
                );
6282
                $subject = sprintf(get_lang('New document added to course %s'), $courseTitle);
6283
                MessageManager::sendMessageToAllUsersInCourse($subject, $message, $courseInfo, $sessionId);
6284
            }
6285
6286
            return $document;
6287
        }
6288
6289
        return false;
6290
    }
6291
6292
    /**
6293
     * @param array  $documentAndFolders
6294
     * @param array  $courseInfo
6295
     * @param bool   $is_certificate_mode
6296
     * @param array  $groupMemberWithUploadRights
6297
     * @param string $path
6298
     * @param bool   $addToEditor
6299
     * @param string $editorUrl
6300
     *
6301
     * @return array
6302
     */
6303
    public static function processDocumentAndFolders(
6304
        $documentAndFolders,
6305
        $courseInfo,
6306
        $is_certificate_mode,
6307
        $groupMemberWithUploadRights,
6308
        $path,
6309
        $addToEditor = false,
6310
        $editorUrl = ''
6311
    ) {
6312
        if (empty($documentAndFolders) || empty($courseInfo)) {
6313
            return [];
6314
        }
6315
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
6316
        $userId = api_get_user_id();
6317
        $currentUserInfo = api_get_user_info();
6318
        $sessionId = api_get_session_id();
6319
        $groupId = api_get_group_id();
6320
        $userIsSubscribed = CourseManager::is_user_subscribed_in_course($userId, $courseInfo['code']);
6321
6322
        $url = api_get_path(WEB_COURSE_PATH).$courseInfo['directory'].'/document';
6323
        if ($addToEditor) {
6324
            $url = api_get_path(REL_COURSE_PATH).$courseInfo['directory'].'/document';
6325
        }
6326
6327
        $courseId = $courseInfo['real_id'];
6328
        $group_properties = GroupManager::get_group_properties($groupId);
6329
6330
        $sortable_data = [];
6331
        foreach ($documentAndFolders as $key => $document_data) {
6332
            $row = [];
6333
            $row['id'] = $document_data['id'];
6334
            $row['type'] = $document_data['filetype'];
6335
6336
            // If the item is invisible, wrap it in a span with class invisible.
6337
            $is_visible = self::is_visible_by_id(
6338
                $document_data['id'],
6339
                $courseInfo,
6340
                $sessionId,
6341
                $userId,
6342
                false,
6343
                $userIsSubscribed
6344
            );
6345
            $invisibility_span_open = 0 == $is_visible ? '<span class="muted">' : '';
6346
            $invisibility_span_close = 0 == $is_visible ? '</span>' : '';
6347
            $size = 1;
6348
            // Get the title or the basename depending on what we're using
6349
            if ('' != $document_data['title']) {
6350
                $document_name = $document_data['title'];
6351
            } else {
6352
                $document_name = basename($document_data['path']);
6353
            }
6354
            $row['name'] = $document_name;
6355
            // Data for checkbox
6356
            if (($isAllowedToEdit || $groupMemberWithUploadRights) && count($documentAndFolders) > 1) {
6357
                $row[] = $document_data['id'];
6358
            }
6359
6360
            if (self::is_folder_to_avoid($document_data['path'], $is_certificate_mode)) {
6361
                continue;
6362
            }
6363
6364
            // Show the owner of the file only in groups
6365
            $user_link = '';
6366
            if (!empty($groupId)) {
6367
                if (!empty($document_data['insert_user_id'])) {
6368
                    $userInfo = api_get_user_info(
6369
                        $document_data['insert_user_id'],
6370
                        false,
6371
                        false,
6372
                        false,
6373
                        false,
6374
                        false
6375
                    );
6376
                    $user_link = '<div class="document_owner">'
6377
                        .get_lang('Owner').': '.UserManager::getUserProfileLink($userInfo)
6378
                        .'</div>';
6379
                }
6380
            }
6381
6382
            // Hack in order to avoid the download icon appearing on cloud links
6383
            if ('link' == $document_data['filetype']) {
6384
                $size = 0;
6385
            }
6386
6387
            // Icons (clickable)
6388
            $row[] = self::create_document_link(
6389
                $url,
6390
                $document_data,
6391
                true,
6392
                $is_visible,
6393
                $size,
6394
                $isAllowedToEdit,
6395
                $is_certificate_mode,
6396
                $addToEditor,
6397
                $editorUrl
6398
            );
6399
6400
            // Validation when belongs to a session
6401
            $session_img = api_get_session_image($document_data['session_id'], $currentUserInfo['status']);
6402
6403
            $link = self::create_document_link(
6404
                $url,
6405
                $document_data,
6406
                false,
6407
                $is_visible,
6408
                $size,
6409
                $isAllowedToEdit,
6410
                $is_certificate_mode,
6411
                $addToEditor,
6412
                $editorUrl
6413
            );
6414
6415
            // Document title with link
6416
            $row[] = $link.$session_img.'<br />'.$invisibility_span_open.'<i>'
6417
                .nl2br(htmlspecialchars($document_data['comment'], ENT_QUOTES, 'utf-8'))
6418
                .'</i>'.$invisibility_span_close.$user_link;
6419
6420
            if ('folder' == $document_data['filetype']) {
6421
                $displaySize = '<span id="document_size_'.$document_data['id']
6422
                    .'" data-path= "'.$document_data['path']
6423
                    .'" class="document_size"></span>';
6424
            } else {
6425
                $displaySize = format_file_size($document_data['size']);
6426
            }
6427
6428
            $row[] = '<span style="display:none;">'.$size.'</span>'.
6429
                $invisibility_span_open.
6430
                $displaySize.
6431
                $invisibility_span_close;
6432
6433
            // Last edit date
6434
            $last_edit_date = api_get_local_time($document_data['updated_at']);
6435
            $display_date = date_to_str_ago($document_data['updated_at']).
6436
                ' <div class="muted"><small>'.$last_edit_date."</small></div>";
6437
6438
            $row[] = $invisibility_span_open.$display_date.$invisibility_span_close;
6439
6440
            $groupMemberWithEditRightsCheckDocument = GroupManager::allowUploadEditDocument(
6441
                $userId,
6442
                $courseId,
6443
                $group_properties,
6444
                $document_data
6445
            );
6446
6447
            // Admins get an edit column
6448
            if ($isAllowedToEdit ||
6449
                $groupMemberWithEditRightsCheckDocument ||
6450
                self::is_my_shared_folder(api_get_user_id(), $path, $sessionId)
6451
            ) {
6452
                $is_template = isset($document_data['is_template']) ? $document_data['is_template'] : false;
6453
6454
                // If readonly, check if it the owner of the file or if the user is an admin
6455
                if ($document_data['creator_id'] == api_get_user_id() || api_is_platform_admin()) {
6456
                    $edit_icons = self::build_edit_icons(
6457
                        $document_data,
6458
                        $key,
6459
                        $is_template,
6460
                        $is_visible
6461
                    );
6462
                } else {
6463
                    $edit_icons = self::build_edit_icons(
6464
                        $document_data,
6465
                        $key,
6466
                        $is_template,
6467
                        $is_visible
6468
                    );
6469
                }
6470
                $row[] = $edit_icons;
6471
            } else {
6472
                $row[] = '';
6473
            }
6474
            $row[] = $last_edit_date;
6475
            $row[] = $size;
6476
            $row[] = $document_name;
6477
6478
            if ((isset($_GET['keyword']) && self::search_keyword($document_name, $_GET['keyword'])) ||
6479
                !isset($_GET['keyword']) ||
6480
                empty($_GET['keyword'])
6481
            ) {
6482
                $sortable_data[] = $row;
6483
            }
6484
        }
6485
6486
        return $sortable_data;
6487
    }
6488
6489
    /**
6490
     * Parse file information into a link.
6491
     *
6492
     * @param array  $userInfo        Current user info
6493
     * @param array  $course_info
6494
     * @param int    $session_id
6495
     * @param array  $resource
6496
     * @param int    $lp_id
6497
     * @param bool   $add_move_button
6498
     * @param string $target
6499
     * @param string $overwrite_url
6500
     *
6501
     * @return string|null
6502
     */
6503
    private static function parseFile(
6504
        $userInfo,
6505
        $course_info,
6506
        $session_id,
6507
        $resource,
6508
        $lp_id,
6509
        $add_move_button,
6510
        $target,
6511
        $overwrite_url
6512
    ) {
6513
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
6514
        $web_code_path = api_get_path(WEB_CODE_PATH);
6515
6516
        $documentId = $resource['id'];
6517
        $path = $resource['path'];
6518
6519
        if (empty($path)) {
6520
            $num = 0;
6521
        } else {
6522
            $num = substr_count($path, '/') - 1;
6523
        }
6524
6525
        // It's a file.
6526
        $icon = choose_image($path);
6527
        $position = strrpos($icon, '.');
6528
        $icon = substr($icon, 0, $position).'_small.gif';
6529
        $my_file_title = $resource['title'];
6530
        $visibility = $resource['visibility'];
6531
6532
        // If title is empty we try to use the path
6533
        if (empty($my_file_title)) {
6534
            $my_file_title = basename($path);
6535
        }
6536
6537
        // Show the "image name" not the filename of the image.
6538
        if ($lp_id) {
6539
            // LP URL
6540
            $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;
6541
        } else {
6542
            // Direct document URL
6543
            $url = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
6544
        }
6545
6546
        if (!empty($overwrite_url)) {
6547
            $overwrite_url = Security::remove_XSS($overwrite_url);
6548
            $url = $overwrite_url.'&cidReq='.$course_info['code'].'&id_session='.$session_id.'&document_id='.$documentId;
6549
        }
6550
6551
        $img = Display::returnIconPath($icon);
6552
        if (!file_exists($img_sys_path.$icon)) {
6553
            $img = Display::returnIconPath('default_small.png');
6554
        }
6555
6556
        $link = Display::url(
6557
            '<img alt="" src="'.$img.'" title="" />&nbsp;'.$my_file_title,
6558
            $url,
6559
            ['target' => $target, 'class' => 'moved']
6560
        );
6561
6562
        $directUrl = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
6563
        $link .= '&nbsp;'.Display::url(
6564
            Display::return_icon('preview_view.png', get_lang('Preview')),
6565
            $directUrl,
6566
            ['target' => '_blank']
6567
        );
6568
6569
        $visibilityClass = null;
6570
        if (0 == $visibility) {
6571
            $visibilityClass = ' text-muted ';
6572
        }
6573
        $return = null;
6574
6575
        if (false == $lp_id) {
6576
            $return .= '<li class="doc_resource '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6577
        } else {
6578
            $return .= '<li class="doc_resource lp_resource_element '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6579
        }
6580
6581
        $return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
6582
        if ($add_move_button) {
6583
            $return .= '<a class="moved" href="#">';
6584
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
6585
            $return .= '</a> ';
6586
        }
6587
        $return .= $link;
6588
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
6589
        $return .= $sessionStar;
6590
6591
        $return .= '</div></li>';
6592
6593
        return $return;
6594
    }
6595
6596
    /**
6597
     * @param int   $folderId
6598
     * @param array $resource
6599
     * @param int   $lp_id
6600
     *
6601
     * @return string|null
6602
     */
6603
    private static function parseFolder($folderId, $resource, $lp_id)
6604
    {
6605
        $title = isset($resource['title']) ? $resource['title'] : null;
6606
        $path = isset($resource['path']) ? $resource['path'] : null;
6607
6608
        if (empty($path)) {
6609
            $num = 0;
6610
        } else {
6611
            $num = substr_count($path, '/');
6612
        }
6613
6614
        // It's a folder.
6615
        //hide some folders
6616
        if (in_array(
6617
            $path,
6618
            ['shared_folder', 'chat_files', 'HotPotatoes_files', 'css', 'certificates']
6619
        )) {
6620
            return null;
6621
        } elseif (preg_match('/_groupdocs/', $path)) {
6622
            return null;
6623
        } elseif (preg_match('/sf_user_/', $path)) {
6624
            return null;
6625
        } elseif (preg_match('/shared_folder_session_/', $path)) {
6626
            return null;
6627
        }
6628
6629
        //$onclick = '';
6630
        // if in LP, hidden folder are displayed in grey
6631
        $folder_class_hidden = '';
6632
        if ($lp_id) {
6633
            if (isset($resource['visible']) && 0 == $resource['visible']) {
6634
                $folder_class_hidden = ' doc_folder_hidden'; // in base.css
6635
            }
6636
        }
6637
        $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
6638
        $return = null;
6639
6640
        if (empty($path)) {
6641
            $return = '<ul class="lp_resource">';
6642
        }
6643
6644
        $return .= '<li class="doc_folder '.$folder_class_hidden.'" id="doc_id_'.$resource['id'].'"  style="margin-left:'.($num * 18).'px; ">';
6645
6646
        $image = Display::returnIconPath('nolines_plus.gif');
6647
        if (empty($path)) {
6648
            $image = Display::returnIconPath('nolines_minus.gif');
6649
        }
6650
        $return .= '<img style="cursor: pointer;" src="'.$image.'" align="absmiddle" id="img_'.$resource['id'].'" '.$onclick.'>';
6651
        $return .= Display::return_icon('lp_folder.png').'&nbsp;';
6652
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.$title.'</span>';
6653
        $return .= '</li>';
6654
6655
        if (empty($path)) {
6656
            if (false == $folderId) {
6657
                $return .= '<div id="res_'.$resource['id'].'" >';
6658
            } else {
6659
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
6660
            }
6661
        }
6662
6663
        return $return;
6664
    }
6665
6666
    /**
6667
     * Get the button to edit document.
6668
     *
6669
     * @param bool   $isReadOnly
6670
     * @param string $extension
6671
     * @param bool   $isCertificateMode
6672
     *
6673
     * @return string
6674
     */
6675
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
6676
    {
6677
        $extension = strtolower($extension);
6678
        $iconEn = Display::return_icon('edit.png', get_lang('Edit'));
6679
        $iconDis = Display::return_icon('edit_na.png', get_lang('Edit'));
6680
        $courseParams = api_get_cidreq();
6681
        $webOdfExtensionList = self::get_web_odf_extension_list();
6682
        $path = $documentData['path'];
6683
        $documentId = $documentData['id'];
6684
6685
        if ($isReadOnly) {
6686
            if (!api_is_course_admin() && !api_is_platform_admin()) {
6687
                return $iconDis;
6688
            }
6689
6690
            if (
6691
                'svg' == $extension && api_browser_support('svg') &&
6692
                'true' == api_get_setting('enabled_support_svg')
6693
            ) {
6694
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$documentId");
6695
            }
6696
6697
            if (
6698
                in_array($extension, $webOdfExtensionList) &&
6699
                true === api_get_configuration_value('enabled_support_odf')
6700
            ) {
6701
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$documentId");
6702
            }
6703
6704
            if (
6705
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
6706
                    'true' == api_get_setting('enabled_support_pixlr')
6707
            ) {
6708
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$documentId");
6709
            }
6710
6711
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId");
6712
        }
6713
6714
        if (in_array($path, self::get_system_folders())) {
6715
            return $iconDis;
6716
        }
6717
6718
        if ($isCertificateMode) {
6719
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId&curdirpath=/certificates");
6720
        }
6721
6722
        $sessionId = api_get_session_id();
6723
6724
        if ($sessionId && $documentData['session_id'] != $sessionId) {
6725
            return $iconDis;
6726
        }
6727
6728
        if (
6729
            'svg' == $extension && api_browser_support('svg') &&
6730
            'true' == api_get_setting('enabled_support_svg')
6731
        ) {
6732
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$documentId");
6733
        }
6734
6735
        if (
6736
            in_array($extension, $webOdfExtensionList) &&
6737
            true === api_get_configuration_value('enabled_support_odf')
6738
        ) {
6739
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$documentId");
6740
        }
6741
6742
        if (
6743
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'pxd']) &&
6744
                'true' == api_get_setting('enabled_support_pixlr')
6745
        ) {
6746
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$documentId");
6747
        }
6748
6749
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$documentId");
6750
    }
6751
6752
    /**
6753
     * Get the button to move document.
6754
     *
6755
     * @param bool $isReadOnly
6756
     * @param bool $isCertificateMode
6757
     * @param int  $parentId
6758
     *
6759
     * @return string
6760
     */
6761
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
6762
    {
6763
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
6764
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
6765
6766
        if ($isReadOnly) {
6767
            return $iconDis;
6768
        }
6769
6770
        $path = $documentData['path'];
6771
        $document_id = $documentData['id'];
6772
        $sessionId = api_get_session_id();
6773
        $courseParams = api_get_cidreq();
6774
6775
        if ($isCertificateMode || in_array($path, self::get_system_folders())) {
6776
            return $iconDis;
6777
        }
6778
6779
        if ($sessionId) {
6780
            if ($documentData['session_id'] != $sessionId) {
6781
                return $iconDis;
6782
            }
6783
        }
6784
6785
        $urlMoveParams = http_build_query(['id' => $parentId, 'move' => $document_id]);
6786
6787
        return Display::url(
6788
            $iconEn,
6789
            api_get_self()."?$courseParams&$urlMoveParams"
6790
        );
6791
    }
6792
6793
    /**
6794
     * Get the button to set visibility to document.
6795
     *
6796
     * @param bool $isReadOnly
6797
     * @param int  $visibility
6798
     * @param bool $isCertificateMode
6799
     * @param int  $parentId
6800
     *
6801
     * @return string|null
6802
     */
6803
    private static function getButtonVisibility(
6804
        $isReadOnly,
6805
        $visibility,
6806
        array $documentData,
6807
        $isCertificateMode,
6808
        $parentId
6809
    ) {
6810
        $visibility_icon = 0 == $visibility ? 'invisible' : 'visible';
6811
        $visibility_command = 0 == $visibility ? 'set_visible' : 'set_invisible';
6812
        $courseParams = api_get_cidreq();
6813
6814
        if ($isReadOnly) {
6815
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6816
                return Display::return_icon($visibility_icon.'.png', get_lang('The visibility cannot be changed'));
6817
            }
6818
6819
            return null;
6820
        }
6821
6822
        if ($isCertificateMode) {
6823
            return Display::return_icon($visibility_icon.'.png', get_lang('The visibility cannot be changed'));
6824
        }
6825
6826
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6827
            $tip_visibility = 'invisible' == $visibility_icon ? get_lang('Show') : get_lang('Hide');
6828
6829
            return Display::url(
6830
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
6831
                api_get_self()."?$courseParams&id=$parentId&action=$visibility_command&document_id={$documentData['id']}"
6832
            );
6833
        }
6834
6835
        return null;
6836
    }
6837
6838
    /**
6839
     * GEt the button to delete a document.
6840
     *
6841
     * @param bool   $isReadOnly
6842
     * @param bool   $isCertificateMode
6843
     * @param string $curDirPath
6844
     * @param int    $parentId
6845
     *
6846
     * @return string
6847
     */
6848
    private static function getButtonDelete(
6849
        $isReadOnly,
6850
        array $documentData,
6851
        $isCertificateMode,
6852
        $curDirPath,
6853
        $parentId
6854
    ) {
6855
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
6856
        $iconDis = Display::return_icon('delete_na.png', get_lang('This folder cannot be deleted'));
6857
        $path = $documentData['path'];
6858
        $id = $documentData['id'];
6859
        $courseParams = api_get_cidreq();
6860
6861
        if ($isReadOnly) {
6862
            return $iconDis;
6863
        }
6864
6865
        if (in_array($path, self::get_system_folders())) {
6866
            return $iconDis;
6867
        }
6868
6869
        $titleToShow = addslashes(basename($documentData['title']));
6870
        $urlDeleteParams = http_build_query([
6871
            'curdirpath' => $curDirPath,
6872
            'action' => 'delete_item',
6873
            'id' => $parentId,
6874
            'deleteid' => $documentData['id'],
6875
        ]);
6876
6877
        $btn = Display::url(
6878
            $iconEn,
6879
            api_get_self()."?$courseParams&$urlDeleteParams",
6880
            [
6881
                'title' => get_lang('Do you want to delete the file?').': '.$titleToShow,
6882
                'class' => 'delete-swal',
6883
            ]
6884
        );
6885
6886
        if (isset($_GET['curdirpath']) &&
6887
            '/certificates' === $_GET['curdirpath'] &&
6888
            self::get_default_certificate_id(api_get_course_int_id()) == $id
6889
        ) {
6890
            return $btn;
6891
        }
6892
6893
        if ($isCertificateMode) {
6894
            return $btn;
6895
        }
6896
6897
        $sessionId = api_get_session_id();
6898
6899
        if ($sessionId) {
6900
            if ($documentData['session_id'] != $sessionId) {
6901
                return $iconDis;
6902
            }
6903
        }
6904
6905
        return $btn;
6906
    }
6907
}
6908