Completed
Push — master ( 172eb0...3032f7 )
by Julito
09:41
created

DocumentManager::processDocumentAndFolders()   F

Complexity

Conditions 26
Paths 3858

Size

Total Lines 188
Code Lines 126

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 126
nc 3858
nop 6
dl 0
loc 188
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Resource\ResourceFile;
5
use Chamilo\CoreBundle\Entity\Resource\ResourceLink;
6
use Chamilo\CoreBundle\Entity\Resource\ResourceRight;
7
use Chamilo\CoreBundle\Framework\Container;
8
use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter;
9
use Chamilo\CourseBundle\Entity\CDocument;
10
use Chamilo\CourseBundle\Entity\CGroupInfo;
11
use Chamilo\UserBundle\Entity\User;
12
use ChamiloSession as Session;
13
use Symfony\Component\HttpFoundation\File\UploadedFile;
14
15
/**
16
 *  Class DocumentManager
17
 *  This is the document library for Chamilo.
18
 *  It is / will be used to provide a service layer to all document-using tools.
19
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
20
 *  Include/require it in your code to use its functionality.
21
 *
22
 * @package chamilo.library
23
 */
24
class DocumentManager
25
{
26
    /**
27
     * Construct.
28
     */
29
    private function __construct()
30
    {
31
    }
32
33
    /**
34
     * @param string $course_code
35
     *
36
     * @return int the document folder quota for the current course in bytes
37
     *             or the default quota
38
     */
39
    public static function get_course_quota($course_code = null)
40
    {
41
        if (empty($course_code)) {
42
            $course_info = api_get_course_info();
43
        } else {
44
            $course_info = api_get_course_info($course_code);
45
        }
46
47
        $course_quota = null;
48
        if (empty($course_info)) {
49
            return DEFAULT_DOCUMENT_QUOTA;
50
        } else {
51
            $course_quota = $course_info['disk_quota'];
52
        }
53
        if (is_null($course_quota) || empty($course_quota)) {
54
            // Course table entry for quota was null, then use default value
55
            $course_quota = DEFAULT_DOCUMENT_QUOTA;
56
        }
57
58
        return $course_quota;
59
    }
60
61
    /**
62
     * Get the content type of a file by checking the extension
63
     * We could use mime_content_type() with php-versions > 4.3,
64
     * but this doesn't work as it should on Windows installations.
65
     *
66
     * @param string $filename or boolean TRUE to return complete array
67
     *
68
     * @author ? first version
69
     * @author Bert Vanderkimpen
70
     *
71
     * @return string
72
     */
73
    public static function file_get_mime_type($filename)
74
    {
75
        // All MIME types in an array (from 1.6, this is the authorative source)
76
        // Please, keep this alphabetical if you add something to this list!
77
        $mimeTypes = [
78
            'ai' => 'application/postscript',
79
            'aif' => 'audio/x-aiff',
80
            'aifc' => 'audio/x-aiff',
81
            'aiff' => 'audio/x-aiff',
82
            'asf' => 'video/x-ms-asf',
83
            'asc' => 'text/plain',
84
            'au' => 'audio/basic',
85
            'avi' => 'video/x-msvideo',
86
            'bcpio' => 'application/x-bcpio',
87
            'bin' => 'application/octet-stream',
88
            'bmp' => 'image/bmp',
89
            'cdf' => 'application/x-netcdf',
90
            'class' => 'application/octet-stream',
91
            'cpio' => 'application/x-cpio',
92
            'cpt' => 'application/mac-compactpro',
93
            'csh' => 'application/x-csh',
94
            'css' => 'text/css',
95
            'dcr' => 'application/x-director',
96
            'dir' => 'application/x-director',
97
            'djv' => 'image/vnd.djvu',
98
            'djvu' => 'image/vnd.djvu',
99
            'dll' => 'application/octet-stream',
100
            'dmg' => 'application/x-diskcopy',
101
            'dms' => 'application/octet-stream',
102
            'doc' => 'application/msword',
103
            'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
104
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
105
            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
106
            'dvi' => 'application/x-dvi',
107
            'dwg' => 'application/vnd.dwg',
108
            'dwf' => 'application/vnd.dwf',
109
            'dxf' => 'application/vnd.dxf',
110
            'dxr' => 'application/x-director',
111
            'eps' => 'application/postscript',
112
            'epub' => 'application/epub+zip',
113
            'etx' => 'text/x-setext',
114
            'exe' => 'application/octet-stream',
115
            'ez' => 'application/andrew-inset',
116
            'flv' => 'video/flv',
117
            'gif' => 'image/gif',
118
            'gtar' => 'application/x-gtar',
119
            'gz' => 'application/x-gzip',
120
            'hdf' => 'application/x-hdf',
121
            'hqx' => 'application/mac-binhex40',
122
            'htm' => 'text/html',
123
            'html' => 'text/html',
124
            'ice' => 'x-conference-xcooltalk',
125
            'ief' => 'image/ief',
126
            'iges' => 'model/iges',
127
            'igs' => 'model/iges',
128
            'jar' => 'application/java-archiver',
129
            'jpe' => 'image/jpeg',
130
            'jpeg' => 'image/jpeg',
131
            'jpg' => 'image/jpeg',
132
            'js' => 'application/x-javascript',
133
            'kar' => 'audio/midi',
134
            'lam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
135
            'latex' => 'application/x-latex',
136
            'lha' => 'application/octet-stream',
137
            'log' => 'text/plain',
138
            'lzh' => 'application/octet-stream',
139
            'm1a' => 'audio/mpeg',
140
            'm2a' => 'audio/mpeg',
141
            'm3u' => 'audio/x-mpegurl',
142
            'man' => 'application/x-troff-man',
143
            'me' => 'application/x-troff-me',
144
            'mesh' => 'model/mesh',
145
            'mid' => 'audio/midi',
146
            'midi' => 'audio/midi',
147
            'mov' => 'video/quicktime',
148
            'movie' => 'video/x-sgi-movie',
149
            'mp2' => 'audio/mpeg',
150
            'mp3' => 'audio/mpeg',
151
            'mp4' => 'video/mp4',
152
            'mpa' => 'audio/mpeg',
153
            'mpe' => 'video/mpeg',
154
            'mpeg' => 'video/mpeg',
155
            'mpg' => 'video/mpeg',
156
            'mpga' => 'audio/mpeg',
157
            'ms' => 'application/x-troff-ms',
158
            'msh' => 'model/mesh',
159
            'mxu' => 'video/vnd.mpegurl',
160
            'nc' => 'application/x-netcdf',
161
            'oda' => 'application/oda',
162
            'oga' => 'audio/ogg',
163
            'ogg' => 'application/ogg',
164
            'ogx' => 'application/ogg',
165
            'ogv' => 'video/ogg',
166
            'pbm' => 'image/x-portable-bitmap',
167
            'pct' => 'image/pict',
168
            'pdb' => 'chemical/x-pdb',
169
            'pdf' => 'application/pdf',
170
            'pgm' => 'image/x-portable-graymap',
171
            'pgn' => 'application/x-chess-pgn',
172
            'pict' => 'image/pict',
173
            'png' => 'image/png',
174
            'pnm' => 'image/x-portable-anymap',
175
            'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
176
            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
177
            'pps' => 'application/vnd.ms-powerpoint',
178
            'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
179
            'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
180
            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
181
            'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
182
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
183
            'ppm' => 'image/x-portable-pixmap',
184
            'ppt' => 'application/vnd.ms-powerpoint',
185
            'ps' => 'application/postscript',
186
            'qt' => 'video/quicktime',
187
            'ra' => 'audio/x-realaudio',
188
            'ram' => 'audio/x-pn-realaudio',
189
            'rar' => 'image/x-rar-compressed',
190
            'ras' => 'image/x-cmu-raster',
191
            'rgb' => 'image/x-rgb',
192
            'rm' => 'audio/x-pn-realaudio',
193
            'roff' => 'application/x-troff',
194
            'rpm' => 'audio/x-pn-realaudio-plugin',
195
            'rtf' => 'text/rtf',
196
            'rtx' => 'text/richtext',
197
            'sgm' => 'text/sgml',
198
            'sgml' => 'text/sgml',
199
            'sh' => 'application/x-sh',
200
            'shar' => 'application/x-shar',
201
            'silo' => 'model/mesh',
202
            'sib' => 'application/X-Sibelius-Score',
203
            'sit' => 'application/x-stuffit',
204
            'skd' => 'application/x-koan',
205
            'skm' => 'application/x-koan',
206
            'skp' => 'application/x-koan',
207
            'skt' => 'application/x-koan',
208
            'smi' => 'application/smil',
209
            'smil' => 'application/smil',
210
            'snd' => 'audio/basic',
211
            'so' => 'application/octet-stream',
212
            'spl' => 'application/x-futuresplash',
213
            'src' => 'application/x-wais-source',
214
            'sv4cpio' => 'application/x-sv4cpio',
215
            'sv4crc' => 'application/x-sv4crc',
216
            'svf' => 'application/vnd.svf',
217
            'svg' => 'image/svg+xml',
218
            //'svgz' => 'image/svg+xml',
219
            'swf' => 'application/x-shockwave-flash',
220
            'sxc' => 'application/vnd.sun.xml.calc',
221
            'sxi' => 'application/vnd.sun.xml.impress',
222
            'sxw' => 'application/vnd.sun.xml.writer',
223
            't' => 'application/x-troff',
224
            'tar' => 'application/x-tar',
225
            'tcl' => 'application/x-tcl',
226
            'tex' => 'application/x-tex',
227
            'texi' => 'application/x-texinfo',
228
            'texinfo' => 'application/x-texinfo',
229
            'tga' => 'image/x-targa',
230
            'tif' => 'image/tif',
231
            'tiff' => 'image/tiff',
232
            'tr' => 'application/x-troff',
233
            'tsv' => 'text/tab-seperated-values',
234
            'txt' => 'text/plain',
235
            'ustar' => 'application/x-ustar',
236
            'vcd' => 'application/x-cdlink',
237
            'vrml' => 'model/vrml',
238
            'wav' => 'audio/x-wav',
239
            'wbmp' => 'image/vnd.wap.wbmp',
240
            'wbxml' => 'application/vnd.wap.wbxml',
241
            'webp' => 'image/webp',
242
            'wml' => 'text/vnd.wap.wml',
243
            'wmlc' => 'application/vnd.wap.wmlc',
244
            'wmls' => 'text/vnd.wap.wmlscript',
245
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
246
            'wma' => 'audio/x-ms-wma',
247
            'wmv' => 'video/x-ms-wmv',
248
            'wrl' => 'model/vrml',
249
            'xbm' => 'image/x-xbitmap',
250
            'xht' => 'application/xhtml+xml',
251
            'xhtml' => 'application/xhtml+xml',
252
            'xls' => 'application/vnd.ms-excel',
253
            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
254
            'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
255
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
256
            'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
257
            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
258
            'xml' => 'text/xml',
259
            'xpm' => 'image/x-xpixmap',
260
            'xsl' => 'text/xml',
261
            'xwd' => 'image/x-windowdump',
262
            'xyz' => 'chemical/x-xyz',
263
            'zip' => 'application/zip',
264
        ];
265
266
        if ($filename === true) {
267
            return $mimeTypes;
268
        }
269
270
        // Get the extension of the file
271
        $extension = explode('.', $filename);
272
273
        // $filename will be an array if a . was found
274
        if (is_array($extension)) {
275
            $extension = strtolower($extension[count($extension) - 1]);
276
        } else {
277
            //file without extension
278
            $extension = 'empty';
279
        }
280
281
        //if the extension is found, return the content type
282
        if (isset($mimeTypes[$extension])) {
283
            return $mimeTypes[$extension];
284
        }
285
286
        return 'application/octet-stream';
287
    }
288
289
    /**
290
     * This function smart streams a file to the client using HTTP headers.
291
     *
292
     * @param string $fullFilename The full path of the file to be sent
293
     * @param string $filename     The name of the file as shown to the client
294
     * @param string $contentType  The MIME type of the file
295
     *
296
     * @return bool false if file doesn't exist, true if stream succeeded
297
     */
298
    public static function smartReadFile($fullFilename, $filename, $contentType = 'application/octet-stream')
299
    {
300
        if (!file_exists($fullFilename)) {
301
            header("HTTP/1.1 404 Not Found");
302
303
            return false;
304
        }
305
306
        $size = filesize($fullFilename);
307
        $time = date('r', filemtime($fullFilename));
308
309
        $fm = @fopen($fullFilename, 'rb');
310
        if (!$fm) {
0 ignored issues
show
introduced by
$fm is of type false|resource, thus it always evaluated to false.
Loading history...
311
            header("HTTP/1.1 505 Internal server error");
312
313
            return false;
314
        }
315
316
        $begin = 0;
317
        $end = $size - 1;
318
319
        if (isset($_SERVER['HTTP_RANGE'])) {
320
            if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
321
                $begin = intval($matches[1]);
322
                if (!empty($matches[2])) {
323
                    $end = intval($matches[2]);
324
                }
325
            }
326
        }
327
328
        if (isset($_SERVER['HTTP_RANGE'])) {
329
            header('HTTP/1.1 206 Partial Content');
330
        } else {
331
            header('HTTP/1.1 200 OK');
332
        }
333
334
        header("Content-Type: $contentType");
335
        header('Cache-Control: public, must-revalidate, max-age=0');
336
        header('Pragma: no-cache');
337
        header('Accept-Ranges: bytes');
338
        header('Content-Length:'.(($end - $begin) + 1));
339
        if (isset($_SERVER['HTTP_RANGE'])) {
340
            header("Content-Range: bytes $begin-$end/$size");
341
        }
342
        header("Content-Disposition: inline; filename=$filename");
343
        header("Content-Transfer-Encoding: binary");
344
        header("Last-Modified: $time");
345
346
        $cur = $begin;
347
        fseek($fm, $begin, 0);
348
349
        while (!feof($fm) && $cur <= $end && (connection_status() == 0)) {
350
            echo fread($fm, min(1024 * 16, ($end - $cur) + 1));
351
            $cur += 1024 * 16;
352
        }
353
    }
