Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

DocumentManager::replace_user_info_into_html()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 51
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 33
nc 4
nop 4
dl 0
loc 51
rs 8.8981
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 *  Class DocumentManager
8
 *  This is the document library for Chamilo.
9
 *  It is / will be used to provide a service layer to all document-using tools.
10
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
11
 *  Include/require it in your code to use its functionality.
12
 *
13
 * @package chamilo.library
14
 */
15
class DocumentManager
16
{
17
    /**
18
     * Construct
19
     */
20
    private function __construct()
21
    {
22
    }
23
24
    /**
25
     * @param string $course_code
26
     *
27
     * @return int the document folder quota for the current course in bytes
28
     * or the default quota
29
     */
30
    public static function get_course_quota($course_code = null)
31
    {
32
        if (empty($course_code)) {
33
            $course_info = api_get_course_info();
34
        } else {
35
            $course_info = api_get_course_info($course_code);
36
        }
37
38
        $course_quota = null;
39
        if (empty($course_info)) {
40
            return DEFAULT_DOCUMENT_QUOTA;
41
        } else {
42
            $course_quota = $course_info['disk_quota'];
43
        }
44
        if (is_null($course_quota) || empty($course_quota)) {
45
            // Course table entry for quota was null, then use default value
46
            $course_quota = DEFAULT_DOCUMENT_QUOTA;
47
        }
48
        return $course_quota;
49
    }
50
51
    /**
52
     * Get the content type of a file by checking the extension
53
     * We could use mime_content_type() with php-versions > 4.3,
54
     * but this doesn't work as it should on Windows installations
55
     *
56
     * @param string $filename or boolean TRUE to return complete array
57
     * @author ? first version
58
     * @author Bert Vanderkimpen
59
     * @return string
60
     *
61
     */
62
    public static function file_get_mime_type($filename)
63
    {
64
        // All MIME types in an array (from 1.6, this is the authorative source)
65
        // Please, keep this alphabetical if you add something to this list!
66
        $mime_types = [
67
            'ai' => 'application/postscript',
68
            'aif' => 'audio/x-aiff',
69
            'aifc' => 'audio/x-aiff',
70
            'aiff' => 'audio/x-aiff',
71
            'asf' => 'video/x-ms-asf',
72
            'asc' => 'text/plain',
73
            'au' => 'audio/basic',
74
            'avi' => 'video/x-msvideo',
75
            'bcpio' => 'application/x-bcpio',
76
            'bin' => 'application/octet-stream',
77
            'bmp' => 'image/bmp',
78
            'cdf' => 'application/x-netcdf',
79
            'class' => 'application/octet-stream',
80
            'cpio' => 'application/x-cpio',
81
            'cpt' => 'application/mac-compactpro',
82
            'csh' => 'application/x-csh',
83
            'css' => 'text/css',
84
            'dcr' => 'application/x-director',
85
            'dir' => 'application/x-director',
86
            'djv' => 'image/vnd.djvu',
87
            'djvu' => 'image/vnd.djvu',
88
            'dll' => 'application/octet-stream',
89
            'dmg' => 'application/x-diskcopy',
90
            'dms' => 'application/octet-stream',
91
            'doc' => 'application/msword',
92
            'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
93
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
94
            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
95
            'dvi' => 'application/x-dvi',
96
            'dwg' => 'application/vnd.dwg',
97
            'dwf' => 'application/vnd.dwf',
98
            'dxf' => 'application/vnd.dxf',
99
            'dxr' => 'application/x-director',
100
            'eps' => 'application/postscript',
101
            'epub' => 'application/epub+zip',
102
            'etx' => 'text/x-setext',
103
            'exe' => 'application/octet-stream',
104
            'ez' => 'application/andrew-inset',
105
            'gif' => 'image/gif',
106
            'gtar' => 'application/x-gtar',
107
            'gz' => 'application/x-gzip',
108
            'hdf' => 'application/x-hdf',
109
            'hqx' => 'application/mac-binhex40',
110
            'htm' => 'text/html',
111
            'html' => 'text/html',
112
            'ice' => 'x-conference-xcooltalk',
113
            'ief' => 'image/ief',
114
            'iges' => 'model/iges',
115
            'igs' => 'model/iges',
116
            'jar' => 'application/java-archiver',
117
            'jpe' => 'image/jpeg',
118
            'jpeg' => 'image/jpeg',
119
            'jpg' => 'image/jpeg',
120
            'js' => 'application/x-javascript',
121
            'kar' => 'audio/midi',
122
            'lam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
123
            'latex' => 'application/x-latex',
124
            'lha' => 'application/octet-stream',
125
            'log' => 'text/plain',
126
            'lzh' => 'application/octet-stream',
127
            'm1a' => 'audio/mpeg',
128
            'm2a' => 'audio/mpeg',
129
            'm3u' => 'audio/x-mpegurl',
130
            'man' => 'application/x-troff-man',
131
            'me' => 'application/x-troff-me',
132
            'mesh' => 'model/mesh',
133
            'mid' => 'audio/midi',
134
            'midi' => 'audio/midi',
135
            'mov' => 'video/quicktime',
136
            'movie' => 'video/x-sgi-movie',
137
            'mp2' => 'audio/mpeg',
138
            'mp3' => 'audio/mpeg',
139
            'mp4' => 'video/mpeg4-generic',
140
            'mpa' => 'audio/mpeg',
141
            'mpe' => 'video/mpeg',
142
            'mpeg' => 'video/mpeg',
143
            'mpg' => 'video/mpeg',
144
            'mpga' => 'audio/mpeg',
145
            'ms' => 'application/x-troff-ms',
146
            'msh' => 'model/mesh',
147
            'mxu' => 'video/vnd.mpegurl',
148
            'nc' => 'application/x-netcdf',
149
            'oda' => 'application/oda',
150
            'oga' => 'audio/ogg',
151
            'ogg' => 'application/ogg',
152
            'ogx' => 'application/ogg',
153
            'ogv' => 'video/ogg',
154
            'pbm' => 'image/x-portable-bitmap',
155
            'pct' => 'image/pict',
156
            'pdb' => 'chemical/x-pdb',
157
            'pdf' => 'application/pdf',
158
            'pgm' => 'image/x-portable-graymap',
159
            'pgn' => 'application/x-chess-pgn',
160
            'pict' => 'image/pict',
161
            'png' => 'image/png',
162
            'pnm' => 'image/x-portable-anymap',
163
            'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
164
            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
165
            'pps' => 'application/vnd.ms-powerpoint',
166
            'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
167
            'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
168
            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
169
            'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
170
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
171
            'ppm' => 'image/x-portable-pixmap',
172
            'ppt' => 'application/vnd.ms-powerpoint',
173
            'pps' => 'application/vnd.ms-powerpoint',
174
            'ps' => 'application/postscript',
175
            'qt' => 'video/quicktime',
176
            'ra' => 'audio/x-realaudio',
177
            'ram' => 'audio/x-pn-realaudio',
178
            'rar' => 'image/x-rar-compressed',
179
            'ras' => 'image/x-cmu-raster',
180
            'rgb' => 'image/x-rgb',
181
            'rm' => 'audio/x-pn-realaudio',
182
            'roff' => 'application/x-troff',
183
            'rpm' => 'audio/x-pn-realaudio-plugin',
184
            'rtf' => 'text/rtf',
185
            'rtx' => 'text/richtext',
186
            'sgm' => 'text/sgml',
187
            'sgml' => 'text/sgml',
188
            'sh' => 'application/x-sh',
189
            'shar' => 'application/x-shar',
190
            'silo' => 'model/mesh',
191
            'sib' => 'application/X-Sibelius-Score',
192
            'sit' => 'application/x-stuffit',
193
            'skd' => 'application/x-koan',
194
            'skm' => 'application/x-koan',
195
            'skp' => 'application/x-koan',
196
            'skt' => 'application/x-koan',
197
            'smi' => 'application/smil',
198
            'smil' => 'application/smil',
199
            'snd' => 'audio/basic',
200
            'so' => 'application/octet-stream',
201
            'spl' => 'application/x-futuresplash',
202
            'src' => 'application/x-wais-source',
203
            'sv4cpio' => 'application/x-sv4cpio',
204
            'sv4crc' => 'application/x-sv4crc',
205
            'svf' => 'application/vnd.svf',
206
            'svg' => 'image/svg+xml',
207
            //'svgz' => 'image/svg+xml',
208
            'swf' => 'application/x-shockwave-flash',
209
            'sxc' => 'application/vnd.sun.xml.calc',
210
            'sxi' => 'application/vnd.sun.xml.impress',
211
            'sxw' => 'application/vnd.sun.xml.writer',
212
            't' => 'application/x-troff',
213
            'tar' => 'application/x-tar',
214
            'tcl' => 'application/x-tcl',
215
            'tex' => 'application/x-tex',
216
            'texi' => 'application/x-texinfo',
217
            'texinfo' => 'application/x-texinfo',
218
            'tga' => 'image/x-targa',
219
            'tif' => 'image/tif',
220
            'tiff' => 'image/tiff',
221
            'tr' => 'application/x-troff',
222
            'tsv' => 'text/tab-seperated-values',
223
            'txt' => 'text/plain',
224
            'ustar' => 'application/x-ustar',
225
            'vcd' => 'application/x-cdlink',
226
            'vrml' => 'model/vrml',
227
            'wav' => 'audio/x-wav',
228
            'wbmp' => 'image/vnd.wap.wbmp',
229
            'wbxml' => 'application/vnd.wap.wbxml',
230
            'wml' => 'text/vnd.wap.wml',
231
            'wmlc' => 'application/vnd.wap.wmlc',
232
            'wmls' => 'text/vnd.wap.wmlscript',
233
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
234
            'wma' => 'audio/x-ms-wma',
235
            'wmv' => 'video/x-ms-wmv',
236
            'wrl' => 'model/vrml',
237
            'xbm' => 'image/x-xbitmap',
238
            'xht' => 'application/xhtml+xml',
239
            'xhtml' => 'application/xhtml+xml',
240
            'xls' => 'application/vnd.ms-excel',
241
            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
242
            'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
243
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
244
            'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
245
            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
246
            'xml' => 'text/xml',
247
            'xpm' => 'image/x-xpixmap',
248
            'xsl' => 'text/xml',
249
            'xwd' => 'image/x-windowdump',
250
            'xyz' => 'chemical/x-xyz',
251
            'zip' => 'application/zip'
252
        ];
253
254
        if ($filename === true) {
255
            return $mime_types;
256
        }
257
258
        //get the extension of the file
259
        $extension = explode('.', $filename);
260
261
        //$filename will be an array if a . was found
262
        if (is_array($extension)) {
263
            $extension = strtolower($extension[sizeof($extension) - 1]);
0 ignored issues
show
Bug introduced by
The call to sizeof() has too few arguments starting with mode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

263
            $extension = strtolower($extension[/** @scrutinizer ignore-call */ sizeof($extension) - 1]);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
264
        } else {
265
            //file without extension
266
            $extension = 'empty';
267
        }
268
269
        //if the extension is found, return the content type
270
        if (isset($mime_types[$extension])) {
271
            return $mime_types[$extension];
272
        }
273
        //else return octet-stream
274
        return 'application/octet-stream';
275
    }
276
277
    /**
278
     * This function streams a file to the client
279
     *
280
     * @param string $full_file_name
281
     * @param boolean $forced
282
     * @param string $name
283
     * @param bool $fixLinksHttpToHttps change file content from http to https
284
     *
285
     * @return false if file doesn't exist, true if stream succeeded
286
     */
287
    public static function file_send_for_download(
288
        $full_file_name,
289
        $forced = false,
290
        $name = '',
291
        $fixLinksHttpToHttps = false
292
    ) {
293
        session_write_close(); //we do not need write access to session anymore
294
        if (!is_file($full_file_name)) {
295
            return false;
296
        }
297
        $filename = ($name == '') ? basename($full_file_name) : api_replace_dangerous_char($name);
298
        $len = filesize($full_file_name);
299
        // Fixing error when file name contains a ","
300
        $filename = str_replace(',', '', $filename);
301
302
        $sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
303
304
        if ($forced) {
305
            // Force the browser to save the file instead of opening it
306
            if (isset($sendFileHeaders) &&
307
                !empty($sendFileHeaders)) {
308
                header("X-Sendfile: $filename");
309
            }
310
311
            header('Content-type: application/octet-stream');
312
            header('Content-length: '.$len);
313
            if (preg_match("/MSIE 5.5/", $_SERVER['HTTP_USER_AGENT'])) {
314
                header('Content-Disposition: filename= '.$filename);
315
            } else {
316
                header('Content-Disposition: attachment; filename= '.$filename);
317
            }
318
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
319
                header('Pragma: ');
320
                header('Cache-Control: ');
321
                header('Cache-Control: public'); // IE cannot download from sessions without a cache
322
            }
323
            header('Content-Description: '.$filename);
324
            header('Content-Transfer-Encoding: binary');
325
326
            if (function_exists('ob_end_clean')) {
327
                // Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
328
                ob_end_clean();
329
            }
330
331
            $res = fopen($full_file_name, 'r');
332
            fpassthru($res);
0 ignored issues
show
Bug introduced by
It seems like $res can also be of type false; however, parameter $handle of fpassthru() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

332
            fpassthru(/** @scrutinizer ignore-type */ $res);
Loading history...
333
334
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type false.
Loading history...
335
        } else {
336
            //no forced download, just let the browser decide what to do according to the mimetype
337
338
            $content_type = self::file_get_mime_type($filename);
339
            $lpFixedEncoding = api_get_configuration_value('lp_fixed_encoding');
340
341
            // Commented to let courses content to be cached in order to improve performance:
342
            //header('Expires: Wed, 01 Jan 1990 00:00:00 GMT');
343
            //header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
344
345
            // Commented to avoid double caching declaration when playing with IE and HTTPS
346
            //header('Cache-Control: no-cache, must-revalidate');
347
            //header('Pragma: no-cache');
348
            switch ($content_type) {
349
                case 'text/html':
350
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
351
                        $content_type .= '; charset=UTF-8';
352
                    } else {
353
                        $encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
354
                        if (!empty($encoding)) {
355
                            $content_type .= '; charset='.$encoding;
356
                        }
357
                    }
358
                    break;
359
                case 'text/plain':
360
                    if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
361
                        $content_type .= '; charset=UTF-8';
362
                    } else {
363
                        $encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
364
                        if (!empty($encoding)) {
365
                            $content_type .= '; charset='.$encoding;
366
                        }
367
                    }
368
                    break;
369
                case 'application/vnd.dwg':
370
                case 'application/vnd.dwf':
371
                    header('Content-type: application/octet-stream');
372
                    break;
373
            }
374
            header('Content-type: '.$content_type);
375
            header('Content-Length: '.$len);
376
            $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
377
            if (strpos($user_agent, 'msie')) {
378
                header('Content-Disposition: ; filename= '.$filename);
379
            } else {
380
                header('Content-Disposition: inline; filename= '.$filename);
381
            }
382
383
            if ($fixLinksHttpToHttps) {
384
                $content = file_get_contents($full_file_name);
385
                $content = str_replace(
386
                    ['http%3A%2F%2F', 'http://'],
387
                    ['https%3A%2F%2F', 'https://'],
388
                    $content
389
                );
390
                echo $content;
391
            } else {
392
                if (function_exists('ob_end_clean')) {
393
                    // Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
394
                    ob_end_clean();
395
                }
396
397
                readfile($full_file_name);
398
            }
399
400
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type false.
Loading history...
401
        }
402
    }
403
404
    /**
405
     * Session folder filters
406
     *
407
     * @param string $path
408
     * @param int    $sessionId
409
     *
410
     * @return null|string
411
     */
412
    public static function getSessionFolderFilters($path, $sessionId)
413
    {
414
        $sessionId = intval($sessionId);
415
        $condition = null;
416
417
        if (!empty($sessionId)) {
418
            // Chat folder filter
419
            if ($path == '/chat_files') {
420
                $condition .= " AND (docs.session_id = '$sessionId') ";
421
            }
422
            // share_folder filter
423
            $condition .= " AND docs.path != '/shared_folder' ";
424
        }
425
426
        return $condition;
427
    }
428
429
    /**
430
     * Fetches all document data for the given user/group
431
     *
432
     * @param array $_course
433
     * @param string $path
434
     * @param int $to_group_id iid
435
     * @param int $to_user_id
436
     * @param boolean $can_see_invisible
437
     * @param boolean $search
438
     * @param int $sessionId
439
     * @return array with all document data
440
     */
441
    public static function get_all_document_data(
442
        $_course,
443
        $path = '/',
444
        $to_group_id = 0,
445
        $to_user_id = null,
446
        $can_see_invisible = false,
447
        $search = false,
448
        $sessionId = 0
449
    ) {
450
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
451
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
452
453
        $userGroupFilter = '';
454
        if (!is_null($to_user_id)) {
455
            $to_user_id = intval($to_user_id);
456
            $userGroupFilter = "last.to_user_id = $to_user_id";
457
            if (empty($to_user_id)) {
458
                $userGroupFilter = " (last.to_user_id = 0 OR last.to_user_id IS NULL) ";
459
            }
460
        } else {
461
            $to_group_id = intval($to_group_id);
462
            $userGroupFilter = "last.to_group_id = $to_group_id";
463
            if (empty($to_group_id)) {
464
                $userGroupFilter = "( last.to_group_id = 0 OR last.to_group_id IS NULL) ";
465
            }
466
        }
467
468
        // Escape underscores in the path so they don't act as a wildcard
469
        $originalPath = $path;
470
        $path = str_replace('_', '\_', $path);
471
472
        $visibility_bit = ' <> 2';
473
474
        // The given path will not end with a slash, unless it's the root '/'
475
        // so no root -> add slash
476
        $added_slash = $path == '/' ? '' : '/';
477
478
        // Condition for the session
479
        $sessionId = $sessionId ?: api_get_session_id();
480
        $condition_session = " AND (last.session_id = '$sessionId' OR (last.session_id = '0' OR last.session_id IS NULL) )";
481
        $condition_session .= self::getSessionFolderFilters($originalPath, $sessionId);
482
483
        $sharedCondition = null;
484
        if ($originalPath == '/shared_folder') {
485
            $students = CourseManager::get_user_list_from_course_code($_course['code'], $sessionId);
486
            if (!empty($students)) {
487
                $conditionList = [];
488
                foreach ($students as $studentId => $studentInfo) {
489
                    $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
490
                }
491
                $sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
492
            }
493
        }
494
495
        $sql = "SELECT
496
                    docs.id,
497
                    docs.filetype,
498
                    docs.path,
499
                    docs.title,
500
                    docs.comment,
501
                    docs.size,
502
                    docs.readonly,
503
                    docs.session_id,
504
                    last.session_id item_property_session_id,
505
                    last.lastedit_date,
506
                    last.visibility,
507
                    last.insert_user_id
508
                FROM $TABLE_ITEMPROPERTY AS last
509
                INNER JOIN $TABLE_DOCUMENT AS docs
510
                ON (
511
                    docs.id = last.ref AND
512
                    docs.c_id = last.c_id
513
                )
514
                WHERE                                
515
                    last.tool = '".TOOL_DOCUMENT."' AND 
516
                    docs.c_id = {$_course['real_id']} AND
517
                    last.c_id = {$_course['real_id']} AND
518
                    docs.path LIKE '".Database::escape_string($path.$added_slash.'%')."' AND
519
                    docs.path NOT LIKE '" . Database::escape_string($path.$added_slash.'%/%')."' AND
520
                    docs.path NOT LIKE '%_DELETED_%' AND
521
                    $userGroupFilter AND
522
                    last.visibility $visibility_bit
523
                    $condition_session
524
                    $sharedCondition
525
                ";
526
        $result = Database::query($sql);
527
528
        $doc_list = [];
529
        $document_data = [];
530
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
531
        $isCoach = api_is_coach();
532
        if ($result !== false && Database::num_rows($result) != 0) {
533
            while ($row = Database::fetch_array($result, 'ASSOC')) {
534
                if ($isCoach) {
535
                    // Looking for course items that are invisible to hide it in the session
536
                    if (in_array($row['id'], array_keys($doc_list))) {
537
                        if ($doc_list[$row['id']]['item_property_session_id'] == 0 &&
538
                            $doc_list[$row['id']]['session_id'] == 0
539
                        ) {
540
                            if ($doc_list[$row['id']]['visibility'] == 0) {
541
                                unset($document_data[$row['id']]);
542
                                continue;
543
                            }
544
                        }
545
                    }
546
                    $doc_list[$row['id']] = $row;
547
                }
548
549
                if (!$isCoach && !$is_allowed_to_edit) {
550
                    $doc_list[] = $row;
551
                }
552
553
                if ($row['filetype'] == 'file' &&
554
                    pathinfo($row['path'], PATHINFO_EXTENSION) == 'html'
555
                ) {
556
                    // Templates management
557
                    $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
558
                    $sql = "SELECT id FROM $table_template
559
                            WHERE
560
                                course_code = '".$_course['code']."' AND
561
                                user_id = '".api_get_user_id()."' AND
562
                                ref_doc = '".$row['id']."'";
563
                    $template_result = Database::query($sql);
564
                    $row['is_template'] = (Database::num_rows($template_result) > 0) ? 1 : 0;
565
                }
566
                $row['basename'] = basename($row['path']);
567
                // Just filling $document_data.
568
                $document_data[$row['id']] = $row;
569
            }
570
571
            // Only for the student we filter the results see BT#1652
572
            if (!$isCoach && !$is_allowed_to_edit) {
573
                $ids_to_remove = [];
574
                $my_repeat_ids = $temp = [];
575
576
                // Selecting repeated ids
577
                foreach ($doc_list as $row) {
578
                    if (in_array($row['id'], array_keys($temp))) {
579
                        $my_repeat_ids[] = $row['id'];
580
                    }
581
                    $temp[$row['id']] = $row;
582
                }
583
584
                //@todo use the self::is_visible function
585
                // Checking visibility in a session
586
                foreach ($my_repeat_ids as $id) {
587
                    foreach ($doc_list as $row) {
588
                        if ($id == $row['id']) {
589
                            if ($row['visibility'] == 0 && $row['item_property_session_id'] == 0) {
590
                                $delete_repeated[$id] = true;
591
                            }
592
                            if ($row['visibility'] == 0 && $row['item_property_session_id'] != 0) {
593
                                $delete_repeated[$id] = true;
594
                            }
595
                        }
596
                    }
597
                }
598
599
                foreach ($doc_list as $key => $row) {
600
                    if (in_array($row['visibility'], ['0', '2']) &&
601
                        !in_array($row['id'], $my_repeat_ids)
602
                    ) {
603
                        $ids_to_remove[] = $row['id'];
604
                        unset($doc_list[$key]);
605
                    }
606
                }
607
608
                foreach ($document_data as $row) {
609
                    if (in_array($row['id'], $ids_to_remove)) {
610
                        unset($document_data[$row['id']]);
611
                    }
612
                    if (isset($delete_repeated[$row['id']]) && $delete_repeated[$row['id']]) {
613
                        unset($document_data[$row['id']]);
614
                    }
615
                }
616
617
                // Checking parents visibility.
618
                $final_document_data = [];
619
                foreach ($document_data as $row) {
620
                    $is_visible = self::check_visibility_tree(
621
                        $row['id'],
622
                        $_course['code'],
623
                        $sessionId,
624
                        api_get_user_id(),
625
                        $to_group_id
626
                    );
627
                    if ($is_visible) {
628
                        $final_document_data[$row['id']] = $row;
629
                    }
630
                }
631
            } else {
632
                $final_document_data = $document_data;
633
            }
634
635
            return $final_document_data;
636
        } else {
637
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
638
        }
639
    }
640
641
    /**
642
     * Gets the paths of all folders in a course
643
     * can show all folders (except for the deleted ones) or only visible ones
644
     *
645
     * @param array $_course
646
     * @param int $groupIid iid
647
     * @param boolean $can_see_invisible
648
     * @param boolean $getInvisibleList
649
     * @param string $path current path
650
     *
651
     * @return array with paths
652
     */
653
    public static function get_all_document_folders(
654
        $_course,
655
        $groupIid = 0,
656
        $can_see_invisible = false,
657
        $getInvisibleList = false,
658
        $path = ''
659
    ) {
660
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
661
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
662
        $groupIid = intval($groupIid);
663
        $document_folders = [];
664
665
        $students = CourseManager::get_user_list_from_course_code(
666
            $_course['code'],
667
            api_get_session_id()
668
        );
669
670
        $conditionList = [];
671
        if (!empty($students)) {
672
            foreach ($students as $studentId => $studentInfo) {
673
                $conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
674
            }
675
        }
676
677
        $groupCondition = " last.to_group_id = $groupIid";
678
        if (empty($groupIid)) {
679
            $groupCondition = " (last.to_group_id = 0 OR last.to_group_id IS NULL)";
680
        }
681
682
        $show_users_condition = '';
683
        if (api_get_setting('show_users_folders') === 'false') {
684
            $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
685
        }
686
687
        if ($can_see_invisible) {
688
            // condition for the session
689
            $session_id = api_get_session_id();
690
            //$condition_session = api_get_session_condition($session_id, true, false, 'docs.session_id');
691
692
            $session_id = $session_id ?: api_get_session_id();
693
            $condition_session = " AND (last.session_id = '$session_id' OR (last.session_id = '0' OR last.session_id IS NULL) )";
694
            $condition_session .= self::getSessionFolderFilters($path, $session_id);
695
696
            if ($groupIid <> 0) {
697
                $sql = "SELECT DISTINCT docs.id, path
698
                       FROM $TABLE_ITEMPROPERTY  AS last
699
                       INNER JOIN $TABLE_DOCUMENT  AS docs
700
                       ON (
701
                            docs.id = last.ref AND
702
                            docs.c_id = last.c_id
703
                       )
704
                       WHERE                       
705
                            last.tool = '".TOOL_DOCUMENT."' AND
706
                            last.c_id = {$_course['real_id']} AND
707
                            docs.c_id = {$_course['real_id']} AND
708
                            docs.filetype = 'folder' AND
709
                            $groupCondition AND
710
                            docs.path NOT LIKE '%shared_folder%' AND
711
                            docs.path NOT LIKE '%_DELETED_%' AND
712
                            last.visibility <> 2                            
713
                            $condition_session ";
714
            } else {
715
                $sql = "SELECT DISTINCT docs.id, path
716
                        FROM $TABLE_ITEMPROPERTY  AS last
717
                        INNER JOIN $TABLE_DOCUMENT  AS docs
718
                        ON (
719
                            docs.id = last.ref AND
720
                            docs.c_id = last.c_id                          
721
                        )
722
                        WHERE
723
                            last.tool = '".TOOL_DOCUMENT."' AND
724
                            last.c_id = {$_course['real_id']} AND
725
                            docs.c_id = {$_course['real_id']} AND
726
                            docs.filetype = 'folder' AND
727
                            docs.path NOT LIKE '%_DELETED_%' AND
728
                            $groupCondition AND
729
                            last.visibility <> 2
730
                            $show_users_condition 
731
                            $condition_session 
732
                        ";
733
            }
734
            $result = Database::query($sql);
735
736
            if ($result && Database::num_rows($result) != 0) {
737
                while ($row = Database::fetch_array($result, 'ASSOC')) {
738
                    if (self::is_folder_to_avoid($row['path'])) {
739
                        continue;
740
                    }
741
742
                    if (strpos($row['path'], '/shared_folder/') !== false) {
743
                        if (!in_array($row['path'], $conditionList)) {
744
                            continue;
745
                        }
746
                    }
747
748
                    $document_folders[$row['id']] = $row['path'];
749
                }
750
751
                if (!empty($document_folders)) {
752
                    natsort($document_folders);
753
                }
754
                return $document_folders;
755
            } else {
756
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
757
            }
758
        } else {
759
            // No invisible folders
760
            // Condition for the session
761
            $session_id = api_get_session_id();
762
            $condition_session = api_get_session_condition(
763
                $session_id,
764
                true,
765
                false,
766
                'docs.session_id'
767
            );
768
769
            $visibilityCondition = 'last.visibility = 1';
770
            $fileType = "docs.filetype = 'folder' AND";
771
            if ($getInvisibleList) {
772
                $visibilityCondition = 'last.visibility = 0';
773
                $fileType = '';
774
            }
775
776
            //get visible folders
777
            $sql = "SELECT DISTINCT docs.id, path
778
                    FROM
779
                    $TABLE_ITEMPROPERTY AS last 
780
                    INNER JOIN $TABLE_DOCUMENT AS docs
781
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
782
                    WHERE
783
                        $fileType
784
                        last.tool = '".TOOL_DOCUMENT."' AND
785
                        $groupCondition AND
786
                        $visibilityCondition
787
                        $show_users_condition
788
                        $condition_session AND
789
                        last.c_id = {$_course['real_id']}  AND
790
                        docs.c_id = {$_course['real_id']} ";
791
792
            $result = Database::query($sql);
793
794
            $visibleFolders = [];
795
            while ($row = Database::fetch_array($result, 'ASSOC')) {
796
                $visibleFolders[$row['id']] = $row['path'];
797
            }
798
799
            if ($getInvisibleList) {
800
                return $visibleFolders;
801
            }
802
803
            //get invisible folders
804
            $sql = "SELECT DISTINCT docs.id, path
805
                    FROM $TABLE_ITEMPROPERTY AS last 
806
                    INNER JOIN $TABLE_DOCUMENT AS docs
807
                    ON (docs.id = last.ref AND last.c_id = docs.c_id)
808
                    WHERE                        
809
                        docs.filetype = 'folder' AND
810
                        last.tool = '".TOOL_DOCUMENT."' AND
811
                        $groupCondition AND
812
                        last.visibility = 0 $condition_session AND
813
                        last.c_id = {$_course['real_id']} AND
814
                        docs.c_id = {$_course['real_id']} ";
815
            $result = Database::query($sql);
816
            $invisibleFolders = [];
817
            while ($row = Database::fetch_array($result, 'ASSOC')) {
818
                //get visible folders in the invisible ones -> they are invisible too
819
                $sql = "SELECT DISTINCT docs.id, path
820
                        FROM $TABLE_ITEMPROPERTY AS last 
821
                        INNER JOIN $TABLE_DOCUMENT AS docs
822
                        ON (docs.id = last.ref AND docs.c_id = last.c_id)
823
                        WHERE                            
824
                            docs.path LIKE '".Database::escape_string($row['path'].'/%')."' AND
825
                            docs.filetype = 'folder' AND
826
                            last.tool = '" . TOOL_DOCUMENT."' AND
827
                            $groupCondition AND
828
                            last.visibility = 1 $condition_session AND
829
                            last.c_id = {$_course['real_id']} AND
830
                            docs.c_id = {$_course['real_id']}  ";
831
                $folder_in_invisible_result = Database::query($sql);
832
                while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
833
                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
834
                }
835
            }
836
837
            // If both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
838
            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
839
                $document_folders = array_diff($visibleFolders, $invisibleFolders);
840
                natsort($document_folders);
841
                return $document_folders;
842
            } elseif (is_array($visibleFolders)) {
843
                natsort($visibleFolders);
844
                return $visibleFolders;
845
            } else {
846
                //no visible folders found
847
                return false;
848
            }
