Completed
Push — master ( 0a6a4f...5b557d )
by Julito
12:14
created

DocumentManager::check_readonly()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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