354
355
    /**
356
     * This function streams a file to the client.
357
     *
358
     * @param string $full_file_name
359
     * @param bool   $forced
360
     * @param string $name
361
     * @param bool   $fixLinksHttpToHttps change file content from http to https
362
     *
363
     * @return false if file doesn't exist, true if stream succeeded
364
     */
365
    public static function file_send_for_download(
366
        $full_file_name,
367
        $forced = false,
368
        $name = '',
369
        $fixLinksHttpToHttps = false
370
    ) {
371
        session_write_close(); //we do not need write access to session anymore
372
        if (!is_file($full_file_name)) {
373
            return false;
374
        }
375
        $filename = $name == '' ? basename($full_file_name) : api_replace_dangerous_char($name);
376
        $len = filesize($full_file_name);
377
        // Fixing error when file name contains a ","
378
        $filename = str_replace(',', '', $filename);
379
        $sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
380
381
        // Allows chrome to make videos and audios seekable
382
        header('Accept-Ranges: bytes');
383
384
        if ($forced) {
385
            // Force the browser to save the file instead of opening it
386
            if (isset($sendFileHeaders) &&
387
                !empty($sendFileHeaders)) {
388
                header("X-Sendfile: $filename");
389
            }
390
391
            header('Content-type: application/octet-stream');
392
            header('Content-length: '.$len);
393
            if (preg_match("/MSIE 5.5/", $_SERVER['HTTP_USER_AGENT'])) {
394
                header('Content-Disposition: filename= '.$filename);
395
            } else {
396
                header('Content-Disposition: attachment; filename= '.$filename);
397
            }
398
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
399
                header('Pragma: ');
400
                header('Cache-Control: ');
401
                header('Cache-Control: public'); // IE cannot download from sessions without a cache
402
            }
403
            header('Content-Description: '.$filename);
404
            header('Content-Transfer-Encoding: binary');
405
406
            if (function_exists('ob_end_clean') && ob_get_length()) {
407
                // Use ob_end_clean() to avoid weird buffering situations
408
                // where file is sent broken/incomplete for download
409
                ob_end_clean();
410
            }
411
412
            $res = fopen($full_file_name, 'r');
413
            fpassthru($res);
414
415
            return true;
416
        } else {
417
            // no forced download, just let the browser decide what to do according to the mimetype
418
            $lpFixedEncoding = api_get_setting('lp.fixed_encoding') === 'true';
419
420
            // Commented to let courses content to be cached in order to improve performance:
421
            //header('Expires: Wed, 01 Jan 1990 00:00:00 GMT');
422
            //header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
423
424
            // Commented to avoid double caching declaration when playing with IE and HTTPS
425
            //header('Cache-Control: no-cache, must-revalidate');
426
            //header('Pragma: no-cache');
427
428
            $contentType = self::file_get_mime_type($filename);
429
430
            switch ($contentType) {
431
                case 'text/html':
432
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
433
                        $contentType .= '; charset=UTF-8';
434
                    } else {
435
                        $encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
436
                        if (!empty($encoding)) {
437
                            $contentType .= '; charset='.$encoding;
438
                        }
439
                    }
440
                    break;
441
                case 'text/plain':
442
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
443
                        $contentType .= '; charset=UTF-8';
444
                    } else {
445
                        $encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
446
                        if (!empty($encoding)) {
447
                            $contentType .= '; charset='.$encoding;
448
                        }
449
                    }