849
        }
850
    }
851
852
    /**
853
     * This check if a document has the readonly property checked, then see if the user
854
     * is the owner of this file, if all this is true then return true.
855
     *
856
     * @param array  $_course
857
     * @param int    $user_id id of the current user
858
     * @param string $file path stored in the database (if not defined, $documentId must be used)
859
     * @param int    $document_id in case you dont have the file path ,
860
     *                insert the id of the file here and leave $file in blank ''
861
     * @param bool $to_delete
862
     * @param int $sessionId
863
     * @return boolean true/false
864
     * */
865
    public static function check_readonly(
866
        $_course,
867
        $user_id,
868
        $file = null,
869
        $document_id = '',
870
        $to_delete = false,
871
        $sessionId = null,
872
        $documentId = null
873
    ) {
874
        if (empty($sessionId)) {
875
            $sessionId = api_get_session_id();
876
        } else {
877
            $sessionId = intval($sessionId);
878
        }
879
880
        if (empty($document_id) || !is_numeric($document_id)) {
881
            $document_id = self::get_document_id($_course, $file, $sessionId);
882
        } else {
883
            $document_id = intval($document_id);
884
        }
885
886
        $TABLE_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
887
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
888
        $course_id = $_course['real_id'];
889
890
        if ($to_delete) {
891
            if (self::is_folder($_course, $document_id)) {
892
                if (!empty($file)) {
893
                    $path = Database::escape_string($file);
894
                    // Check
895
                    $sql = "SELECT td.id, readonly, tp.insert_user_id
896
                            FROM $TABLE_DOCUMENT td 
897
                            INNER JOIN $TABLE_PROPERTY tp
898
                            ON (td.c_id = tp.c_id AND tp.ref= td.id)
899
                            WHERE
900
                                td.c_id = $course_id AND
901
                                tp.c_id = $course_id AND
902
                                td.session_id = $sessionId AND                                
903
                                (path='".$path."' OR path LIKE BINARY '".$path."/%' ) ";
904
                    // Get all id's of documents that are deleted
905
                    $what_to_check_result = Database::query($sql);
906
907
                    if ($what_to_check_result && Database::num_rows($what_to_check_result) != 0) {
908
                        // file with readonly set to 1 exist?
909
                        $readonly_set = false;
910
                        while ($row = Database::fetch_array($what_to_check_result)) {
911
                            //query to delete from item_property table
912
                            if ($row['readonly'] == 1) {
913
                                if (!($row['insert_user_id'] == $user_id)) {
914
                                    $readonly_set = true;
915
                                    break;
916
                                }
917
                            }
918
                        }
919
920
                        if ($readonly_set) {
921
                            return true;
922
                        }
923
                    }
924
                }
925
                return false;
926
            }
927
        }
928
929
        if (!empty($document_id)) {
930
            $sql = "SELECT a.insert_user_id, b.readonly
931
                   FROM $TABLE_PROPERTY a 
932
                   INNER JOIN $TABLE_DOCUMENT b
933
                   ON (a.c_id = b.c_id AND a.ref= b.id)
934
                   WHERE
935
            			a.c_id = $course_id AND
936
                        b.c_id = $course_id AND
937
            			a.ref = $document_id 
938
                    LIMIT 1";
939
            $result = Database::query($sql);
940
            $doc_details = Database::fetch_array($result, 'ASSOC');
941
942
            if ($doc_details['readonly'] == 1) {
943
                return !($doc_details['insert_user_id'] == $user_id || api_is_platform_admin());
944
            }
945
        }
946
        return false;
947
    }
948
949
    /**
950
     * This check if a document is a folder or not
951
     * @param array  $_course
952
     * @param int    $id document id
953
     * @return boolean true/false
954
     * */
955
    public static function is_folder($_course, $id)
956
    {
957
        $table = Database::get_course_table(TABLE_DOCUMENT);
958
        if (empty($_course)) {
959
            return false;
960
        }
961
        $course_id = $_course['real_id'];
962
        $id = (int) $id;
963
        $sql = "SELECT filetype FROM $table
964
                WHERE c_id = $course_id AND id= $id";
965
        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
966
967
        return $result['filetype'] == 'folder';
968
    }
969
970
    /**
971
     * @param int $document_id
972
     * @param array $course_info
973
     * @param int $session_id
974
     * @param bool $remove_content_from_db
975
     */
976
    public static function deleteDocumentFromDb(
977
        $document_id,
978
        $course_info = [],
979
        $session_id = 0,
980
        $remove_content_from_db = false
981
    ) {
982
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
983
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
984
985
        // Deleting from the DB
986
        $user_id = api_get_user_id();
987
        $document_id = intval($document_id);
988
989
        if (empty($course_info)) {
990
            $course_info = api_get_course_info();
991
        }
992
993
        if (empty($session_id)) {
994
            $session_id = api_get_session_id();
995
        }
996
        // Soft DB delete
997
        api_item_property_update(
998
            $course_info,
999
            TOOL_DOCUMENT,
1000
            $document_id,
1001
            'delete',
1002
            $user_id,
1003
            null,
1004
            null,
1005
            null,
1006
            null,
1007
            $session_id
1008
        );
1009
        self::delete_document_from_search_engine($course_info['code'], $document_id);
1010
        self::unset_document_as_template($document_id, $course_info['code'], $user_id);
1011
1012
        //Hard DB delete
1013
        if ($remove_content_from_db) {
1014
            $sql = "DELETE FROM $TABLE_ITEMPROPERTY
1015
                    WHERE
1016
                        c_id = {$course_info['real_id']} AND
1017
                        ref = ".$document_id." AND
1018
                        tool='".TOOL_DOCUMENT."'";
1019
            Database::query($sql);
1020
1021
            $sql = "DELETE FROM $TABLE_DOCUMENT
1022
                    WHERE c_id = {$course_info['real_id']} AND id = ".$document_id;
1023
            Database::query($sql);
1024
        }
1025
    }
1026
1027
    /**
1028
     * This deletes a document by changing visibility to 2, renaming it to filename_DELETED_#id
1029
     * Files/folders that are inside a deleted folder get visibility 2
1030
     *
1031
     * @param array  $_course
1032
     * @param string $path Path stored in the database
1033
     * @param string $base_work_dir Path to the documents folder (if not defined, $documentId must be used)
1034
     * @param int    $sessionId The ID of the session, if any
1035
     * @param int    $documentId The document id, if available
1036
     * @param int    $groupId iid
1037
     * @return boolean true/false
1038
     * @todo now only files/folders in a folder get visibility 2, we should rename them too.
1039
     * @todo We should be able to get rid of this later when using only documentId (check further usage)
1040
     */
1041
    public static function delete_document(
1042
        $_course,
1043
        $path = null,
1044
        $base_work_dir = null,
1045
        $sessionId = null,
1046
        $documentId = null,
1047
        $groupId = 0
1048
    ) {
1049
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1050
1051
        if (empty($groupId)) {
1052
            $groupId = api_get_group_id();
1053
        } else {
1054
            $groupId = intval($groupId);
1055
        }
1056
1057
        if (empty($sessionId)) {
1058
            $sessionId = api_get_session_id();
1059
        } else {
1060
            $sessionId = intval($sessionId);
1061
        }
1062
1063
        $course_id = $_course['real_id'];
1064
1065
        if (empty($course_id)) {
1066
            return false;
1067
        }
1068
1069
        if (empty($base_work_dir)) {
1070
            return false;
1071
        }
1072
1073
        if (empty($documentId)) {
1074
            $documentId = self::get_document_id($_course, $path, $sessionId);
1075
            $docInfo = self::get_document_data_by_id(
1076
                $documentId,
1077
                $_course['code'],
1078
                false,
1079
                $sessionId
1080
            );
1081
            $path = $docInfo['path'];
1082
        } else {
1083
            $docInfo = self::get_document_data_by_id(
1084
                $documentId,
1085
                $_course['code'],
1086
                false,
1087
                $sessionId
1088
            );
1089
            if (empty($docInfo)) {
1090
                return false;
1091
            }
1092
            $path = $docInfo['path'];
1093
        }
1094
1095
        $documentId = intval($documentId);
1096
1097
        if (empty($path) || empty($docInfo) || empty($documentId)) {
1098
            return false;
1099
        }
1100
1101
        $itemInfo = api_get_item_property_info(
1102
            $_course['real_id'],
1103
            TOOL_DOCUMENT,
1104
            $documentId,
1105
            $sessionId,
1106
            $groupId
1107
        );
1108
1109
        if (empty($itemInfo)) {
1110
            return false;
1111
        }
1112
1113
        // File was already deleted.
1114
        if ($itemInfo['lastedit_type'] == 'DocumentDeleted' ||
1115
            $itemInfo['lastedit_type'] == 'delete' ||
1116
            $itemInfo['visibility'] == 2
1117
        ) {
1118
            return false;
1119
        }
1120
1121
        // Filtering by group.
1122
        if ($itemInfo['to_group_id'] != $groupId) {
1123
            return false;
1124
        }
1125
1126
        $document_exists_in_disk = file_exists($base_work_dir.$path);
1127
        $new_path = $path.'_DELETED_'.$documentId;
1128
1129
        $file_deleted_from_db = false;
1130
        $file_deleted_from_disk = false;
1131
        $file_renamed_from_disk = false;
1132
1133
        if ($documentId) {
1134
            // Deleting doc from the DB.
1135
            self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1136
            // Checking
1137
            // $file_exists_in_db = self::get_document_data_by_id($documentId, $_course['code']);
1138
            $file_deleted_from_db = true;
1139
        }
1140
1141
        // Looking for children.
1142
        if ($docInfo['filetype'] == 'folder') {
1143
            $cleanPath = Database::escape_string($path);
1144
1145
            // Deleted files inside this folder.
1146
            $sql = "SELECT id FROM $TABLE_DOCUMENT
1147
                    WHERE
1148
                        c_id = $course_id AND
1149
                        session_id = $sessionId AND
1150
                        path LIKE BINARY '".$cleanPath."/%'";
1151
1152
            // Get all id's of documents that are deleted.
1153
            $result = Database::query($sql);
1154
1155
            if ($result && Database::num_rows($result) != 0) {
1156
                // Recursive delete.
1157
                while ($row = Database::fetch_array($result)) {
1158
                    self::delete_document(
1159
                        $_course,
1160
                        null,
1161
                        $base_work_dir,
1162
                        $sessionId,
1163
                        $row['id'],
1164
                        $groupId
1165
                    );
1166
                }
1167
            }
1168
        }
1169
1170
        if ($document_exists_in_disk) {
1171
            if (api_get_setting('permanently_remove_deleted_files') === 'true') {
1172
                // Delete documents, do it like this so metadata gets deleted too
1173
                my_delete($base_work_dir.$path);
1174
                // Hard delete.
1175
                self::deleteDocumentFromDb($documentId, $_course, $sessionId, true);
1176
                $file_deleted_from_disk = true;
1177
            } else {
1178
                // Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
1179
                if (is_file($base_work_dir.$path) || is_dir($base_work_dir.$path)) {
1180
                    if (rename($base_work_dir.$path, $base_work_dir.$new_path)) {
1181
                        $new_path = Database::escape_string($new_path);
1182
1183
                        $sql = "UPDATE $TABLE_DOCUMENT
1184
                                SET path = '".$new_path."'
1185
                                WHERE
1186
                                    c_id = $course_id AND
1187
                                    session_id = $sessionId AND
1188
                                    id = ".$documentId;
1189
                        Database::query($sql);
1190
1191
                        // Soft delete.
1192
                        self::deleteDocumentFromDb($documentId, $_course, $sessionId);
1193
1194
                        // Change path of sub folders and documents in database.
1195
                        $old_item_path = $docInfo['path'];
1196
                        $new_item_path = $new_path.substr($old_item_path, strlen($path));
1197
                        $new_item_path = Database::escape_string($new_item_path);
1198
1199
                        $sql = "UPDATE $TABLE_DOCUMENT
1200
                                SET path = '".$new_item_path."'
1201
                                WHERE
1202
                                    c_id = $course_id AND
1203
                                    session_id = $sessionId AND
1204
                                    id = ".$documentId;
1205
                        Database::query($sql);
1206
1207
                        $file_renamed_from_disk = true;
1208
                    } else {
1209
                        // Couldn't rename - file permissions problem?
1210
                        error_log(
1211
                            __FILE__.' '.__LINE__.': Error renaming '.$base_work_dir.$path.' to '.$base_work_dir.$new_path.'. This is probably due to file permissions',
1212
                            0
1213
                        );
1214
                    }
1215
                }
1216
            }
1217
        }
1218
        // Checking inconsistency
1219
        //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').')');
1220
        if ($file_deleted_from_db && $file_deleted_from_disk ||
1221
            $file_deleted_from_db && $file_renamed_from_disk
1222
        ) {
1223
            return true;
1224
        } else {
1225
            //Something went wrong
1226
            //The file or directory isn't there anymore (on the filesystem)
1227
            // This means it has been removed externally. To prevent a
1228
            // blocking error from happening, we drop the related items from the
1229
            // item_property and the document table.
1230
            error_log(
1231
                __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',
1232
                0
1233
            );
1234
            return false;
1235
        }
1236
    }
1237
1238
    /**
1239
     * Removes documents from search engine database
1240
     *
1241
     * @param string $course_id Course code
1242
     * @param int $document_id Document id to delete
1243
     */
1244
    public static function delete_document_from_search_engine($course_id, $document_id)
1245
    {
1246
        // remove from search engine if enabled
1247
        if (api_get_setting('search_enabled') === 'true') {
1248
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1249
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1250
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1251
            $res = Database::query($sql);
1252
            if (Database::num_rows($res) > 0) {
1253
                $row2 = Database::fetch_array($res);
1254
                $di = new ChamiloIndexer();
1255
                $di->remove_document($row2['search_did']);
1256
            }
1257
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
1258
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_DOCUMENT, $document_id);
1259
            Database::query($sql);
1260
1261
            // remove terms from db
1262
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1263
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $document_id);
1264
        }
1265
    }
1266
1267
    /**
1268
     * Gets the id of a document with a given path
1269
     *
1270
     * @param array $courseInfo
1271
     * @param string $path
1272
     * @param int $sessionId
1273
     * @return int id of document / false if no doc found
1274
     */
1275
    public static function get_document_id($courseInfo, $path, $sessionId = null)
1276
    {
1277
        $table = Database::get_course_table(TABLE_DOCUMENT);
1278
        $courseId = $courseInfo['real_id'];
1279
1280
        if (!isset($sessionId)) {
1281
            $sessionId = api_get_session_id();
1282
        } else {
1283
            $sessionId = intval($sessionId);
1284
        }
1285
1286
        $path = Database::escape_string($path);
1287
        if (!empty($courseId) && !empty($path)) {
1288
            $sql = "SELECT id FROM $table
1289
                    WHERE
1290
                        c_id = $courseId AND
1291
                        path LIKE BINARY '$path' AND
1292
                        session_id = $sessionId
1293
                    LIMIT 1";
1294
1295
            $result = Database::query($sql);
1296
            if (Database::num_rows($result)) {
1297
                $row = Database::fetch_array($result);
1298
1299
                return intval($row['id']);
1300
            }
1301
        }
1302
1303
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
1304
    }
1305
1306
    /**
1307
     * Gets the document data with a given id
1308
     *
1309
     * @param int $id Document Id (id field in c_document table)
1310
     * @param string $course_code Course code
1311
     * @param bool $load_parents load folder parents.
1312
     * @param int $session_id The session ID,
1313
     * 0 if requires context *out of* session, and null to use global context
1314
     * @return array document content
1315
     */
1316
    public static function get_document_data_by_id(
1317
        $id,
1318
        $course_code,
1319
        $load_parents = false,
1320
        $session_id = null
1321
    ) {
1322
        $course_info = api_get_course_info($course_code);
1323
        $course_id = $course_info['real_id'];
1324
1325
        if (empty($course_info)) {
1326
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
1327
        }
1328
1329
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
1330
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
1331
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
1332
        $id = intval($id);
1333
        $sessionCondition = api_get_session_condition($session_id, true, true);
1334
1335
        $sql = "SELECT * FROM $TABLE_DOCUMENT
1336
                WHERE c_id = $course_id $sessionCondition AND id = $id";
1337
1338
        $result = Database::query($sql);
1339
        if ($result && Database::num_rows($result) == 1) {
1340
            $row = Database::fetch_array($result, 'ASSOC');
1341
            //@todo need to clarify the name of the URLs not nice right now
1342
            $url_path = urlencode($row['path']);
1343
            $path = str_replace('%2F', '/', $url_path);
1344
            $pathinfo = pathinfo($row['path']);
1345
1346
            $row['url'] = api_get_path(WEB_CODE_PATH).'document/showinframes.php?cidReq='.$course_code.'&id='.$id;
1347
            $row['document_url'] = api_get_path(WEB_CODE_PATH).'document/document.php?cidReq='.$course_code.'&id='.$id;
1348
            $row['absolute_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1349
            $row['absolute_path_from_document'] = '/document'.$row['path'];
1350
            $row['absolute_parent_path'] = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$pathinfo['dirname'].'/';
1351
            $row['direct_url'] = $www.$path;
1352
            $row['basename'] = basename($row['path']);
1353
1354
            if (dirname($row['path']) == '.') {
1355
                $row['parent_id'] = '0';
1356
            } else {
1357
                $row['parent_id'] = self::get_document_id($course_info, dirname($row['path']), $session_id);
1358
            }
1359
            $parents = [];
1360
1361
            //Use to generate parents (needed for the breadcrumb)
1362
            //@todo sorry but this for is here because there's not a parent_id in the document table so we parsed the path!!
1363
            if ($load_parents) {
1364
                $dir_array = explode('/', $row['path']);
1365
                $dir_array = array_filter($dir_array);
1366
                $array_len = count($dir_array) + 1;
1367
                $real_dir = '';
1368
1369
                for ($i = 1; $i < $array_len; $i++) {
1370
                    $real_dir .= '/'.(isset($dir_array[$i]) ? $dir_array[$i] : '');
1371
                    $parent_id = self::get_document_id($course_info, $real_dir);
1372
                    if ($session_id != 0 && empty($parent_id)) {
1373
                        $parent_id = self::get_document_id($course_info, $real_dir, 0);
1374
                    }
1375
                    if (!empty($parent_id)) {
1376
                        $sub_document_data = self::get_document_data_by_id(
1377
                            $parent_id,
1378
                            $course_code,
1379
                            false,
1380
                            $session_id
1381
                        );
1382
                        if ($session_id != 0 and !$sub_document_data) {
1383
                            $sub_document_data = self::get_document_data_by_id(
1384
                                $parent_id,
1385
                                $course_code,
1386
                                false,
1387
                                0
1388
                            );
1389
                        }
1390
                        //@todo add visibility here
1391
                        $parents[] = $sub_document_data;
1392
                    }
1393
                }
1394
            }
1395
            $row['parents'] = $parents;
1396
1397
            return $row;
1398
        }
1399
1400
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
1401
    }
1402
1403
    /**
1404
     * Allow to set a specific document as a new template for CKeditor
1405
     * for a particular user in a particular course
1406
     *
1407
     * @param string $title
1408
     * @param string $description
1409
     * @param int $document_id_for_template the document id
1410
     * @param string $course_code
1411
     * @param int $user_id
1412
     * @param string $image
1413
     * @return bool
1414
     */
1415
    public static function set_document_as_template(
1416
        $title,
1417
        $description,
1418
        $document_id_for_template,
1419
        $course_code,
1420
        $user_id,
1421
        $image
1422
    ) {
1423
        // Database table definition
1424
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1425
        $params = [
1426
            'title' => $title,
1427
            'description' => $description,
1428
            'course_code' => $course_code,
1429
            'user_id' => $user_id,
1430
            'ref_doc' => $document_id_for_template,
1431
            'image' => $image,
1432
        ];
1433
        Database::insert($table_template, $params);
1434
        return true;
1435
    }
1436
1437
    /**
1438
     * Unset a document as template
1439
     *
1440
     * @param int $document_id
1441
     * @param string $course_code
1442
     * @param int $user_id
1443
     */
1444
    public static function unset_document_as_template(
1445
        $document_id,
1446
        $course_code,
1447
        $user_id
1448
    ) {
1449
        $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
1450
        $course_code = Database::escape_string($course_code);
1451
        $user_id = intval($user_id);
1452
        $document_id = intval($document_id);
1453
1454
        $sql = 'SELECT id FROM '.$table_template.'
1455
                WHERE
1456
                    course_code="' . $course_code.'" AND
1457
                    user_id="' . $user_id.'" AND
1458
                    ref_doc="' . $document_id.'"';
1459
        $result = Database::query($sql);
1460
        $template_id = Database::result($result, 0, 0);
1461
1462
        my_delete(api_get_path(SYS_CODE_PATH).'upload/template_thumbnails/'.$template_id.'.jpg');
1463
1464
        $sql = 'DELETE FROM '.$table_template.'
1465
                WHERE
1466
                    course_code="' . $course_code.'" AND
1467
                    user_id="' . $user_id.'" AND
1468
                    ref_doc="' . $document_id.'"';
1469
1470
        Database::query($sql);
1471
    }
1472
1473
    /**
1474
     * Return true if the documentpath have visibility=1 as
1475
     * item_property (you should use the is_visible_by_id)
1476
     *
1477
     * @param string $doc_path the relative complete path of the document
1478
     * @param array  $course the _course array info of the document's course
1479
     * @param int
1480
     * @param string
1481
     * @return bool
1482
     */
1483
    public static function is_visible(
1484
        $doc_path,
1485
        $course,
1486
        $session_id = 0,
1487
        $file_type = 'file'
1488
    ) {
1489
        $docTable = Database::get_course_table(TABLE_DOCUMENT);
1490
        $propTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
1491
1492
        $course_id = $course['real_id'];
1493
        //note the extra / at the end of doc_path to match every path in the document table that is part of the document path
1494
1495
        $session_id = intval($session_id);
1496
        $condition = "AND d.session_id IN  ('$session_id', '0') ";
1497
        // The " d.filetype='file' " let the user see a file even if the folder is hidden see #2198
1498
1499
        /*
1500
          When using hotpotatoes files, a new html files are generated
1501
          in the hotpotatoes folder to display the test.
1502
          The genuine html file is copied to math4.htm(user_id).t.html
1503
          Images files are not copied, and keep same name.
1504
          To check the html file visibility, we don't have to check file math4.htm(user_id).t.html but file math4.htm
1505
          In this case, we have to remove (user_id).t.html to check the visibility of the file
1506
          For images, we just check the path of the image file.
1507
1508
          Exemple of hotpotatoes folder :
1509
          A.jpg
1510
          maths4-consigne.jpg
1511
          maths4.htm
1512
          maths4.htm1.t.html
1513
          maths4.htm52.t.html
1514
          maths4.htm654.t.html
1515
          omega.jpg
1516
          theta.jpg
1517
         */
1518
1519
        if (strpos($doc_path, 'HotPotatoes_files') && preg_match("/\.t\.html$/", $doc_path)) {
1520
            $doc_path = substr($doc_path, 0, strlen($doc_path) - 7 - strlen(api_get_user_id()));
1521
        }
1522
1523
        if (!in_array($file_type, ['file', 'folder'])) {
1524
            $file_type = 'file';
1525
        }
1526
        $doc_path = Database::escape_string($doc_path).'/';
1527
1528
        $sql = "SELECT visibility
1529
                FROM $docTable d
1530
                INNER JOIN $propTable ip
1531
                ON (d.id = ip.ref AND d.c_id = ip.c_id)
1532
        		WHERE
1533
        		    d.c_id  = $course_id AND 
1534
        		    ip.c_id = $course_id AND
1535
        		    ip.tool = '".TOOL_DOCUMENT."' $condition AND
1536
        			filetype = '$file_type' AND
1537
        			locate(concat(path,'/'), '$doc_path')=1
1538
                ";
1539
1540
        $result = Database::query($sql);
1541
        $is_visible = false;
1542
        if (Database::num_rows($result) > 0) {
1543
            $row = Database::fetch_array($result, 'ASSOC');
1544
            if ($row['visibility'] == 1) {
1545
                $is_visible = api_is_allowed_in_course() || api_is_platform_admin();
1546
            }
1547
        }
1548
1549
        /* improved protection of documents viewable directly through the url:
1550
            incorporates the same protections of the course at the url of
1551
            documents:
1552
            access allowed for the whole world Open, access allowed for
1553
            users registered on the platform Private access, document accessible
1554
            only to course members (see the Users list), Completely closed;
1555
            the document is only accessible to the course admin and
1556
            teaching assistants.*/
1557
        //return $_SESSION ['is_allowed_in_course'] || api_is_platform_admin();
1558
        return $is_visible;
1559
    }
1560
1561
    /**
1562
     * Return true if user can see a file
1563
     *
1564
     * @param   int     document id
1565
     * @param   array   course info
1566
     * @param   int
1567
     * @param   int
1568
     * @param bool
1569
     * @return  bool
1570
     */
1571
    public static function is_visible_by_id(
1572
        $doc_id,
1573
        $course_info,
1574
        $session_id,
1575
        $user_id,
1576
        $admins_can_see_everything = true,
1577
        $userIsSubscribed = null
1578
    ) {
1579
        $user_in_course = false;
1580
1581
        //1. Checking the course array
1582
        if (empty($course_info)) {
1583
            $course_info = api_get_course_info();
1584
            if (empty($course_info)) {
1585
                return false;
1586
            }
1587
        }
1588
1589
        $doc_id = intval($doc_id);
1590
        $session_id = intval($session_id);
1591
1592
        //2. Course and Session visibility are handle in local.inc.php/global.inc.php
1593
        //3. Checking if user exist in course/session
1594
        if ($session_id == 0) {
1595
            if (is_null($userIsSubscribed)) {
1596
                $userIsSubscribed = CourseManager::is_user_subscribed_in_course(
1597
                    $user_id,
1598
                    $course_info['code']
1599
                );
1600
            }
1601
1602
            if ($userIsSubscribed === true || api_is_platform_admin()) {
1603
                $user_in_course = true;
1604
            }
1605
1606
            // Check if course is open then we can consider that the student is registered to the course
1607
            if (isset($course_info) &&
1608
                in_array(
1609
                    $course_info['visibility'],
1610
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
1611
                )
1612
            ) {
1613
                $user_in_course = true;
1614
            }
1615
        } else {
1616
            $user_status = SessionManager::get_user_status_in_course_session(
1617
                $user_id,
1618
                $course_info['real_id'],
1619
                $session_id
1620
            );
1621
1622
            if (in_array($user_status, ['0', '2', '6'])) {
1623
                //is true if is an student, course session teacher or coach
1624
                $user_in_course = true;
1625
            }
1626
1627
            if (api_is_platform_admin()) {
1628
                $user_in_course = true;
1629
            }
1630
        }
1631
1632
        // 4. Checking document visibility (i'm repeating the code in order to be more clear when reading ) - jm
1633
        if ($user_in_course) {
1634
            // 4.1 Checking document visibility for a Course
1635
            if ($session_id == 0) {
1636
                $item_info = api_get_item_property_info(
1637
                    $course_info['real_id'],
1638
                    'document',
1639
                    $doc_id,
1640
                    0
1641
                );
1642
1643
                if (isset($item_info['visibility'])) {
1644
                    // True for admins if document exists
1645
                    if ($admins_can_see_everything && api_is_platform_admin()) {
1646
                        return true;
1647
                    }
1648
                    if ($item_info['visibility'] == 1) {
1649
                        return true;
1650
                    }
1651
                }
1652
            } else {
1653
                // 4.2 Checking document visibility for a Course in a Session
1654
                $item_info = api_get_item_property_info(
1655
                    $course_info['real_id'],
1656
                    'document',
1657
                    $doc_id,
1658
                    0
1659
                );
1660
1661
                $item_info_in_session = api_get_item_property_info(
1662
                    $course_info['real_id'],
1663
                    'document',
1664
                    $doc_id,
1665
                    $session_id
1666
                );
1667
1668
                // True for admins if document exists
1669
                if (isset($item_info['visibility'])) {
1670
                    if ($admins_can_see_everything && api_is_platform_admin()) {
1671
                        return true;
1672
                    }
1673
                }
1674
1675
                if (isset($item_info_in_session['visibility'])) {
1676
                    if ($item_info_in_session['visibility'] == 1) {
1677
                        return true;
1678
                    }
1679
                } else {
1680
                    if ($item_info['visibility'] == 1) {
1681
                        return true;
1682
                    }
1683
                }
1684
            }
1685
        } elseif ($admins_can_see_everything && api_is_platform_admin()) {
1686
            return true;
1687
        }
1688
        return false;
1689
    }
1690
1691
    /**
1692
     * Allow attach a certificate to a course
1693
     * @todo move to certificate.lib.php
1694
     * @param string $course_id
1695
     * @param int $document_id
1696
     * @param int $session_id
1697
     * @return void
1698
     */
1699
    public static function attach_gradebook_certificate($course_id, $document_id, $session_id = 0)
1700
    {
1701
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1702
        $session_id = intval($session_id);
1703
        if (empty($session_id)) {
1704
            $session_id = api_get_session_id();
1705
        }
1706
1707
        if (empty($session_id)) {
1708
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1709
        } elseif ($session_id > 0) {
1710
            $sql_session = 'AND session_id='.intval($session_id);
1711
        } else {
1712
            $sql_session = '';
1713
        }
1714
        $sql = 'UPDATE '.$tbl_category.' SET document_id="'.intval($document_id).'"
1715
                WHERE course_code="' . Database::escape_string($course_id).'" '.$sql_session;
1716
        Database::query($sql);
1717
    }
1718
1719
    /**
1720
     * get the document id of default certificate
1721
     * @todo move to certificate.lib.php
1722
     * @param string $course_id
1723
     * @param int $session_id
1724
     *
1725
     * @return int The default certificate id
1726
     */
1727
    public static function get_default_certificate_id($course_id, $session_id = 0)
1728
    {
1729
        $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1730
        $session_id = intval($session_id);
1731
        if (empty($session_id)) {
1732
            $session_id = api_get_session_id();
1733
        }
1734
1735
        if (empty($session_id)) {
1736
            $sql_session = 'AND (session_id = 0 OR isnull(session_id)) ';
1737
        } elseif ($session_id > 0) {
1738
            $sql_session = 'AND session_id='.intval($session_id);
1739
        } else {
1740
            $sql_session = '';
1741
        }
1742
        $sql = 'SELECT document_id FROM '.$tbl_category.'
1743
                WHERE course_code="' . Database::escape_string($course_id).'" '.$sql_session;
1744
1745
        $rs = Database::query($sql);
1746
        $num = Database::num_rows($rs);
1747
        if ($num == 0) {
1748
            return null;
1749
        }
1750
        $row = Database::fetch_array($rs);
1751
1752
        return $row['document_id'];
1753
    }
1754
1755
    /**
1756
     * Allow replace user info in file html
1757
     * @param int $user_id
1758
     * @param string $course_code
1759
     * @param int $sessionId
1760
     * @param bool $is_preview
1761
     * @return string The html content of the certificate
1762
     */
1763
    public static function replace_user_info_into_html(
1764
        $user_id,
1765
        $course_code,
1766
        $sessionId,
1767
        $is_preview = false
1768
    ) {
1769
        $user_id = intval($user_id);
1770
        $course_info = api_get_course_info($course_code);
1771
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1772
        $course_id = $course_info['real_id'];
1773
1774
        $document_id = self::get_default_certificate_id(
1775
            $course_code,
1776
            $sessionId
1777
        );
1778
1779
        $my_content_html = null;
1780
        if ($document_id) {
1781
            $sql = "SELECT path FROM $tbl_document
1782
                    WHERE c_id = $course_id AND id = $document_id";
1783
            $rs = Database::query($sql);
1784
            $new_content = '';
1785
            $all_user_info = [];
1786
            if (Database::num_rows($rs)) {
1787
                $row = Database::fetch_array($rs);
1788
                $filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
1789
                if (is_file($filepath)) {
1790
                    $my_content_html = file_get_contents($filepath);
1791
                }
1792
                $all_user_info = self::get_all_info_to_certificate(
1793
                    $user_id,
1794
                    $course_code,
1795
                    $is_preview
1796
                );
1797
1798
                $info_to_be_replaced_in_content_html = $all_user_info[0];
1799
                $info_to_replace_in_content_html = $all_user_info[1];
1800
                $new_content = str_replace(
1801
                    $info_to_be_replaced_in_content_html,
1802
                    $info_to_replace_in_content_html,
1803
                    $my_content_html
1804
                );
1805
            }
1806
1807
            return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('content' =...les' => $all_user_info) returns the type array<string,string|array|mixed> which is incompatible with the documented return type string.
Loading history...
1808
                'content' => $new_content,
1809
                'variables' => $all_user_info
1810
            ];
1811
        }
1812
1813
        return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type string.
Loading history...
1814
    }
1815
1816
    /**
1817
     * Return all content to replace and all content to be replace
1818
     * @param int $user_id
1819
     * @param int $course_id
1820
     * @param bool $is_preview
1821
     * @return array
1822
     */
1823
    public static function get_all_info_to_certificate($user_id, $course_id, $is_preview = false)
1824
    {
1825
        $info_list = [];
1826
        $user_id = intval($user_id);
1827
        $course_info = api_get_course_info($course_id);
1828
1829
        // Portal info
1830
        $organization_name = api_get_setting('Institution');
1831
        $portal_name = api_get_setting('siteName');
1832
1833
        // Extra user data information
1834
        $extra_user_info_data = UserManager::get_extra_user_data(
1835
            $user_id,
1836
            false,
1837
            false,
1838
            false,
1839
            true
1840
        );
1841
1842
        // get extra fields
1843
        $extraField = new ExtraField('user');
1844
        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
1845
1846
        // Student information
1847
        $user_info = api_get_user_info($user_id);
1848
        $first_name = $user_info['firstname'];
1849
        $last_name = $user_info['lastname'];
1850
        $official_code = $user_info['official_code'];
1851
1852
        // Teacher information
1853
        $info_teacher_id = UserManager::get_user_id_of_course_admin_or_session_admin($course_info);
1854
        $teacher_info = api_get_user_info($info_teacher_id);
1855
        $teacher_first_name = $teacher_info['firstname'];
1856
        $teacher_last_name = $teacher_info['lastname'];
1857
1858
        // info gradebook certificate
1859
        $info_grade_certificate = UserManager::get_info_gradebook_certificate($course_id, $user_id);
1860
        $date_certificate = $info_grade_certificate['created_at'];
1861
        $date_long_certificate = '';
1862
1863
        $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1864
        if (!empty($date_certificate)) {
1865
            $date_long_certificate = api_convert_and_format_date($date_certificate);
1866
            $date_no_time = api_convert_and_format_date($date_certificate, DATE_FORMAT_LONG_NO_DAY);
1867
        }
1868
1869
        if ($is_preview) {
1870
            $date_long_certificate = api_convert_and_format_date(api_get_utc_datetime());
1871
            $date_no_time = api_convert_and_format_date(api_get_utc_datetime(), DATE_FORMAT_LONG_NO_DAY);
1872
        }
1873
1874
        $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$info_grade_certificate['id'];
1875
        $externalStyleFile = api_get_path(SYS_CSS_PATH).'themes/'.api_get_visual_theme().'/certificate.css';
1876
        $externalStyle = '';
1877
        if (is_file($externalStyleFile)) {
1878
            $externalStyle = file_get_contents($externalStyleFile);
1879
        }
1880
1881
        // Replace content
1882
        $info_to_replace_in_content_html = [
1883
            $first_name,
1884
            $last_name,
1885
            $organization_name,
1886
            $portal_name,
1887
            $teacher_first_name,
1888
            $teacher_last_name,
1889
            $official_code,
1890
            $date_long_certificate,
1891
            $date_no_time,
1892
            $course_id,
1893
            $course_info['name'],
1894
            $info_grade_certificate['grade'],
1895
            $url,
1896
            '<a href="'.$url.'" target="_blank">'.get_lang('CertificateOnlineLink').'</a>',
1897
            '((certificate_barcode))',
1898
            $externalStyle
1899
        ];
1900
1901
        $tags = [
1902
            '((user_firstname))',
1903
            '((user_lastname))',
1904
            '((gradebook_institution))',
1905
            '((gradebook_sitename))',
1906
            '((teacher_firstname))',
1907
            '((teacher_lastname))',
1908
            '((official_code))',
1909
            '((date_certificate))',
1910
            '((date_certificate_no_time))',
1911
            '((course_code))',
1912
            '((course_title))',
1913
            '((gradebook_grade))',
1914
            '((certificate_link))',
1915
            '((certificate_link_html))',
1916
            '((certificate_barcode))',
1917
            '((external_style))'
1918
        ];
1919
1920
        if (!empty($extraFields)) {
1921
            foreach ($extraFields as $extraField) {
1922
                $valueExtra = isset($extra_user_info_data[$extraField['variable']]) ? $extra_user_info_data[$extraField['variable']] : '';
1923
                $tags[] = '(('.strtolower($extraField['variable']).'))';
1924
                $info_to_replace_in_content_html[] = $valueExtra;
1925
            }
1926
        }
1927
1928
        $info_list[] = $tags;
1929
        $info_list[] = $info_to_replace_in_content_html;
1930
1931
        return $info_list;
1932
    }
1933
1934
    /**
1935
     * Remove default certificate
1936
     * @param string $course_id The course code
1937
     * @param int $default_certificate_id The document id of the default certificate
1938
     * @return void
1939
     */
1940
    public static function remove_attach_certificate($course_id, $default_certificate_id)
1941
    {
1942
        if (empty($default_certificate_id)) {
1943
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
1944
        }
1945
1946
        $default_certificate = self::get_default_certificate_id($course_id);
1947
        if ((int) $default_certificate == (int) $default_certificate_id) {
1948
            $tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1949
            $session_id = api_get_session_id();
1950
            if ($session_id == 0 || is_null($session_id)) {
1951
                $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
1952
            } elseif ($session_id > 0) {
1953
                $sql_session = 'AND session_id='.intval($session_id);
1954
            } else {
1955
                $sql_session = '';
1956
            }
1957
1958
            $sql = 'UPDATE '.$tbl_category.' SET document_id = null
1959
                    WHERE
1960
                        course_code = "' . Database::escape_string($course_id).'" AND
1961
                        document_id="' . $default_certificate_id.'" '.$sql_session;
1962
            Database::query($sql);
1963
        }
1964
    }
1965
1966
    /**
1967
     * Create directory certificate
1968
     * @param array $courseInfo
1969
     * @return void
1970
     */
1971
    public static function create_directory_certificate_in_course($courseInfo)
1972
    {
1973
        if (!empty($courseInfo)) {
1974
            $to_group_id = 0;
1975
            $to_user_id = null;
1976
            $course_dir = $courseInfo['path']."/document/";
1977
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
1978
            $base_work_dir = $sys_course_path.$course_dir;
1979
            $base_work_dir_test = $base_work_dir.'certificates';
1980
            $dir_name = '/certificates';
1981
            $post_dir_name = get_lang('CertificatesFiles');
1982
            $visibility_command = 'invisible';
1983
1984
            $id = self::get_document_id_of_directory_certificate();
1985
1986
            if (empty($id)) {
1987
                create_unexisting_directory(
1988
                    $courseInfo,
1989
                    api_get_user_id(),
1990
                    api_get_session_id(),
1991
                    $to_group_id,
1992
                    $to_user_id,
1993
                    $base_work_dir,
1994
                    $dir_name,
1995
                    $post_dir_name,
1996
                    null,
1997
                    false
1998
                );
1999
2000
                $id = self::get_document_id_of_directory_certificate();
2001
2002
                if (empty($id)) {
2003
                    $id = add_document(
2004
                        $courseInfo,
2005
                        $dir_name,
2006
                        'folder',
2007
                        0,
2008
                        $post_dir_name,
2009
                        null,
2010
                        0,
2011
                        true,
2012
                        $to_group_id
2013
                    );
2014
                }
2015
2016
                if (!empty($id)) {
2017
                    api_item_property_update(
2018
                        $courseInfo,
2019
                        TOOL_DOCUMENT,
2020
                        $id,
2021
                        $visibility_command,
2022
                        api_get_user_id()
2023
                    );
2024
                }
2025
            }
2026
        }
2027
    }
2028
2029
    /**
2030
     * Get the document id of the directory certificate
2031
     * @return int The document id of the directory certificate
2032
     * @todo move to certificate.lib.php
2033
     */
2034
    public static function get_document_id_of_directory_certificate()
2035
    {
2036
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
2037
        $course_id = api_get_course_int_id();
2038
        $sql = "SELECT id FROM $tbl_document 
2039
                WHERE c_id = $course_id AND path='/certificates' ";
2040
        $rs = Database::query($sql);
2041
        $row = Database::fetch_array($rs);
2042
        return $row['id'];
2043
    }
2044
2045
    /**
2046
     * Check if a directory given is for certificate
2047
     * @todo move to certificate.lib.php
2048
     * @param string $dir path of directory
2049
     * @return bool  true if is a certificate or false otherwise
2050
     */
2051
    public static function is_certificate_mode($dir)
2052
    {
2053
        // I'm in the certification module?
2054
        $is_certificate_mode = false;
2055
        $is_certificate_array = explode('/', $dir);
2056
        array_shift($is_certificate_array);
2057
        if (isset($is_certificate_array[0]) && $is_certificate_array[0] == 'certificates') {
2058
            $is_certificate_mode = true;
2059
        }
2060
        return $is_certificate_mode;
2061
    }
2062
2063
    /**
2064
     * Gets the list of included resources as a list of absolute or relative paths from a html file or string html
2065
     * This allows for a better SCORM export or replace urls inside content html from copy course
2066
     * The list will generally include pictures, flash objects, java applets, or any other
2067
     * stuff included in the source of the current item. The current item is expected
2068
     * to be an HTML file or string html. If it is not, then the function will return and empty list.
2069
     * @param    string  source html (content or path)
2070
     * @param    bool    is file or string html
2071
     * @param    string    type (one of the app tools) - optional (otherwise takes the current item's type)
2072
     * @param    int        level of recursivity we're in
2073
     * @return   array    List of file paths. An additional field containing 'local' or 'remote' helps determine
2074
     * if the file should be copied into the zip or just linked
2075
     */
2076
    public static function get_resources_from_source_html(
2077
        $source_html,
2078
        $is_file = false,
2079
        $type = null,
2080
        $recursivity = 1
2081
    ) {
2082
        $max = 5;
2083
        $attributes = [];
2084
        $wanted_attributes = [
2085
            'src',
2086
            'url',
2087
            '@import',
2088
            'href',
2089
            'value',
2090
            'flashvars',
2091
            'poster'
2092
        ];
2093
        $explode_attributes = ['flashvars' => 'file'];
2094
        $abs_path = '';
2095
2096
        if ($recursivity > $max) {
2097
            return [];
2098
        }
2099
2100
        if (!isset($type)) {
2101
            $type = TOOL_DOCUMENT;
2102
        }
2103
2104
        if (!$is_file) {
2105
            $attributes = self::parse_HTML_attributes(
2106
                $source_html,
2107
                $wanted_attributes,
2108
                $explode_attributes
2109
            );
2110
        } else {
2111
            if (is_file($source_html)) {
2112
                $abs_path = $source_html;
2113
                //for now, read the whole file in one go (that's gonna be a problem when the file is too big)
2114
                $info = pathinfo($abs_path);
2115
                $ext = $info['extension'];
2116
                switch (strtolower($ext)) {
2117
                    case 'html':
2118
                    case 'htm':
2119
                    case 'shtml':
2120
                    case 'css':
2121
                        $file_content = file_get_contents($abs_path);
2122
                        //get an array of attributes from the HTML source
2123
                        $attributes = self::parse_HTML_attributes($file_content, $wanted_attributes, $explode_attributes);
2124
                        break;
2125
                    default:
2126
                        break;
2127
                }
2128
            } else {
2129
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2130
            }
2131
        }
2132
2133
        $files_list = [];
2134
        switch ($type) {
2135
            case TOOL_DOCUMENT:
2136
            case TOOL_QUIZ:
2137
            case 'sco':
2138
                foreach ($wanted_attributes as $attr) {
2139
                    if (isset($attributes[$attr])) {
2140
                        //find which kind of path these are (local or remote)
2141
                        $sources = $attributes[$attr];
2142
                        foreach ($sources as $source) {
2143
                            //skip what is obviously not a resource
2144
                            if (strpos($source, '+this.')) {
2145
                                continue; //javascript code - will still work unaltered
2146
                            }
2147
                            if (strpos($source, '.') === false) {
2148
                                continue; //no dot, should not be an external file anyway
2149
                            }
2150
                            if (strpos($source, 'mailto:')) {
2151
                                continue; //mailto link
2152
                            }
2153
                            if (strpos($source, ';') && !strpos($source, '&amp;')) {
2154
                                continue; //avoid code - that should help
2155
                            }
2156
2157
                            if ($attr == 'value') {
2158
                                if (strpos($source, 'mp3file')) {
2159
                                    $files_list[] = [
2160
                                        substr($source, 0, strpos($source, '.swf') + 4),
2161
                                        'local',
2162
                                        'abs',
2163
                                    ];
2164
                                    $mp3file = substr($source, strpos($source, 'mp3file=') + 8);
2165
                                    if (substr($mp3file, 0, 1) == '/') {
2166
                                        $files_list[] = [$mp3file, 'local', 'abs'];
2167
                                    } else {
2168
                                        $files_list[] = [$mp3file, 'local', 'rel'];
2169
                                    }
2170
                                } elseif (strpos($source, 'flv=') === 0) {
2171
                                    $source = substr($source, 4);
2172
                                    if (strpos($source, '&') > 0) {
2173
                                        $source = substr($source, 0, strpos($source, '&'));
2174
                                    }
2175
                                    if (strpos($source, '://') > 0) {
2176
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2177
                                            //we found the current portal url
2178
                                            $files_list[] = [$source, 'local', 'url'];
2179
                                        } else {
2180
                                            //we didn't find any trace of current portal
2181
                                            $files_list[] = [$source, 'remote', 'url'];
2182
                                        }
2183
                                    } else {
2184
                                        $files_list[] = [$source, 'local', 'abs'];
2185
                                    }
2186
                                    /* skipping anything else to avoid two entries
2187
                                    (while the others can have sub-files in their url, flv's can't)*/
2188
                                    continue;
2189
                                }
2190
                            }
2191
                            if (strpos($source, '://') > 0) {
2192
                                //cut at '?' in a URL with params
2193
                                if (strpos($source, '?') > 0) {
2194
                                    $second_part = substr($source, strpos($source, '?'));
2195
                                    if (strpos($second_part, '://') > 0) {
2196
                                        //if the second part of the url contains a url too, treat the second one before cutting
2197
                                        $pos1 = strpos($second_part, '=');
2198
                                        $pos2 = strpos($second_part, '&');
2199
                                        $second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
2200
                                        if (strpos($second_part, api_get_path(WEB_PATH)) !== false) {
2201
                                            //we found the current portal url
2202
                                            $files_list[] = [$second_part, 'local', 'url'];
2203
                                            $in_files_list[] = self::get_resources_from_source_html(
2204
                                                $second_part,
2205
                                                true,
2206
                                                TOOL_DOCUMENT,
2207
                                                $recursivity + 1
2208
                                            );
2209
                                            if (count($in_files_list) > 0) {
2210
                                                $files_list = array_merge($files_list, $in_files_list);
2211
                                            }
2212
                                        } else {
2213
                                            //we didn't find any trace of current portal
2214
                                            $files_list[] = [$second_part, 'remote', 'url'];
2215
                                        }
2216
                                    } elseif (strpos($second_part, '=') > 0) {
2217
                                        if (substr($second_part, 0, 1) === '/') {
2218
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2219
                                            $files_list[] = [$second_part, 'local', 'abs'];
2220
                                            $in_files_list[] = self::get_resources_from_source_html(
2221
                                                $second_part,
2222
                                                true,
2223
                                                TOOL_DOCUMENT,
2224
                                                $recursivity + 1
2225
                                            );
2226
                                            if (count($in_files_list) > 0) {
2227
                                                $files_list = array_merge($files_list, $in_files_list);
2228
                                            }
2229
                                        } elseif (strstr($second_part, '..') === 0) {
2230
                                            //link is relative but going back in the hierarchy
2231
                                            $files_list[] = [$second_part, 'local', 'rel'];
2232
                                            //$dir = api_get_path(SYS_CODE_PATH);//dirname($abs_path);
2233
                                            //$new_abs_path = realpath($dir.'/'.$second_part);
2234
                                            $dir = '';
2235
                                            if (!empty($abs_path)) {
2236
                                                $dir = dirname($abs_path).'/';
2237
                                            }
2238
                                            $new_abs_path = realpath($dir.$second_part);
2239
                                            $in_files_list[] = self::get_resources_from_source_html(
2240
                                                $new_abs_path,
2241
                                                true,
2242
                                                TOOL_DOCUMENT,
2243
                                                $recursivity + 1
2244
                                            );
2245
                                            if (count($in_files_list) > 0) {
2246
                                                $files_list = array_merge($files_list, $in_files_list);
2247
                                            }
2248
                                        } else {
2249
                                            //no starting '/', making it relative to current document's path
2250
                                            if (substr($second_part, 0, 2) == './') {
2251
                                                $second_part = substr($second_part, 2);
2252
                                            }
2253
                                            $files_list[] = [$second_part, 'local', 'rel'];
2254
                                            $dir = '';
2255
                                            if (!empty($abs_path)) {
2256
                                                $dir = dirname($abs_path).'/';
2257
                                            }
2258
                                            $new_abs_path = realpath($dir.$second_part);
2259
                                            $in_files_list[] = self::get_resources_from_source_html(
2260
                                                $new_abs_path,
2261
                                                true,
2262
                                                TOOL_DOCUMENT,
2263
                                                $recursivity + 1
2264
                                            );
2265
                                            if (count($in_files_list) > 0) {
2266
                                                $files_list = array_merge($files_list, $in_files_list);
2267
                                            }
2268
                                        }
2269
                                    }
2270
                                    //leave that second part behind now
2271
                                    $source = substr($source, 0, strpos($source, '?'));
2272
                                    if (strpos($source, '://') > 0) {
2273
                                        if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2274
                                            //we found the current portal url
2275
                                            $files_list[] = [$source, 'local', 'url'];
2276
                                            $in_files_list[] = self::get_resources_from_source_html(
2277
                                                $source,
2278
                                                true,
2279
                                                TOOL_DOCUMENT,
2280
                                                $recursivity + 1
2281
                                            );
2282
                                            if (count($in_files_list) > 0) {
2283
                                                $files_list = array_merge($files_list, $in_files_list);
2284
                                            }
2285
                                        } else {
2286
                                            //we didn't find any trace of current portal
2287
                                            $files_list[] = [$source, 'remote', 'url'];
2288
                                        }
2289
                                    } else {
2290
                                        //no protocol found, make link local
2291
                                        if (substr($source, 0, 1) === '/') {
2292
                                            //link starts with a /, making it absolute (relative to DocumentRoot)
2293
                                            $files_list[] = [$source, 'local', 'abs'];
2294
                                            $in_files_list[] = self::get_resources_from_source_html(
2295
                                                $source,
2296
                                                true,
2297
                                                TOOL_DOCUMENT,
2298
                                                $recursivity + 1
2299
                                            );
2300
                                            if (count($in_files_list) > 0) {
2301
                                                $files_list = array_merge($files_list, $in_files_list);
2302
                                            }
2303
                                        } elseif (strstr($source, '..') === 0) {
2304
                                            //link is relative but going back in the hierarchy
2305
                                            $files_list[] = [$source, 'local', 'rel'];
2306
                                            $dir = '';
2307
                                            if (!empty($abs_path)) {
2308
                                                $dir = dirname($abs_path).'/';
2309
                                            }
2310
                                            $new_abs_path = realpath($dir.$source);
2311
                                            $in_files_list[] = self::get_resources_from_source_html(
2312
                                                $new_abs_path,
2313
                                                true,
2314
                                                TOOL_DOCUMENT,
2315
                                                $recursivity + 1
2316
                                            );
2317
                                            if (count($in_files_list) > 0) {
2318
                                                $files_list = array_merge($files_list, $in_files_list);
2319
                                            }
2320
                                        } else {
2321
                                            //no starting '/', making it relative to current document's path
2322
                                            if (substr($source, 0, 2) == './') {
2323
                                                $source = substr($source, 2);
2324
                                            }
2325
                                            $files_list[] = [$source, 'local', 'rel'];
2326
                                            $dir = '';
2327
                                            if (!empty($abs_path)) {
2328
                                                $dir = dirname($abs_path).'/';
2329
                                            }
2330
                                            $new_abs_path = realpath($dir.$source);
2331
                                            $in_files_list[] = self::get_resources_from_source_html(
2332
                                                $new_abs_path,
2333
                                                true,
2334
                                                TOOL_DOCUMENT,
2335
                                                $recursivity + 1
2336
                                            );
2337
                                            if (count($in_files_list) > 0) {
2338
                                                $files_list = array_merge($files_list, $in_files_list);
2339
                                            }
2340
                                        }
2341
                                    }
2342
                                }
2343
                                //found some protocol there
2344
                                if (strpos($source, api_get_path(WEB_PATH)) !== false) {
2345
                                    //we found the current portal url
2346
                                    $files_list[] = [$source, 'local', 'url'];
2347
                                    $in_files_list[] = self::get_resources_from_source_html(
2348
                                        $source,
2349
                                        true,
2350
                                        TOOL_DOCUMENT,
2351
                                        $recursivity + 1
2352
                                    );
2353
                                    if (count($in_files_list) > 0) {
2354
                                        $files_list = array_merge($files_list, $in_files_list);
2355
                                    }
2356
                                } else {
2357
                                    //we didn't find any trace of current portal
2358
                                    $files_list[] = [$source, 'remote', 'url'];
2359
                                }
2360
                            } else {
2361
                                //no protocol found, make link local
2362
                                if (substr($source, 0, 1) === '/') {
2363
                                    //link starts with a /, making it absolute (relative to DocumentRoot)
2364
                                    $files_list[] = [$source, 'local', 'abs'];
2365
                                    $in_files_list[] = self::get_resources_from_source_html(
2366
                                        $source,
2367
                                        true,
2368
                                        TOOL_DOCUMENT,
2369
                                        $recursivity + 1
2370
                                    );
2371
                                    if (count($in_files_list) > 0) {
2372
                                        $files_list = array_merge($files_list, $in_files_list);
2373
                                    }
2374
                                } elseif (strpos($source, '..') === 0) {
2375
                                    //link is relative but going back in the hierarchy
2376
                                    $files_list[] = [$source, 'local', 'rel'];
2377
                                    $dir = '';
2378
                                    if (!empty($abs_path)) {
2379
                                        $dir = dirname($abs_path).'/';
2380
                                    }
2381
                                    $new_abs_path = realpath($dir.$source);
2382
                                    $in_files_list[] = self::get_resources_from_source_html(
2383
                                        $new_abs_path,
2384
                                        true,
2385
                                        TOOL_DOCUMENT,
2386
                                        $recursivity + 1
2387
                                    );
2388
                                    if (count($in_files_list) > 0) {
2389
                                        $files_list = array_merge($files_list, $in_files_list);
2390
                                    }
2391
                                } else {
2392
                                    //no starting '/', making it relative to current document's path
2393
                                    if (substr($source, 0, 2) == './') {
2394
                                        $source = substr($source, 2);
2395
                                    }
2396
                                    $files_list[] = [$source, 'local', 'rel'];
2397
                                    $dir = '';
2398
                                    if (!empty($abs_path)) {
2399
                                        $dir = dirname($abs_path).'/';
2400
                                    }
2401
                                    $new_abs_path = realpath($dir.$source);
2402
                                    $in_files_list[] = self::get_resources_from_source_html(
2403
                                        $new_abs_path,
2404
                                        true,
2405
                                        TOOL_DOCUMENT,
2406
                                        $recursivity + 1
2407
                                    );
2408
                                    if (count($in_files_list) > 0) {
2409
                                        $files_list = array_merge($files_list, $in_files_list);
2410
                                    }
2411
                                }
2412
                            }
2413
                        }
2414
                    }
2415
                }
2416
                break;
2417
            default: //ignore
2418
                break;
2419
        }