450
                    break;
451
                case 'video/mp4':
452
                case 'audio/mpeg':
453
                case 'audio/mp4':
454
                case 'audio/ogg':
455
                case 'audio/webm':
456
                case 'audio/wav':
457
                case 'video/ogg':
458
                case 'video/webm':
459
                    self::smartReadFile($full_file_name, $filename, $contentType);
460
                    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...
461
                case 'application/vnd.dwg':
462
                case 'application/vnd.dwf':
463
                    header('Content-type: application/octet-stream');
464
                    break;
465
            }
466
467
            header('Content-type: '.$contentType);
468
            header('Content-Length: '.$len);
469
            $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
470
471
            if (strpos($userAgent, 'msie')) {
472
                header('Content-Disposition: ; filename= '.$filename);
473
            } else {
474
                //header('Content-Disposition: inline');
475
                header('Content-Disposition: inline;');
476
            }
477
478
            if ($fixLinksHttpToHttps) {
479
                $content = file_get_contents($full_file_name);
480
                $content = str_replace(
481
                    ['http%3A%2F%2F', 'http://'],
482
                    ['https%3A%2F%2F', 'https://'],
483
                    $content
484
                );
485
                echo $content;
486
            } else {
487
                if (function_exists('ob_end_clean') && ob_get_length()) {
488
                    // Use ob_end_clean() to avoid weird buffering situations
489
                    // where file is sent broken/incomplete for download
490
                    ob_end_clean();
491
                }
492
493
                readfile($full_file_name);
494
            }
495
496
            return true;
497
        }
498
    }
499
500
    /**
501
     * Session folder filters.
502
     *
503
     * @param string $path
504
     * @param int    $sessionId
505
     *
506
     * @return string|null
507
     */
508
    public static function getSessionFolderFilters($path, $sessionId)
509
    {
510
        $sessionId = (int) $sessionId;
511
        $condition = null;
512
513
        if (!empty($sessionId)) {
514
            // Chat folder filter
515
            if ($path == '/chat_files') {
516
                $condition .= " AND (docs.session_id = '$sessionId') ";
517
            }
518
            // share_folder filter
519
            $condition .= " AND docs.path != '/shared_folder' ";
520
        }
521
522
        return $condition;
523
    }
524
525
    /**
526
     * Fetches all document data for the given user/group.
527
     *
528
     * @param array     $courseInfo
529
     * @param string    $path
530
     * @param int       $toGroupId       iid
531
     * @param int       $toUserId
532
     * @param bool      $canSeeInvisible
533
     * @param bool      $search
534
     * @param int       $sessionId
535
     * @param User|null $currentUser
536
     *
537
     * @return array with all document data
538
     */
539
    public static function getAllDocumentData(
540
        $courseInfo,
541
        $path = '/',
542
        $toGroupId = 0,
543
        $toUserId = null,
544
        $canSeeInvisible = false,
545
        $search = false,
546
        $sessionId = 0,
547
        User $currentUser = null
548
    ) {
549
        $tblDocument = Database::get_course_table(TABLE_DOCUMENT);
550
        $currentUser = $currentUser ?: api_get_current_user();
551
552
        $userGroupFilter = '';
553
        if (!is_null($toUserId)) {
554
            $toUserId = (int) $toUserId;
555
            $userGroupFilter = "last.to_user_id = $toUserId";
556
            if (empty($toUserId)) {
557
                $userGroupFilter = " (last.to_user_id = 0 OR last.to_user_id IS NULL) ";
558
            }
559
        } else {
560
            $toGroupId = (int) $toGroupId;
561
            $userGroupFilter = "last.to_group_id = $toGroupId";
562
            if (empty($toGroupId)) {
563
                $userGroupFilter = "( last.to_group_id = 0 OR last.to_group_id IS NULL) ";
564
            }
565
        }
566
567
        // Escape underscores in the path so they don't act as a wildcard
568
        $originalPath = $path;
569
        $path = str_replace('_', '\_', $path);
570
571
        $visibilityBit = ' <> 2';
572
573
        // The given path will not end with a slash, unless it's the root '/'
574
        // so no root -> add slash
575
        $addedSlash = $path == '/' ? '' : '/';
576
577
        // Condition for the session
578
        $sessionId = $sessionId ?: api_get_session_id();
579
        $conditionSession = " AND (last.session_id = '$sessionId' OR (last.session_id = '0' OR last.session_id IS NULL) )";
580
        $conditionSession .= self::getSessionFolderFilters($originalPath, $sessionId);
581
582
        $sharedCondition = null;
583
        if ($originalPath == '/shared_folder') {
584
            $students = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
585
            if (!empty($students)) {
586
                $conditionList = [];
587
                foreach ($students as $studentInfo) {
588
                    $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
589
                }
590
                $sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
591
            }
592
        }
593
594
        $sql = "SELECT
595
                    docs.id,
596
                    docs.filetype,
597
                    docs.path,
598
                    docs.title,
599
                    docs.comment,
600
                    docs.size,
601
                    docs.readonly,
602
                    docs.session_id,
603
                    creator_id,
604
                    visibility,
605
                    n.updated_at,
606
                    n.created_at,
607
                    n.creator_id                                     
608
                FROM resource_node AS n
609
                INNER JOIN $tblDocument AS docs
610
                ON (docs.resource_node_id = n.id)
611
                INNER JOIN resource_link l
612
                ON (l.resource_node_id = n.id)                
613
                WHERE
614
                    docs.c_id = {$courseInfo['real_id']} AND                    
615
                    docs.path LIKE '".Database::escape_string($path.$addedSlash.'%')."' AND
616
                    docs.path NOT LIKE '".Database::escape_string($path.$addedSlash.'%/%')."' AND
617
                    docs.path NOT LIKE '%_DELETED_%' AND
618
                    l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')
619
                    $sharedCondition               
620
                ";
621
        //$userGroupFilter AND
622
        //$conditionSession
623
        $result = Database::query($sql);
624
625
        $documentData = [];
626
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
627
        $isCoach = api_is_coach();
628
        if ($result !== false && Database::num_rows($result) != 0) {
629
            $rows = [];
630
631
            $hideInvisibleDocuments = api_get_configuration_value('hide_invisible_course_documents_in_sessions');
632
633
            while ($row = Database::fetch_array($result, 'ASSOC')) {
634
                if (isset($rows[$row['id']])) {
635
                    continue;
636
                }
637
638
                // If we are in session and hide_invisible_course_documents_in_sessions is enabled
639
                // Then we avoid the documents that have visibility in session but that they come from a base course
640
                if ($hideInvisibleDocuments && $sessionId) {
641
                    if ($row['item_property_session_id'] == $sessionId && empty($row['session_id'])) {
642
                        continue;
643
                    }
644
                }
645
646
                $rows[$row['id']] = $row;
647
            }
648
649
            // If we are in session and hide_invisible_course_documents_in_sessions is enabled
650
            // Or if we are students
651
            // Then don't list the invisible or deleted documents
652
            if (($sessionId && $hideInvisibleDocuments) || (!$isCoach && !$isAllowedToEdit)) {
653
                $rows = array_filter($rows, function ($row) {
654
                    if (in_array(
655
                        $row['visibility'],
656
                        [
657
                            ResourceLink::VISIBILITY_DELETED,
658
                            ResourceLink::VISIBILITY_DRAFT,
659
                        ]
660
                    )) {
661
                        return false;
662
                    }
663
664
                    return true;
665
                });
666
            }
667
668
            foreach ($rows as $row) {
669
                if ($row['filetype'] == 'file' &&
670
                    pathinfo($row['path'], PATHINFO_EXTENSION) == 'html'
671
                ) {
672
                    // Templates management
673
                    $tblTemplate = Database::get_main_table(TABLE_MAIN_TEMPLATES);
674
                    $sql = "SELECT id FROM $tblTemplate
675
                            WHERE
676
                                c_id = '".$courseInfo['real_id']."' AND
677
                                user_id = '".$currentUser->getId()."' AND
678
                                ref_doc = '".$row['id']."'";
679
                    $templateResult = Database::query($sql);
680
                    $row['is_template'] = (Database::num_rows($templateResult) > 0) ? 1 : 0;
681
                }
682
                $row['basename'] = basename($row['path']);
683
                // Just filling $document_data.
684
                $documentData[$row['id']] = $row;
685
            }
686
687
            // Only for the student we filter the results see BT#1652
688
            if (!$isCoach && !$isAllowedToEdit) {
689
                // Checking parents visibility.
690
                $finalDocumentData = [];
691
                foreach ($documentData as $row) {
692
                    $isVisible = self::check_visibility_tree(
693
                        $row['id'],
694
                        $courseInfo,
695
                        $sessionId,
696
                        $currentUser->getId(),
697
                        $toGroupId
698
                    );
699
                    if ($isVisible) {
700
                        $finalDocumentData[$row['id']] = $row;
701
                    }
702
                }
703
            } else {
704
                $finalDocumentData = $documentData;
705
            }
706
707
            return $finalDocumentData;
708
        } else {
709
            return [];
710
        }
711
    }
712
713
    /**
714
     * Gets the paths of all folders in a course
715
     * can show all folders (except for the deleted ones) or only visible ones.
716
     *
717
     * @param array  $courseInfo
718
     * @param int    $groupIid          iid
719
     * @param bool   $can_see_invisible
720
     * @param bool   $getInvisibleList
721
     * @param string $path              current path
722
     *
723
     * @return array with paths
724
     */
725
    public static function get_all_document_folders(
726
        $courseInfo,
727
        $groupIid = 0,
728
        $can_see_invisible = false,
729
        $getInvisibleList = false,
730
        $path = ''
731
    ) {
732
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
733
        $groupIid = (int) $groupIid;
734
        $courseId = $courseInfo['real_id'];
735
        $sessionId = api_get_session_id();
736
737
        $folders = [];
738
        $students = CourseManager::get_user_list_from_course_code(
739
            $courseInfo['code'],
740
            api_get_session_id()
741
        );
742
743
        $conditionList = [];
744
        if (!empty($students)) {
745
            foreach ($students as $studentId => $studentInfo) {
746
                $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
747
            }
748
        }
749
750
        $groupCondition = " l.group_id = $groupIid";
751
        if (empty($groupIid)) {
752
            $groupCondition = ' (l.group_id = 0 OR l.group_id IS NULL)';
753
        }
754
755
        $show_users_condition = '';
756
        if (api_get_setting('show_users_folders') === 'false') {
757
            $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
758
        }
759
760
        if ($can_see_invisible) {
761
            $sessionId = $sessionId ?: api_get_session_id();
762
            $condition_session = " AND (l.session_id = '$sessionId' OR (l.session_id = '0' OR l.session_id IS NULL) )";
763
            $condition_session .= self::getSessionFolderFilters($path, $sessionId);
764
765
            $sql = "SELECT DISTINCT docs.id, docs.path
766
                    FROM resource_node AS n
767
                    INNER JOIN $TABLE_DOCUMENT  AS docs
768
                    ON (docs.resource_node_id = n.id)
769
                    INNER JOIN resource_link l
770
                    ON (l.resource_node_id = n.id)
771
                    WHERE                      
772
                        docs.c_id = $courseId AND
773
                        docs.filetype = 'folder' AND
774
                        $groupCondition AND
775
                        docs.path NOT LIKE '%shared_folder%' AND
776
                        docs.path NOT LIKE '%_DELETED_%' AND
777
                        l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."')                           
778
                        $condition_session ";
779
780
            if ($groupIid != 0) {
781
                $sql .= " AND docs.path NOT LIKE '%shared_folder%' ";
782
            } else {
783
                $sql .= $show_users_condition;
784
            }
785
786
            $result = Database::query($sql);
787
            if ($result && Database::num_rows($result) != 0) {
788
                while ($row = Database::fetch_array($result, 'ASSOC')) {
789
                    if (self::is_folder_to_avoid($row['path'])) {
790
                        continue;
791
                    }
792
793
                    if (strpos($row['path'], '/shared_folder/') !== false) {
794
                        if (!in_array($row['path'], $conditionList)) {
795
                            continue;
796
                        }
797
                    }
798
799
                    $folders[$row['id']] = $row['path'];
800
                }
801
802
                if (!empty($folders)) {
803
                    natsort($folders);
804
                }
805
806
                return $folders;
807
            } else {
808
                return false;
809
            }
810
        } else {
811
            // No invisible folders
812
            // Condition for the session
813
            $condition_session = api_get_session_condition(
814
                $sessionId,
815
                true,
816
                false,
817
                'docs.session_id'
818
            );
819
820
            $visibilityCondition = 'l.visibility = 1';
821
            $fileType = "docs.filetype = 'folder' AND";
822
            if ($getInvisibleList) {
823
                $visibilityCondition = 'l.visibility = 0';
824
                $fileType = '';
825
            }
826
827
            //get visible folders
828
            $sql = "SELECT DISTINCT docs.id, docs.path
829
                    FROM resource_node AS n
830
                    INNER JOIN $TABLE_DOCUMENT  AS docs
831
                    ON (docs.resource_node_id = n.id)
832
                    INNER JOIN resource_link l
833
                    ON (l.resource_node_id = n.id)
834
                    WHERE
835
                        $fileType                        
836
                        $groupCondition AND
837
                        $visibilityCondition
838
                        $show_users_condition
839
                        $condition_session AND                        
840
                        docs.c_id = $courseId ";
841
            $result = Database::query($sql);
842
            $visibleFolders = [];
843
            while ($row = Database::fetch_array($result, 'ASSOC')) {
844
                $visibleFolders[$row['id']] = $row['path'];
845
            }
846
847
            if ($getInvisibleList) {
848
                return $visibleFolders;
849
            }
850
851
            // get invisible folders
852
            $sql = "SELECT DISTINCT docs.id, docs.path
853
                    FROM resource_node AS n
854
                    INNER JOIN $TABLE_DOCUMENT  AS docs
855
                    ON (docs.resource_node_id = n.id)
856
                    INNER JOIN resource_link l
857
                    ON (l.resource_node_id = n.id)
858
                    WHERE                        
859
                        docs.filetype = 'folder' AND                        
860
                        $groupCondition AND                        
861
                        l.visibility IN ('".ResourceLink::VISIBILITY_PENDING."') 
862
                        $condition_session AND                        
863
                        docs.c_id = $courseId ";
864
            $result = Database::query($sql);
865
            $invisibleFolders = [];
866
            while ($row = Database::fetch_array($result, 'ASSOC')) {
867
                //get visible folders in the invisible ones -> they are invisible too
868
                $sql = "SELECT DISTINCT docs.id, docs.path
869
                        FROM resource_node AS n
870
                        INNER JOIN $TABLE_DOCUMENT  AS docs
871
                        ON (docs.resource_node_id = n.id)
872
                        INNER JOIN resource_link l
873
                        ON (l.resource_node_id = n.id)
874
                        WHERE                            
875
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
876
                            docs.filetype = 'folder' AND                            
877
                            $groupCondition AND
878
                            l.visibility NOT IN ('".ResourceLink::VISIBILITY_DELETED."') 
879
                            $condition_session AND                            
880
                            docs.c_id = $courseId ";
881
                $folder_in_invisible_result = Database::query($sql);
882
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
883
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
884
                }
885
            }
886
887
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
888
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
889
                $folders = array_diff($visibleFolders, $invisibleFolders);
890
                natsort($folders);
891
892
                return $folders;
893
            }