2420
2421
        $checked_files_list = [];
2422
        $checked_array_list = [];
2423
2424
        if (count($files_list) > 0) {
2425
            foreach ($files_list as $idx => $file) {
2426
                if (!empty($file[0])) {
2427
                    if (!in_array($file[0], $checked_files_list)) {
2428
                        $checked_files_list[] = $files_list[$idx][0];
2429
                        $checked_array_list[] = $files_list[$idx];
2430
                    }
2431
                }
2432
            }
2433
        }
2434
2435
        return $checked_array_list;
2436
    }
2437
2438
    /**
2439
     * Parses the HTML attributes given as string.
2440
     *
2441
     * @param    string  HTML attribute string
2442
     * @param	 array	 List of attributes that we want to get back
2443
     * @param    array
2444
     * @return   array   An associative array of attributes
2445
     * @author 	 Based on a function from the HTML_Common2 PEAR module     *
2446
     */
2447
    public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
2448
    {
2449
        $attributes = [];
2450
        $regs = [];
2451
        $reduced = false;
2452
        if (count($wanted) > 0) {
2453
            $reduced = true;
2454
        }
2455
        try {
2456
            //Find all occurences of something that looks like a URL
2457
            // The structure of this regexp is:
2458
            // (find protocol) then
2459
            // (optionally find some kind of space 1 or more times) then
2460
            // find (either an equal sign or a bracket) followed by an optional space
2461
            // followed by some text without quotes (between quotes itself or not)
2462
            // then possible closing brackets if we were in the opening bracket case
2463
            // OR something like @import()
2464
            $res = preg_match_all(
2465
                '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]*))'.
2466
                // '/(((([A-Za-z_:])([A-Za-z0-9_:\.-]|[^\x00-\x7F])*)' . -> seems to be taking too much
2467
                // '/(((([A-Za-z_:])([^\x00-\x7F])*)' . -> takes only last letter of parameter name
2468
                '([ \n\t\r]+)?('.
2469
                // '(=([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+))' . -> doesn't restrict close enough to the url itself
2470
                '(=([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+))'.
2471
                '|'.
2472
                // '(\(([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)\))' . -> doesn't restrict close enough to the url itself
2473
                '(\(([ \n\t\r]+)?("[^"\)]+"|\'[^\'\)]+\'|[^ \n\t\r\)]+)\))'.
2474
                '))'.
2475
                '|'.
2476
                // '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))?/', -> takes a lot (like 100's of thousands of empty possibilities)
2477
                '(@import([ \n\t\r]+)?("[^"]+"|\'[^\']+\'|[^ \n\t\r]+)))/',
2478
                $attrString,
2479
                $regs
2480
            );
2481
        } catch (Exception $e) {
2482
            error_log('Caught exception: '.$e->getMessage(), 0);
2483
        }
2484
        if ($res) {
2485
            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...
2486
                $name = trim($regs[3][$i]);
2487
                $check = trim($regs[0][$i]);
2488
                $value = trim($regs[10][$i]);
2489
                if (empty($value) and !empty($regs[13][$i])) {
2490
                    $value = $regs[13][$i];
2491
                }
2492
                if (empty($name) && !empty($regs[16][$i])) {
2493
                    $name = '@import';
2494
                    $value = trim($regs[16][$i]);
2495
                }
2496
                if (!empty($name)) {
2497
                    if (!$reduced || in_array(strtolower($name), $wanted)) {
2498
                        if ($name == $check) {
2499
                            $attributes[strtolower($name)][] = strtolower($name);
2500
                        } else {
2501
                            if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) {
2502
                                $value = substr($value, 1, -1);
2503
                            }
2504
2505
                            if ($value == 'API.LMSGetValue(name') {
2506
                                $value = 'API.LMSGetValue(name)';
2507
                            }
2508
                            //Gets the xx.flv value from the string flashvars="width=320&height=240&autostart=false&file=xxx.flv&repeat=false"
2509
                            if (isset($explode_variables[$name])) {
2510
                                $value_modified = str_replace('&amp;', '&', $value);
2511
                                $value_array = explode('&', $value_modified);
2512
                                foreach ($value_array as $item) {
2513
                                    $itemParts = explode('=', $item);
2514
                                    $key = $itemParts[0];
2515
                                    $item_value = !empty($itemParts[1]) ? $itemParts[1] : '';
2516
                                    if ($key == $explode_variables[$name]) {
2517
                                        $attributes[strtolower($name)][] = $item_value;
2518
                                    }
2519
                                }
2520
                            }
2521
                            $attributes[strtolower($name)][] = $value;
2522
                        }
2523
                    }
2524
                }
2525
            }
2526
        }
2527
2528
        return $attributes;
2529
    }
2530
2531
    /**
2532
     * Replace urls inside content html from a copy course
2533
     * @param string $content_html
2534
     * @param string $origin_course_code
2535
     * @param string $destination_course_directory
2536
     * @param string $origin_course_path_from_zip
2537
     * @param string $origin_course_info_path
2538
     *
2539
     * @return string	new content html with replaced urls or return false if content is not a string
2540
     */
2541
    public static function replaceUrlWithNewCourseCode(
2542
        $content_html,
2543
        $origin_course_code,
2544
        $destination_course_directory,
2545
        $origin_course_path_from_zip = null,
2546
        $origin_course_info_path = null
2547
    ) {
2548
        if (empty($content_html)) {
2549
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2550
        }
2551
2552
        $orig_source_html = self::get_resources_from_source_html($content_html);
2553
        $orig_course_info = api_get_course_info($origin_course_code);
2554
2555
        // Course does not exist in the current DB probably this came from a zip file?
2556
        if (empty($orig_course_info)) {
2557
            if (!empty($origin_course_path_from_zip)) {
2558
                $orig_course_path = $origin_course_path_from_zip.'/';
2559
                $orig_course_info_path = $origin_course_info_path;
2560
            }
2561
        } else {
2562
            $orig_course_path = api_get_path(SYS_COURSE_PATH).$orig_course_info['path'].'/';
2563
            $orig_course_info_path = $orig_course_info['path'];
2564
        }
2565
2566
        $destination_course_code = CourseManager::getCourseCodeFromDirectory($destination_course_directory);
2567
        $destination_course_info = api_get_course_info($destination_course_code);
2568
        $dest_course_path = api_get_path(SYS_COURSE_PATH).$destination_course_directory.'/';
2569
        $dest_course_path_rel = api_get_path(REL_COURSE_PATH).$destination_course_directory.'/';
2570
2571
        $user_id = api_get_user_id();
2572
2573
        if (!empty($orig_source_html)) {
2574
            foreach ($orig_source_html as $source) {
2575
                // Get information about source url
2576
                $real_orig_url = $source[0]; // url
2577
                $scope_url = $source[1]; // scope (local, remote)
2578
                $type_url = $source[2]; // type (rel, abs, url)
2579
2580
                // Get path and query from origin url
2581
                $orig_parse_url = parse_url($real_orig_url);
2582
                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
2583
                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
2584
2585
                // Replace origin course code by destination course code from origin url query
2586
                $dest_url_query = '';
2587
2588
                if (!empty($real_orig_query)) {
2589
                    $dest_url_query = '?'.$real_orig_query;
2590
                    if (strpos($dest_url_query, $origin_course_code) !== false) {
2591
                        $dest_url_query = str_replace($origin_course_code, $destination_course_code, $dest_url_query);
2592
                    }
2593
                }
2594
2595
                if ($scope_url == 'local') {
2596
                    if ($type_url == 'abs' || $type_url == 'rel') {
2597
                        $document_file = strstr($real_orig_path, 'document');
2598
2599
                        if (strpos($real_orig_path, $document_file) !== false) {
2600
                            $origin_filepath = $orig_course_path.$document_file;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $orig_course_path does not seem to be defined for all execution paths leading up to this point.
Loading history...
2601
                            $destination_filepath = $dest_course_path.$document_file;
2602
2603
                            // copy origin file inside destination course
2604
                            if (file_exists($origin_filepath)) {
2605
                                $filepath_dir = dirname($destination_filepath);
2606
2607
                                if (!is_dir($filepath_dir)) {
2608
                                    $perm = api_get_permissions_for_new_directories();
2609
                                    $result = @mkdir($filepath_dir, $perm, true);
2610
                                    if ($result) {
2611
                                        $filepath_to_add = str_replace([$dest_course_path, 'document'], '', $filepath_dir);
2612
2613
                                        //Add to item properties to the new folder
2614
                                        $doc_id = add_document(
2615
                                            $destination_course_info,
2616
                                            $filepath_to_add,
2617
                                            'folder',
2618
                                            0,
2619
                                            basename($filepath_to_add)
2620
                                        );
2621
                                        api_item_property_update(
2622
                                            $destination_course_info,
2623
                                            TOOL_DOCUMENT,
2624
                                            $doc_id,
2625
                                            'FolderCreated',
2626
                                            $user_id,
2627
                                            null,
2628
                                            null,
2629
                                            null,
2630
                                            null
2631
                                        );
2632
                                    }
2633
                                }
2634
2635
                                if (!file_exists($destination_filepath)) {
2636
                                    $result = @copy($origin_filepath, $destination_filepath);
2637
                                    if ($result) {
2638
                                        $filepath_to_add = str_replace([$dest_course_path, 'document'], '', $destination_filepath);
2639
                                        $size = filesize($destination_filepath);
2640
2641
                                        // Add to item properties to the file
2642
                                        $doc_id = add_document(
2643
                                            $destination_course_info,
2644
                                            $filepath_to_add,
2645
                                            'file',
2646
                                            $size,
2647
                                            basename($filepath_to_add)
2648
                                        );
2649
                                        api_item_property_update(
2650
                                            $destination_course_info,
2651
                                            TOOL_DOCUMENT,
2652
                                            $doc_id,
2653
                                            'FolderCreated',
2654
                                            $user_id,
2655
                                            null,
2656
                                            null,
2657
                                            null,
2658
                                            null
2659
                                        );
2660
                                    }
2661
                                }
2662
                            }
2663
2664
                            // Replace origin course path by destination course path.
2665
                            if (strpos($content_html, $real_orig_url) !== false) {
2666
                                $url_course_path = str_replace($orig_course_info_path.'/'.$document_file, '', $real_orig_path);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $orig_course_info_path does not seem to be defined for all execution paths leading up to this point.
Loading history...
2667
2668
                                //$destination_url = $url_course_path . $destination_course_directory . '/' . $document_file . $dest_url_query;
2669
                                // See BT#7780
2670
                                $destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
2671
2672
                                // If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
2673
                                if (strpos($real_orig_path, $origin_course_code) === false) {
2674
                                    $url_course_path = $real_orig_path;
2675
                                    $destination_url = $real_orig_path;
2676
                                }
2677
                                $content_html = str_replace($real_orig_url, $destination_url, $content_html);
2678
                            }
2679
                        }
2680
2681
                        // replace origin course code by destination course code  from origin url
2682
                        if (strpos($real_orig_url, '?') === 0) {
2683
                            $dest_url = str_replace($origin_course_code, $destination_course_code, $real_orig_url);
2684
                            $content_html = str_replace($real_orig_url, $dest_url, $content_html);
2685
                        }
2686
                    }
2687
                }
2688
            }
2689
        }
2690
2691
        return $content_html;
2692
    }
2693
2694
    /**
2695
     * Export document to PDF
2696
     *
2697
     * @param int $document_id
2698
     * @param string $courseCode
2699
     * @param string $orientation
2700
     * @param bool $showHeaderAndFooter
2701
     */
2702
    public static function export_to_pdf(
2703
        $document_id,
2704
        $courseCode,
2705
        $orientation = 'landscape',
2706
        $showHeaderAndFooter = true
2707
    ) {
2708
        $course_data = api_get_course_info($courseCode);
2709
        $document_data = self::get_document_data_by_id($document_id, $courseCode);
2710
        $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$document_data['path'];
2711
        if ($orientation == 'landscape') {
2712
            $pageFormat = 'A4-L';
2713
            $pdfOrientation = 'L';
2714
        } else {
2715
            $pageFormat = 'A4';
2716
            $pdfOrientation = 'P';
2717
        }
2718
        $pdf = new PDF($pageFormat, $pdfOrientation);
2719
2720
        if (api_get_configuration_value('use_alternative_document_pdf_footer')) {
2721
            $view = new Template('', false, false, false, true, false, false);
2722
            $template = $view->get_template('export/alt_pdf_footer.tpl');
2723
2724
            $pdf->set_custom_footer([
2725
                'html' => $view->fetch($template)
2726
            ]);
2727
        }
2728
2729
        $pdf->html_to_pdf(
2730
            $file_path,
2731
            $document_data['title'],
2732
            $courseCode,
2733
            false,
2734
            $showHeaderAndFooter
2735
        );
2736
    }
2737
2738
    /**
2739
     * Uploads a document
2740
     *
2741
     * @param array $files the $_FILES variable
2742
     * @param string $path
2743
     * @param string $title
2744
     * @param string $comment
2745
     * @param int $unzip unzip or not the file
2746
     * @param string $if_exists overwrite, rename or warn (default)
2747
     * @param bool $index_document index document (search xapian module)
2748
     * @param bool $show_output print html messages
2749
     * @param string $fileKey
2750
     * @param bool $treat_spaces_as_hyphens
2751
     *
2752
     * @return array|bool
2753
     */
2754
    public static function upload_document(
2755
        $files,
2756
        $path,
2757
        $title = null,
2758
        $comment = null,
2759
        $unzip = 0,
2760
        $if_exists = null,
2761
        $index_document = false,
2762
        $show_output = false,
2763
        $fileKey = 'file',
2764
        $treat_spaces_as_hyphens = true
2765
    ) {
2766
        $course_info = api_get_course_info();
2767
        $sessionId = api_get_session_id();
2768
        $course_dir = $course_info['path'].'/document';
2769
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
2770
        $base_work_dir = $sys_course_path.$course_dir;
2771
2772
        if (isset($files[$fileKey])) {
2773
            $upload_ok = process_uploaded_file($files[$fileKey], $show_output);
2774
2775
            if ($upload_ok) {
2776
                $new_path = handle_uploaded_document(
2777
                    $course_info,
2778
                    $files[$fileKey],
2779
                    $base_work_dir,
2780
                    $path,
2781
                    api_get_user_id(),
2782
                    api_get_group_id(),
2783
                    null,
2784
                    $unzip,
2785
                    $if_exists,
2786
                    $show_output,
2787
                    false,
2788
                    null,
2789
                    $sessionId,
2790
                    $treat_spaces_as_hyphens
2791
                );
2792
2793
                // Showing message when sending zip files
2794
                if ($new_path === true && $unzip == 1) {
2795
                    if ($show_output) {
2796
                        echo Display::return_message(
2797
                            get_lang('UplUploadSucceeded').'<br />',
2798
                            'confirm',
2799
                            false
2800
                        );
2801
                    }
2802
2803
                    return [
2804
                        'title' => $path,
2805
                        'url' => $path,
2806
                    ];
2807
                }
2808
2809
                if ($new_path) {
2810
                    $documentId = self::get_document_id(
2811
                        $course_info,
2812
                        $new_path,
2813
                        $sessionId
2814
                    );
2815
2816
                    if (!empty($documentId)) {
2817
                        $table_document = Database::get_course_table(TABLE_DOCUMENT);
2818
                        $params = [];
2819
2820
                        if (!empty($title)) {
2821
                            $params['title'] = $title;
2822
                        }
2823
2824
                        if (!empty($comment)) {
2825
                            $params['comment'] = trim($comment);
2826
                        }
2827
2828
                        Database::update(
2829
                            $table_document,
2830
                            $params,
2831
                            [
2832
                                'id = ? AND c_id = ? ' => [
2833
                                    $documentId,
2834
                                    $course_info['real_id']
2835
                                ]
2836
                            ]
2837
                        );
2838
                    }
2839
2840
                    if ($index_document) {
2841
                        self::index_document(
2842
                            $documentId,
2843
                            $course_info['code'],
2844
                            null,
2845
                            $_POST['language'],
2846
                            $_REQUEST,
2847
                            $if_exists
2848
                        );
2849
                    }
2850
2851
                    if (!empty($documentId) && is_numeric($documentId)) {
2852
                        $documentData = self::get_document_data_by_id(
2853
                            $documentId,
2854
                            $course_info['code'],
2855
                            false,
2856
                            $sessionId
2857
                        );
2858
                        return $documentData;
2859
                    }
2860
                }
2861
            }
2862
        }
2863
2864
        return false;
2865
    }
2866
2867
    /**
2868
     * Obtains the text inside the file with the right parser
2869
     */
2870
    public static function get_text_content($doc_path, $doc_mime)
2871
    {
2872
        // TODO: review w$ compatibility
2873
        // Use usual exec output lines array to store stdout instead of a temp file
2874
        // because we need to store it at RAM anyway before index on ChamiloIndexer object
2875
        $ret_val = null;
2876
        switch ($doc_mime) {
2877
            case 'text/plain':
2878
                $handle = fopen($doc_path, 'r');
2879
                $output = [fread($handle, filesize($doc_path))];
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2879
                $output = [fread(/** @scrutinizer ignore-type */ $handle, filesize($doc_path))];
Loading history...
2880
                fclose($handle);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2880
                fclose(/** @scrutinizer ignore-type */ $handle);
Loading history...
2881
                break;
2882
            case 'application/pdf':
2883
                exec("pdftotext $doc_path -", $output, $ret_val);
2884
                break;
2885
            case 'application/postscript':
2886
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
2887
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
2888
                if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
2889
                    return false;
2890
                }
2891
                exec("pdftotext $temp_file -", $output, $ret_val);
2892
                unlink($temp_file);
2893
                break;
2894
            case 'application/msword':
2895
                exec("catdoc $doc_path", $output, $ret_val);
2896
                break;
2897
            case 'text/html':
2898
                exec("html2text $doc_path", $output, $ret_val);
2899
                break;
2900
            case 'text/rtf':
2901
                // Note: correct handling of code pages in unrtf
2902
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
2903
                exec("unrtf --text $doc_path", $output, $ret_val);
2904
                if ($ret_val == 127) { // command not found
2905
                    return false;
2906
                }
2907
                // Avoid index unrtf comments
2908
                if (is_array($output) && count($output) > 1) {
2909
                    $parsed_output = [];
2910
                    foreach ($output as & $line) {
2911
                        if (!preg_match('/^###/', $line, $matches)) {
2912
                            if (!empty($line)) {
2913
                                $parsed_output[] = $line;
2914
                            }
2915
                        }
2916
                    }
2917
                    $output = $parsed_output;
2918
                }
2919
                break;
2920
            case 'application/vnd.ms-powerpoint':
2921
                exec("catppt $doc_path", $output, $ret_val);
2922
                break;
2923
            case 'application/vnd.ms-excel':
2924
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
2925
                break;
2926
        }
2927
2928
        $content = '';
2929
        if (!is_null($ret_val)) {
2930
            if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
2931
                return false;
2932
            }
2933
        }
2934
        if (isset($output)) {
2935
            foreach ($output as & $line) {
2936
                $content .= $line."\n";
2937
            }
2938
            return $content;
2939
        } else {
2940
            return false;
2941
        }
2942
    }
2943
2944
    /**
2945
     * Calculates the total size of all documents in a course
2946
     *
2947
     * @author Bert vanderkimpen
2948
     * @param  int $course_id
2949
     * @param  int $group_id (to calculate group document space)
2950
     * @param  int $session_id
2951
     *
2952
     * @return int total size
2953
     */
2954
    public static function documents_total_space($course_id = null, $group_id = null, $session_id = null)
2955
    {
2956
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
2957
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
2958
        $session_id = intval($session_id);
2959
        $group_id = intval($group_id);
2960
        $course_id = intval($course_id);
2961
2962
        if (!$course_id) {
2963
            $course_id = api_get_course_int_id();
2964
        }
2965
2966
        $group_condition = null;
2967
        if ($group_id) {
2968
            $group_condition = " AND props.to_group_id='".$group_id."' ";
2969
        }
2970
2971
        $session_condition = null;
2972
        if ($session_id) {
2973
            $session_condition = " AND props.session_id='".$session_id."' ";
2974
        }
2975
2976
        $sql = "SELECT SUM(size)
2977
                FROM $TABLE_ITEMPROPERTY AS props
2978
                INNER JOIN $TABLE_DOCUMENT AS docs
2979
                ON (docs.id = props.ref AND props.c_id = docs.c_id)
2980
                WHERE
2981
                    props.c_id 	= $course_id AND
2982
                    docs.c_id 	= $course_id AND
2983
                    props.tool 	= '".TOOL_DOCUMENT."' AND
2984
                    props.visibility <> 2
2985
                    $group_condition
2986
                    $session_condition
2987
                ";
2988
        $result = Database::query($sql);
2989
2990
        if ($result && Database::num_rows($result) != 0) {
2991
            $row = Database::fetch_row($result);
2992
            return $row[0];
2993
        } else {
2994
            return 0;
2995
        }
2996
    }
2997
2998
    /**
2999
     * Display the document quota in a simple way
3000
     *
3001
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
3002
     */
3003
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
3004
    {
3005
        $course_quota_m = round($course_quota / 1048576);
3006
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
3007
        $percentage = $already_consumed_space / $course_quota * 100;
3008
        $percentage = round($percentage, 1);
3009
        $message = get_lang('YouAreCurrentlyUsingXOfYourX');
3010
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
3011
3012
        return Display::div($message, ['id' => 'document_quota']);
3013
    }
3014
3015
    /**
3016
     * Checks if there is enough place to add a file on a directory
3017
     * on the base of a maximum directory size allowed
3018
     *
3019
     * @author Bert Vanderkimpen
3020
     * @param  int $file_size size of the file in byte
3021
     * @param  int $max_dir_space maximum size
3022
     * @return boolean true if there is enough space, false otherwise
3023
     *
3024
     * @see enough_space() uses  documents_total_space() function
3025
     */
3026
    public static function enough_space($file_size, $max_dir_space)
3027
    {
3028
        if ($max_dir_space) {
3029
            $already_filled_space = self::documents_total_space();
3030
            if (($file_size + $already_filled_space) > $max_dir_space) {
3031
                return false;
3032
            }
3033
        }
3034
        return true;
3035
    }
3036
3037
    /**
3038
     * @param array $params count, url, extension
3039
     * @return string
3040
     */
3041
    public static function generate_jplayer_jquery($params = [])
3042
    {
3043
        $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
3044
3045
        $js = '
3046
            $("#jquery_jplayer_' . $params['count'].'").jPlayer({
3047
                ready: function() {
3048
                    $(this).jPlayer("setMedia", {
3049
                        ' . $params['extension'].' : "'.$params['url'].'"
3050
                    });
3051
                },
3052
                play: function() { // To avoid both jPlayers playing together.
3053
                    $(this).jPlayer("pauseOthers");
3054
                },
3055
                //errorAlerts: true,
3056
                //warningAlerts: true,
3057
                swfPath: "' . $js_path.'jquery-jplayer/jplayer/",
3058
                //supplied: "m4a, oga, mp3, ogg, wav",
3059
                supplied: "' . $params['extension'].'",
3060
                wmode: "window",
3061
                solution: "flash, html",  // Do not change this setting
3062
                cssSelectorAncestor: "#jp_container_' . $params['count'].'",
3063
            });  	 ' . "\n\n";
3064
3065
        return $js;
3066
    }
3067
3068
    /**
3069
     *
3070
     * Shows a play icon next to the document title in the document list
3071
     * @param int
3072
     * @param string
3073
     * @return string	html content
3074
     */
3075
    public static function generate_media_preview($i, $type = 'simple')
3076
    {
3077
        $i = intval($i);
3078
        $extra_controls = $progress = '';
3079
        if ($type == 'advanced') {
3080
            $extra_controls = ' <li><a href="javascript:;" class="jp-stop" tabindex="1">stop</a></li>
3081
                                <li><a href="#" class="jp-mute" tabindex="1">mute</a></li>
3082
                                <li><a href="#" class="jp-unmute" tabindex="1">unmute</a></li>';
3083
            $progress = '<div class="jp-progress">
3084
                                <div class="jp-seek-bar">
3085
                                    <div class="jp-play-bar"></div>
3086
                                </div>
3087
                            </div>';
3088
        }
3089
3090
        //Shows only the play button
3091
        $html = '<div id="jquery_jplayer_'.$i.'" class="jp-jplayer"></div>
3092
                <div id="jp_container_' . $i.'" class="jp-audio">
3093
                    <div class="jp-type-single">
3094
                        <div class="jp-gui jp-interface">
3095
                            <ul class="jp-controls">
3096
                                <li><a href="javascript:;" class="jp-play" tabindex="1">play</a></li>
3097
                                <li><a href="javascript:;" class="jp-pause" tabindex="1">pause</a></li>
3098
                                ' . $extra_controls.'
3099
                            </ul>
3100
                            ' . $progress.'
3101
                        </div>
3102
                    </div>
3103
                </div>';
3104
        //<div id="jplayer_inspector_'.$i.'"></div>
3105
        return $html;
3106
    }
3107
3108
    /**
3109
     * @param array $document_data
3110
     * @return string
3111
     */
3112
    public static function generate_video_preview($document_data = [])
3113
    {
3114
        //<button class="jp-video-play-icon" role="button" tabindex="0">play</button>
3115
        $html = '
3116
        <div id="jp_container_1" class="jp-video center-block" role="application" aria-label="media player">
3117
            <div class="jp-type-single">
3118
                <div id="jquery_jplayer_1" class="jp-jplayer"></div>
3119
                <div class="jp-gui">
3120
                    <div class="jp-video-play">
3121
                    </div>
3122
                    <div class="jp-interface">
3123
                        <div class="jp-progress">
3124
                            <div class="jp-seek-bar">
3125
                                <div class="jp-play-bar"></div>
3126
                            </div>
3127
                        </div>
3128
                        <div class="jp-current-time" role="timer" aria-label="time">&nbsp;</div>
3129
                        <div class="jp-duration" role="timer" aria-label="duration">&nbsp;</div>
3130
                        <div class="jp-controls-holder">
3131
                          <div class="jp-controls">
3132
                            <button class="jp-play" role="button" tabindex="0">play</button>
3133
                            <button class="jp-stop" role="button" tabindex="0">stop</button>
3134
                          </div>
3135
                          <div class="jp-volume-controls">
3136
                            <button class="jp-mute" role="button" tabindex="0">mute</button>
3137
                            <button class="jp-volume-max" role="button" tabindex="0">max volume</button>
3138
                            <div class="jp-volume-bar">
3139
                                <div class="jp-volume-bar-value"></div>
3140
                            </div>
3141
                          </div>
3142
                          <div class="jp-toggles">
3143
                            <button class="jp-repeat" role="button" tabindex="0">repeat</button>
3144
                            <button class="jp-full-screen" role="button" tabindex="0">full screen</button>
3145
                          </div>
3146
                        </div>
3147
                        <div class="jp-details">
3148
                          <div class="jp-title" aria-label="title">&nbsp;</div>
3149
                        </div>
3150
                    </div>
3151
                </div>
3152
                <div class="jp-no-solution">
3153
                    <span>' . get_lang('UpdateRequire').'</span>
3154
                    ' . get_lang("ToPlayTheMediaYouWillNeedToUpdateYourBrowserToARecentVersionYouCanAlsoDownloadTheFile").'
3155
                </div>
3156
            </div>
3157
        </div>';
3158
        return $html;
3159
    }
3160
3161
    /**
3162
     * @param array $course_info
3163
     * @param bool $lp_id
3164
     * @param string $target
3165
     * @param int $session_id
3166
     * @param bool $add_move_button
3167
     * @param string $filter_by_folder
3168
     * @param string $overwrite_url
3169
     * @param bool $showInvisibleFiles
3170
     * @param bool $showOnlyFolders
3171
     * @param int $folderId
3172
     *
3173
     * @return string
3174
     */
3175
    public static function get_document_preview(
3176
        $course_info,
3177
        $lp_id = false,
3178
        $target = '',
3179
        $session_id = 0,
3180
        $add_move_button = false,
3181
        $filter_by_folder = null,
3182
        $overwrite_url = '',
3183
        $showInvisibleFiles = false,
3184
        $showOnlyFolders = false,
3185
        $folderId = false
3186
    ) {
3187
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
3188
            return '';
3189
        }
3190
3191
        $user_id = api_get_user_id();