894
895
            if (is_array($visibleFolders)) {
896
                natsort($visibleFolders);
897
898
                return $visibleFolders;
899
            }
900
901
            // no visible folders found
902
            return false;
903
        }
904
    }
905
906
    /**
907
     * This check if a document has the readonly property checked, then see if the user
908
     * is the owner of this file, if all this is true then return true.
909
     *
910
     * @param array  $_course
911
     * @param int    $user_id     id of the current user
912
     * @param string $file        path stored in the database (if not defined, $documentId must be used)
913
     * @param int    $document_id in case you don't have the file path ,
914
     *                            insert the id of the file here and leave $file in blank ''
915
     * @param bool   $to_delete
916
     * @param int    $sessionId
917
     *
918
     * @return bool true/false
919
     * */
920
    public static function check_readonly(
921
        $_course,
922
        $user_id,
923
        $file = null,
924
        $document_id = 0,
925
        $to_delete = false,
926
        $sessionId = null,
927
        $documentId = null
928
    ) {
929
        $sessionId = (int) $sessionId;
930
        if (empty($sessionId)) {
931
            $sessionId = api_get_session_id();
932
        }
933
        $document_id = (int) $document_id;
934
        if (empty($document_id)) {
935
            $document_id = self::get_document_id($_course, $file, $sessionId);
936
        }
937
938
        $TABLE_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
939
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
940
        $course_id = $_course['real_id'];
941
942
        if ($to_delete) {
943
            if (self::isFolder($_course, $document_id)) {
944
                if (!empty($file)) {
945
                    $path = Database::escape_string($file);
946
                    // Check
947
                    $sql = "SELECT td.id, readonly, tp.insert_user_id
948
                            FROM $TABLE_DOCUMENT td 
949
                            INNER JOIN $TABLE_PROPERTY tp
950
                            ON (td.c_id = tp.c_id AND tp.ref= td.id)
951
                            WHERE
952
                                td.c_id = $course_id AND
953
                                tp.c_id = $course_id AND
954
                                td.session_id = $sessionId AND                                
955
                                (path='".$path."' OR path LIKE BINARY '".$path."/%' ) ";
956
                    // Get all id's of documents that are deleted
957
                    $what_to_check_result = Database::query($sql);
958
959
                    if ($what_to_check_result && Database::num_rows($what_to_check_result) != 0) {
960
                        // file with readonly set to 1 exist?
961
                        $readonly_set = false;
962
                        while ($row = Database::fetch_array($what_to_check_result)) {
963
                            //query to delete from item_property table
964
                            if ($row['readonly'] == 1) {
965
                                if (!($row['insert_user_id'] == $user_id)) {
966
                                    $readonly_set = true;
967
                                    break;
968
                                }
969
                            }
970
                        }
971
972
                        if ($readonly_set) {
973
                            return true;
974
                        }
975
                    }
976
                }
977
978
                return false;
979
            }
980
        }
981
982
        if (!empty($document_id)) {
983
            $sql = "SELECT a.insert_user_id, b.readonly
984
                   FROM $TABLE_PROPERTY a 
985
                   INNER JOIN $TABLE_DOCUMENT b
986
                   ON (a.c_id = b.c_id AND a.ref= b.id)
987
                   WHERE
988
            			a.c_id = $course_id AND
989
                        b.c_id = $course_id AND
990
            			a.ref = $document_id 
991
                    LIMIT 1";
992
            $result = Database::query($sql);
993
            $doc_details = Database::fetch_array($result, 'ASSOC');
994
995
            if ($doc_details['readonly'] == 1) {
996
                return !($doc_details['insert_user_id'] == $user_id || api_is_platform_admin());
997
            }
998
        }
999
1000
        return false;
1001
    }
1002
1003
    /**
1004
     * This check if a document is a folder or not.
1005
     *
1006
     * @param array $_course
1007
     * @param int   $id      document id
1008
     *
1009
     * @return bool true/false
1010
     * */
1011
    public static function isFolder($_course, $id)
1012
    {
1013
        $table = Database::get_course_table(TABLE_DOCUMENT);
1014
        if (empty($_course)) {
1015
            return false;
1016
        }
1017
        $course_id = $_course['real_id'];
1018
        $id = (int) $id;
1019
        $sql = "SELECT filetype FROM $table
1020
                WHERE c_id = $course_id AND id= $id";
1021
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
1022
1023
        return $result['filetype'] === 'folder';
1024
    }
1025
1026
    /**
1027
     * @param int   $document_id
1028
     * @param array $course_info
1029
     * @param int   $session_id
1030
     * @param bool  $remove_content_from_db
1031
     */
1032
    public static function deleteDocumentFromDb(
1033
        $document_id,
1034
        $course_info = [],
1035
        $session_id = 0,
1036
        $remove_content_from_db = false
1037
    ) {
1038
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1039
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
1040
1041
        // Deleting from the DB
1042
        $user_id = api_get_user_id();
1043
        $document_id = intval($document_id);
1044
1045
        if (empty($course_info)) {
1046
            $course_info = api_get_course_info();
1047
        }
1048
1049
        if (empty($session_id)) {
1050
            $session_id = api_get_session_id();
1051
        }
1052
        // Soft DB delete
1053
        api_item_property_update(
1054
            $course_info,
1055
            TOOL_DOCUMENT,
1056
            $document_id,
1057
            'delete',
1058
            $user_id,
1059
            null,
1060
            null,
1061
            null,
1062
            null,
1063
            $session_id
1064
        );
1065
        self::delete_document_from_search_engine($course_info['code'], $document_id);
1066
        self::unsetDocumentAsTemplate($document_id, $course_info['real_id'], $user_id);
1067
1068
        //Hard DB delete
1069
        if ($remove_content_from_db) {
1070
            $sql = "DELETE FROM $TABLE_ITEMPROPERTY
1071
                    WHERE
1072
                        c_id = {$course_info['real_id']} AND
1073
                        ref = ".$document_id." AND
1074
                        tool='".TOOL_DOCUMENT."'";
1075
            Database::query($sql);
1076
1077
            $sql = "DELETE FROM $TABLE_DOCUMENT
1078
                    WHERE c_id = {$course_info['real_id']} AND id = ".$document_id;
1079
            Database::query($sql);
1080
        }
1081
    }
1082
1083
    /**
1084
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
1085
     * Files/folders that are inside a deleted folder get visibility 2.
1086
     *
1087
     * @param array  $_course
1088
     * @param string $path          Path stored in the database
1089
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
1090
     * @param int    $sessionId     The ID of the session, if any
1091
     * @param int    $documentId    The document id, if available
1092
     * @param int    $groupId       iid
1093
     *
1094
     * @return bool true/false
1095
     *
1096
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1097
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1098
     */
1099
    public static function delete_document(
1100
        $_course,
1101
        $path = null,
1102
        $base_work_dir = null,
1103
        $sessionId = null,
1104
        $documentId = null,
1105
        $groupId = 0
1106
    ) {
1107
        $groupId = (int) $groupId;
1108
        if (empty($groupId)) {
1109
            $groupId = api_get_group_id();
1110
        }
1111
1112
        $sessionId = (int) $sessionId;
1113
        if (empty($sessionId)) {
1114
            $sessionId = api_get_session_id();
1115
        }
1116
1117
        $course_id = $_course['real_id'];
1118
1119
        if (empty($course_id)) {
1120
            return false;
1121
        }
1122
1123
        if (empty($base_work_dir)) {
1124
            return false;
1125
        }
1126
1127
        if (empty($documentId)) {
1128
            $documentId = self::get_document_id($_course, $path, $sessionId);
1129
            $docInfo = self::get_document_data_by_id(
1130
                $documentId,
1131
                $_course['code'],
1132
                false,
1133
                $sessionId
1134
            );
1135
            $path = $docInfo['path'];
1136
        } else {
1137
            $docInfo = self::get_document_data_by_id(
1138
                $documentId,
1139
                $_course['code'],
1140
                false,
1141
                $sessionId
1142
            );
1143
            if (empty($docInfo)) {
1144
                return false;
1145
            }
1146
            $path = $docInfo['path'];
1147
        }
1148
1149
        $em = Database::getManager();
1150
        $documentId = (int) $documentId;
1151
1152
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1153
            return false;
1154
        }
1155
1156
        /** @var CDocument $document */
1157
        $document = $em->getRepository('ChamiloCourseBundle:CDocument')->find($docInfo['iid']);
1158
        $document->setSoftDelete();
1159
        $em->persist($document);
1160
        $em->flush();
1161
1162
        return true;
1163
1164
        $itemInfo = api_get_item_property_info(
0 ignored issues
show
Unused Code introduced by
$itemInfo = api_get_item..., $sessionId, $groupId) is not reachable.

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

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

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

    return false;
}

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

Loading history...
1165
            $_course['real_id'],
1166
            TOOL_DOCUMENT,
1167
            $documentId,
1168
            $sessionId,
1169
            $groupId
1170
        );
1171
1172
        if (empty($itemInfo)) {
1173
            return false;
1174
        }
1175
1176
        // File was already deleted.
1177
        if ($itemInfo['lastedit_type'] == 'DocumentDeleted' ||
1178
            $itemInfo['lastedit_type'] == 'delete' ||
1179
            $itemInfo['visibility'] == 2
1180
        ) {
1181
            return false;
1182
        }
1183
1184
        // Filtering by group.
1185
        if ($itemInfo['to_group_id'] != $groupId) {
1186
            return false;
1187
        }
1188
1189
        $document_exists_in_disk = file_exists($base_work_dir.$path);
1190
        $new_path = $path.'_DELETED_'.$documentId;
1191
1192
        $file_deleted_from_db = false;
1193
        $file_deleted_from_disk = false;
1194
        $file_renamed_from_disk = false;
1195
1196
        if ($documentId) {
1197
            // Deleting doc from the DB.
1198
            self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1199
            // Checking
1200
            // $file_exists_in_db = self::get_document_data_by_id($documentId, $_course['code']);
1201
            $file_deleted_from_db = true;
1202
        }
1203
1204
        // Looking for children.
1205
        if ($docInfo['filetype'] == 'folder') {
1206
            $cleanPath = Database::escape_string($path);
1207
1208
            // Deleted files inside this folder.
1209
            $sql = "SELECT id FROM $TABLE_DOCUMENT
1210
                    WHERE
1211
                        c_id = $course_id AND
1212
                        session_id = $sessionId AND
1213
                        path LIKE BINARY '".$cleanPath."/%'";
1214
1215
            // Get all id's of documents that are deleted.
1216
            $result = Database::query($sql);
1217
1218
            if ($result && Database::num_rows($result) != 0) {
1219
                // Recursive delete.
1220
                while ($row = Database::fetch_array($result)) {
1221
                    self::delete_document(
1222
                        $_course,
1223
                        null,
1224
                        $base_work_dir,
1225
                        $sessionId,
1226
                        $row['id'],
1227
                        $groupId
1228
                    );
1229
                }
1230
            }
1231
        }
1232
1233
        if ($document_exists_in_disk) {
1234
            if (api_get_setting('permanently_remove_deleted_files') === 'true') {
1235
                // Delete documents, do it like this so metadata gets deleted too
1236
                my_delete($base_work_dir.$path);
1237
                // Hard delete.
1238
                self::deleteDocumentFromDb($documentId, $_course, $sessionId, true);
1239
                $file_deleted_from_disk = true;
1240
            } else {
1241
                // Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
1242
                if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
1243
                    if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
1244
                        $new_path = Database::escape_string($new_path);
1245
1246
                        $sql = "UPDATE $TABLE_DOCUMENT
1247
                                SET path = '".$new_path."'
1248
                                WHERE
1249
                                    c_id = $course_id AND
1250
                                    session_id = $sessionId AND
1251
                                    id = ".$documentId;
1252
                        Database::query($sql);
1253
1254
                        // Soft delete.
1255
                        self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1256
1257
                        // Change path of sub folders and documents in database.
1258
                        $old_item_path = $docInfo['path'];
1259
                        $new_item_path = $new_path.substr($old_item_path, strlen($path));
1260
                        $new_item_path = Database::escape_string($new_item_path);
1261
1262
                        $sql = "UPDATE $TABLE_DOCUMENT
1263
                                SET path = '".$new_item_path."'
1264
                                WHERE
1265
                                    c_id = $course_id AND
1266
                                    session_id = $sessionId AND
1267
                                    id = ".$documentId;
1268
                        Database::query($sql);
1269
1270
                        $file_renamed_from_disk = true;
1271
                    } else {
1272
                        // Couldn't rename - file permissions problem?
1273
                        error_log(
1274
                            __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '.$base_work_dir.$new_path.'. This is probably due to file permissions',
1275
                            0
1276
                        );
1277
                    }
1278
                }
1279
            }
1280
        }
1281
        // Checking inconsistency
1282
        //error_log('Doc status: (1 del db :'.($file_deleted_from_db?'yes':'no').') - (2 del disk: '.($file_deleted_from_disk?'yes':'no').') - (3 ren disk: '.($file_renamed_from_disk?'yes':'no').')');