3192
        $userInfo = api_get_user_info();
3193
3194
        $user_in_course = false;
3195
        if (api_is_platform_admin()) {
3196
            $user_in_course = true;
3197
        }
3198
3199
        if (!$user_in_course) {
3200
            if (CourseManager::is_course_teacher($user_id, $course_info['code'])) {
3201
                $user_in_course = true;
3202
            }
3203
        }
3204
3205
        // Condition for the session
3206
        $session_id = intval($session_id);
3207
3208
        if (!$user_in_course) {
3209
            if (empty($session_id)) {
3210
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
3211
                    $user_in_course = true;
3212
                }
3213
                // Check if course is open then we can consider that the student is registered to the course
3214
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3215
                    $user_in_course = true;
3216
                }
3217
            } else {
3218
                $user_status = SessionManager::get_user_status_in_course_session(
3219
                    $user_id,
3220
                    $course_info['real_id'],
3221
                    $session_id
3222
                );
3223
                //is true if is an student, course session teacher or coach
3224
                if (in_array($user_status, ['0', '2', '6'])) {
3225
                    $user_in_course = true;
3226
                }
3227
            }
3228
        }
3229
3230
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3231
        $tbl_item_prop = Database::get_course_table(TABLE_ITEM_PROPERTY);
3232
        $condition_session = " AND (last.session_id = '$session_id' OR last.session_id = '0' OR last.session_id IS NULL)";
3233
3234
        $add_folder_filter = null;
3235
        if (!empty($filter_by_folder)) {
3236
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3237
        }
3238
3239
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3240
        $lp_visibility_condition = null;
3241
        if ($lp_id) {
3242
            // $lp_visibility_condition = " OR filetype='folder'";
3243
            if ($showInvisibleFiles) {
3244
                $lp_visibility_condition .= ' OR last.visibility = 0';
3245
            }
3246
        }
3247
3248
        $showOnlyFoldersCondition = null;
3249
        if ($showOnlyFolders) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3250
            //$showOnlyFoldersCondition = " AND docs.filetype = 'folder' ";
3251
        }
3252
3253
        $folderCondition = " AND docs.path LIKE '/%' ";
3254
3255
        if (!api_is_allowed_to_edit()) {
3256
            $protectedFolders = self::getProtectedFolderFromStudent();
3257
            foreach ($protectedFolders as $folder) {
3258
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3259
            }
3260
        }
3261
3262
        $parentData = [];
3263
        if ($folderId !== false) {
3264
            $parentData = self::get_document_data_by_id(
3265
                $folderId,
3266
                $course_info['code'],
3267
                false,
3268
                $session_id
3269
            );
3270
            if (!empty($parentData)) {
3271
                $cleanedPath = $parentData['path'];
3272
                $num = substr_count($cleanedPath, '/');
3273
3274
                $notLikeCondition = '';
3275
                for ($i = 1; $i <= $num; $i++) {
3276
                    $repeat = str_repeat('/%', $i + 1);
3277
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3278
                }
3279
3280
                $folderId = (int) $folderId;
3281
                $folderCondition = " AND
3282
                    docs.id <> $folderId AND
3283
                    docs.path LIKE '".$cleanedPath."/%'
3284
                    $notLikeCondition
3285
                ";
3286
            } else {
3287
                $folderCondition = " AND docs.filetype = 'file' ";
3288
            }
3289
        }
3290
3291
        $levelCondition = null;
3292
        if ($folderId === false) {
3293
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3294
        }
3295
3296
        $sql = "SELECT DISTINCT last.visibility, docs.*
3297
                FROM $tbl_item_prop AS last 
3298
                INNER JOIN $tbl_doc AS docs
3299
                ON (docs.id = last.ref AND docs.c_id = last.c_id)
3300
                WHERE
3301
                    docs.path NOT LIKE '%_DELETED_%' AND
3302
                    last.tool = '".TOOL_DOCUMENT."' $condition_session AND
3303
                    (last.visibility = '1' $lp_visibility_condition) AND
3304
                    last.visibility <> 2 AND
3305
                    docs.c_id = {$course_info['real_id']} AND
3306
                    last.c_id = {$course_info['real_id']}
3307
                    $showOnlyFoldersCondition
3308
                    $folderCondition
3309
                    $levelCondition
3310
                    $add_folder_filter
3311
                ORDER BY docs.filetype DESC, docs.title ASC";
3312
3313
        $res_doc = Database::query($sql);
3314
        $resources = Database::store_result($res_doc, 'ASSOC');
3315
3316
        $return = '';
3317
        if ($lp_id) {
3318
            if ($folderId === false) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3319
                /*$return .= '<div class="lp_resource_element">';
3320
                $return .= Display::return_icon('new_doc.gif', '', [], ICON_SIZE_SMALL);
3321
                $return .= Display::url(
3322
                    get_lang('CreateTheDocument'),
3323
                    api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_DOCUMENT.'&lp_id='.$_SESSION['oLP']->lp_id
3324
                );
3325
                $return .= '</div>';*/
3326
            }
3327
        } else {
3328
            $return .= Display::div(
3329
                Display::url(
3330
                    Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3331
                    ' javascript:void(0);',
3332
                    ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3333
                ),
3334
                ['style' => 'position:absolute;right:10px']
3335
            );
3336
        }
3337
3338
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3339
        $newResources = [];
3340
        if (!empty($resources) && $user_in_course) {
3341
            foreach ($resources as $resource) {
3342
                $is_visible = self::is_visible_by_id(
3343
                    $resource['id'],
3344
                    $course_info,
3345
                    $session_id,
3346
                    api_get_user_id()
3347
                );
3348
3349
                if ($showInvisibleFiles == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
3350
                    if (!$is_visible) {
3351
                        continue;
3352
                    }
3353
                }
3354
3355
                $newResources[] = $resource;
3356
            }
3357
        }
3358
3359
        $label = get_lang('Documents');
3360
3361
        $documents = [];
3362
        if ($folderId === false) {
3363
            $documents[$label] = [
3364
                'id' => 0,
3365
                'files' => $newResources
3366
            ];
3367
        } else {
3368
            if (is_array($parentData)) {
3369
                $documents[$parentData['title']] = [
3370
                    'id' => intval($folderId),
3371
                    'files' => $newResources
3372
                ];
3373
            }
3374
        }
3375
3376
        $write_result = self::write_resources_tree(
3377
            $userInfo,
3378
            $course_info,
3379
            $session_id,
3380
            $documents,
3381
            $lp_id,
3382
            $target,
3383
            $add_move_button,
3384
            $overwrite_url,
3385
            $folderId
0 ignored issues
show
Bug introduced by
It seems like $folderId can also be of type false; however, parameter $folderId of DocumentManager::write_resources_tree() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3385
            /** @scrutinizer ignore-type */ $folderId
Loading history...
3386
        );
3387
3388
        $return .= $write_result;
3389
        if ($lp_id == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
3390
            $url = api_get_path(WEB_AJAX_PATH).'lp.ajax.php?a=get_documents&url='.$overwrite_url.'&lp_id='.$lp_id.'&cidReq='.$course_info['code'];
0 ignored issues
show
Bug introduced by
Are you sure $lp_id of type false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3390
            $url = api_get_path(WEB_AJAX_PATH).'lp.ajax.php?a=get_documents&url='.$overwrite_url.'&lp_id='./** @scrutinizer ignore-type */ $lp_id.'&cidReq='.$course_info['code'];
Loading history...
3391
            $return .= "<script>
3392
            $('.doc_folder').click(function() {
3393
                var realId = this.id;
3394
                var my_id = this.id.split('_')[2];
3395
                var tempId = 'temp_'+my_id;
3396
                $('#res_'+my_id).show();
3397
3398
                var tempDiv = $('#'+realId).find('#'+tempId);
3399
                if (tempDiv.length == 0) {
3400
                    $.ajax({
3401
                        async: false,
3402
                        type: 'GET',
3403
                        url:  '".$url."',
3404
                        data: 'folder_id='+my_id,
3405
                        success: function(data) {
3406
                            $('#'+realId).append('<div id='+tempId+'>'+data+'</div>');
3407
                        }
3408
                    });
3409
                }
3410
            });
3411
3412
            $('.close_div').click(function() {
3413
                var course_id = this.id.split('_')[2];
3414
                var session_id = this.id.split('_')[3];
3415
                $('#document_result_'+course_id+'_'+session_id).hide();
3416
                $('.lp_resource').remove();
3417
                $('.document_preview_container').html('');
3418
            });
3419
3420
            </script>";
3421
        } else {
3422
            //For LPs
3423
            $url = api_get_path(WEB_AJAX_PATH).'lp.ajax.php?a=get_documents&lp_id='.$lp_id.'&'.api_get_cidreq();
3424
            $return .= "<script>
3425
3426
            function testResources(id, img) {
3427
                var numericId = id.split('_')[1];
3428
                var parentId = 'doc_id_'+numericId;
3429
                var tempId = 'temp_'+numericId;
3430
                var image = $('#'+img);
3431
3432
                if (image.hasClass('open')) {
3433
                    image.removeClass('open');
3434
                    image.attr('src', '" . Display::returnIconPath('nolines_plus.gif')."');
3435
                    $('#'+id).show();
3436
                    $('#'+tempId).hide();
3437
                } else {
3438
                    image.addClass('open');
3439
                    image.attr('src', '" . Display::returnIconPath('nolines_minus.gif')."');
3440
                    $('#'+id).hide();
3441
                    $('#'+tempId).show();
3442
3443
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3444
                    if (tempDiv.length == 0) {
3445
                        $.ajax({
3446
                            type: 'GET',
3447
                            async: false,
3448
                            url:  '".$url."',
3449
                            data: 'folder_id='+numericId,
3450
                            success: function(data) {
3451
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3452
                            }
3453
                        });
3454
                    }
3455
                }
3456
            }
3457
            </script>";
3458
        }
3459
3460
        if (!$user_in_course) {
3461
            $return = '';
3462
        }
3463
3464
        return $return;
3465
    }
3466
3467
    /**
3468
     * Parse file information into a link
3469
     *
3470
     * @param array $userInfo Current user info
3471
     * @param array $course_info
3472
     * @param int $session_id
3473
     * @param array $resource
3474
     * @param int $lp_id
3475
     * @param bool $add_move_button
3476
     * @param string $target
3477
     * @param string $overwrite_url
3478
     * @return null|string
3479
     */
3480
    private static function parseFile(
3481
        $userInfo,
3482
        $course_info,
3483
        $session_id,
3484
        $resource,
3485
        $lp_id,
3486
        $add_move_button,
3487
        $target,
3488
        $overwrite_url
3489
    ) {
3490
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
3491
        $web_code_path = api_get_path(WEB_CODE_PATH);
3492
3493
        $documentId = $resource['id'];
3494
        $path = $resource['path'];
3495
3496
        if (empty($path)) {
3497
            $num = 0;
3498
        } else {
3499
            $num = substr_count($path, '/') - 1;
3500
        }
3501
3502
        // It's a file.
3503
        $icon = choose_image($path);
3504
        $position = strrpos($icon, '.');
3505
        $icon = substr($icon, 0, $position).'_small.gif';
3506
        $my_file_title = $resource['title'];
3507
        $visibility = $resource['visibility'];
3508
3509
        // If title is empty we try to use the path
3510
        if (empty($my_file_title)) {
3511
            $my_file_title = basename($path);
3512
        }
3513
3514
        // Show the "image name" not the filename of the image.
3515
        if ($lp_id) {
3516
            // LP URL
3517
            $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;
3518
            if (!empty($overwrite_url)) {
3519
                $url = $overwrite_url.'&cidReq='.$course_info['code'].'&id_session='.$session_id.'&document_id='.$documentId.'';
3520
            }
3521
        } else {
3522
            // Direct document URL
3523
            $url = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
3524
            if (!empty($overwrite_url)) {
3525
                $url = $overwrite_url.'&cidReq='.$course_info['code'].'&id_session='.$session_id.'&document_id='.$documentId;
3526
            }
3527
        }
3528
3529
        $img = Display::returnIconPath($icon);
3530
        if (!file_exists($img_sys_path.$icon)) {
3531
            $img = Display::returnIconPath('default_small.gif');
3532
        }
3533
3534
        $link = Display::url(
3535
            '<img alt="" src="'.$img.'" title="" />&nbsp;'.$my_file_title,
3536
            $url,
3537
            ['target' => $target, 'class' => 'moved']
3538
        );
3539
3540
        $directUrl = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
3541
        $link .= '&nbsp;'.Display::url(
3542
            Display::return_icon('preview_view.png', get_lang('Preview')),
3543
            $directUrl,
3544
            ['target' => '_blank']
3545
        );
3546
3547
        $visibilityClass = null;
3548
        if ($visibility == 0) {
3549
            $visibilityClass = ' text-muted ';
3550
        }
3551
        $return = null;
3552
3553
        if ($lp_id == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $lp_id of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3554
            $return .= '<li class="doc_resource '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
3555
        } else {
3556
            $return .= '<li class="doc_resource lp_resource_element '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
3557
        }
3558
3559
        $return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
3560
3561
        if ($add_move_button) {
3562
            $return .= '<a class="moved" href="#">';
3563
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
3564
            $return .= '</a> ';
3565
        }
3566
        $return .= $link;
3567
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
3568
        $return .= $sessionStar;
3569
3570
        $return .= '</div></li>';
3571
3572
        return $return;
3573
    }
3574
3575
    /**
3576
     * @param int $folderId
3577
     * @param array $resource
3578
     * @param int $lp_id
3579
     * @return null|string
3580
     */
3581
    private static function parseFolder($folderId, $resource, $lp_id)
3582
    {
3583
        $title = isset($resource['title']) ? $resource['title'] : null;
3584
        $path = isset($resource['path']) ? $resource['path'] : null;
3585
3586
        if (empty($path)) {
3587
            $num = 0;
3588
        } else {
3589
            $num = substr_count($path, '/');
3590
        }
3591
3592
        // It's a folder.
3593
        //hide some folders
3594
        if (in_array(
3595
            $path,
3596
            ['shared_folder', 'chat_files', 'HotPotatoes_files', 'css', 'certificates']
3597
        )) {
3598
            return null;
3599
        } elseif (preg_match('/_groupdocs/', $path)) {
3600
            return null;
3601
        } elseif (preg_match('/sf_user_/', $path)) {
3602
            return null;
3603
        } elseif (preg_match('/shared_folder_session_/', $path)) {
3604
            return null;
3605
        }
3606
3607
        //trad some titles
3608
        /*
3609
        if ($key == 'images') {
3610
            $key = get_lang('Images');
3611
        } elseif ($key == 'gallery') {
3612
            $key = get_lang('Gallery');
3613
        } elseif ($key == 'flash') {
3614
            $key = get_lang('Flash');
3615
        } elseif ($key == 'audio') {
3616
            $key = get_lang('Audio');
3617
        } elseif ($key == 'video') {
3618
            $key = get_lang('Video');
3619
        }*/
3620
3621
        $onclick = '';
3622
3623
        // if in LP, hidden folder are displayed in grey
3624
        $folder_class_hidden = '';
3625
        if ($lp_id) {
3626
            if (isset($resource['visible']) && $resource['visible'] == 0) {
3627
                $folder_class_hidden = "doc_folder_hidden"; // in base.css
3628
            }
3629
            $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
3630
        }
3631
        $return = null;
3632
3633
        if (empty($path)) {
3634
            $return = '<ul class="lp_resource">';
3635
        }
3636
3637
        $return .= '<li class="doc_folder '.$folder_class_hidden.'" id="doc_id_'.$resource['id'].'"  style="margin-left:'.($num * 18).'px; ">';
3638
3639
        $image = Display::returnIconPath('nolines_plus.gif');
3640
        if (empty($path)) {
3641
            $image = Display::returnIconPath('nolines_minus.gif');
3642
        }
3643
        $return .= '<img style="cursor: pointer;" src="'.$image.'" align="absmiddle" id="img_'.$resource['id'].'" '.$onclick.'>';
3644
        $return .= Display::return_icon('lp_folder.gif').'&nbsp;';
3645
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.$title.'</span>';
3646
        $return .= '</li>';
3647
3648
        if (empty($path)) {
3649
            if ($folderId == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $folderId of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3650
                $return .= '<div id="res_'.$resource['id'].'" >';
3651
            } else {
3652
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
3653
            }
3654
        }
3655
3656
        return $return;
3657
    }
3658
3659
    /**
3660
     * Generate and return an HTML list of resources based on a given array.
3661
     * This list is used to show the course creator a list of available resources to choose from
3662
     * when creating a learning path.
3663
     * @param array $userInfo current user info
3664
     * @param array $course_info
3665
     * @param int $session_id
3666
     * @param array $documents
3667
     * @param bool $lp_id
3668
     * @param string $target
3669
     * @param bool $add_move_button
3670
     * @param string $overwrite_url
3671
     * @param int $folderId
3672
     *
3673
     * @return string
3674
     */
3675
    public static function write_resources_tree(
3676
        $userInfo,
3677
        $course_info,
3678
        $session_id,
3679
        $documents,
3680
        $lp_id = false,
3681
        $target = '',
3682
        $add_move_button = false,
3683
        $overwrite_url = '',
3684
        $folderId = false
3685
    ) {
3686
        $return = '';
3687
        if (!empty($documents)) {
3688
            foreach ($documents as $key => $resource) {
3689
                if (isset($resource['id']) && is_int($resource['id'])) {
3690
                    $mainFolderResource = [
3691
                        'id' => $resource['id'],
3692
                        'title' => $key,
3693
                    ];
3694
3695
                    if ($folderId === false) {
3696
                        $return .= self::parseFolder($folderId, $mainFolderResource, $lp_id);
0 ignored issues
show
Bug introduced by
It seems like $folderId can also be of type false; however, parameter $folderId of DocumentManager::parseFolder() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3696
                        $return .= self::parseFolder(/** @scrutinizer ignore-type */ $folderId, $mainFolderResource, $lp_id);
Loading history...
3697
                    }
3698
3699
                    if (isset($resource['files'])) {
3700
                        $return .= self::write_resources_tree(
3701
                            $userInfo,
3702
                            $course_info,
3703
                            $session_id,
3704
                            $resource['files'],
3705
                            $lp_id,
3706
                            $target,
3707
                            $add_move_button,
3708
                            $overwrite_url
3709
                        );
3710
                    }
3711
                    $return .= '</div>';
3712
                    $return .= '</ul>';
3713
                } else {
3714
                    if ($resource['filetype'] == 'folder') {
3715
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3716
                    } else {
3717
                        $return .= self::parseFile(
3718
                            $userInfo,
3719
                            $course_info,
3720
                            $session_id,
3721
                            $resource,
3722
                            $lp_id,
3723
                            $add_move_button,
3724
                            $target,
3725
                            $overwrite_url
3726
                        );
3727
                    }
3728
                }
3729
            }
3730
        }
3731
3732
        return $return;
3733
    }
3734
3735
    /**
3736
     * @param int $doc_id
3737
     * @param string $course_code
3738
     * @param int $session_id
3739
     * @param int $user_id
3740
     * @param int $groupId iid
3741
     * @return bool
3742
     */
3743
    public static function check_visibility_tree(
3744
        $doc_id,
3745
        $course_code,
3746
        $session_id,
3747
        $user_id,
3748
        $groupId = 0
3749
    ) {
3750
        $document_data = self::get_document_data_by_id(
3751
            $doc_id,
3752
            $course_code,
3753
            null,
3754
            $session_id
3755
        );
3756
        if ($session_id != 0 && !$document_data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $document_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3757
            $document_data = self::get_document_data_by_id(
3758
                $doc_id,
3759
                $course_code,
3760
                null,
3761
                0
3762
            );
3763
        }
3764
3765
        if (!empty($document_data)) {
3766
            // If admin or course teacher, allow anyway
3767
            if (api_is_platform_admin() || CourseManager::is_course_teacher($user_id, $course_code)) {
3768
                return true;
3769
            }
3770
            $course_info = api_get_course_info($course_code);
3771
            if ($document_data['parent_id'] == false || empty($document_data['parent_id'])) {
3772
                if (!empty($groupId)) {
3773
                    return true;
3774
                }
3775
                $visible = self::is_visible_by_id($doc_id, $course_info, $session_id, $user_id);
3776
                return $visible;
3777
            } else {
3778
                $visible = self::is_visible_by_id($doc_id, $course_info, $session_id, $user_id);
3779
3780
                if (!$visible) {
3781
                    return false;
3782
                } else {
3783
                    return self::check_visibility_tree(
3784
                        $document_data['parent_id'],
3785
                        $course_code,
3786
                        $session_id,
3787
                        $user_id,
3788
                        $groupId
3789
                    );
3790
                }
3791
            }
3792
        } else {
3793
            return false;
3794
        }
3795
    }
3796
3797
    /**
3798
     * Index a given document.
3799
     * @param   int     Document ID inside its corresponding course
3800
     * @param   string  Course code
3801
     * @param   int     Session ID (not used yet)
3802
     * @param   string  Language of document's content (defaults to course language)
3803
     * @param   array   Array of specific fields (['code'=>'value',...])
3804
     * @param   string  What to do if the file already exists (default or overwrite)
3805
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3806
     * @return  bool    Returns true on presumed success, false on failure
3807
     */
3808
    public static function index_document(
3809
        $docid,
3810
        $course_code,
3811
        $session_id = 0,
3812
        $lang = 'english',
3813
        $specific_fields_values = [],
3814
        $if_exists = '',
3815
        $simulation = false
3816
    ) {
3817
        if (api_get_setting('search_enabled') !== 'true') {
3818
            return false;
3819
        }
3820
        if (empty($docid) or $docid != intval($docid)) {
3821
            return false;
3822
        }
3823
        if (empty($session_id)) {
3824
            $session_id = api_get_session_id();
3825
        }
3826
        $course_info = api_get_course_info($course_code);
3827
        $course_dir = $course_info['path'].'/document';
3828
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3829
        $base_work_dir = $sys_course_path.$course_dir;
3830
3831
        $course_id = $course_info['real_id'];
3832
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3833
3834
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3835
        $result = Database::query($qry);
3836
        if (Database::num_rows($result) == 1) {
3837
            $row = Database::fetch_array($result);
3838
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3839
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3840
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3841
            $doc_mime = mime_content_type($doc_path);
3842
            $allowed_mime_types = self::file_get_mime_type(true);
3843
3844
            // mime_content_type does not detect correctly some formats that
3845
            // are going to be supported for index, so an extensions array is used for the moment
3846
            if (empty($doc_mime)) {
3847
                $allowed_extensions = [
3848
                    'doc',
3849
                    'docx',
3850
                    'ppt',
3851
                    'pptx',
3852
                    'pps',
3853
                    'ppsx',
3854
                    'xls',
3855
                    'xlsx',
3856
                    'odt',
3857
                    'odp',
3858
                    'ods',
3859
                    'pdf',
3860
                    'txt',
3861
                    'rtf',
3862
                    'msg',
3863
                    'csv',
3864
                    'html',
3865
                    'htm',
3866
                ];
3867
                $extensions = preg_split("/[\/\\.]/", $doc_path);
3868
                $doc_ext = strtolower($extensions[count($extensions) - 1]);
0 ignored issues
show
Bug introduced by
It seems like $extensions can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3868
                $doc_ext = strtolower($extensions[count(/** @scrutinizer ignore-type */ $extensions) - 1]);
Loading history...
3869
                if (in_array($doc_ext, $allowed_extensions)) {
3870
                    switch ($doc_ext) {
3871
                        case 'ppt':
3872
                        case 'pps':
3873
                            $doc_mime = 'application/vnd.ms-powerpoint';
3874
                            break;
3875
                        case 'xls':
3876
                            $doc_mime = 'application/vnd.ms-excel';
3877
                            break;
3878
                    }
3879
                }
3880
            }
3881
3882
            //@todo move this nightmare in a search controller or something like that!!! J.M
3883
3884
            if (in_array($doc_mime, $allowed_mime_types)) {
3885
                $file_title = $row['title'];
3886
                $file_content = self::get_text_content($doc_path, $doc_mime);
3887
                $course_code = Database::escape_string($course_code);
3888
                $ic_slide = new IndexableChunk();
3889
                $ic_slide->addValue('title', $file_title);
3890
                $ic_slide->addCourseId($course_code);
3891
                $ic_slide->addToolId(TOOL_DOCUMENT);
3892
                $xapian_data = [
3893
                    SE_COURSE_ID => $course_code,
3894
                    SE_TOOL_ID => TOOL_DOCUMENT,
3895
                    SE_DATA => ['doc_id' => $docid],
3896
                    SE_USER => api_get_user_id(),
3897
                ];
3898
3899
                $ic_slide->xapian_data = serialize($xapian_data);
3900
                $di = new ChamiloIndexer();
3901
                $return = $di->connectDb(null, null, $lang);
3902
3903
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
3904
                $specific_fields = get_specific_field_list();
3905
3906
                // process different depending on what to do if file exists
3907
                /**
3908
                 * @TODO Find a way to really verify if the file had been
3909
                 * overwriten. Now all work is done at
3910
                 * handle_uploaded_document() and it's difficult to verify it
3911
                 */
3912
                if (!empty($if_exists) && $if_exists == 'overwrite') {
3913
                    // Overwrite the file on search engine
3914
                    // Actually, it consists on a delete of terms from db,
3915
                    // insert new ones, create a new search engine document,
3916
                    // and remove the old one
3917
                    // Get search_did
3918
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3919
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
3920
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
3921
3922
                    $res = Database::query($sql);
3923
3924
                    if (Database::num_rows($res) > 0) {
3925
                        $se_ref = Database::fetch_array($res);
3926
                        if (!$simulation) {
3927
                            $di->remove_document($se_ref['search_did']);
3928
                        }
3929
                        $all_specific_terms = '';
3930
                        foreach ($specific_fields as $specific_field) {
3931
                            if (!$simulation) {
3932
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
3933
                            }
3934
                            // Update search engine
3935
                            if (isset($specific_fields_values[$specific_field['code']])) {
3936
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
3937
                            } else { //if the specific field is not defined, force an empty one
3938
                                $sterms = '';
3939
                            }
3940
                            $all_specific_terms .= ' '.$sterms;
3941
                            $sterms = explode(',', $sterms);
3942
                            foreach ($sterms as $sterm) {
3943
                                $sterm = trim($sterm);
3944
                                if (!empty($sterm)) {
3945
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
3946
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
3947
                                    if (!$simulation) {
3948
                                        add_specific_field_value(
3949
                                            $specific_field['id'],
3950
                                            $course_code,
3951
                                            TOOL_DOCUMENT,
3952
                                            $docid,
3953
                                            $sterm
3954
                                        );
3955
                                    }
3956
                                }
3957
                            }
3958
                        }
3959
                        // Add terms also to content to make terms findable by probabilistic search
3960
                        $file_content = $all_specific_terms.' '.$file_content;
0 ignored issues
show
Bug introduced by
Are you sure $file_content of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3960
                        $file_content = $all_specific_terms.' './** @scrutinizer ignore-type */ $file_content;
Loading history...
3961
3962
                        if (!$simulation) {
3963
                            $ic_slide->addValue('content', $file_content);
3964
                            $di->addChunk($ic_slide);
3965
                            // Index and return a new search engine document id
3966
                            $did = $di->index();
3967
3968
                            if ($did) {
3969
                                // update the search_did on db
3970
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3971
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
3972
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
3973
                                Database::query($sql);
3974
                            }
3975
                        }
3976
                    }
3977
                } else {
3978
                    // Add all terms
3979
                    $all_specific_terms = '';
3980
                    foreach ($specific_fields as $specific_field) {
3981
                        if (isset($specific_fields_values[$specific_field['code']])) {
3982
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
3983
                        } else { //if the specific field is not defined, force an empty one
3984
                            $sterms = '';
3985
                        }
3986
                        $all_specific_terms .= ' '.$sterms;
3987
                        if (!empty($sterms)) {
3988
                            $sterms = explode(',', $sterms);
3989
                            foreach ($sterms as $sterm) {
3990
                                if (!$simulation) {
3991
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
3992
                                    add_specific_field_value(
3993
                                        $specific_field['id'],
3994
                                        $course_code,
3995
                                        TOOL_DOCUMENT,
3996
                                        $docid,
3997
                                        $sterm
3998
                                    );
3999
                                }
4000
                            }
4001
                        }
4002
                    }
4003
                    // Add terms also to content to make terms findable by probabilistic search
4004
                    $file_content = $all_specific_terms.' '.$file_content;
4005
                    if (!$simulation) {
4006
                        $ic_slide->addValue('content', $file_content);
4007
                        $di->addChunk($ic_slide);
4008
                        // Index and return search engine document id
4009
                        $did = $di->index();
4010
                        if ($did) {
4011
                            // Save it to db
4012
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4013
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
4014
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
4015
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
4016
                            Database::query($sql);
4017
                        } else {
4018
                            return false;
4019
                        }
4020
                    }
4021
                }
4022
            } else {
4023
                return false;
4024
            }
4025
        }
4026
        return true;
4027
    }
4028
4029
    /**
4030
     * @return array
4031
     */
4032
    public static function get_web_odf_extension_list()
4033
    {
4034
        return ['ods', 'odt', 'odp'];
4035
    }
4036
4037
    /**
4038
     * Set of extension allowed to use Jodconverter
4039
     * @param $mode 'from'
4040
     *              'to'
4041
     *              'all'
4042
     * @param $format   'text'
4043
     *                  'spreadsheet'
4044
     *                  'presentation'
4045
     *                  'drawing'
4046
     *                  'all'
4047
     * @return array
4048
     */
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...
4049
    public static function getJodconverterExtensionList($mode, $format)
4050
    {
4051
        $extensionList = [];
4052
        $extensionListFromText = [
4053
            'odt',
4054
            'sxw',
4055
            'rtf',
4056
            'doc',
4057
            'docx',
4058
            'wpd',
4059
            'txt',
4060
        ];
4061
        $extensionListToText = [
4062
            'pdf',
4063
            'odt',
4064
            'sxw',
4065
            'rtf',
4066
            'doc',
4067
            'docx',
4068
            'txt',
4069
        ];
4070
        $extensionListFromSpreadsheet = [
4071
            'ods',
4072
            'sxc',
4073
            'xls',
4074
            'xlsx',
4075
            'csv',
4076
            'tsv',
4077
        ];
4078
        $extensionListToSpreadsheet = [
4079
            'pdf',
4080
            'ods',
4081
            'sxc',
4082
            'xls',
4083
            'xlsx',
4084
            'csv',
4085
            'tsv',
4086
        ];
4087
        $extensionListFromPresentation = [
4088
            'odp',
4089
            'sxi',
4090
            'ppt',
4091
            'pptx',
4092
        ];
4093
        $extensionListToPresentation = [
4094
            'pdf',
4095
            'swf',
4096
            'odp',
4097
            'sxi',
4098
            'ppt',
4099
            'pptx',
4100
        ];
4101
        $extensionListFromDrawing = ['odg'];
4102
        $extensionListToDrawing = ['svg', 'swf'];
4103
4104
        if ($mode === 'from') {
4105
            if ($format === 'text') {
4106
                $extensionList = array_merge($extensionList, $extensionListFromText);
4107
            } elseif ($format === 'spreadsheet') {
4108
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4109
            } elseif ($format === 'presentation') {
4110
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4111
            } elseif ($format === 'drawing') {
4112
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4113
            } elseif ($format === 'all') {
4114
                $extensionList = array_merge($extensionList, $extensionListFromText);
4115
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4116
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4117
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4118
            }
4119
        } elseif ($mode === 'to') {
4120
            if ($format === 'text') {
4121
                $extensionList = array_merge($extensionList, $extensionListToText);
4122
            } elseif ($format === 'spreadsheet') {
4123
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4124
            } elseif ($format === 'presentation') {
4125
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4126
            } elseif ($format === 'drawing') {
4127
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4128
            } elseif ($format === 'all') {
4129
                $extensionList = array_merge($extensionList, $extensionListToText);
4130
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4131
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4132
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4133
            }
4134
        } elseif ($mode === 'all') {
4135
            if ($format === 'text') {
4136
                $extensionList = array_merge($extensionList, $extensionListFromText);
4137
                $extensionList = array_merge($extensionList, $extensionListToText);
4138
            } elseif ($format === 'spreadsheet') {
4139
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4140
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4141
            } elseif ($format === 'presentation') {
4142
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4143
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4144
            } elseif ($format === 'drawing') {
4145
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4146
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4147
            } elseif ($format === 'all') {
4148
                $extensionList = array_merge($extensionList, $extensionListFromText);
4149
                $extensionList = array_merge($extensionList, $extensionListToText);
4150
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4151
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4152
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4153
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4154
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4155
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4156
            }
4157
        }
4158
        return $extensionList;
4159
    }
4160
4161
    /**
4162
     * Get Format type list by extension and mode
4163
     * @param string $mode Mode to search format type list
4164
     * @example 'from'
4165
     * @example 'to'
4166
     * @param string $extension file extension to check file type
4167
     * @return array
4168
     */
4169
    public static function getFormatTypeListConvertor($mode = 'from', $extension)
4170
    {
4171
        $formatTypesList = [];
4172
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
4173
        foreach ($formatTypes as $formatType) {
4174
            if (
4175
            in_array(
4176
                $extension,
4177
                self::getJodconverterExtensionList($mode, $formatType)
4178
            )
4179
            ) {
4180
                $formatTypesList[] = $formatType;
4181
            }
4182
        }
4183
        return $formatTypesList;
4184
    }
4185
4186
    /**
4187
     * @param string $path
4188
     * @param bool $is_certificate_mode
4189
     * @return bool
4190
     */
4191
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
4192
    {
4193
        $foldersToAvoid = [
4194
            '/HotPotatoes_files',
4195
            '/certificates',
4196
        ];
4197
        $systemFolder = api_get_course_setting('show_system_folders');
4198
4199
        if ($systemFolder == 1) {
4200
            $foldersToAvoid = [];
4201
        }
4202
4203
        if (basename($path) == 'css') {
4204
            return true;
4205
        }
4206
4207
        if ($is_certificate_mode == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
4208
            //Certificate results
4209
            if (strstr($path, 'certificates')) {
4210
                return true;
4211
            }
4212
        }
4213
4214
        // Admin setting for Hide/Show the folders of all users
4215
        if (api_get_setting('show_users_folders') == 'false') {
4216
            $foldersToAvoid[] = '/shared_folder';
4217
4218
            if (strstr($path, 'shared_folder_session_')) {
4219
                return true;
4220
            }
4221
        }
4222
4223
        // Admin setting for Hide/Show Default folders to all users
4224
        if (api_get_setting('show_default_folders') == 'false') {
4225
            $foldersToAvoid[] = '/images';
4226
            $foldersToAvoid[] = '/flash';
4227
            $foldersToAvoid[] = '/audio';
4228
            $foldersToAvoid[] = '/video';
4229
        }
4230
4231
        // Admin setting for Hide/Show chat history folder
4232
        if (api_get_setting('show_chat_folder') == 'false') {
4233
            $foldersToAvoid[] = '/chat_files';
4234
        }
4235
4236
        if (is_array($foldersToAvoid)) {
4237
            return in_array($path, $foldersToAvoid);
4238
        } else {
4239
            return false;
4240
        }
4241
    }
4242
4243
    /**
4244
     * @return array
4245
     */
4246
    public static function get_system_folders()
4247
    {
4248
        return [
4249
            '/certificates',
4250
            '/HotPotatoes_files',
4251
            '/chat_files',
4252
            '/images',
4253
            '/flash',
4254
            '/audio',
4255
            '/video',
4256
            '/shared_folder',
4257
            '/learning_path'
4258
        ];
4259
    }
4260
4261
    /**
4262
     * @return array
4263
     */
4264
    public static function getProtectedFolderFromStudent()
4265
    {
4266
        return [
4267
            '/certificates',
4268
            '/HotPotatoes_files',
4269
            '/chat_files',
4270
            '/shared_folder',
4271
            '/learning_path'
4272
        ];
4273
    }
4274
4275
    /**
4276
     * @param string $courseCode
4277
     * @return string 'visible' or 'invisible' string
4278
     */
4279
    public static function getDocumentDefaultVisibility($courseCode)
4280
    {
4281
        $settings = api_get_setting('tool_visible_by_default_at_creation');
4282
        $defaultVisibility = 'visible';
4283
4284
        if (isset($settings['documents'])) {
4285
            $portalDefaultVisibility = 'invisible';
4286
            if ($settings['documents'] == 'true') {
4287
                $portalDefaultVisibility = 'visible';
4288
            }
4289
4290
            $defaultVisibility = $portalDefaultVisibility;
4291
        }
4292
4293
        if (api_get_setting('documents_default_visibility_defined_in_course') == 'true') {
4294
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseCode);
4295
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
4296
                $defaultVisibility = $courseVisibility;
4297
            }
4298
        }
4299
        return $defaultVisibility;
4300
    }
4301
4302
    /**
4303
     * @param array $courseInfo
4304
     * @param int $id doc id
4305
     * @param string $visibility visible/invisible
4306
     * @param int $userId
4307
     */
4308
    public static function updateVisibilityFromAllSessions($courseInfo, $id, $visibility, $userId)
4309
    {
4310
        $sessionList = SessionManager::get_session_by_course($courseInfo['real_id']);
4311
4312
        if (!empty($sessionList)) {
4313
            foreach ($sessionList as $session) {
4314
                $sessionId = $session['id'];
4315
                api_item_property_update(
4316
                    $courseInfo,
4317
                    TOOL_DOCUMENT,
4318
                    $id,
4319
                    $visibility,
4320
                    $userId,
4321
                    null,
4322
                    null,
4323
                    null,
4324
                    null,
4325
                    $sessionId
4326
                );
4327
            }
4328
        }
4329
    }
4330
4331
    /**
4332
     * @param string $file
4333
     * @return string
4334
     */
4335
    public static function readNanogongFile($file)
4336
    {
4337
        $nanoGongJarFile = api_get_path(WEB_LIBRARY_PATH).'nanogong/nanogong.jar';
4338
        $html = '<applet id="applet" archive="'.$nanoGongJarFile.'" code="gong.NanoGong" width="160" height="95">';
4339
        $html .= '<param name="SoundFileURL" value="'.$file.'" />';
4340
        $html .= '<param name="ShowSaveButton" value="false" />';
4341
        $html .= '<param name="ShowTime" value="true" />';
4342
        $html .= '<param name="ShowRecordButton" value="false" />';
4343
        $html .= '</applet>';
4344
4345
        return $html;
4346
    }
4347
4348
    /**
4349
     * @param string $filePath
4350
     * @param string $path
4351
     * @param array $courseInfo
4352
     * @param int $sessionId
4353
     * @param string $whatIfFileExists overwrite|rename
4354
     * @param int $userId
4355
     * @param int $groupId
4356
     * @param int $toUserId
4357
     * @param string $comment
4358
     * @return bool|path
4359
     */
4360
    public static function addFileToDocumentTool(
4361
        $filePath,
4362
        $path,
4363
        $courseInfo,
4364
        $sessionId,
4365
        $userId,
4366
        $whatIfFileExists = 'overwrite',
4367
        $groupId = null,
4368
        $toUserId = null,
4369
        $comment = null
4370
    ) {
4371
        if (!file_exists($filePath)) {
4372
            return false;
4373
        }
4374
4375
        $fileInfo = pathinfo($filePath);
4376
4377
        $file = [
4378
            'name' => $fileInfo['basename'],
4379
            'tmp_name' => $filePath,
4380
            'size' => filesize($filePath),
4381
            'from_file' => true
4382
        ];
4383
4384
        $course_dir = $courseInfo['path'].'/document';
4385
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
4386
4387
        $filePath = handle_uploaded_document(
4388
            $courseInfo,
4389
            $file,
4390
            $baseWorkDir,
4391
            $path,
4392
            $userId,
4393
            $groupId,
4394
            $toUserId,
4395
            false,
4396
            $whatIfFileExists,
4397
            false,
4398
            false,
4399
            $comment,
4400
            $sessionId
4401
        );
4402
4403
        if ($filePath) {
4404
            return self::get_document_id(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::get_documen... $filePath, $sessionId) returns the type integer which is incompatible with the documented return type boolean|path.
Loading history...
4405
                $courseInfo,
4406
                $filePath,
4407
                $sessionId
4408
            );
4409
        }
4410
        return false;
4411
    }
4412
4413
    /**
4414
     * Converts wav to mp3 file.
4415
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg
4416
     * @param string $wavFile
4417
     * @param bool $removeWavFileIfSuccess
4418
     * @return bool
4419
     */
4420
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
4421
    {
4422
        if (file_exists($wavFile)) {
4423
            try {
4424
                $ffmpeg = \FFMpeg\FFMpeg::create();
4425
                $video = $ffmpeg->open($wavFile);
4426
4427
                $mp3File = str_replace('wav', 'mp3', $wavFile);
4428
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
4429
                if ($result && $removeWavFileIfSuccess) {
4430
                    unlink($wavFile);
4431
                }
4432
4433
                if (file_exists($mp3File)) {
4434
                    return $mp3File;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $mp3File returns the type string which is incompatible with the documented return type boolean.
Loading history...
4435
                }
4436
            } catch (Exception $e) {
4437
                error_log($e->getMessage());
4438
                error_log($e->getPrevious()->getMessage());
4439
            }
4440
        }
4441
        return false;
4442
    }
4443
4444
    /**
4445
     * @param string $documentData wav document information
4446
     * @param array $courseInfo
4447
     * @param int $sessionId
4448
     * @param int $userId user that adds the document
4449
     * @param string $whatIfFileExists
4450
     * @param bool $deleteWavFile
4451
     *
4452
     * @return bool
4453
     */
4454
    public static function addAndConvertWavToMp3(
4455
        $documentData,
4456
        $courseInfo,
4457
        $sessionId,
4458
        $userId,
4459
        $whatIfFileExists = 'overwrite',
4460
        $deleteWavFile = false
4461
    ) {
4462
        if (empty($documentData)) {
4463
            return false;
4464
        }
4465
4466
        if (isset($documentData['absolute_path']) &&
4467
            file_exists($documentData['absolute_path'])
4468
        ) {
4469
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4470
            error_log($mp3FilePath);
4471
4472
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4473
                $documentId = self::addFileToDocumentTool(
4474
                    $mp3FilePath,
4475
                    dirname($documentData['path']),
4476
                    $courseInfo,
4477
                    $sessionId,
4478
                    $userId,
4479
                    $whatIfFileExists,
4480
                    null,
4481
                    null,
4482
                    $documentData['comment']
4483
                );
4484
4485
                if (!empty($documentId)) {
4486
                    if ($deleteWavFile) {
4487
                        $coursePath = $courseInfo['directory'].'/document';
4488
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4489
                        self::delete_document(
4490
                            $courseInfo,
4491
                            null,
4492
                            $documentPath,
4493
                            $sessionId,
4494
                            $documentData['id']
4495
                        );
4496
                    }
4497
4498
                    return $documentId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $documentId also could return the type path which is incompatible with the documented return type boolean.
Loading history...
4499
                }
4500
            }
4501
        }
4502
4503
        return false;
4504
    }
4505
4506
    /**
4507
     * Sets
4508
     * @param string $file ($document_data['path'])
4509
     * @param string $file_url_sys
4510
     * @return string
4511
     */
4512
    public static function generateAudioTempFile($file, $file_url_sys)
4513
    {
4514
        //make temp audio
4515
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4516
        if (!file_exists($temp_folder)) {
4517
            @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

4517
            /** @scrutinizer ignore-unhandled */ @mkdir($temp_folder, api_get_permissions_for_new_directories(), true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4518
        }
4519
4520
        //make htaccess with allow from all, and file index.html into temp/audio
4521
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4522
        if (!file_exists($htaccess)) {
4523
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4524
            $fp = @ fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4525
            if ($fp) {
4526
                fwrite($fp, $htaccess_content);
4527
                fclose($fp);
4528
            }
4529
        }
4530
4531
        //encript temp name file
4532
        $name_crip = sha1(uniqid()); //encript
4533
        $findext = explode(".", $file);
4534
        $extension = $findext[count($findext) - 1];
4535
        $file_crip = $name_crip.'.'.$extension;
4536
4537
        //copy file to temp/audio directory
4538
        $from_sys = $file_url_sys;
4539
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4540
4541
        if (file_exists($from_sys)) {
4542
            copy($from_sys, $to_sys);
4543
        }
4544
4545
        // get file from tmp directory
4546
        Session::write('temp_audio_nanogong', $to_sys);
4547
4548
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4549
    }
4550
4551
    /**
4552
     * Erase temp nanogong audio.
4553
     */
4554
    public static function removeGeneratedAudioTempFile()
4555
    {
4556
        $tempAudio = Session::read('temp_audio_nanogong');
4557
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4558
            unlink($tempAudio);
4559
            Session::erase('temp_audio_nanogong');
4560
        }
4561
    }
4562
4563
    /**
4564
     * Check if the past is used in this course.
4565
     * @param array $courseInfo
4566
     * @param string $path
4567
     *
4568
     * @return array
4569
     */
4570
    public static function getDocumentByPathInCourse($courseInfo, $path)
4571
    {
4572
        $table = Database::get_course_table(TABLE_DOCUMENT);
4573
        $path = Database::escape_string($path);
4574
        $courseId = $courseInfo['real_id'];
4575
        if (empty($courseId)) {
4576
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
4577
        }
4578
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4579
        $result = Database::query($sql);
4580
        return Database::store_result($result, 'ASSOC');
4581
    }
4582
4583
    /**
4584
     * @param array $_course
4585
     * @return int
4586
     */
4587
    public static function createDefaultAudioFolder($_course)
4588
    {
4589
        if (!isset($_course['path'])) {
4590
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
4591
        }
4592
4593
        $audioId = null;
4594
        $path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
4595
        if (!is_dir($path.'audio')) {
4596
            mkdir($path.'audio', api_get_permissions_for_new_directories());
4597
            $audioId = add_document($_course, '/audio', 'folder', 0, 'Audio');
4598
            api_item_property_update(
4599
                $_course,
4600
                TOOL_DOCUMENT,
4601
                $audioId,
4602
                'FolderCreated',
4603
                api_get_user_id(),
4604
                null,
4605
                null,
4606
                null,
4607
                null,
4608
                api_get_session_id()
4609
            );
4610
        }
4611
4612
        return $audioId;
4613
    }
4614
4615
    /**
4616
     * Generate a default certificate for a courses
4617
     * @todo move to certificate lib
4618
     * @global string $css CSS directory
4619
     * @global string $img_dir image directory
4620
     * @global string $default_course_dir Course directory
4621
     * @global string $js JS directory
4622
     * @param array $courseData The course info
4623
     * @param bool $fromBaseCourse
4624
     * @param int $sessionId
4625
     */
4626
    public static function generateDefaultCertificate(
4627
        $courseData,
4628
        $fromBaseCourse = false,
4629
        $sessionId = 0
4630
    ) {
4631
        if (empty($courseData)) {
4632
            return false;
4633
        }
4634
4635
        global $css, $img_dir, $default_course_dir, $js;
4636
        $codePath = api_get_path(REL_CODE_PATH);
4637
        $dir = '/certificates';
4638
        $comment = null;
4639
        $title = get_lang('DefaultCertificate');
4640
        $fileName = api_replace_dangerous_char($title);
4641
        $filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4642
4643
        if (!is_dir($filePath)) {
4644
            mkdir($filePath, api_get_permissions_for_new_directories());
4645
        }
4646
4647
        $fileFullPath = "$filePath/$fileName.html";
4648
        $fileType = 'file';
4649
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4650
4651
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4652
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4653
4654
        $fileContent = str_replace($search, $replace, $templateContent);
4655
        $saveFilePath = "$dir/$fileName.html";
4656
4657
        if ($fromBaseCourse) {
4658
            $defaultCertificateId = self::get_default_certificate_id(
4659
                $courseData['code'],
4660
                0
4661
            );
4662
            if (!empty($defaultCertificateId)) {
4663
                // We have a certificate from the course base
4664
                $documentData = self::get_document_data_by_id(
4665
                    $defaultCertificateId,
4666
                    $courseData['code'],
4667
                    false,
4668
                    0
4669
                );
4670
4671
                if ($documentData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documentData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
4672
                    $fileContent = file_get_contents($documentData['absolute_path']);
4673
                }
4674
            }
4675
        }
4676
4677
        if (file_exists($fileFullPath) === false) {
4678
            $result = file_put_contents($fileFullPath, $fileContent);
4679
            if ($result) {
4680
                $fileSize = filesize($fileFullPath);
4681
4682
                $documentId = add_document(
4683
                    $courseData,
4684
                    $saveFilePath,
4685
                    $fileType,
4686
                    $fileSize,
4687
                    $title,
4688
                    $comment,
4689
                    0, //$readonly = 0,
4690
                    true, //$save_visibility = true,
4691
                    null, //$group_id = null,
4692
                    $sessionId
4693
                );
4694
4695
                api_item_property_update(
4696
                    $courseData,
4697
                    TOOL_DOCUMENT,
4698
                    $documentId,
4699
                    'DocumentAdded',
4700
                    api_get_user_id(),
4701
                    null,
4702
                    null,
4703
                    null,
4704
                    null,
4705
                    $sessionId
4706
                );
4707
4708
                $defaultCertificateId = self::get_default_certificate_id(
4709
                    $courseData['code'],
4710
                    $sessionId
4711
                );
4712
4713
                if (!isset($defaultCertificateId)) {
4714
                    self::attach_gradebook_certificate(
4715
                        $courseData['code'],
4716
                        $documentId,
4717
                        $sessionId
4718
                    );
4719
                }
4720
            }
4721
        }
4722
    }
4723
4724
    /**
4725
     * Update the document name
4726
     * @param int $documentId The document id
4727
     * @param string $newName The new name
4728
     */
4729
    public static function renameDocument($documentId, $newName)
4730
    {
4731
        $documentId = intval($documentId);
4732
        $newName = Database::escape_string($newName);
4733
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4734
4735
        $values = [
4736
            'title' => $newName
4737
        ];
4738
4739
        $whereConditions = [
4740
            'id = ?' => $documentId
4741
        ];
4742
4743
        Database::update($docuentTable, $values, $whereConditions);
4744
    }
4745
4746
    /**
4747
     * Get folder/file suffix
4748
     *
4749
     * @param array $courseInfo
4750
     * @param int $sessionId
4751
     * @param int $groupId
4752
     *
4753
     * @return string
4754
     */
4755
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4756
    {
4757
        // If no session or group, then no suffix.
4758
        if (empty($sessionId) && empty($groupId)) {
4759
            return '';
4760
        }
4761
4762
        return '__'.intval($sessionId).'__'.intval($groupId);
4763
    }
4764
4765
    /**
4766
     * Fix a document name adding session id and group id
4767
     * Turns picture.jpg -> picture__1__2.jpg
4768
     * Where 1 = session id and 2 group id
4769
     * Of session id and group id are empty then the function returns:
4770
     * picture.jpg ->  picture.jpg
4771
     *
4772
     * @param string $name folder or file name
4773
     * @param string $type 'folder' or 'file'
4774
     * @param array $courseInfo
4775
     * @param int $sessionId
4776
     * @param int $groupId
4777
     *
4778
     * @return string
4779
     */
4780
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4781
    {
4782
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4783
4784
        switch ($type) {
4785
            case 'folder':
4786
                $name = $name.$suffix;
4787
                break;
4788
            case 'file':
4789
                $name = self::addSuffixToFileName($name, $suffix);
4790
                break;
4791
        }
4792
4793
        return $name;
4794
    }
4795
4796
    /**
4797
     * Add a suffix to a file Example:
4798
     * /folder/picture.jpg => to /folder/picture_this.jpg
4799
     * where "_this" is the suffix
4800
     * @param string $name
4801
     * @param string $suffix
4802
     * @return string
4803
     */
4804
    public static function addSuffixToFileName($name, $suffix)
4805
    {
4806
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4807
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4808
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4809
4810
        if ($dir == '.') {
4811
            $dir = null;
4812
        }
4813
4814
        if (!empty($dir) && $dir != '/') {
4815
            $dir = $dir.'/';
4816
        }
4817
4818
        $name = $dir.$fileName.$suffix.'.'.$extension;
4819
4820
        return $name;
4821
    }
4822
4823
    /**
4824
     * Check if folder exist in the course base or in the session course
4825
     * @param string $folder Example: /folder/folder2
4826
     * @param array $courseInfo
4827
     * @param int $sessionId
4828
     * @param int $groupId group.id
4829
     *
4830
     * @return bool
4831
     */
4832
    public static function folderExists(
4833
        $folder,
4834
        $courseInfo,
4835
        $sessionId,
4836
        $groupId
4837
    ) {
4838
        $courseId = $courseInfo['real_id'];
4839
4840
        if (empty($courseId)) {
4841
            return false;
4842
        }
4843
4844
        $sessionId = intval($sessionId);
4845
        $folderWithSuffix = self::fixDocumentName(
4846
            $folder,
4847
            'folder',
4848
            $courseInfo,
4849
            $sessionId,
4850
            $groupId
4851
        );
4852
4853
        $folder = Database::escape_string($folder);
4854
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4855
4856
        // Check if pathname already exists inside document table
4857
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4858
        $sql = "SELECT id, path FROM $tbl_document
4859
                WHERE
4860
                    filetype = 'folder' AND
4861
                    c_id = $courseId AND
4862
                    (path = '$folder' OR path = '$folderWithSuffix') AND
4863
                    (session_id = 0 OR session_id = $sessionId)
4864
        ";
4865
4866
        $rs = Database::query($sql);
4867
        if (Database::num_rows($rs)) {
4868
            return true;
4869
        }
4870
4871
        return false;
4872
    }
4873
4874
    /**
4875
     * Check if file exist in the course base or in the session course
4876
     * @param string $fileName Example: /folder/picture.jpg
4877
     * @param array $courseInfo
4878
     * @param int $sessionId
4879
     * @param int $groupId
4880
     *
4881
     * @return bool
4882
     */
4883
    public static function documentExists(
4884
        $fileName,
4885
        $courseInfo,
4886
        $sessionId,
4887
        $groupId
4888
    ) {
4889
        $courseId = $courseInfo['real_id'];
4890
4891
        if (empty($courseId)) {
4892
            return false;
4893
        }
4894
4895
        $sessionId = intval($sessionId);
4896
        $fileNameEscape = Database::escape_string($fileName);
4897
4898
        $fileNameWithSuffix = self::fixDocumentName(
4899
            $fileName,
4900
            'file',
4901
            $courseInfo,
4902
            $sessionId,
4903
            $groupId
4904
        );
4905
4906
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
4907
4908
        // Check if pathname already exists inside document table
4909
        $table = Database::get_course_table(TABLE_DOCUMENT);
4910
        $sql = "SELECT id, path FROM $table
4911
                WHERE
4912
                    filetype = 'file' AND
4913
                    c_id = $courseId AND
4914
                    (
4915
                        path = '".$fileNameEscape."' OR
4916
                        path = '$fileNameWithSuffix'
4917
                    ) AND
4918
                    (session_id = 0 OR session_id = $sessionId)
4919
        ";
4920
        $rs = Database::query($sql);
4921
        if (Database::num_rows($rs)) {
4922
            return true;
4923
        }
4924
4925
        return false;
4926
    }
4927
4928
    /**
4929
     * Undo the suffix applied to a file example:
4930
     * turns picture__1__1.jpg to picture.jpg
4931
     * @param string $name
4932
     * @param int $courseId
4933
     * @param int $sessionId
4934
     * @param int $groupId
4935
     *
4936
     * @return string
4937
     */
4938
    public static function undoFixDocumentName(
4939
        $name,
4940
        $courseId,
4941
        $sessionId,
4942
        $groupId
4943
    ) {
4944
        if (empty($sessionId) && empty($groupId)) {
4945
            return $name;
4946
        }
4947
4948
        $suffix = self::getDocumentSuffix(
4949
            ['real_id' => $courseId],
4950
            $sessionId,
4951
            $groupId
4952
        );
4953
4954
        $name = str_replace($suffix, '', $name);
4955
4956
        return $name;
4957
    }
4958
4959
    /**
4960
     * @param string $path
4961
     * @param string $name
4962
     * @param array $courseInfo
4963
     * @param int $sessionId
4964
     * @param int $groupId
4965
     *
4966
     * @return string
4967
     */
4968
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
4969
    {
4970
        $counter = 1;
4971
        $filePath = $path.$name;
4972
        $uniqueName = $name;
4973
        while ($documentExists = self::documentExists(
4974
            $filePath,
4975
            $courseInfo,
4976
            $sessionId,
4977
            $groupId
4978
        )) {
4979
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
4980
            $filePath = $path.$uniqueName;
4981
            $counter++;
4982
        }
4983
4984
        return $uniqueName;
4985
    }
4986
4987
    /**
4988
     * Builds the form that enables the user to
4989
     * select a directory to browse/upload in
4990
     *
4991
     * @param array    An array containing the folders we want to be able to select
4992
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
4993
     * @param string    Group directory, if empty, prevents documents to be uploaded
4994
     * (because group documents cannot be uploaded in root)
4995
     * @param boolean    Whether to change the renderer (this will add a template <span>
4996
     * to the QuickForm object displaying the form)
4997
     * @return string html form
4998
     */