1283
        if ($file_deleted_from_db && $file_deleted_from_disk ||
1284
            $file_deleted_from_db && $file_renamed_from_disk
1285
        ) {
1286
            return true;
1287
        } else {
1288
            //Something went wrong
1289
            //The file or directory isn't there anymore (on the filesystem)
1290
            // This means it has been removed externally. To prevent a
1291
            // blocking error from happening, we drop the related items from the
1292
            // item_property and the document table.
1293
            error_log(
1294
                __FILE__.' '.__LINE__.': System inconsistency detected. The file or directory '.$base_work_dir.$path.' seems to have been removed from the filesystem independently from the web platform. To restore consistency, the elements using the same path will be removed from the database',
1295
                0
1296
            );
1297
1298
            return false;
1299
        }
1300
    }
1301
1302
    /**
1303
     * Removes documents from search engine database.
1304
     *
1305
     * @param string $course_id   Course code
1306
     * @param int    $document_id Document id to delete
1307
     */
1308
    public static function delete_document_from_search_engine($course_id, $document_id)
1309
    {
1310
        // remove from search engine if enabled
1311
        if (api_get_setting('search_enabled') === 'true') {
1312
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1313
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1314
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1315
            $res = Database::query($sql);
1316
            if (Database::num_rows($res) > 0) {
1317
                $row2 = Database::fetch_array($res);
1318
                $di = new ChamiloIndexer();
1319
                $di->remove_document($row2['search_did']);
1320
            }
1321
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1322
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1323
            Database::query($sql);
1324
1325
            // remove terms from db
1326
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1327
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1328
        }
1329
    }
1330
1331
    /**
1332
     * Gets the id of a document with a given path.
1333
     *
1334
     * @param array  $courseInfo
1335
     * @param string $path
1336
     * @param int    $sessionId
1337
     *
1338
     * @return int id of document / false if no doc found
1339
     */
1340
    public static function get_document_id($courseInfo, $path, $sessionId = 0)
1341
    {
1342
        $table = Database::get_course_table(TABLE_DOCUMENT);
1343
        $courseId = $courseInfo['real_id'];
1344
1345
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
1346
        $sessionCondition = api_get_session_condition($sessionId, true);
1347
1348
        $path = Database::escape_string($path);
1349
        if (!empty($courseId) && !empty($path)) {
1350
            $sql = "SELECT id FROM $table
1351
                    WHERE
1352
                        c_id = $courseId AND
1353
                        path LIKE BINARY '$path'
1354
                        $sessionCondition
1355
                    LIMIT 1";
1356
1357
            $result = Database::query($sql);
1358
            if (Database::num_rows($result)) {
1359
                $row = Database::fetch_array($result);
1360
1361
                return (int) $row['id'];
1362
            }
1363
        }
1364
1365
        return false;
1366
    }
1367
1368
    /**
1369
     * Gets the document data with a given id.
1370
     *
1371
     * @param int    $id            Document Id (id field in c_document table)
1372
     * @param string $course_code   Course code
1373
     * @param bool   $load_parents  load folder parents
1374
     * @param int    $session_id    The session ID,
1375
     *                              0 if requires context *out of* session, and null to use global context
1376
     * @param bool   $ignoreDeleted
1377
     *
1378
     * @return array document content
1379
     */
1380
    public static function get_document_data_by_id(
1381
        $id,
1382
        $course_code,
1383
        $load_parents = false,
1384
        $session_id = null,
1385
        $ignoreDeleted = false
1386
    ) {
1387
        $course_info = api_get_course_info($course_code);
1388
        $course_id = $course_info['real_id'];
1389
1390
        if (empty($course_info)) {
1391
            return false;
1392
        }
1393
1394
        $session_id = empty($session_id) ? api_get_session_id() : (int) $session_id;
1395
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
1396
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1397
        $id = (int) $id;
1398
        $sessionCondition = api_get_session_condition($session_id, true, true);
1399
1400
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1401
                WHERE c_id = $course_id $sessionCondition AND id = $id";
1402
1403
        if ($ignoreDeleted) {
1404
            $sql .= " AND path NOT LIKE '%_DELETED_%' ";
1405
        }
1406
1407
        $result = Database::query($sql);
1408
        if ($result && Database::num_rows($result) == 1) {
1409
            $row = Database::fetch_array($result, 'ASSOC');
1410
            //@todo need to clarify the name of the URLs not nice right now
1411
            $url_path = urlencode($row['path']);
1412
            $path = str_replace('%2F', '/', $url_path);
1413
            $pathinfo = pathinfo($row['path']);
1414
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?cidReq='.$course_code.'&id='.$id;
1415
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?cidReq='.$course_code.'&id='.$id;
1416
            $row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1417
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1418
            $row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1419
            $row['direct_url'] = $www.$path;
1420
            $row['basename'] = basename($row['path']);
1421
1422
            if (dirname($row['path']) == '.') {
1423
                $row['parent_id'] = '0';
1424
            } else {
1425
                $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), $session_id);
1426
                if (empty($row['parent_id'])) {
1427
                    // Try one more with session id = 0
1428
                    $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), 0);
1429
                }
1430
            }
1431
            $parents = [];
1432
1433
            //Use to generate parents (needed for the breadcrumb)
1434
            //@todo sorry but this for is here because there's not a parent_id in the document table so we parsed the path!!
1435
            if ($load_parents) {
1436
                $dir_array = explode('/', $row['path']);
1437
                $dir_array = array_filter($dir_array);
1438
                $array_len = count($dir_array) + 1;
1439
                $real_dir = '';
1440
1441
                for ($i = 1; $i < $array_len; $i++) {
1442
                    $real_dir .= '/'.(isset($dir_array[$i]) ? $dir_array[$i] : '');
1443
                    $parent_id = self::get_document_id($course_info, $real_dir);
1444
                    if ($session_id != 0 && empty($parent_id)) {
1445
                        $parent_id = self::get_document_id($course_info, $real_dir, 0);
1446
                    }
1447
                    if (!empty($parent_id)) {
1448
                        $sub_document_data = self::get_document_data_by_id(
1449
                            $parent_id,
1450
                            $course_code,
1451
                            false,
1452
                            $session_id
1453
                        );
1454
                        if ($session_id != 0 and !$sub_document_data) {
1455
                            $sub_document_data = self::get_document_data_by_id(
1456
                                $parent_id,
1457
                                $course_code,
1458
                                false,
1459
                                0
1460
                            );
1461
                        }
1462
                        //@todo add visibility here
1463
                        $parents[] = $sub_document_data;
1464
                    }
1465
                }
1466
            }
1467
            $row['parents'] = $parents;
1468
1469
            return $row;
1470
        }
1471
1472
        return false;
1473
    }
1474
1475
    /**
1476
     * Allow to set a specific document as a new template for CKeditor
1477
     * for a particular user in a particular course.
1478
     *
1479
     * @param string $title
1480
     * @param string $description
1481
     * @param int    $document_id_for_template the document id
1482
     * @param int    $courseId
1483
     * @param int    $user_id
1484
     * @param string $image
1485
     *
1486
     * @return bool
1487
     */
1488
    public static function setDocumentAsTemplate(
1489
        $title,
1490
        $description,
1491
        $document_id_for_template,
1492
        $courseId,
1493
        $user_id,
1494
        $image
1495
    ) {
1496
        // Database table definition
1497
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1498
        $params = [
1499
            'title' => $title,
1500
            'description' => $description,
1501
            'c_id' => $courseId,
1502
            'user_id' => $user_id,
1503
            'ref_doc' => $document_id_for_template,
1504
            'image' => $image,
1505
        ];
1506
        Database::insert($table_template, $params);
1507
1508
        return true;
1509
    }
1510
1511
    /**
1512
     * Unset a document as template.
1513
     *
1514
     * @param int $document_id
1515
     * @param int $courseId
1516
     * @param int $user_id
1517
     */
1518
    public static function unsetDocumentAsTemplate(
1519
        $document_id,
1520
        $courseId,
1521
        $user_id
1522
    ) {
1523
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1524
        $courseId = (int) $courseId;
1525
        $user_id = (int) $user_id;
1526
        $document_id = (int) $document_id;
1527
1528
        $sql = 'SELECT id FROM '.$table_template.'
1529
                WHERE
1530
                    c_id = "'.$courseId.'" AND
1531
                    user_id = "'.$user_id.'" AND
1532
                    ref_doc = "'.$document_id.'"';
1533
        $result = Database::query($sql);
1534
        $template_id = Database::result($result, 0, 0);
1535
1536
        my_delete(api_get_path(SYS_CODE_PATH).'upload/template_thumbnails/'.$template_id.'.jpg');
1537
1538
        $sql = 'DELETE FROM '.$table_template.'
1539
                WHERE
1540
                    c_id ="'.$courseId.'" AND
1541
                    user_id="'.$user_id.'" AND
1542
                    ref_doc="'.$document_id.'"';
1543
1544
        Database::query($sql);
1545
    }
1546
1547
    /**
1548
     * Return true if the documentpath have visibility=1 as
1549
     * item_property (you should use the is_visible_by_id).
1550
     *
1551
     * @param string $doc_path the relative complete path of the document
1552
     * @param array  $course   the _course array info of the document's course
1553
     * @param int
1554
     * @param string
1555
     *
1556
     * @return bool
1557
     */
1558
    public static function is_visible(
1559
        $doc_path,
1560
        $course,
1561
        $session_id = 0,
1562
        $file_type = 'file'
1563
    ) {
1564
        $docTable = Database::get_course_table(TABLE_DOCUMENT);
1565
        $propTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
1566
1567
        $course_id = $course['real_id'];
1568
        // note the extra / at the end of doc_path to match every path in
1569
        // the document table that is part of the document path
1570
1571
        $session_id = intval($session_id);
1572
        $condition = "AND d.session_id IN  ('$session_id', '0') ";
1573
        // The " d.filetype='file' " let the user see a file even if the folder is hidden see #2198
1574
1575
        /*
1576
          When using hotpotatoes files, a new html files are generated
1577
          in the hotpotatoes folder to display the test.
1578
          The genuine html file is copied to math4.htm(user_id).t.html
1579
          Images files are not copied, and keep same name.
1580
          To check the html file visibility, we don't have to check file math4.htm(user_id).t.html but file math4.htm
1581
          In this case, we have to remove (user_id).t.html to check the visibility of the file
1582
          For images, we just check the path of the image file.
1583
1584
          Exemple of hotpotatoes folder :
1585
          A.jpg
1586
          maths4-consigne.jpg
1587
          maths4.htm
1588
          maths4.htm1.t.html
1589
          maths4.htm52.t.html
1590
          maths4.htm654.t.html
1591
          omega.jpg
1592
          theta.jpg
1593
         */
1594
1595
        if (strpos($doc_path, 'HotPotatoes_files') && preg_match("/\.t\.html$/", $doc_path)) {
1596
            $doc_path = substr($doc_path, 0, strlen($doc_path) - 7 - strlen(api_get_user_id()));
1597
        }
1598
1599
        if (!in_array($file_type, ['file', 'folder'])) {
1600
            $file_type = 'file';
1601
        }
1602
        $doc_path = Database::escape_string($doc_path).'/';
1603
1604
        $sql = "SELECT visibility
1605
                FROM $docTable d
1606
                INNER JOIN $propTable ip
1607
                ON (d.id = ip.ref AND d.c_id = ip.c_id)
1608
        		WHERE
1609
        		    d.c_id  = $course_id AND 
1610
        		    ip.c_id = $course_id AND
1611
        		    ip.tool = '".TOOL_DOCUMENT."' $condition AND
1612
        			filetype = '$file_type' AND
1613
        			locate(concat(path,'/'), '$doc_path')=1
1614
                ";
1615
1616
        $result = Database::query($sql);
1617
        $is_visible = false;
1618
        if (Database::num_rows($result) > 0) {
1619
            $row = Database::fetch_array($result, 'ASSOC');
1620
            if ($row['visibility'] == 1) {
1621
                $is_visible = api_is_allowed_in_course() || api_is_platform_admin();
1622
            }
1623
        }
1624
1625
        /* improved protection of documents viewable directly through the url:
1626
            incorporates the same protections of the course at the url of
1627
            documents:
1628
            access allowed for the whole world Open, access allowed for
1629
            users registered on the platform Private access, document accessible
1630
            only to course members (see the Users list), Completely closed;
1631
            the document is only accessible to the course admin and
1632
            teaching assistants.*/
1633
        //return $_SESSION ['is_allowed_in_course'] || api_is_platform_admin();
1634
        return $is_visible;
1635
    }
1636
1637
    /**
1638
     * Return true if user can see a file.
1639
     *
1640
     * @param   int     document id
1641
     * @param   array   course info
1642
     * @param   int
1643
     * @param   int
1644
     * @param bool
1645
     *
1646
     * @return bool
1647
     */
1648
    public static function is_visible_by_id(
1649
        $doc_id,
1650
        $course_info,
1651
        $session_id,
1652
        $user_id,
1653
        $admins_can_see_everything = true,
1654
        $userIsSubscribed = null
1655
    ) {
1656
        $user_in_course = false;
1657
1658
        //1. Checking the course array
1659
        if (empty($course_info)) {
1660
            $course_info = api_get_course_info();
1661
            if (empty($course_info)) {
1662
                return false;
1663
            }
1664
        }
1665
1666
        $doc_id = (int) $doc_id;
1667
        $session_id = (int) $session_id;
1668
        // 2. Course and Session visibility are handle in local.inc.php/global.inc.php
1669
        // 3. Checking if user exist in course/session
1670
        if ($session_id == 0) {
1671
            if (is_null($userIsSubscribed)) {
1672
                $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
1673
                    $user_id,
1674
                    $course_info['code']
1675
                );
1676
            }
1677
1678
            if ($userIsSubscribed === true || api_is_platform_admin()) {
1679
                $user_in_course = true;
1680
            }
1681
1682
            // Check if course is open then we can consider that the student is registered to the course
1683
            if (isset($course_info) &&
1684
                in_array(
1685
                    $course_info['visibility'],
1686
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
1687
                )
1688
            ) {
1689
                $user_in_course = true;
1690
            }
1691
        } else {
1692
            $user_status = SessionManager::get_user_status_in_course_session(
1693
                $user_id,
1694
                $course_info['real_id'],
1695
                $session_id
1696
            );
1697
1698
            if (in_array($user_status, ['0', '2', '6'])) {
1699
                //is true if is an student, course session teacher or coach
1700
                $user_in_course = true;
1701
            }
1702
1703
            if (api_is_platform_admin()) {
1704
                $user_in_course = true;
1705
            }
1706
        }
1707
1708
        $em = Database::getManager();
1709
1710
        // 4. Checking document visibility (i'm repeating the code in order to be more clear when reading ) - jm
1711
        if ($user_in_course) {
1712
            $repo = $em->getRepository('ChamiloCourseBundle:CDocument');
1713
            /** @var \Chamilo\CourseBundle\Entity\CDocument $document */
1714
            $document = $repo->find($doc_id);
1715
            $link = $document->getCourseSessionResourceLink();
1716
1717
            if ($link && $link->getVisibility() == ResourceLink::VISIBILITY_PUBLISHED) {
1718
                return true;
1719
            }
1720
1721
            return false;
1722
1723
            // 4.1 Checking document visibility for a Course
1724
            if ($session_id == 0) {
0 ignored issues
show
Unused Code introduced by
IfNode is not reachable.

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

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

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

    return false;
}

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

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