4999
    public static function build_directory_selector(
5000
        $folders,
5001
        $document_id,
5002
        $group_dir = '',
5003
        $change_renderer = false,
5004
        & $form = null,
5005
        $selectName = 'id'
5006
    ) {
5007
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
5008
        $course_id = api_get_course_int_id();
5009
        $folder_titles = [];
5010
5011
        if (is_array($folders)) {
5012
            $escaped_folders = [];
5013
            foreach ($folders as $key => & $val) {
5014
                $escaped_folders[$key] = Database::escape_string($val);
5015
            }
5016
            $folder_sql = implode("','", $escaped_folders);
5017
5018
            $sql = "SELECT path, title 
5019
                    FROM $doc_table
5020
                    WHERE 
5021
                        filetype = 'folder' AND 
5022
                        c_id = $course_id AND 
5023
                        path IN ('".$folder_sql."')";
5024
            $res = Database::query($sql);
5025
            $folder_titles = [];
5026
            while ($obj = Database::fetch_object($res)) {
5027
                $folder_titles[$obj->path] = $obj->title;
5028
            }
5029
        }
5030
5031
        $attributes = [];
5032
        if (empty($form)) {
5033
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
5034
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
5035
        }
5036
        $form->addElement('hidden', 'cidReq', api_get_course_id());
5037
        $parent_select = $form->addSelect(
5038
            $selectName,
5039
            get_lang('CurrentDirectory'),
5040
            '',
5041
            $attributes
5042
        );
5043
5044
        // Group documents cannot be uploaded in the root
5045
        if (empty($group_dir)) {
5046
            $parent_select->addOption(get_lang('Documents'), '/');
5047
5048
            if (is_array($folders)) {
5049
                foreach ($folders as $folder_id => & $folder) {
5050
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5051
                    $path_parts = explode('/', $folder);
5052
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
5053
                    $counter = count($path_parts) - 2;
5054
                    if ($counter > 0) {
5055
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
5056
                    } else {
5057
                        $label = ' &mdash; '.$folder_titles[$folder];
5058
                    }
5059
                    $parent_select->addOption($label, $folder_id);
5060
                    if ($selected != '') {
5061
                        $parent_select->setSelected($folder_id);
5062
                    }
5063
                }
5064
            }
5065
        } else {
5066
            if (!empty($folders)) {
5067
                foreach ($folders as $folder_id => & $folder) {
5068
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
5069
                    $label = $folder_titles[$folder];
5070
                    if ($folder == $group_dir) {
5071
                        $label = get_lang('Documents');
5072
                    } else {
5073
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
5074
                        $label = cut($label, 80);
5075
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
5076
                    }
5077
                    $parent_select->addOption($label, $folder_id);
5078
                    if ($selected != '') {
5079
                        $parent_select->setSelected($folder_id);
5080
                    }
5081
                }
5082
            }
5083
        }
5084
5085
        $html = $form->toHtml();
5086
5087
        return $html;
5088
    }
5089
5090
    /**
5091
     * Create a html hyperlink depending on if it's a folder or a file
5092
     *
5093
     * @param array $document_data
5094
     * @param array $course_info
5095
     * @param bool $show_as_icon - if it is true, only a clickable icon will be shown
5096
     * @param int $visibility (1/0)
5097
     * @param int $counter
5098
     * @param int $size
5099
     * @param bool $isAllowedToEdit
5100
     *
5101
     * @return string url
5102
     */
5103
    public static function create_document_link(
5104
        $document_data,
5105
        $course_info,
5106
        $show_as_icon = false,
5107
        $counter = null,
5108
        $visibility,
5109
        $size = 0,
5110
        $isAllowedToEdit = false
5111
    ) {
5112
        global $dbl_click_id;
5113
5114
        $current_session_id = api_get_session_id();
5115
        $courseParams = api_get_cidreq();
5116
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
5117
        $webODFList = self::get_web_odf_extension_list();
5118
5119
        // Get the title or the basename depending on what we're using
5120
        if ($document_data['title'] != '') {
5121
            $title = $document_data['title'];
5122
        } else {
5123
            $title = basename($document_data['path']);
5124
        }
5125
5126
        $filetype = $document_data['filetype'];
5127
        $path = $document_data['path'];
5128
        $url_path = urlencode($document_data['path']);
5129
5130
        // Add class="invisible" on invisible files
5131
        $visibility_class = $visibility == false ? ' class="muted"' : '';
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $visibility of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
5132
        $forcedownload_link = '';
5133
        $forcedownload_icon = '';
5134
        $prevent_multiple_click = '';
5135
        $force_download_html = '';
5136
5137
        if (!$show_as_icon) {
5138
            // Build download link (icon)
5139
            $forcedownload_link = ($filetype == 'folder') ? api_get_self().'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id'] : api_get_self().'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
5140
            // Folder download or file download?
5141
            $forcedownload_icon = ($filetype == 'folder') ? 'save_pack.png' : 'save.png';
5142
            // Prevent multiple clicks on zipped folder download
5143
            $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; }\"" : '';
5144
        }
5145
5146
        $target = '_self';
5147
        $is_browser_viewable_file = false;
5148
5149
        if ($filetype == 'file') {
5150
            // Check the extension
5151
            $ext = explode('.', $path);
5152
            $ext = strtolower($ext[sizeof($ext) - 1]);
0 ignored issues
show
Bug introduced by
The call to sizeof() has too few arguments starting with mode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

5152
            $ext = strtolower($ext[/** @scrutinizer ignore-call */ sizeof($ext) - 1]);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
5153
5154
            // HTML-files an some other types are shown in a frameset by default.
5155
            $is_browser_viewable_file = self::isBrowserViewable($ext);
5156
5157
            if ($is_browser_viewable_file) {
5158
                if ($ext == 'pdf' || in_array($ext, $webODFList)) {
5159
                    $url = api_get_self().'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
5160
                } else {
5161
                    $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5162
                }
5163
            } else {
5164
                // url-encode for problematic characters (we may not call them dangerous characters...)
5165
                //$path = str_replace('%2F', '/', $url_path).'?'.$courseParams;
5166
                $url = $www.str_replace('%2F', '/', $url_path).'?'.$courseParams;
5167
            }
5168
        } else {
5169
            $url = api_get_self().'?'.$courseParams.'&id='.$document_data['id'];
5170
        }
5171
5172
        // The little download icon
5173
        $tooltip_title = $title;
5174
5175
        $tooltip_title_alt = $tooltip_title;
5176
5177
        if ($filetype == 'link') {
5178
            $tooltip_title_alt = $title;
5179
            $url = $document_data['comment'].'" target="_blank';
5180
        }
5181
5182
        if ($path == '/shared_folder') {
5183
            $tooltip_title_alt = get_lang('UserFolders');
5184
        } elseif (strstr($path, 'shared_folder_session_')) {
5185
            $tooltip_title_alt = get_lang('UserFolders').' ('.api_get_session_name(api_get_session_id()).')';
5186
        } elseif (strstr($tooltip_title, 'sf_user_')) {
5187
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
5188
            $tooltip_title_alt = get_lang('UserFolder').' '.$userinfo['complete_name'];
5189
        } elseif ($path == '/chat_files') {
5190
            $tooltip_title_alt = get_lang('ChatFiles');
5191
        } elseif ($path == '/learning_path') {
5192
            $tooltip_title_alt = get_lang('LearningPaths');
5193
        } elseif ($path == '/video') {
5194
            $tooltip_title_alt = get_lang('Video');
5195
        } elseif ($path == '/audio') {
5196
            $tooltip_title_alt = get_lang('Audio');
5197
        } elseif ($path == '/flash') {
5198
            $tooltip_title_alt = get_lang('Flash');
5199
        } elseif ($path == '/images') {
5200
            $tooltip_title_alt = get_lang('Images');
5201
        } elseif ($path == '/images/gallery') {
5202
            $tooltip_title_alt = get_lang('DefaultCourseImages');
5203
        }
5204
5205
        $copyToMyFiles = $open_in_new_window_link = '';
5206
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
5207
        $send_to = null;
5208
        $checkExtension = $path;
5209
5210
        if (!$show_as_icon) {
5211
            if ($filetype == 'folder') {
5212
                if ($isAllowedToEdit ||
5213
                    api_is_platform_admin() ||
5214
                    api_get_setting('students_download_folders') == 'true'
5215
                ) {
5216
                    // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
5217
                    if (self::is_shared_folder($curdirpath, $current_session_id)) {
5218
                        if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5219
                            preg_match('/shared_folder_session_'.$current_session_id.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5220
                            $isAllowedToEdit || api_is_platform_admin()
5221
                        ) {
5222
                            $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5223
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5224
                        }
5225
                    } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
5226
                        $isAllowedToEdit ||
5227
                        api_is_platform_admin()
5228
                    ) {
5229
                        $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5230
                            Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5231
                    }
5232
                }
5233
            } else {
5234
                $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
5235
                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5236
            }
5237
5238
            // Copy files to user's myfiles
5239
            if (api_get_setting('allow_my_files') === 'true' &&
5240
                api_get_setting('users_copy_files') === 'true' && api_is_anonymous() === false
5241
            ) {
5242
                $copy_myfiles_link = $filetype == 'file' ? api_get_self().'?'.$courseParams.'&action=copytomyfiles&id='.$document_data['id'] : api_get_self().'?'.$courseParams;
5243
                if ($filetype == 'file') {
5244
                    $copyToMyFiles = '<a href="'.$copy_myfiles_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5245
                        Display::return_icon('briefcase.png', get_lang('CopyToMyFiles'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5246
5247
                    if (api_get_setting('allow_my_files') === 'false') {
5248
                        $copyToMyFiles = '';
5249
                    }
5250
                }
5251
5252
                if ($filetype == 'file') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
5253
                    //$send_to = Portfolio::share('document', $document_data['id'], array('style' => 'float:right;'));
5254
                }
5255
            }
5256
5257
            $pdf_icon = '';
5258
            $extension = pathinfo($path, PATHINFO_EXTENSION);
5259
            if (!$isAllowedToEdit &&
5260
                api_get_setting('students_export2pdf') == 'true' &&
5261
                $filetype == 'file' &&
5262
                in_array($extension, ['html', 'htm'])
5263
            ) {
5264
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.api_get_self().'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&curdirpath='.$curdirpath.'">'.
5265
                    Display::return_icon('pdf.png', get_lang('Export2PDF'), [], ICON_SIZE_SMALL).'</a> ';
5266
            }
5267
5268
            if ($is_browser_viewable_file) {
5269
                $open_in_new_window_link = '<a href="'.$www.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
5270
                    Display::return_icon('open_in_new_window.png', get_lang('OpenInANewWindow'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5271
            }
5272
5273
            if ($filetype == 'file') {
5274
                // Sound preview with jplayer
5275
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5276
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
5277
                    preg_match('/ogg$/i', urldecode($checkExtension))
5278
                ) {
5279
                    return '<span style="float:left" '.$visibility_class.'>'.
5280
                    $title.
5281
                    '</span>'.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5282
                } elseif (
5283
5284
                    // Show preview
5285
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
5286
                    preg_match('/png$/i', urldecode($checkExtension)) ||
5287
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
5288
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
5289
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5290
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
5291
                    preg_match('/svg$/i', urldecode($checkExtension))
5292
                ) {
5293
                    // Simpler version of showinframesmin.php with no headers
5294
                    $url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
5295
                    $class = 'ajax';
5296
                    if ($visibility == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $visibility of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
5297
                        $class = "ajax text-muted";
5298
                    }
5299
                    return Display::url(
5300
                        $title,
5301
                        $url,
5302
                        [
5303
                            'class' => $class,
5304
                            'title' => $tooltip_title_alt,
5305
                            'data-title' => $title,
5306
                            'style' => 'float:left;'
5307
                        ]
5308
                    )
5309
                    . $force_download_html.$send_to.$copyToMyFiles
5310
                    . $open_in_new_window_link.$pdf_icon;
5311
                } else {
5312
                    // For a "PDF Download" of the file.
5313
                    $pdfPreview = null;
5314
                    if ($ext != 'pdf' && !in_array($ext, $webODFList)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ext does not seem to be defined for all execution paths leading up to this point.
Loading history...
5315
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5316
                    } else {
5317
                        $pdfPreview = Display::url(
5318
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
5319
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
5320
                            ['style' => 'float:right']
5321
                        );
5322
                    }
5323
                    // No plugin just the old and good showinframes.php page
5324
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
5325
                    $pdfPreview.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5326
                }
5327
            } else {
5328
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'.
5329
                $force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5330
            }
5331
            // end copy files to users myfiles
5332
        } else {
5333
            // Icon column
5334
            if (preg_match('/shared_folder/', urldecode($checkExtension)) &&
5335
                preg_match('/shared_folder$/', urldecode($checkExtension)) == false &&
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/shared_fold...ecode($checkExtension)) of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
5336
                preg_match('/shared_folder_session_'.$current_session_id.'$/', urldecode($url)) == false
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/shared_fold... '$/', urldecode($url)) of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
5337
            ) {
5338
                if ($filetype == 'file') {
5339
                    //Sound preview with jplayer
5340
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5341
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5342
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5343
                        $sound_preview = self::generate_media_preview($counter);
5344
5345
                        return $sound_preview;
5346
                    } elseif (
5347
                        // Show preview
5348
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5349
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5350
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5351
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5352
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5353
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5354
                        preg_match('/svg$/i', urldecode($checkExtension))
5355
                    ) {
5356
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5357
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5358
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5359
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5360
                        '</a>';
5361
                    } else {
5362
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5363
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5364
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5365
                        '</a>';
5366
                    }
5367
                } else {
5368
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5369
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5370
                        Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5371
                    '</a>';
5372
                }
5373
            } else {
5374
                if ($filetype == 'file') {
5375
                    // Sound preview with jplayer
5376
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5377
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5378
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5379
                        $sound_preview = self::generate_media_preview($counter);
5380
5381
                        return $sound_preview;
5382
                    } elseif (
5383
                        //Show preview
5384
                        preg_match('/html$/i', urldecode($checkExtension)) ||
5385
                        preg_match('/htm$/i', urldecode($checkExtension)) ||
5386
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5387
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5388
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5389
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5390
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5391
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5392
                        preg_match('/svg$/i', urldecode($checkExtension))
5393
                    ) {
5394
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
5395
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5396
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5397
                        '</a>';
5398
                    } else {
5399
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5400
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5401
                        '</a>';
5402
                    }
5403
                } else {
5404
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5405
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5406
                    '</a>';
5407
                }
5408
            }
5409
        }
5410
    }
5411
5412
    /**
5413
     * Builds an img html tag for the file type
5414
     *
5415
     * @param string $type (file/folder)
5416
     * @param string $path
5417
     * @param bool $isAllowedToEdit
5418
     * @return string img html tag
5419
     */
5420
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
5421
    {
5422
        $basename = basename($path);
5423
        $current_session_id = api_get_session_id();
5424
        if (is_null($isAllowedToEdit)) {
5425
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
5426
        }
5427
        $user_image = false;
5428
        if ($type == 'file') {
5429
            $icon = choose_image($basename);
5430
            $basename = substr(strrchr($basename, '.'), 1);
5431
        } elseif ($type == 'link') {
5432
            $icon = 'clouddoc.png';
5433
            $basename = get_lang('CloudFileLink');
5434
        } else {
5435
            if ($path == '/shared_folder') {
5436
                $icon = 'folder_users.png';
5437
                if ($isAllowedToEdit) {
5438
                    $basename = get_lang('HelpUsersFolder');
5439
                } else {
5440
                    $basename = get_lang('UserFolders');
5441
                }
5442
            } elseif (strstr($basename, 'sf_user_')) {
5443
                $userInfo = api_get_user_info(substr($basename, 8));
5444
                $icon = $userInfo['avatar_small'];
5445
                $basename = get_lang('UserFolder').' '.$userInfo['complete_name'];
5446
                $user_image = true;
5447
            } elseif (strstr($path, 'shared_folder_session_')) {
5448
                $sessionName = api_get_session_name($current_session_id);
5449
                if ($isAllowedToEdit) {
5450
                    $basename = '***('.$sessionName.')*** '.get_lang('HelpUsersFolder');
5451
                } else {
5452
                    $basename = get_lang('UserFolders').' ('.$sessionName.')';
5453
                }
5454
                $icon = 'folder_users.png';
5455
            } else {
5456
                $icon = 'folder_document.png';
5457
5458
                if ($path == '/audio') {
5459
                    $icon = 'folder_audio.png';
5460
                    if ($isAllowedToEdit) {
5461
                        $basename = get_lang('HelpDefaultDirDocuments');
5462
                    } else {
5463
                        $basename = get_lang('Audio');
5464
                    }
5465
                } elseif ($path == '/flash') {
5466
                    $icon = 'folder_flash.png';
5467
                    if ($isAllowedToEdit) {
5468
                        $basename = get_lang('HelpDefaultDirDocuments');
5469
                    } else {
5470
                        $basename = get_lang('Flash');
5471
                    }
5472
                } elseif ($path == '/images') {
5473
                    $icon = 'folder_images.png';
5474
                    if ($isAllowedToEdit) {
5475
                        $basename = get_lang('HelpDefaultDirDocuments');
5476
                    } else {
5477
                        $basename = get_lang('Images');
5478
                    }
5479
                } elseif ($path == '/video') {
5480
                    $icon = 'folder_video.png';
5481
                    if ($isAllowedToEdit) {
5482
                        $basename = get_lang('HelpDefaultDirDocuments');
5483
                    } else {
5484
                        $basename = get_lang('Video');
5485
                    }
5486
                } elseif ($path == '/images/gallery') {
5487
                    $icon = 'folder_gallery.png';
5488
                    if ($isAllowedToEdit) {
5489
                        $basename = get_lang('HelpDefaultDirDocuments');
5490
                    } else {
5491
                        $basename = get_lang('Gallery');
5492
                    }
5493
                } elseif ($path == '/chat_files') {
5494
                    $icon = 'folder_chat.png';
5495
                    if ($isAllowedToEdit) {
5496
                        $basename = get_lang('HelpFolderChat');
5497
                    } else {
5498
                        $basename = get_lang('ChatFiles');
5499
                    }
5500
                } elseif ($path == '/learning_path') {
5501
                    $icon = 'folder_learningpath.png';
5502
                    if ($isAllowedToEdit) {
5503
                        $basename = get_lang('HelpFolderLearningPaths');
5504
                    } else {
5505
                        $basename = get_lang('LearningPaths');
5506
                    }
5507
                }
5508
            }
5509
        }
5510
5511
        if ($user_image) {
5512
            return Display::img($icon, $basename, [], false);
5513
        }
5514
5515
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5516
    }
5517
5518
    /**
5519
     * Get the button to edit document
5520
     * @param boolean $isReadOnly
5521
     * @param array $documentData
5522
     * @param string $extension
5523
     * @param boolean $isCertificateMode
5524
     * @return string
5525
     */
5526
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
5527
    {
5528
        $iconEn = Display::return_icon('edit.png', get_lang('Modify'));
5529
        $iconDis = Display::return_icon('edit_na.png', get_lang('Modify'));
5530
        $courseParams = api_get_cidreq();
5531
        $webOdfExtensionList = self::get_web_odf_extension_list();
5532
        $path = $documentData['path'];
5533
        $document_id = $documentData['id'];
5534
5535
        if ($isReadOnly) {
5536
            if (!api_is_course_admin() && !api_is_platform_admin()) {
5537
                return $iconDis;
5538
            }
5539
5540
            if (
5541
                $extension == 'svg' && api_browser_support('svg') &&
5542
                api_get_setting('enabled_support_svg') == 'true'
5543
            ) {
5544
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
5545
            }
5546
5547
            if (
5548
                in_array($extension, $webOdfExtensionList) &&
5549
                api_get_configuration_value('enabled_support_odf') === true
5550
            ) {
5551
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
5552
            }
5553
5554
            if (
5555
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif']) ||
5556
                ($extension == 'pxd' && api_get_setting('enabled_support_pixlr') == 'true')
5557
            ) {
5558
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
5559
            }
5560
5561
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
5562
        }
5563
5564
        if (in_array($path, self::get_system_folders())) {
5565
            return $iconDis;
5566
        }
5567
5568
        if ($isCertificateMode) {
5569
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id&curdirpath=/certificates");
5570
        }
5571
5572
        $sessionId = api_get_session_id();
5573
5574
        if ($sessionId && $documentData['session_id'] != $sessionId) {
5575
            return $iconDis;
5576
        }
5577
5578
        if (
5579
            $extension == 'svg' && api_browser_support('svg') &&
5580
            api_get_setting('enabled_support_svg') == 'true'
5581
        ) {
5582
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
5583
        }
5584
5585
        if (
5586
            in_array($extension, $webOdfExtensionList) &&
5587
            api_get_configuration_value('enabled_support_odf') === true
5588
        ) {
5589
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
5590
        }
5591
5592
        if (
5593
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif']) ||
5594
            ($extension == 'pxd' && api_get_setting('enabled_support_pixlr') == 'true')
5595
        ) {
5596
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
5597
        }
5598
5599
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
5600
    }
5601
5602
    /**
5603
     * Get the button to move document
5604
     * @param boolean $isReadOnly
5605
     * @param array $documentData
5606
     * @param boolean $isCertificateMode
5607
     * @param int $parentId
5608
     * @return string
5609
     */
5610
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
5611
    {
5612
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
5613
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
5614
5615
        if ($isReadOnly) {
5616
            return $iconDis;
5617
        }
5618
5619
        $path = $documentData['path'];
5620
        $document_id = $documentData['id'];
5621
        $sessionId = api_get_session_id();
5622
        $courseParams = api_get_cidreq();
5623
5624
        if ($isCertificateMode || in_array($path, self::get_system_folders())) {
5625
            return $iconDis;
5626
        }
5627
5628
        if ($sessionId) {
5629
            if ($documentData['session_id'] != $sessionId) {
5630
                return $iconDis;
5631
            }
5632
        }
5633
5634
        $urlMoveParams = http_build_query(['id' => $parentId, 'move' => $document_id]);
5635
5636
        return Display::url(
5637
            $iconEn,
5638
            api_get_self()."?$courseParams&$urlMoveParams"
5639
        );
5640
    }
5641
5642
    /**
5643
     * Get the button to set visibility to document
5644
     * @param boolean $isReadOnly
5645
     * @param int $visibility
5646
     * @param array $documentData
5647
     * @param boolean $isCertificateMode
5648
     * @param int $parentId
5649
     * @return null|string
5650
     */
5651
    private static function getButtonVisibility(
5652
        $isReadOnly,
5653
        $visibility,
5654
        array $documentData,
5655
        $isCertificateMode,
5656
        $parentId
5657
    ) {
5658
        $visibility_icon = $visibility == 0 ? 'invisible' : 'visible';
5659
        $visibility_command = $visibility == 0 ? 'set_visible' : 'set_invisible';
5660
        $courseParams = api_get_cidreq();
5661
5662
        if ($isReadOnly) {
5663
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
5664
                return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
5665
            }
5666
5667
            return null;
5668
        }
5669
5670
        if ($isCertificateMode) {
5671
            return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
5672
        }
5673
5674
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
5675
            $tip_visibility = $visibility_icon == 'invisible' ? get_lang('Show') : get_lang('Hide');
5676
5677
            return Display::url(
5678
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
5679
                api_get_self()."?$courseParams&id=$parentId&$visibility_command={$documentData['id']}"
5680
            );
5681
        }
5682
5683
        return null;
5684
    }
5685
5686
    /**
5687
     * GEt the button to delete a document
5688
     * @param boolean $isReadOnly
5689
     * @param array $documentData
5690
     * @param boolean $isCertificateMode
5691
     * @param string $curDirPath
5692
     * @param int $parentId
5693
     * @return string
5694
     */
5695
    private static function getButtonDelete(
5696
        $isReadOnly,
5697
        array $documentData,
5698
        $isCertificateMode,
5699
        $curDirPath,
5700
        $parentId
5701
    ) {
5702
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
5703
        $iconDis = Display::return_icon('delete_na.png', get_lang('ThisFolderCannotBeDeleted'));
5704
        $path = $documentData['path'];
5705
        $id = $documentData['id'];
5706
        $courseParams = api_get_cidreq();
5707
5708
        if ($isReadOnly) {
5709
            return $iconDis;
5710
        }
5711
5712
        if (in_array($path, self::get_system_folders())) {
5713
            return $iconDis;
5714
        }
5715
5716
        $titleToShow = addslashes(basename($documentData['title']));
5717
        $urlDeleteParams = http_build_query([
5718
            'curdirpath' => $curDirPath,
5719
            'action' => 'delete_item',
5720
            'id' => $parentId,
5721
            'deleteid' => $documentData['id']
5722
        ]);
5723
        $btn = Display::url(
5724
            $iconEn,
5725
            api_get_self()."?$courseParams&$urlDeleteParams",
5726
            ['onclick' => "return confirmation('$titleToShow');"]
5727
        );
5728
5729
        if (
5730
            isset($_GET['curdirpath']) &&
5731
            $_GET['curdirpath'] == '/certificates' &&
5732
            self::get_default_certificate_id(api_get_course_id()) == $id
5733
        ) {
5734
            return $btn;
5735
        }
5736
5737
        if ($isCertificateMode) {
5738
            return $btn;
5739
        }
5740
5741
        $sessionId = api_get_session_id();
5742
5743
        if ($sessionId) {
5744
            if ($documentData['session_id'] != $sessionId) {
5745
                return $iconDis;
5746
            }
5747
        }
5748
5749
        return $btn;
5750
    }
5751
5752
    /**
5753
     * Creates the row of edit icons for a file/folder
5754
     *
5755
     * @param string $curdirpath current path (cfr open folder)
5756
     * @param string $type (file/folder)
5757
     * @param string $path dbase path of file/folder
5758
     * @param int $visibility (1/0)
5759
     * @param int $id dbase id of the document
5760
     * @return string html img tags with hyperlinks
5761
     */
5762
    public static function build_edit_icons($document_data, $id, $is_template, $is_read_only = 0, $visibility)
5763
    {
5764
        $sessionId = api_get_session_id();
5765
        $courseParams = api_get_cidreq();
5766
        $document_id = $document_data['id'];
5767
        $type = $document_data['filetype'];
5768
        $is_read_only = $document_data['readonly'];
5769
        $path = $document_data['path'];
5770
5771
        if ($type == 'link') {
5772
            $parent_id = self::get_document_id(
5773
                api_get_course_info(),
5774
                rtrim($path, '/'),
5775
                0
5776
            );
5777
        } else {
5778
            $parent_id = self::get_document_id(
5779
                api_get_course_info(),
5780
                dirname($path),
5781
                0
5782
            );
5783
        }
5784
5785
        if (empty($parent_id) && !empty($sessionId)) {
5786
            $parent_id = self::get_document_id(
5787
                api_get_course_info(),
5788
                dirname($path),
5789
                $sessionId
5790
            );
5791
        }
5792
5793
        $curdirpath = dirname($document_data['path']);
5794
        $is_certificate_mode = self::is_certificate_mode($path);
5795
        $curdirpath = urlencode($curdirpath);
5796
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5797
        //@todo Implement remote support for converter
5798
        $usePpt2lp = api_get_setting('service_ppt2lp', 'active') == 'true' && api_get_setting('service_ppt2lp', 'host') == 'localhost';
5799
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5800
        $formatType = current($formatTypeList);
5801
5802
        // If document is read only *or* we're in a session and the document
5803
        // is from a non-session context, hide the edition capabilities
5804
        $modify_icons = [];
5805
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5806
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5807
        $modify_icons[] = self::getButtonVisibility(
5808
            $is_read_only,
5809
            $visibility,
5810
            $document_data,
5811
            $is_certificate_mode,
5812
            $parent_id
5813
        );
5814
        $modify_icons[] = self::getButtonDelete(
5815
            $is_read_only,
5816
            $document_data,
5817
            $is_certificate_mode,
5818
            $curdirpath,
5819
            $parent_id
5820
        );
5821
5822
        if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
5823
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5824
            // @TODO: add prompt to select a format target
5825
            if (!in_array($path, self::get_system_folders())) {
5826
                if ($usePpt2lp && $formatType) {
5827
                    $modify_icons[] = Display::url(
5828
                        Display::return_icon('convert.png', get_lang('Convert')),
5829
                        '#',
5830
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5831
                    );
5832
                }
5833
            }
5834
        }
5835
5836
        if ($type == 'file' && ($extension == 'html' || $extension == 'htm')) {
5837
            if ($is_template == 0) {
5838
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] != '/certificates') || !isset($_GET['curdirpath'])) {
5839
                    $modify_icons[] = Display::url(
5840
                        Display::return_icon('wizard.png', get_lang('AddAsTemplate')),
5841
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id"
5842
                    );
5843
                }
5844
                if (isset($_GET['curdirpath']) && $_GET['curdirpath'] == '/certificates') {//allow attach certificate to course
5845
                    $visibility_icon_certificate = 'nocertificate';
5846
                    if (self::get_default_certificate_id(api_get_course_id()) == $id) {
5847
                        $visibility_icon_certificate = 'certificate';
5848
                        $certificate = get_lang('DefaultCertificate');
5849
                        $preview = get_lang('PreviewCertificate');
5850
                        $is_preview = true;
5851
                    } else {
5852
                        $is_preview = false;
5853
                        $certificate = get_lang('NoDefaultCertificate');
5854
                    }
5855
                    if (isset($_GET['selectcat'])) {
5856
                        $modify_icons[] = Display::url(
5857
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5858
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id"
5859
                        );
5860
                        if ($is_preview) {
5861
                            $modify_icons[] = Display::url(
5862
                                Display::return_icon('preview_view.png', $preview),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $preview does not seem to be defined for all execution paths leading up to this point.
Loading history...
5863
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id"
5864
                            );
5865
                        }
5866
                    }
5867
                }
5868
            } else {
5869
                $modify_icons[] = Display::url(
5870
                    Display::return_icon('wizard_na.png', get_lang('RemoveAsTemplate')),
5871
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id"
5872
                );
5873
            }
5874
5875
            $modify_icons[] = Display::url(
5876
                Display::return_icon('pdf.png', get_lang('Export2PDF')),
5877
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath"
5878
            );
5879
        }
5880
5881
        return implode(PHP_EOL, $modify_icons);
5882
    }
5883
5884
    /**
5885
     * @param $folders
5886
     * @param $curdirpath
5887
     * @param $move_file
5888
     * @param string $group_dir
5889
     *
5890
     * @return string
5891
     */
5892
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5893
    {
5894
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5895
5896
        // Form title
5897
        $form->addElement('hidden', 'move_file', $move_file);
5898
5899
        $options = [];
5900
5901
        // Group documents cannot be uploaded in the root
5902
        if ($group_dir == '') {
5903
            if ($curdirpath != '/') {
5904
                $options['/'] = get_lang('Documents');
5905
            }
5906
5907
            if (is_array($folders)) {
5908
                foreach ($folders as & $folder) {
5909
                    // Hide some folders
5910
                    if ($folder == '/HotPotatoes_files' ||
5911
                        $folder == '/certificates' ||
5912
                        basename($folder) == 'css'
5913
                    ) {
5914
                        continue;
5915
                    }
5916
                    // Admin setting for Hide/Show the folders of all users
5917
                    if (api_get_setting('show_users_folders') == 'false' &&
5918
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5919
                    ) {
5920
                        continue;
5921
                    }
5922
5923
                    // Admin setting for Hide/Show Default folders to all users
5924
                    if (api_get_setting('show_default_folders') == 'false' &&
5925
                        (
5926
                            $folder == '/images' ||
5927
                            $folder == '/flash' ||
5928
                            $folder == '/audio' ||
5929
                            $folder == '/video' ||
5930
                            strstr($folder, '/images/gallery') ||
5931
                            $folder == '/video/flv'
5932
                        )
5933
                    ) {
5934
                        continue;
5935
                    }
5936
5937
                    // Admin setting for Hide/Show chat history folder
5938
                    if (api_get_setting('show_chat_folder') == 'false' &&
5939
                        $folder == '/chat_files') {
5940
                        continue;
5941
                    }
5942
5943
                    // You cannot move a file to:
5944
                    // 1. current directory
5945
                    // 2. inside the folder you want to move
5946
                    // 3. inside a subfolder of the folder you want to move
5947
                    if (($curdirpath != $folder) &&
5948
                        ($folder != $move_file) &&
5949
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5950
                    ) {
5951
                        $path_displayed = $folder;
5952
                        // If document title is used, we have to display titles instead of real paths...
5953
                        $path_displayed = self::get_titles_of_path($folder);
5954
5955
                        if (empty($path_displayed)) {
5956
                            $path_displayed = get_lang('Untitled');
5957
                        }
5958
                        $options[$folder] = $path_displayed;
5959
                    }
5960
                }
5961
            }
5962
        } else {
5963
            foreach ($folders as $folder) {
5964
                if (($curdirpath != $folder) &&
5965
                    ($folder != $move_file) &&
5966
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5967
                ) {
5968
                    // Cannot copy dir into his own subdir
5969
                    $path_displayed = self::get_titles_of_path($folder);
5970
                    $display_folder = substr($path_displayed, strlen($group_dir));
5971
                    $display_folder = ($display_folder == '') ? get_lang('Documents') : $display_folder;
5972
                    //$form .= '<option value="'.$folder.'">'.$display_folder.'</option>';
5973
                    $options[$folder] = $display_folder;
5974
                }
5975
            }
5976
        }
5977
        $form->addElement('select', 'move_to', get_lang('MoveTo'), $options);
5978
        $form->addButtonNext(get_lang('MoveElement'), 'move_file_submit');
5979
5980
        return $form->returnForm();
5981
    }
5982
5983
    /**
5984
     * Gets the path translated with title of docs and folders
5985
     * @param string $path the real path
5986
     * @return the path which should be displayed
5987
     */
5988
    public static function get_titles_of_path($path)
5989
    {
5990
        global $tmp_folders_titles;
5991
        $course_id = api_get_course_int_id();
5992
        $nb_slashes = substr_count($path, '/');
5993
        $current_slash_pos = 0;
5994
        $path_displayed = '';
5995
        for ($i = 0; $i < $nb_slashes; $i++) {
5996
            // For each folder of the path, retrieve title.
5997
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
5998
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
5999
6000
            if (empty($tmp_path)) {
6001
                // If empty, then we are in the final part of the path
6002
                $tmp_path = $path;
6003
            }
6004
6005
            if (!empty($tmp_folders_titles[$tmp_path])) {
6006
                // If this path has soon been stored here we don't need a new query
6007
                $path_displayed .= $tmp_folders_titles[$tmp_path];
6008
            } else {
6009
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
6010
                        WHERE c_id = ' . $course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
6011
                $rs = Database::query($sql);
6012
                $tmp_title = '/'.Database::result($rs, 0, 0);
6013
                $path_displayed .= $tmp_title;
6014
                $tmp_folders_titles[$tmp_path] = $tmp_title;
6015
            }
6016
        }
6017
6018
        return $path_displayed;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path_displayed returns the type string which is incompatible with the documented return type the.
Loading history...
6019
    }
6020
6021
    /**
6022
     * Creates form that asks for the directory name.
6023
     * @return string	html-output text for the form
6024
     */
6025
    public static function create_dir_form($dirId)
6026
    {
6027
        global $document_id;
6028
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
6029
        $form->addElement('hidden', 'create_dir', 1);
6030
        $form->addElement('hidden', 'dir_id', intval($document_id));
6031
        $form->addElement('hidden', 'id', intval($dirId));
6032
        $form->addElement('header', get_lang('CreateDir'));
6033
        $form->addText('dirname', get_lang('NewDir'), ['autofocus' => 'autofocus']);
6034
        $form->addButtonCreate(get_lang('CreateFolder'));
6035
6036
        return $form->returnForm();
6037
    }
6038
6039
    /**
6040
     * Checks whether the user is in shared folder
6041
     * @param string $curdirpath
6042
     * @param int $current_session_id
6043
     * @return bool Return true when user is into shared folder
6044
     */
6045
    public static function is_shared_folder($curdirpath, $current_session_id)
6046
    {
6047
        $clean_curdirpath = Security::remove_XSS($curdirpath);
6048
        if ($clean_curdirpath == '/shared_folder') {
6049
            return true;
6050
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$current_session_id) {
6051
            return true;
6052
        } else {
6053
            return false;
6054
        }
6055
    }
6056
6057
    /**
6058
     * Checks whether the user is into any user shared folder
6059
     * @param string $path
6060
     * @param int $current_session_id
6061
     * @return bool Return true when user is in any user shared folder
6062
     */
6063
    public static function is_any_user_shared_folder($path, $current_session_id)
6064
    {
6065
        $clean_path = Security::remove_XSS($path);
6066
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
6067
            return true;
6068
        } elseif (strpos($clean_path, 'shared_folder_session_'.$current_session_id.'/sf_user_')) {
6069
            return true;
6070
        } else {
6071
            return false;
6072
        }
6073
    }
6074
6075
    /**
6076
     * Create users shared folder for course
6077
     * @param int $userId
6078
     * @param array $courseInfo
6079
     * @param int $sessionId
6080
     */
6081
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
6082
    {
6083
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
6084
        $userInfo = api_get_user_info($userId);
6085
6086
        if (!$sessionId) {
6087
            //Create shared folder. Necessary for recycled courses.
6088
            if (!file_exists($documentDirectory.'/shared_folder')) {
6089
                create_unexisting_directory(
6090
                    $courseInfo,
6091
                    $userId,
6092
                    0,
6093
                    0,
6094
                    0,
6095
                    $documentDirectory,
6096
                    '/shared_folder',
6097
                    get_lang('UserFolders'),
6098
                    0
6099
                );
6100
            }
6101
            // Create dynamic user shared folder
6102
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
6103
                create_unexisting_directory(
6104
                    $courseInfo,
6105
                    $userId,
6106
                    0,
6107
                    0,
6108
                    0,
6109
                    $documentDirectory,
6110
                    '/shared_folder/sf_user_'.$userId,
6111
                    $userInfo['complete_name'],
6112
                    1
6113
                );
6114
            }
6115
6116
            return;
6117
        }
6118
6119
        // Create shared folder session.
6120
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
6121
            create_unexisting_directory(
6122
                $courseInfo,
6123
                api_get_user_id(),
6124
                $sessionId,
6125
                0,
6126
                0,
6127
                $documentDirectory,
6128
                '/shared_folder_session_'.$sessionId,
6129
                get_lang('UserFolders').' ('.api_get_session_name($sessionId).')',
6130
                0
6131
            );
6132
        }
6133
        //Create dynamic user shared folder into a shared folder session
6134
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
6135
            create_unexisting_directory(
6136
                $courseInfo,
6137
                $userId,
6138
                $sessionId,
6139
                0,
6140
                0,
6141
                $documentDirectory,
6142
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
6143
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
6144
                1
6145
            );
6146
        }
6147
    }
6148
6149
    /**
6150
     * Checks whether the user is into his shared folder or into a subfolder
6151
     * @param int $user_id
6152
     * @param string $path
6153
     * @param int $current_session_id
6154
     * @return bool Return true when user is in his user shared folder or into a subfolder
6155
     */
6156
    public static function is_my_shared_folder($user_id, $path, $current_session_id)
6157
    {
6158
        $clean_path = Security::remove_XSS($path).'/';
6159
        //for security does not remove the last slash
6160
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
6161
        //for security does not remove the last slash
6162
        $main_user_shared_folder_session = '/shared_folder_session_'.$current_session_id.'\/sf_user_'.$user_id.'\//';
6163
6164
        if (preg_match($main_user_shared_folder, $clean_path)) {
6165
            return true;
6166
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
6167
            return true;
6168
        } else {
6169
            return false;
6170
        }
6171
    }
6172
6173
    /**
6174
     * Check if the file name or folder searched exist
6175
     * @return bool Return true when exist
6176
     */
6177
    public static function search_keyword($document_name, $keyword)
6178
    {
6179
        if (api_strripos($document_name, $keyword) !== false) {
6180
            return true;
6181
        } else {
6182
            return false;
6183
        }
6184
    }
6185
6186
    /**
6187
     * Checks whether a document can be previewed by using the browser.
6188
     * @param string $file_extension    The filename extension of the document (it must be in lower case).
6189
     * @return bool                     Returns TRUE or FALSE.
6190
     */
6191
    public static function isBrowserViewable($file_extension)
6192
    {
6193
        static $allowed_extensions = [
6194
            'htm', 'html', 'xhtml',
6195
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
6196
            'pdf', 'svg', 'swf',
6197
            'txt', 'log',
6198
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
6199
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar'
6200
        ];
6201
6202
        /*
6203
          //TODO: make a admin switch to strict mode
6204
          1. global default $allowed_extensions only: 'htm', 'html', 'xhtml', 'gif', 'jpg', 'jpeg', 'png', 'bmp', 'txt', 'log'
6205
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
6206
          return true;
6207
          }
6208
          2. check native support
6209
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
6210
         */
6211
6212
        if (!($result = in_array($file_extension, $allowed_extensions))) { // Assignment + a logical check.
6213
            return false;
6214
        }
6215
6216
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
6217
        if ($file_extension == "pdf") {
6218
            return api_browser_support('pdf');
6219
        } elseif ($file_extension == "mp3") {
6220
            return api_browser_support('mp3');
6221
        } elseif ($file_extension == "mp4") {
6222
            return api_browser_support('mp4');
6223
        } elseif ($file_extension == "ogg" || $file_extension == "ogx" || $file_extension == "ogv" || $file_extension == "oga") {
6224
            return api_browser_support('ogg');
6225
        } elseif ($file_extension == "svg") {
6226
            return api_browser_support('svg');
6227
        } elseif ($file_extension == "mpg" || $file_extension == "mpeg") {
6228
            return api_browser_support('mpg');
6229
        } elseif ($file_extension == "mov") {
6230
            return api_browser_support('mov');
6231
        } elseif ($file_extension == "wav") {
6232
            return api_browser_support('wav');
6233
        } elseif ($file_extension == "mid" || $file_extension == "kar") {
6234
            return api_browser_support('mid');
6235
        } elseif ($file_extension == "avi") {
6236
            return api_browser_support('avi');
6237
        } elseif ($file_extension == "wma") {
6238
            return api_browser_support('wma');
6239
        } elseif ($file_extension == "wmv") {
6240
            return api_browser_support('wmv');
6241
        } elseif ($file_extension == "tif" || $file_extension == "tiff") {
6242
            return api_browser_support('tif');
6243
        } elseif ($file_extension == "mov") {
6244
            return api_browser_support('mov');
6245
        } elseif ($file_extension == "au") {
6246
            return api_browser_support('au');
6247
        } elseif ($file_extension == "webm") {
6248
            return api_browser_support('webm');
6249
        }
6250
        return $result;
6251
    }
6252
6253
    /**
6254
     * @param array $courseInfo
6255
     * @param int $sessionId
6256
     *
6257
     * @return array
6258
     */
6259
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
6260
    {
6261
        $table = Database::get_course_table(TABLE_DOCUMENT);
6262
        $courseId = $courseInfo['real_id'];
6263
        $sessionCondition = api_get_session_condition($sessionId);
6264
        $sql = "SELECT * FROM $table
6265
                WHERE
6266
                  path LIKE '%DELETED%' AND
6267
                  c_id = $courseId
6268
                  $sessionCondition
6269
                ORDER BY path
6270
        ";
6271
6272
        $result = Database::query($sql);
6273
        $files = [];
6274
        while ($document = Database::fetch_array($result, 'ASSOC')) {
6275
            $files[] = $document;
6276
        }
6277
6278
        return $files;
6279
    }
6280
6281
    /**
6282
     * @param int $id
6283
     * @param array $courseInfo
6284
     * @param int $sessionId
6285
     *
6286
     * @return array
6287
     */
6288
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
6289
    {
6290
        if (empty($courseInfo)) {
6291
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
6292
        }
6293
6294
        $table = Database::get_course_table(TABLE_DOCUMENT);
6295
        $courseId = $courseInfo['real_id'];
6296
        $sessionCondition = api_get_session_condition($sessionId);
6297
        $sql = "SELECT * FROM $table
6298
                WHERE
6299
                  path LIKE '%DELETED%' AND
6300
                  id = $id AND
6301
                  c_id = $courseId
6302
                  $sessionCondition
6303
                LIMIT 1
6304
        ";
6305
        $result = Database::query($sql);
6306
        if (Database::num_rows($result)) {
6307
            $result = Database::fetch_array($result, 'ASSOC');
6308
6309
            return $result;
6310
        }
6311
6312
        return [];
6313
    }
6314
6315
    /**
6316
     * @param int $id
6317
     * @param array $courseInfo
6318
     * @param int $sessionId
6319
     * @return bool
6320
     */
6321
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
6322
    {
6323
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6324
        if (!empty($document)) {
6325
            $path = $document['path'];
6326
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6327
            my_delete($coursePath.$path);
6328
            // Hard delete.
6329
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
6330
6331
            return true;
6332
        }
6333
        return false;
6334
    }
6335
6336
    /**
6337
     * @param array $courseInfo
6338
     * @param int $sessionId
6339
     */
6340
    public static function purgeDocuments($courseInfo, $sessionId)
6341
    {
6342
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6343
        foreach ($files as $file) {
6344
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
6345
        }
6346
    }
6347
6348
    /**
6349
    * @param int $id
6350
    * @param array $courseInfo
6351
    * @param int $sessionId
6352
    */
6353
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
6354
    {
6355
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6356
        if (!empty($document)) {
6357
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6358
6359
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
6360
                self::file_send_for_download($coursePath.$document['path']);
6361
                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...
6362
            }
6363
        }
6364
    }
6365
6366
    /**
6367
     * @param array $courseInfo
6368
     * @param int $sessionId
6369
     *
6370
     * @return bool
6371
     */
6372
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
6373
    {
6374
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6375
6376
        if (empty($files)) {
6377
            return false;
6378
        }
6379
6380
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
6381
6382
        // Creating a ZIP file.
6383
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
6384
        $zip = new PclZip($tempZipFile);
6385
        foreach ($files as $file) {
6386
            $zip->add(
6387
                $coursePath.$file['path'],
6388
                PCLZIP_OPT_REMOVE_PATH,
6389
                $coursePath
6390
            );
6391
        }
6392
6393
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
6394
            self::file_send_for_download($tempZipFile, true);
6395
            @unlink($tempZipFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

6395
            /** @scrutinizer ignore-unhandled */ @unlink($tempZipFile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
6396
            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...
6397
        }
6398
    }
6399
6400
    /**
6401
     *
6402
     * Delete documents from a session in a course.
6403
     * @param array $courseInfo
6404
     * @param int $sessionId
6405
     *
6406
     * @return bool
6407
     */
6408
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
6409
    {
6410
        if (empty($courseInfo)) {
6411
            return false;
6412
        }
6413
6414
        if (empty($sessionId)) {
6415
            return false;
6416
        }
6417
6418
        $itemPropertyTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
6419
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
6420
6421
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
6422
        $courseId = $courseInfo['real_id'];
6423
6424
        // get invisible folders
6425
        $sql = "SELECT DISTINCT d.id, path
6426
                FROM $itemPropertyTable i
6427
                INNER JOIN $documentTable d
6428
                ON (i.c_id = d.c_id)
6429
                WHERE
6430
                    d.id = i.ref AND
6431
                    i.tool = '".TOOL_DOCUMENT."'
6432
                    $conditionSession AND
6433
                    i.c_id = $courseId AND
6434
                    d.c_id = $courseId ";
6435
6436
        $result = Database::query($sql);
6437
        $documents = Database::store_result($result, 'ASSOC');
6438
        if ($documents) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documents of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
6439
            $course_dir = $courseInfo['directory'].'/document';
6440
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
6441
            $base_work_dir = $sys_course_path.$course_dir;
6442
6443
            foreach ($documents as $document) {
6444
                $documentId = $document['id'];
6445
                self::delete_document(
6446
                    $courseInfo,
6447
                    null,
6448
                    $base_work_dir,
6449
                    $sessionId,
6450
                    $documentId
6451
                );
6452
            }
6453
        }
6454
6455
        $sql = "DELETE FROM $documentTable
6456
                WHERE c_id = $courseId AND session_id = $sessionId";
6457
        Database::query($sql);
6458
6459
        $sql = "DELETE FROM $itemPropertyTable
6460
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
6461
        Database::query($sql);
6462
    }
6463
6464
    /**
6465
     * Update the file or directory path in the document db document table
6466
     *
6467
     * @author - Hugues Peeters <[email protected]>
6468
     * @param string $action - action type require : 'delete' or 'update'
6469
     * @param string $old_path - old path info stored to change
6470
     * @param string $new_path - new path info to substitute
6471
     *
6472
     * @desc Update the file or directory path in the document db document table
6473
     *
6474
     */
6475
    public static function updateDbInfo($action, $old_path, $new_path = '')
6476
    {
6477
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
6478
        $course_id = api_get_course_int_id();
6479
        $old_path = Database::escape_string($old_path);
6480
        switch ($action) {
6481
            case 'delete':
6482
                $query = "DELETE FROM $dbTable
6483
                          WHERE
6484
                            c_id = $course_id AND
6485
                            (
6486
                                path LIKE BINARY '".$old_path."' OR
6487
                                path LIKE BINARY '".$old_path."/%'
6488
                            )";
6489
                Database::query($query);
6490
                break;
6491
            case 'update':
6492
                if ($new_path[0] == '.') {
6493
                    $new_path = substr($new_path, 1);
6494
                }
6495
                $new_path = str_replace('//', '/', $new_path);
6496
6497
                // Attempt to update	- tested & working for root	dir
6498
                $new_path = Database::escape_string($new_path);
6499
                $query = "UPDATE $dbTable SET
6500
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
6501
                          WHERE c_id = $course_id AND (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
6502
                Database::query($query);
6503
                break;
6504
        }
6505
    }
6506
6507
    /**
6508
     * This function calculates the resized width and resized heigt
6509
     * according to the source and target widths
6510
     * and heights, height so that no distortions occur
6511
     * parameters
6512
     * @param $image = the absolute path to the image
6513
     * @param $target_width = how large do you want your resized image
6514
     * @param $target_height = how large do you want your resized image
6515
     * @param $slideshow (default=0) =
6516
     *      indicates weither we are generating images for a slideshow or not,
6517
     *		this overrides the $_SESSION["image_resizing"] a bit so that a thumbnail
6518
     *	    view is also possible when you choose not to resize the source images
6519
     * @return array
6520
     */
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...
6521
    public static function resizeImageSlideShow(
6522
        $image,
6523
        $target_width,
6524
        $target_height,
6525
        $slideshow = 0
6526
    ) {
6527
        // Modifications by Ivan Tcholakov, 04-MAY-2009.
6528
        $result = [];
6529
        $imageResize = Session::read('image_resizing');
6530
        if ($imageResize == 'resizing' || $slideshow == 1) {
6531
            $new_sizes = api_resize_image($image, $target_width, $target_height);
6532
            $result[] = $new_sizes['height'];
6533
            $result[] = $new_sizes['width'];
6534
        } else {
6535
            $size = api_getimagesize($image);
6536
            $result[] = $size['height'];
6537
            $result[] = $size['width'];
6538
        }
6539
6540
        return $result;
6541
    }
6542
6543
    /**
6544
     * Calculates the total size of a directory by adding the sizes (that
6545
     * are stored in the database) of all files & folders in this directory.
6546
     *
6547
     * @param string $path
6548
     * @param boolean $can_see_invisible
6549
     * @return int Total size
6550
     */
6551
    public static function getTotalFolderSize($path, $can_see_invisible = false)
6552
    {
6553
        $table_itemproperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6554
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
6555
        $tool_document = TOOL_DOCUMENT;
6556
6557
        $course_id = api_get_course_int_id();
6558
        $session_id = api_get_session_id();
6559
        $session_condition = api_get_session_condition(
6560
            $session_id,
6561
            true,
6562
            true,
6563
            'props.session_id'
6564
        );
6565
6566
        if (empty($course_id)) {
6567
            return 0;
6568
        }
6569
6570
        $path = Database::escape_string($path);
6571
        $visibility_rule = ' props.visibility '.($can_see_invisible ? '<> 2' : '= 1');
6572
6573
        $sql = "SELECT SUM(table1.size) FROM (
6574
                SELECT props.ref, size
6575
                FROM $table_itemproperty AS props 
6576
                INNER JOIN $table_document AS docs
6577
                ON (docs.id = props.ref AND docs.c_id = props.c_id)
6578
                WHERE
6579
                    docs.c_id = $course_id AND                    
6580
                    docs.path LIKE '$path/%' AND
6581
                    props.c_id = $course_id AND
6582
                    props.tool = '$tool_document' AND
6583
                    $visibility_rule
6584
                    $session_condition
6585
                GROUP BY ref
6586
            ) as table1";
6587
6588
        $result = Database::query($sql);
6589
        if ($result && Database::num_rows($result) != 0) {
6590
            $row = Database::fetch_row($result);
6591
6592
            return $row[0] == null ? 0 : $row[0];
6593
        } else {
6594
            return 0;
6595
        }
6596
    }
6597
    /**
6598
     * Adds a cloud link to the database
6599
     *
6600
     * @author - Aquilino Blanco Cores <[email protected]>
6601
     * @param array $_course
6602
     * @param string $path
6603
     * @param string $url
6604
     * @param string $name
6605
     * @return int id of document or 0 if already exists or there was a problem creating it
6606
     */
6607
    public static function addCloudLink($_course, $path, $url, $name)
6608
    {
6609
        $file_path = $path;
6610
        if (!self::cloudLinkExists($_course, $path, $url)) {
6611
            $doc_id = add_document($_course, $file_path, 'link', 0, $name, $url);
6612
            if ($doc_id) {
6613
                // Update document item_property
6614
                api_item_property_update(
6615
                    $_course,
6616
                    TOOL_DOCUMENT,
6617
                    $doc_id,
6618
                    'DocumentAdded',
6619
                    api_get_user_id(),
6620
                    api_get_group_id(),
6621
                    api_get_user_id(),
6622
                    null,
6623
                    null,
6624
                    api_get_session_id()
6625
                );
6626
            }
6627
6628
            // If the file is in a folder, we need to update all parent folders
6629
            item_property_update_on_folder($_course, $file_path, api_get_user_id());
6630
6631
            return $doc_id;
6632
        } else {
6633
            return 0;
6634
        }
6635
    }
6636
6637
    /**
6638
     * Deletes a cloud link from the database
6639
     *
6640
     * @author - Aquilino Blanco Cores <[email protected]>
6641
     * @param array $courseInfo
6642
     * @param string $documentId
6643
     * @return boolean true if success / false if an error occurred
6644
     */
6645
    public static function deleteCloudLink($courseInfo, $documentId)
6646
    {
6647
        if (empty($documentId) || empty($courseInfo)) {
6648
            return false;
6649
        }
6650
6651
        $documentId = (int) $documentId;
6652
        $fileDeletedFromDb = false;
6653
        if (!empty($documentId)) {
6654
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
6655
            // checking
6656
            $table = Database::get_course_table(TABLE_DOCUMENT);
6657
            $courseId = $courseInfo['real_id'];
6658
            echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
6659
            $result = Database::query($sql);
6660
            $exists = Database::num_rows($result) > 0;
6661
            $fileDeletedFromDb = !$exists;
6662
        }
6663
6664
        return $fileDeletedFromDb;
6665
    }
6666
6667
    /**
6668
     * Gets the id of a cloud link with a given path
6669
     *
6670
     * @author - Aquilino Blanco Cores <[email protected]>
6671
     * @param array $courseInfo
6672
     * @param string $path
6673
     * @param string $url
6674
     * @return int link's id / false if no link found
6675
     */
6676
    public static function getCloudLinkId($courseInfo, $path, $url)
6677
    {
6678
        $table = Database::get_course_table(TABLE_DOCUMENT);
6679
6680
        if (empty($courseInfo)) {
6681
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
6682
        }
6683
6684
        $courseId = (int) $courseInfo['real_id'];
6685
        $path = Database::escape_string($path);
6686
6687
        if (substr($path, -1) != '/') {
6688
            // Add final slash to path if not present
6689
            $path .= '/';
6690
        }
6691
6692
        if (!empty($courseId) && !empty($path)) {
6693
            $sql = "SELECT id FROM $table 
6694
                    WHERE 
6695
                        c_id = $courseId AND 
6696
                        path LIKE BINARY '$path' AND 
6697
                        comment = '$url' AND 
6698
                        filetype = 'link' 
6699
                    LIMIT 1";
6700
            $result = Database::query($sql);
6701
            if ($result && Database::num_rows($result)) {
6702
                $row = Database::fetch_array($result);
6703
                return intval($row[0]);
6704
            }
6705
        }
6706
6707
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
6708
    }
6709
6710
    /**
6711
     * Checks if a cloud link exists
6712
     *
6713
     * @author - Aquilino Blanco Cores <[email protected]>
6714
     * @param array $courseInfo
6715
     * @param string $path
6716
     * @param string $url
6717
     * @return boolean true if it exists false in other case
6718
     */
6719
    public static function cloudLinkExists($courseInfo, $path, $url)
6720
    {
6721
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6722
        return $exists;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $exists returns the type integer which is incompatible with the documented return type boolean.
Loading history...
6723
    }
6724
6725
    /**
6726
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications
6727
     *
6728
     * @author Aquilino Blanco Cores <[email protected]>
6729
     * @return string the well formed URLs regular expressions string
6730
     */
6731
    public static function getWellFormedUrlRegex()
6732
    {
6733
        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';
6734
    }
6735
6736
    /**
6737
     * Gets the files hosting sites' whitelist
6738
     *
6739
     * @author Aquilino Blanco Cores <[email protected]>
6740
     * @return array the sites list.
6741
     */
6742
    public static function getFileHostingWhiteList()
6743
    {
6744
        return [
6745
            'asuswebstorage.com',
6746
            'dropbox.com',
6747
            'dropboxusercontent.com',
6748
            'fileserve.com',
6749
            'drive.google.com',
6750
            'icloud.com',
6751
            'mediafire.com',
6752
            'mega.nz',
6753
            'onedrive.live.com',
6754
            'slideshare.net',
6755
            'scribd.com',
6756
            'wetransfer.com',
6757
            'box.com',
6758
            'livefilestore.com' // OneDrive
6759
        ];
6760
    }
6761
}
6762