Completed
Push — master ( 7bef58...5c053f )
by Julito
25:30
created

DocumentManager::generate_video_preview()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 47
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 47
rs 9.0303
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 *  Class DocumentManager
8
 *  This is the document library for Chamilo.
9
 *  It is / will be used to provide a service layer to all document-using tools.
10
 *  and eliminate code duplication fro group documents, scorm documents, main documents.
11
 *  Include/require it in your code to use its functionality.
12
 *
13
 * @package chamilo.library
14
 */
15
class DocumentManager
16
{
17
    /**
18
     * Construct.
19
     */
20
    private function __construct()
21
    {
22
    }
23
24
    /**
25
     * @param string $course_code
26
     *
27
     * @return int the document folder quota for the current course in bytes
28
     *             or the default quota
29
     */
30
    public static function get_course_quota($course_code = null)
31
    {
32
        if (empty($course_code)) {
33
            $course_info = api_get_course_info();
34
        } else {
35
            $course_info = api_get_course_info($course_code);
36
        }
37
38
        $course_quota = null;
39
        if (empty($course_info)) {
40
            return DEFAULT_DOCUMENT_QUOTA;
41
        } else {
42
            $course_quota = $course_info['disk_quota'];
43
        }
44
        if (is_null($course_quota) || empty($course_quota)) {
45
            // Course table entry for quota was null, then use default value
46
            $course_quota = DEFAULT_DOCUMENT_QUOTA;
47
        }
48
49
        return $course_quota;
50
    }
51
52
    /**
53
     * Get the content type of a file by checking the extension
54
     * We could use mime_content_type() with php-versions > 4.3,
55
     * but this doesn't work as it should on Windows installations.
56
     *
57
     * @param string $filename or boolean TRUE to return complete array
58
     *
59
     * @author ? first version
60
     * @author Bert Vanderkimpen
61
     *
62
     * @return string
63
     */
64
    public static function file_get_mime_type($filename)
65
    {
66
        // All MIME types in an array (from 1.6, this is the authorative source)
67
        // Please, keep this alphabetical if you add something to this list!
68
        $mime_types = [
69
            'ai' => 'application/postscript',
70
            'aif' => 'audio/x-aiff',
71
            'aifc' => 'audio/x-aiff',
72
            'aiff' => 'audio/x-aiff',
73
            'asf' => 'video/x-ms-asf',
74
            'asc' => 'text/plain',
75
            'au' => 'audio/basic',
76
            'avi' => 'video/x-msvideo',
77
            'bcpio' => 'application/x-bcpio',
78
            'bin' => 'application/octet-stream',
79
            'bmp' => 'image/bmp',
80
            'cdf' => 'application/x-netcdf',
81
            'class' => 'application/octet-stream',
82
            'cpio' => 'application/x-cpio',
83
            'cpt' => 'application/mac-compactpro',
84
            'csh' => 'application/x-csh',
85
            'css' => 'text/css',
86
            'dcr' => 'application/x-director',
87
            'dir' => 'application/x-director',
88
            'djv' => 'image/vnd.djvu',
89
            'djvu' => 'image/vnd.djvu',
90
            'dll' => 'application/octet-stream',
91
            'dmg' => 'application/x-diskcopy',
92
            'dms' => 'application/octet-stream',
93
            'doc' => 'application/msword',
94
            'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
95
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
96
            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
97
            'dvi' => 'application/x-dvi',
98
            'dwg' => 'application/vnd.dwg',
99
            'dwf' => 'application/vnd.dwf',
100
            'dxf' => 'application/vnd.dxf',
101
            'dxr' => 'application/x-director',
102
            'eps' => 'application/postscript',
103
            'epub' => 'application/epub+zip',
104
            'etx' => 'text/x-setext',
105
            'exe' => 'application/octet-stream',
106
            'ez' => 'application/andrew-inset',
107
            'gif' => 'image/gif',
108
            'gtar' => 'application/x-gtar',
109
            'gz' => 'application/x-gzip',
110
            'hdf' => 'application/x-hdf',
111
            'hqx' => 'application/mac-binhex40',
112
            'htm' => 'text/html',
113
            'html' => 'text/html',
114
            'ice' => 'x-conference-xcooltalk',
115
            'ief' => 'image/ief',
116
            'iges' => 'model/iges',
117
            'igs' => 'model/iges',
118
            'jar' => 'application/java-archiver',
119
            'jpe' => 'image/jpeg',
120
            'jpeg' => 'image/jpeg',
121
            'jpg' => 'image/jpeg',
122
            'js' => 'application/x-javascript',
123
            'kar' => 'audio/midi',
124
            'lam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
125
            'latex' => 'application/x-latex',
126
            'lha' => 'application/octet-stream',
127
            'log' => 'text/plain',
128
            'lzh' => 'application/octet-stream',
129
            'm1a' => 'audio/mpeg',
130
            'm2a' => 'audio/mpeg',
131
            'm3u' => 'audio/x-mpegurl',
132
            'man' => 'application/x-troff-man',
133
            'me' => 'application/x-troff-me',
134
            'mesh' => 'model/mesh',
135
            'mid' => 'audio/midi',
136
            'midi' => 'audio/midi',
137
            'mov' => 'video/quicktime',
138
            'movie' => 'video/x-sgi-movie',
139
            'mp2' => 'audio/mpeg',
140
            'mp3' => 'audio/mpeg',
141
            'mp4' => 'video/mpeg4-generic',
142
            'mpa' => 'audio/mpeg',
143
            'mpe' => 'video/mpeg',
144
            'mpeg' => 'video/mpeg',
145
            'mpg' => 'video/mpeg',
146
            'mpga' => 'audio/mpeg',
147
            'ms' => 'application/x-troff-ms',
148
            'msh' => 'model/mesh',
149
            'mxu' => 'video/vnd.mpegurl',
150
            'nc' => 'application/x-netcdf',
151
            'oda' => 'application/oda',
152
            'oga' => 'audio/ogg',
153
            'ogg' => 'application/ogg',
154
            'ogx' => 'application/ogg',
155
            'ogv' => 'video/ogg',
156
            'pbm' => 'image/x-portable-bitmap',
157
            'pct' => 'image/pict',
158
            'pdb' => 'chemical/x-pdb',
159
            'pdf' => 'application/pdf',
160
            'pgm' => 'image/x-portable-graymap',
161
            'pgn' => 'application/x-chess-pgn',
162
            'pict' => 'image/pict',
163
            'png' => 'image/png',
164
            'pnm' => 'image/x-portable-anymap',
165
            'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
166
            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
167
            'pps' => 'application/vnd.ms-powerpoint',
168
            'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
169
            'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
170
            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
171
            'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
172
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
173
            'ppm' => 'image/x-portable-pixmap',
174
            'ppt' => 'application/vnd.ms-powerpoint',
175
            'pps' => 'application/vnd.ms-powerpoint',
176
            'ps' => 'application/postscript',
177
            'qt' => 'video/quicktime',
178
            'ra' => 'audio/x-realaudio',
179
            'ram' => 'audio/x-pn-realaudio',
180
            'rar' => 'image/x-rar-compressed',
181
            'ras' => 'image/x-cmu-raster',
182
            'rgb' => 'image/x-rgb',
183
            'rm' => 'audio/x-pn-realaudio',
184
            'roff' => 'application/x-troff',
185
            'rpm' => 'audio/x-pn-realaudio-plugin',
186
            'rtf' => 'text/rtf',
187
            'rtx' => 'text/richtext',
188
            'sgm' => 'text/sgml',
189
            'sgml' => 'text/sgml',
190
            'sh' => 'application/x-sh',
191
            'shar' => 'application/x-shar',
192
            'silo' => 'model/mesh',
193
            'sib' => 'application/X-Sibelius-Score',
194
            'sit' => 'application/x-stuffit',
195
            'skd' => 'application/x-koan',
196
            'skm' => 'application/x-koan',
197
            'skp' => 'application/x-koan',
198
            'skt' => 'application/x-koan',
199
            'smi' => 'application/smil',
200
            'smil' => 'application/smil',
201
            'snd' => 'audio/basic',
202
            'so' => 'application/octet-stream',
203
            'spl' => 'application/x-futuresplash',
204
            'src' => 'application/x-wais-source',
205
            'sv4cpio' => 'application/x-sv4cpio',
206
            'sv4crc' => 'application/x-sv4crc',
207
            'svf' => 'application/vnd.svf',
208
            'svg' => 'image/svg+xml',
209
            //'svgz' => 'image/svg+xml',
210
            'swf' => 'application/x-shockwave-flash',
211
            'sxc' => 'application/vnd.sun.xml.calc',
212
            'sxi' => 'application/vnd.sun.xml.impress',
213
            'sxw' => 'application/vnd.sun.xml.writer',
214
            't' => 'application/x-troff',
215
            'tar' => 'application/x-tar',
216
            'tcl' => 'application/x-tcl',
217
            'tex' => 'application/x-tex',
218
            'texi' => 'application/x-texinfo',
219
            'texinfo' => 'application/x-texinfo',
220
            'tga' => 'image/x-targa',
221
            'tif' => 'image/tif',
222
            'tiff' => 'image/tiff',
223
            'tr' => 'application/x-troff',
224
            'tsv' => 'text/tab-seperated-values',
225
            'txt' => 'text/plain',
226
            'ustar' => 'application/x-ustar',
227
            'vcd' => 'application/x-cdlink',
228
            'vrml' => 'model/vrml',
229
            'wav' => 'audio/x-wav',
230
            'wbmp' => 'image/vnd.wap.wbmp',
231
            'wbxml' => 'application/vnd.wap.wbxml',
232
            'wml' => 'text/vnd.wap.wml',
233
            'wmlc' => 'application/vnd.wap.wmlc',
234
            'wmls' => 'text/vnd.wap.wmlscript',
235
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
236
            'wma' => 'audio/x-ms-wma',
237
            'wmv' => 'video/x-ms-wmv',
238
            'wrl' => 'model/vrml',
239
            'xbm' => 'image/x-xbitmap',
240
            'xht' => 'application/xhtml+xml',
241
            'xhtml' => 'application/xhtml+xml',
242
            'xls' => 'application/vnd.ms-excel',
243
            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
244
            'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
245
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
246
            'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
247
            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
248
            'xml' => 'text/xml',
249
            'xpm' => 'image/x-xpixmap',
250
            'xsl' => 'text/xml',
251
            'xwd' => 'image/x-windowdump',
252
            'xyz' => 'chemical/x-xyz',
253
            'zip' => 'application/zip',
254
        ];
255
256
        if ($filename === true) {
257
            return $mime_types;
258
        }
259
260
        //get the extension of the file
261
        $extension = explode('.', $filename);
262
263
        //$filename will be an array if a . was found
264
        if (is_array($extension)) {
265
            $extension = strtolower($extension[sizeof($extension) - 1]);
266
        } else {
267
            //file without extension
268
            $extension = 'empty';
269
        }
270
271
        //if the extension is found, return the content type
272
        if (isset($mime_types[$extension])) {
273
            return $mime_types[$extension];
274
        }
275
        //else return octet-stream
276
        return 'application/octet-stream';
277
    }
278
279
    /**
280
     * This function streams a file to the client.
281
     *
282
     * @param string $full_file_name
283
     * @param bool   $forced
284
     * @param string $name
285
     * @param bool   $fixLinksHttpToHttps change file content from http to https
286
     *
287
     * @return false if file doesn't exist, true if stream succeeded
288
     */
289
    public static function file_send_for_download(
290
        $full_file_name,
291
        $forced = false,
292
        $name = '',
293
        $fixLinksHttpToHttps = false
294
    ) {
295
        session_write_close(); //we do not need write access to session anymore
296
        if (!is_file($full_file_name)) {
297
            return false;
298
        }
299
        $filename = $name == '' ? basename($full_file_name) : api_replace_dangerous_char($name);
300
        $len = filesize($full_file_name);
301
        // Fixing error when file name contains a ","
302
        $filename = str_replace(',', '', $filename);
303
        $sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
304
305
        // Allows chrome to make videos and audios seekable
306
        header('Accept-Ranges: bytes');
307
308
        if ($forced) {
309
            // Force the browser to save the file instead of opening it
310
            if (isset($sendFileHeaders) &&
311
                !empty($sendFileHeaders)) {
312
                header("X-Sendfile: $filename");
313
            }
314
315
            header('Content-type: application/octet-stream');
316
            header('Content-length: '.$len);
317
            if (preg_match("/MSIE 5.5/", $_SERVER['HTTP_USER_AGENT'])) {
318
                header('Content-Disposition: filename= '.$filename);
319
            } else {
320
                header('Content-Disposition: attachment; filename= '.$filename);
321
            }
322
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
323
                header('Pragma: ');
324
                header('Cache-Control: ');
325
                header('Cache-Control: public'); // IE cannot download from sessions without a cache
326
            }
327
            header('Content-Description: '.$filename);
328
            header('Content-Transfer-Encoding: binary');
329
330
            if (function_exists('ob_end_clean')) {
331
                // Use ob_end_clean() to avoid weird buffering situations
332
                // where file is sent broken/incomplete for download
333
                ob_end_clean();
334
            }
335
336
            $res = fopen($full_file_name, 'r');
337
            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

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

2906
                $output = [fread(/** @scrutinizer ignore-type */ $handle, filesize($doc_path))];
Loading history...
2907
                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

2907
                fclose(/** @scrutinizer ignore-type */ $handle);
Loading history...
2908
                break;
2909
            case 'application/pdf':
2910
                exec("pdftotext $doc_path -", $output, $ret_val);
2911
                break;
2912
            case 'application/postscript':
2913
                $temp_file = tempnam(sys_get_temp_dir(), 'chamilo');
2914
                exec("ps2pdf $doc_path $temp_file", $output, $ret_val);
2915
                if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
2916
                    return false;
2917
                }
2918
                exec("pdftotext $temp_file -", $output, $ret_val);
2919
                unlink($temp_file);
2920
                break;
2921
            case 'application/msword':
2922
                exec("catdoc $doc_path", $output, $ret_val);
2923
                break;
2924
            case 'text/html':
2925
                exec("html2text $doc_path", $output, $ret_val);
2926
                break;
2927
            case 'text/rtf':
2928
                // Note: correct handling of code pages in unrtf
2929
                // on debian lenny unrtf v0.19.2 can not, but unrtf v0.20.5 can
2930
                exec("unrtf --text $doc_path", $output, $ret_val);
2931
                if ($ret_val == 127) { // command not found
2932
                    return false;
2933
                }
2934
                // Avoid index unrtf comments
2935
                if (is_array($output) && count($output) > 1) {
2936
                    $parsed_output = [];
2937
                    foreach ($output as &$line) {
2938
                        if (!preg_match('/^###/', $line, $matches)) {
2939
                            if (!empty($line)) {
2940
                                $parsed_output[] = $line;
2941
                            }
2942
                        }
2943
                    }
2944
                    $output = $parsed_output;
2945
                }
2946
                break;
2947
            case 'application/vnd.ms-powerpoint':
2948
                exec("catppt $doc_path", $output, $ret_val);
2949
                break;
2950
            case 'application/vnd.ms-excel':
2951
                exec("xls2csv -c\" \" $doc_path", $output, $ret_val);
2952
                break;
2953
        }
2954
2955
        $content = '';
2956
        if (!is_null($ret_val)) {
2957
            if ($ret_val !== 0) { // shell fail, probably 127 (command not found)
2958
                return false;
2959
            }
2960
        }
2961
        if (isset($output)) {
2962
            foreach ($output as &$line) {
2963
                $content .= $line."\n";
2964
            }
2965
2966
            return $content;
2967
        } else {
2968
            return false;
2969
        }
2970
    }
2971
2972
    /**
2973
     * Calculates the total size of all documents in a course.
2974
     *
2975
     * @author Bert vanderkimpen
2976
     *
2977
     * @param int $course_id
2978
     * @param int $group_id   (to calculate group document space)
2979
     * @param int $session_id
2980
     *
2981
     * @return int total size
2982
     */
2983
    public static function documents_total_space($course_id = null, $group_id = null, $session_id = null)
2984
    {
2985
        $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
2986
        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
2987
        $session_id = intval($session_id);
2988
        $group_id = intval($group_id);
2989
        $course_id = intval($course_id);
2990
2991
        if (!$course_id) {
2992
            $course_id = api_get_course_int_id();
2993
        }
2994
2995
        $group_condition = '';
2996
        if ($group_id) {
2997
            $group_condition = " AND props.to_group_id='".$group_id."' ";
2998
        }
2999
3000
        $session_condition = '';
3001
        if ($session_id) {
3002
            $session_condition = " AND props.session_id='".$session_id."' ";
3003
        }
3004
3005
        $sql = "SELECT SUM(size)
3006
                FROM $TABLE_ITEMPROPERTY AS props
3007
                INNER JOIN $TABLE_DOCUMENT AS docs
3008
                ON (docs.id = props.ref AND props.c_id = docs.c_id)
3009
                WHERE
3010
                    props.c_id 	= $course_id AND
3011
                    docs.c_id 	= $course_id AND
3012
                    props.tool 	= '".TOOL_DOCUMENT."' AND
3013
                    props.visibility <> 2
3014
                    $group_condition
3015
                    $session_condition
3016
                ";
3017
        $result = Database::query($sql);
3018
3019
        if ($result && Database::num_rows($result) != 0) {
3020
            $row = Database::fetch_row($result);
3021
3022
            return $row[0];
3023
        } else {
3024
            return 0;
3025
        }
3026
    }
3027
3028
    /**
3029
     * Display the document quota in a simple way.
3030
     *
3031
     *  Here we count 1 Kilobyte = 1024 Bytes, 1 Megabyte = 1048576 Bytes
3032
     */
3033
    public static function displaySimpleQuota($course_quota, $already_consumed_space)
3034
    {
3035
        $course_quota_m = round($course_quota / 1048576);
3036
        $already_consumed_space_m = round($already_consumed_space / 1048576, 2);
3037
        $percentage = $already_consumed_space / $course_quota * 100;
3038
        $percentage = round($percentage, 1);
3039
        $message = get_lang('YouAreCurrentlyUsingXOfYourX');
3040
        $message = sprintf($message, $already_consumed_space_m, $percentage.'%', $course_quota_m.' ');
3041
3042
        return Display::div($message, ['id' => 'document_quota']);
3043
    }
3044
3045
    /**
3046
     * Checks if there is enough place to add a file on a directory
3047
     * on the base of a maximum directory size allowed.
3048
     *
3049
     * @author Bert Vanderkimpen
3050
     *
3051
     * @param int $file_size     size of the file in byte
3052
     * @param int $max_dir_space maximum size
3053
     *
3054
     * @return bool true if there is enough space, false otherwise
3055
     *
3056
     * @see enough_space() uses  documents_total_space() function
3057
     */
3058
    public static function enough_space($file_size, $max_dir_space)
3059
    {
3060
        if ($max_dir_space) {
3061
            $already_filled_space = self::documents_total_space();
3062
            if (($file_size + $already_filled_space) > $max_dir_space) {
3063
                return false;
3064
            }
3065
        }
3066
3067
        return true;
3068
    }
3069
3070
    /**
3071
     * @param array $params count, url, extension
3072
     *
3073
     * @return string
3074
     */
3075
    public static function generate_jplayer_jquery($params = [])
3076
    {
3077
        $js_path = api_get_path(WEB_LIBRARY_PATH).'javascript/';
3078
3079
        $js = '
3080
            $("#jquery_jplayer_'.$params['count'].'").jPlayer({
3081
                ready: function() {
3082
                    $(this).jPlayer("setMedia", {
3083
                        '.$params['extension'].' : "'.$params['url'].'"
3084
                    });
3085
                },
3086
                play: function() { // To avoid both jPlayers playing together.
3087
                    $(this).jPlayer("pauseOthers");
3088
                },
3089
                //errorAlerts: true,
3090
                //warningAlerts: true,
3091
                swfPath: "'.$js_path.'jquery-jplayer/jplayer/",
3092
                //supplied: "m4a, oga, mp3, ogg, wav",
3093
                supplied: "'.$params['extension'].'",
3094
                wmode: "window",
3095
                solution: "flash, html",  // Do not change this setting
3096
                cssSelectorAncestor: "#jp_container_'.$params['count'].'",
3097
            });  	 '."\n\n";
3098
3099
        return $js;
3100
    }
3101
3102
    /**
3103
     * Shows a play icon next to the document title in the document list.
3104
     *
3105
     * @param int
3106
     * @param string
3107
     *
3108
     * @return string html content
3109
     */
3110
    public static function generate_media_preview($i, $type = 'simple')
3111
    {
3112
        $i = intval($i);
3113
        $extra_controls = $progress = '';
3114
        if ($type == 'advanced') {
3115
            $extra_controls = ' <li><a href="javascript:;" class="jp-stop" tabindex="1">stop</a></li>
3116
                                <li><a href="#" class="jp-mute" tabindex="1">mute</a></li>
3117
                                <li><a href="#" class="jp-unmute" tabindex="1">unmute</a></li>';
3118
            $progress = '<div class="jp-progress">
3119
                                <div class="jp-seek-bar">
3120
                                    <div class="jp-play-bar"></div>
3121
                                </div>
3122
                            </div>';
3123
        }
3124
3125
        //Shows only the play button
3126
        $html = '<div id="jquery_jplayer_'.$i.'" class="jp-jplayer"></div>
3127
                <div id="jp_container_'.$i.'" class="jp-audio">
3128
                    <div class="jp-type-single">
3129
                        <div class="jp-gui jp-interface">
3130
                            <ul class="jp-controls">
3131
                                <li><a href="javascript:;" class="jp-play" tabindex="1">play</a></li>
3132
                                <li><a href="javascript:;" class="jp-pause" tabindex="1">pause</a></li>
3133
                                '.$extra_controls.'
3134
                            </ul>
3135
                            '.$progress.'
3136
                        </div>
3137
                    </div>
3138
                </div>';
3139
3140
        return $html;
3141
    }
3142
3143
    /**
3144
     * @param array $document_data
3145
     *
3146
     * @return string
3147
     */
3148
    public static function generate_video_preview($document_data = [])
3149
    {
3150
        $html = '
3151
        <div id="jp_container_1" class="jp-video center-block" role="application" aria-label="media player">
3152
            <div class="jp-type-single">
3153
                <div id="jquery_jplayer_1" class="jp-jplayer"></div>
3154
                <div class="jp-gui">
3155
                    <div class="jp-video-play">
3156
                    </div>
3157
                    <div class="jp-interface">
3158
                        <div class="jp-progress">
3159
                            <div class="jp-seek-bar">
3160
                                <div class="jp-play-bar"></div>
3161
                            </div>
3162
                        </div>
3163
                        <div class="jp-current-time" role="timer" aria-label="time">&nbsp;</div>
3164
                        <div class="jp-duration" role="timer" aria-label="duration">&nbsp;</div>
3165
                        <div class="jp-controls-holder">
3166
                          <div class="jp-controls">
3167
                            <button class="jp-play" role="button" tabindex="0">play</button>
3168
                            <button class="jp-stop" role="button" tabindex="0">stop</button>
3169
                          </div>
3170
                          <div class="jp-volume-controls">
3171
                            <button class="jp-mute" role="button" tabindex="0">mute</button>
3172
                            <button class="jp-volume-max" role="button" tabindex="0">max volume</button>
3173
                            <div class="jp-volume-bar">
3174
                                <div class="jp-volume-bar-value"></div>
3175
                            </div>
3176
                          </div>
3177
                          <div class="jp-toggles">
3178
                            <button class="jp-repeat" role="button" tabindex="0">repeat</button>
3179
                            <button class="jp-full-screen" role="button" tabindex="0">full screen</button>
3180
                          </div>
3181
                        </div>
3182
                        <div class="jp-details">
3183
                          <div class="jp-title" aria-label="title">&nbsp;</div>
3184
                        </div>
3185
                    </div>
3186
                </div>
3187
                <div class="jp-no-solution">
3188
                    <span>'.get_lang('UpdateRequire').'</span>
3189
                    '.get_lang("ToPlayTheMediaYouWillNeedToUpdateYourBrowserToARecentVersionYouCanAlsoDownloadTheFile").'
3190
                </div>
3191
            </div>
3192
        </div>';
3193
3194
        return $html;
3195
    }
3196
3197
    /**
3198
     * @param array  $course_info
3199
     * @param bool   $lp_id
3200
     * @param string $target
3201
     * @param int    $session_id
3202
     * @param bool   $add_move_button
3203
     * @param string $filter_by_folder
3204
     * @param string $overwrite_url
3205
     * @param bool   $showInvisibleFiles
3206
     * @param bool   $showOnlyFolders
3207
     * @param int    $folderId
3208
     *
3209
     * @return string
3210
     */
3211
    public static function get_document_preview(
3212
        $course_info,
3213
        $lp_id = false,
3214
        $target = '',
3215
        $session_id = 0,
3216
        $add_move_button = false,
3217
        $filter_by_folder = null,
3218
        $overwrite_url = '',
3219
        $showInvisibleFiles = false,
3220
        $showOnlyFolders = false,
3221
        $folderId = false
3222
    ) {
3223
        if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
3224
            return '';
3225
        }
3226
3227
        $user_id = api_get_user_id();
3228
        $userInfo = api_get_user_info();
3229
3230
        $user_in_course = false;
3231
        if (api_is_platform_admin()) {
3232
            $user_in_course = true;
3233
        }
3234
3235
        if (!$user_in_course) {
3236
            if (CourseManager::is_course_teacher($user_id, $course_info['code'])) {
3237
                $user_in_course = true;
3238
            }
3239
        }
3240
3241
        // Condition for the session
3242
        $session_id = intval($session_id);
3243
3244
        if (!$user_in_course) {
3245
            if (empty($session_id)) {
3246
                if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
3247
                    $user_in_course = true;
3248
                }
3249
                // Check if course is open then we can consider that the student is registered to the course
3250
                if (isset($course_info) && in_array($course_info['visibility'], [2, 3])) {
3251
                    $user_in_course = true;
3252
                }
3253
            } else {
3254
                $user_status = SessionManager::get_user_status_in_course_session(
3255
                    $user_id,
3256
                    $course_info['real_id'],
3257
                    $session_id
3258
                );
3259
                //is true if is an student, course session teacher or coach
3260
                if (in_array($user_status, ['0', '2', '6'])) {
3261
                    $user_in_course = true;
3262
                }
3263
            }
3264
        }
3265
3266
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
3267
        $tbl_item_prop = Database::get_course_table(TABLE_ITEM_PROPERTY);
3268
        $condition_session = " AND (last.session_id = '$session_id' OR last.session_id = '0' OR last.session_id IS NULL)";
3269
3270
        $add_folder_filter = null;
3271
        if (!empty($filter_by_folder)) {
3272
            $add_folder_filter = " AND docs.path LIKE '".Database::escape_string($filter_by_folder)."%'";
3273
        }
3274
3275
        // If we are in LP display hidden folder https://support.chamilo.org/issues/6679
3276
        $lp_visibility_condition = null;
3277
        if ($lp_id) {
3278
            // $lp_visibility_condition = " OR filetype='folder'";
3279
            if ($showInvisibleFiles) {
3280
                $lp_visibility_condition .= ' OR last.visibility = 0';
3281
            }
3282
        }
3283
3284
        $showOnlyFoldersCondition = null;
3285
        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...
3286
            //$showOnlyFoldersCondition = " AND docs.filetype = 'folder' ";
3287
        }
3288
3289
        $folderCondition = " AND docs.path LIKE '/%' ";
3290
3291
        if (!api_is_allowed_to_edit()) {
3292
            $protectedFolders = self::getProtectedFolderFromStudent();
3293
            foreach ($protectedFolders as $folder) {
3294
                $folderCondition .= " AND docs.path NOT LIKE '$folder' ";
3295
            }
3296
        }
3297
3298
        $parentData = [];
3299
        if ($folderId !== false) {
3300
            $parentData = self::get_document_data_by_id(
3301
                $folderId,
3302
                $course_info['code'],
3303
                false,
3304
                $session_id
3305
            );
3306
            if (!empty($parentData)) {
3307
                $cleanedPath = $parentData['path'];
3308
                $num = substr_count($cleanedPath, '/');
3309
3310
                $notLikeCondition = '';
3311
                for ($i = 1; $i <= $num; $i++) {
3312
                    $repeat = str_repeat('/%', $i + 1);
3313
                    $notLikeCondition .= " AND docs.path NOT LIKE '".Database::escape_string($cleanedPath.$repeat)."' ";
3314
                }
3315
3316
                $folderId = (int) $folderId;
3317
                $folderCondition = " AND
3318
                    docs.id <> $folderId AND
3319
                    docs.path LIKE '".$cleanedPath."/%'
3320
                    $notLikeCondition
3321
                ";
3322
            } else {
3323
                $folderCondition = " AND docs.filetype = 'file' ";
3324
            }
3325
        }
3326
3327
        $levelCondition = null;
3328
        if ($folderId === false) {
3329
            $levelCondition = " AND docs.path NOT LIKE'/%/%'";
3330
        }
3331
3332
        $sql = "SELECT DISTINCT last.visibility, docs.*
3333
                FROM $tbl_item_prop AS last 
3334
                INNER JOIN $tbl_doc AS docs
3335
                ON (docs.id = last.ref AND docs.c_id = last.c_id)
3336
                WHERE
3337
                    docs.path NOT LIKE '%_DELETED_%' AND
3338
                    last.tool = '".TOOL_DOCUMENT."' $condition_session AND
3339
                    (last.visibility = '1' $lp_visibility_condition) AND
3340
                    last.visibility <> 2 AND
3341
                    docs.c_id = {$course_info['real_id']} AND
3342
                    last.c_id = {$course_info['real_id']}
3343
                    $showOnlyFoldersCondition
3344
                    $folderCondition
3345
                    $levelCondition
3346
                    $add_folder_filter
3347
                ORDER BY docs.filetype DESC, docs.title ASC";
3348
3349
        $res_doc = Database::query($sql);
3350
        $resources = Database::store_result($res_doc, 'ASSOC');
3351
3352
        $return = '';
3353
        if ($lp_id) {
3354
            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...
3355
                /*$return .= '<div class="lp_resource_element">';
3356
                $return .= Display::return_icon('new_doc.gif', '', [], ICON_SIZE_SMALL);
3357
                $return .= Display::url(
3358
                    get_lang('CreateTheDocument'),
3359
                    api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_DOCUMENT.'&lp_id='.$_SESSION['oLP']->lp_id
3360
                );
3361
                $return .= '</div>';*/
3362
            }
3363
        } else {
3364
            $return .= Display::div(
3365
                Display::url(
3366
                    Display::return_icon('close.png', get_lang('Close'), [], ICON_SIZE_SMALL),
3367
                    ' javascript:void(0);',
3368
                    ['id' => 'close_div_'.$course_info['real_id'].'_'.$session_id, 'class' => 'close_div']
3369
                ),
3370
                ['style' => 'position:absolute;right:10px']
3371
            );
3372
        }
3373
3374
        // If you want to debug it, I advise you to do "echo" on the eval statements.
3375
        $newResources = [];
3376
        if (!empty($resources) && $user_in_course) {
3377
            foreach ($resources as $resource) {
3378
                $is_visible = self::is_visible_by_id(
3379
                    $resource['id'],
3380
                    $course_info,
3381
                    $session_id,
3382
                    api_get_user_id()
3383
                );
3384
3385
                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...
3386
                    if (!$is_visible) {
3387
                        continue;
3388
                    }
3389
                }
3390
3391
                $newResources[] = $resource;
3392
            }
3393
        }
3394
3395
        $label = get_lang('Documents');
3396
3397
        $documents = [];
3398
        if ($folderId === false) {
3399
            $documents[$label] = [
3400
                'id' => 0,
3401
                'files' => $newResources,
3402
            ];
3403
        } else {
3404
            if (is_array($parentData)) {
3405
                $documents[$parentData['title']] = [
3406
                    'id' => intval($folderId),
3407
                    'files' => $newResources,
3408
                ];
3409
            }
3410
        }
3411
3412
        $write_result = self::write_resources_tree(
3413
            $userInfo,
3414
            $course_info,
3415
            $session_id,
3416
            $documents,
3417
            $lp_id,
3418
            $target,
3419
            $add_move_button,
3420
            $overwrite_url,
3421
            $folderId
3422
        );
3423
3424
        $return .= $write_result;
3425
        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...
3426
            $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

3426
            $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...
3427
            $return .= "<script>
3428
            $('.doc_folder').click(function() {
3429
                var realId = this.id;
3430
                var my_id = this.id.split('_')[2];
3431
                var tempId = 'temp_'+my_id;
3432
                $('#res_'+my_id).show();
3433
3434
                var tempDiv = $('#'+realId).find('#'+tempId);
3435
                if (tempDiv.length == 0) {
3436
                    $.ajax({
3437
                        async: false,
3438
                        type: 'GET',
3439
                        url:  '".$url."',
3440
                        data: 'folder_id='+my_id,
3441
                        success: function(data) {
3442
                            $('#'+realId).append('<div id='+tempId+'>'+data+'</div>');
3443
                        }
3444
                    });
3445
                }
3446
            });
3447
3448
            $('.close_div').click(function() {
3449
                var course_id = this.id.split('_')[2];
3450
                var session_id = this.id.split('_')[3];
3451
                $('#document_result_'+course_id+'_'+session_id).hide();
3452
                $('.lp_resource').remove();
3453
                $('.document_preview_container').html('');
3454
            });
3455
3456
            </script>";
3457
        } else {
3458
            //For LPs
3459
            $url = api_get_path(WEB_AJAX_PATH).'lp.ajax.php?a=get_documents&lp_id='.$lp_id.'&'.api_get_cidreq();
3460
            $return .= "<script>
3461
3462
            function testResources(id, img) {
3463
                var numericId = id.split('_')[1];
3464
                var parentId = 'doc_id_'+numericId;
3465
                var tempId = 'temp_'+numericId;
3466
                var image = $('#'+img);
3467
3468
                if (image.hasClass('open')) {
3469
                    image.removeClass('open');
3470
                    image.attr('src', '".Display::returnIconPath('nolines_plus.gif')."');
3471
                    $('#'+id).show();
3472
                    $('#'+tempId).hide();
3473
                } else {
3474
                    image.addClass('open');
3475
                    image.attr('src', '".Display::returnIconPath('nolines_minus.gif')."');
3476
                    $('#'+id).hide();
3477
                    $('#'+tempId).show();
3478
3479
                    var tempDiv = $('#'+parentId).find('#'+tempId);
3480
                    if (tempDiv.length == 0) {
3481
                        $.ajax({
3482
                            type: 'GET',
3483
                            async: false,
3484
                            url:  '".$url."',
3485
                            data: 'folder_id='+numericId,
3486
                            success: function(data) {
3487
                                tempDiv = $('#doc_id_'+numericId).append('<div id='+tempId+'>'+data+'</div>');
3488
                            }
3489
                        });
3490
                    }
3491
                }
3492
            }
3493
            </script>";
3494
        }
3495
3496
        if (!$user_in_course) {
3497
            $return = '';
3498
        }
3499
3500
        return $return;
3501
    }
3502
3503
    /**
3504
     * Generate and return an HTML list of resources based on a given array.
3505
     * This list is used to show the course creator a list of available resources to choose from
3506
     * when creating a learning path.
3507
     *
3508
     * @param array  $userInfo        current user info
3509
     * @param array  $course_info
3510
     * @param int    $session_id
3511
     * @param array  $documents
3512
     * @param bool   $lp_id
3513
     * @param string $target
3514
     * @param bool   $add_move_button
3515
     * @param string $overwrite_url
3516
     * @param int    $folderId
3517
     *
3518
     * @return string
3519
     */
3520
    public static function write_resources_tree(
3521
        $userInfo,
3522
        $course_info,
3523
        $session_id,
3524
        $documents,
3525
        $lp_id = false,
3526
        $target = '',
3527
        $add_move_button = false,
3528
        $overwrite_url = '',
3529
        $folderId = false
3530
    ) {
3531
        $return = '';
3532
        if (!empty($documents)) {
3533
            foreach ($documents as $key => $resource) {
3534
                if (isset($resource['id']) && is_int($resource['id'])) {
3535
                    $mainFolderResource = [
3536
                        'id' => $resource['id'],
3537
                        'title' => $key,
3538
                    ];
3539
3540
                    if ($folderId === false) {
3541
                        $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

3541
                        $return .= self::parseFolder(/** @scrutinizer ignore-type */ $folderId, $mainFolderResource, $lp_id);
Loading history...
3542
                    }
3543
3544
                    if (isset($resource['files'])) {
3545
                        $return .= self::write_resources_tree(
3546
                            $userInfo,
3547
                            $course_info,
3548
                            $session_id,
3549
                            $resource['files'],
3550
                            $lp_id,
3551
                            $target,
3552
                            $add_move_button,
3553
                            $overwrite_url
3554
                        );
3555
                    }
3556
                    $return .= '</div>';
3557
                    $return .= '</ul>';
3558
                } else {
3559
                    if ($resource['filetype'] == 'folder') {
3560
                        $return .= self::parseFolder($folderId, $resource, $lp_id);
3561
                    } else {
3562
                        $return .= self::parseFile(
3563
                            $userInfo,
3564
                            $course_info,
3565
                            $session_id,
3566
                            $resource,
3567
                            $lp_id,
3568
                            $add_move_button,
3569
                            $target,
3570
                            $overwrite_url
3571
                        );
3572
                    }
3573
                }
3574
            }
3575
        }
3576
3577
        return $return;
3578
    }
3579
3580
    /**
3581
     * @param int    $doc_id
3582
     * @param string $course_code
3583
     * @param int    $session_id
3584
     * @param int    $user_id
3585
     * @param int    $groupId     iid
3586
     *
3587
     * @return bool
3588
     */
3589
    public static function check_visibility_tree(
3590
        $doc_id,
3591
        $course_code,
3592
        $session_id,
3593
        $user_id,
3594
        $groupId = 0
3595
    ) {
3596
        $document_data = self::get_document_data_by_id(
3597
            $doc_id,
3598
            $course_code,
3599
            null,
3600
            $session_id
3601
        );
3602
        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...
3603
            $document_data = self::get_document_data_by_id(
3604
                $doc_id,
3605
                $course_code,
3606
                null,
3607
                0
3608
            );
3609
        }
3610
3611
        if (!empty($document_data)) {
3612
            // If admin or course teacher, allow anyway
3613
            if (api_is_platform_admin() || CourseManager::is_course_teacher($user_id, $course_code)) {
3614
                return true;
3615
            }
3616
            $course_info = api_get_course_info($course_code);
3617
            if ($document_data['parent_id'] == false || empty($document_data['parent_id'])) {
3618
                if (!empty($groupId)) {
3619
                    return true;
3620
                }
3621
                $visible = self::is_visible_by_id($doc_id, $course_info, $session_id, $user_id);
3622
3623
                return $visible;
3624
            } else {
3625
                $visible = self::is_visible_by_id($doc_id, $course_info, $session_id, $user_id);
3626
3627
                if (!$visible) {
3628
                    return false;
3629
                } else {
3630
                    return self::check_visibility_tree(
3631
                        $document_data['parent_id'],
3632
                        $course_code,
3633
                        $session_id,
3634
                        $user_id,
3635
                        $groupId
3636
                    );
3637
                }
3638
            }
3639
        } else {
3640
            return false;
3641
        }
3642
    }
3643
3644
    /**
3645
     * Index a given document.
3646
     *
3647
     * @param   int     Document ID inside its corresponding course
3648
     * @param   string  Course code
3649
     * @param   int     Session ID (not used yet)
3650
     * @param   string  Language of document's content (defaults to course language)
3651
     * @param   array   Array of specific fields (['code'=>'value',...])
3652
     * @param   string  What to do if the file already exists (default or overwrite)
3653
     * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
3654
     *
3655
     * @return bool Returns true on presumed success, false on failure
3656
     */
3657
    public static function index_document(
3658
        $docid,
3659
        $course_code,
3660
        $session_id = 0,
3661
        $lang = 'english',
3662
        $specific_fields_values = [],
3663
        $if_exists = '',
3664
        $simulation = false
3665
    ) {
3666
        if (api_get_setting('search_enabled') !== 'true') {
3667
            return false;
3668
        }
3669
        if (empty($docid) or $docid != intval($docid)) {
3670
            return false;
3671
        }
3672
        if (empty($session_id)) {
3673
            $session_id = api_get_session_id();
3674
        }
3675
        $course_info = api_get_course_info($course_code);
3676
        $course_dir = $course_info['path'].'/document';
3677
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
3678
        $base_work_dir = $sys_course_path.$course_dir;
3679
3680
        $course_id = $course_info['real_id'];
3681
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
3682
3683
        $qry = "SELECT path, title FROM $table_document WHERE c_id = $course_id AND id = '$docid' LIMIT 1";
3684
        $result = Database::query($qry);
3685
        if (Database::num_rows($result) == 1) {
3686
            $row = Database::fetch_array($result);
3687
            $doc_path = api_get_path(SYS_COURSE_PATH).$course_dir.$row['path'];
3688
            //TODO: mime_content_type is deprecated, fileinfo php extension is enabled by default as of PHP 5.3.0
3689
            // now versions of PHP on Debian testing(5.2.6-5) and Ubuntu(5.2.6-2ubuntu) are lower, so wait for a while
3690
            $doc_mime = mime_content_type($doc_path);
3691
            $allowed_mime_types = self::file_get_mime_type(true);
3692
3693
            // mime_content_type does not detect correctly some formats that
3694
            // are going to be supported for index, so an extensions array is used for the moment
3695
            if (empty($doc_mime)) {
3696
                $allowed_extensions = [
3697
                    'doc',
3698
                    'docx',
3699
                    'ppt',
3700
                    'pptx',
3701
                    'pps',
3702
                    'ppsx',
3703
                    'xls',
3704
                    'xlsx',
3705
                    'odt',
3706
                    'odp',
3707
                    'ods',
3708
                    'pdf',
3709
                    'txt',
3710
                    'rtf',
3711
                    'msg',
3712
                    'csv',
3713
                    'html',
3714
                    'htm',
3715
                ];
3716
                $extensions = preg_split("/[\/\\.]/", $doc_path);
3717
                $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

3717
                $doc_ext = strtolower($extensions[count(/** @scrutinizer ignore-type */ $extensions) - 1]);
Loading history...
3718
                if (in_array($doc_ext, $allowed_extensions)) {
3719
                    switch ($doc_ext) {
3720
                        case 'ppt':
3721
                        case 'pps':
3722
                            $doc_mime = 'application/vnd.ms-powerpoint';
3723
                            break;
3724
                        case 'xls':
3725
                            $doc_mime = 'application/vnd.ms-excel';
3726
                            break;
3727
                    }
3728
                }
3729
            }
3730
3731
            //@todo move this nightmare in a search controller or something like that!!! J.M
3732
3733
            if (in_array($doc_mime, $allowed_mime_types)) {
3734
                $file_title = $row['title'];
3735
                $file_content = self::get_text_content($doc_path, $doc_mime);
3736
                $course_code = Database::escape_string($course_code);
3737
                $ic_slide = new IndexableChunk();
3738
                $ic_slide->addValue('title', $file_title);
3739
                $ic_slide->addCourseId($course_code);
3740
                $ic_slide->addToolId(TOOL_DOCUMENT);
3741
                $xapian_data = [
3742
                    SE_COURSE_ID => $course_code,
3743
                    SE_TOOL_ID => TOOL_DOCUMENT,
3744
                    SE_DATA => ['doc_id' => $docid],
3745
                    SE_USER => api_get_user_id(),
3746
                ];
3747
3748
                $ic_slide->xapian_data = serialize($xapian_data);
3749
                $di = new ChamiloIndexer();
3750
                $return = $di->connectDb(null, null, $lang);
3751
3752
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
3753
                $specific_fields = get_specific_field_list();
3754
3755
                // process different depending on what to do if file exists
3756
                /**
3757
                 * @TODO Find a way to really verify if the file had been
3758
                 * overwriten. Now all work is done at
3759
                 * handle_uploaded_document() and it's difficult to verify it
3760
                 */
3761
                if (!empty($if_exists) && $if_exists == 'overwrite') {
3762
                    // Overwrite the file on search engine
3763
                    // Actually, it consists on a delete of terms from db,
3764
                    // insert new ones, create a new search engine document,
3765
                    // and remove the old one
3766
                    // Get search_did
3767
                    $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3768
                    $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
3769
                    $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid);
3770
3771
                    $res = Database::query($sql);
3772
3773
                    if (Database::num_rows($res) > 0) {
3774
                        $se_ref = Database::fetch_array($res);
3775
                        if (!$simulation) {
3776
                            $di->remove_document($se_ref['search_did']);
3777
                        }
3778
                        $all_specific_terms = '';
3779
                        foreach ($specific_fields as $specific_field) {
3780
                            if (!$simulation) {
3781
                                delete_all_specific_field_value($course_code, $specific_field['id'], TOOL_DOCUMENT, $docid);
3782
                            }
3783
                            // Update search engine
3784
                            if (isset($specific_fields_values[$specific_field['code']])) {
3785
                                $sterms = trim($specific_fields_values[$specific_field['code']]);
3786
                            } else { //if the specific field is not defined, force an empty one
3787
                                $sterms = '';
3788
                            }
3789
                            $all_specific_terms .= ' '.$sterms;
3790
                            $sterms = explode(',', $sterms);
3791
                            foreach ($sterms as $sterm) {
3792
                                $sterm = trim($sterm);
3793
                                if (!empty($sterm)) {
3794
                                    $ic_slide->addTerm($sterm, $specific_field['code']);
3795
                                    // updated the last param here from $value to $sterm without being sure - see commit15464
3796
                                    if (!$simulation) {
3797
                                        add_specific_field_value(
3798
                                            $specific_field['id'],
3799
                                            $course_code,
3800
                                            TOOL_DOCUMENT,
3801
                                            $docid,
3802
                                            $sterm
3803
                                        );
3804
                                    }
3805
                                }
3806
                            }
3807
                        }
3808
                        // Add terms also to content to make terms findable by probabilistic search
3809
                        $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

3809
                        $file_content = $all_specific_terms.' './** @scrutinizer ignore-type */ $file_content;
Loading history...
3810
3811
                        if (!$simulation) {
3812
                            $ic_slide->addValue('content', $file_content);
3813
                            $di->addChunk($ic_slide);
3814
                            // Index and return a new search engine document id
3815
                            $did = $di->index();
3816
3817
                            if ($did) {
3818
                                // update the search_did on db
3819
                                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3820
                                $sql = 'UPDATE %s SET search_did=%d WHERE id=%d LIMIT 1';
3821
                                $sql = sprintf($sql, $tbl_se_ref, (int) $did, (int) $se_ref['id']);
3822
                                Database::query($sql);
3823
                            }
3824
                        }
3825
                    }
3826
                } else {
3827
                    // Add all terms
3828
                    $all_specific_terms = '';
3829
                    foreach ($specific_fields as $specific_field) {
3830
                        if (isset($specific_fields_values[$specific_field['code']])) {
3831
                            $sterms = trim($specific_fields_values[$specific_field['code']]);
3832
                        } else { //if the specific field is not defined, force an empty one
3833
                            $sterms = '';
3834
                        }
3835
                        $all_specific_terms .= ' '.$sterms;
3836
                        if (!empty($sterms)) {
3837
                            $sterms = explode(',', $sterms);
3838
                            foreach ($sterms as $sterm) {
3839
                                if (!$simulation) {
3840
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
3841
                                    add_specific_field_value(
3842
                                        $specific_field['id'],
3843
                                        $course_code,
3844
                                        TOOL_DOCUMENT,
3845
                                        $docid,
3846
                                        $sterm
3847
                                    );
3848
                                }
3849
                            }
3850
                        }
3851
                    }
3852
                    // Add terms also to content to make terms findable by probabilistic search
3853
                    $file_content = $all_specific_terms.' '.$file_content;
3854
                    if (!$simulation) {
3855
                        $ic_slide->addValue('content', $file_content);
3856
                        $di->addChunk($ic_slide);
3857
                        // Index and return search engine document id
3858
                        $did = $di->index();
3859
                        if ($did) {
3860
                            // Save it to db
3861
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
3862
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
3863
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
3864
                            $sql = sprintf($sql, $tbl_se_ref, $course_code, TOOL_DOCUMENT, $docid, $did);
3865
                            Database::query($sql);
3866
                        } else {
3867
                            return false;
3868
                        }
3869
                    }
3870
                }
3871
            } else {
3872
                return false;
3873
            }
3874
        }
3875
3876
        return true;
3877
    }
3878
3879
    /**
3880
     * @return array
3881
     */
3882
    public static function get_web_odf_extension_list()
3883
    {
3884
        return ['ods', 'odt', 'odp'];
3885
    }
3886
3887
    /**
3888
     * Set of extension allowed to use Jodconverter.
3889
     *
3890
     * @param $mode 'from'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'from' at position 0 could not be parsed: Unknown type name ''from'' at position 0 in 'from'.
Loading history...
3891
     *              'to'
3892
     *              'all'
3893
     * @param $format   'text'
3894
     *                  'spreadsheet'
3895
     *                  'presentation'
3896
     *                  'drawing'
3897
     *                  'all'
3898
     *
3899
     * @return array
3900
     */
3901
    public static function getJodconverterExtensionList($mode, $format)
3902
    {
3903
        $extensionList = [];
3904
        $extensionListFromText = [
3905
            'odt',
3906
            'sxw',
3907
            'rtf',
3908
            'doc',
3909
            'docx',
3910
            'wpd',
3911
            'txt',
3912
        ];
3913
        $extensionListToText = [
3914
            'pdf',
3915
            'odt',
3916
            'sxw',
3917
            'rtf',
3918
            'doc',
3919
            'docx',
3920
            'txt',
3921
        ];
3922
        $extensionListFromSpreadsheet = [
3923
            'ods',
3924
            'sxc',
3925
            'xls',
3926
            'xlsx',
3927
            'csv',
3928
            'tsv',
3929
        ];
3930
        $extensionListToSpreadsheet = [
3931
            'pdf',
3932
            'ods',
3933
            'sxc',
3934
            'xls',
3935
            'xlsx',
3936
            'csv',
3937
            'tsv',
3938
        ];
3939
        $extensionListFromPresentation = [
3940
            'odp',
3941
            'sxi',
3942
            'ppt',
3943
            'pptx',
3944
        ];
3945
        $extensionListToPresentation = [
3946
            'pdf',
3947
            'swf',
3948
            'odp',
3949
            'sxi',
3950
            'ppt',
3951
            'pptx',
3952
        ];
3953
        $extensionListFromDrawing = ['odg'];
3954
        $extensionListToDrawing = ['svg', 'swf'];
3955
3956
        if ($mode === 'from') {
3957
            if ($format === 'text') {
3958
                $extensionList = array_merge($extensionList, $extensionListFromText);
3959
            } elseif ($format === 'spreadsheet') {
3960
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3961
            } elseif ($format === 'presentation') {
3962
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3963
            } elseif ($format === 'drawing') {
3964
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3965
            } elseif ($format === 'all') {
3966
                $extensionList = array_merge($extensionList, $extensionListFromText);
3967
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3968
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3969
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3970
            }
3971
        } elseif ($mode === 'to') {
3972
            if ($format === 'text') {
3973
                $extensionList = array_merge($extensionList, $extensionListToText);
3974
            } elseif ($format === 'spreadsheet') {
3975
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3976
            } elseif ($format === 'presentation') {
3977
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3978
            } elseif ($format === 'drawing') {
3979
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3980
            } elseif ($format === 'all') {
3981
                $extensionList = array_merge($extensionList, $extensionListToText);
3982
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3983
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3984
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3985
            }
3986
        } elseif ($mode === 'all') {
3987
            if ($format === 'text') {
3988
                $extensionList = array_merge($extensionList, $extensionListFromText);
3989
                $extensionList = array_merge($extensionList, $extensionListToText);
3990
            } elseif ($format === 'spreadsheet') {
3991
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
3992
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
3993
            } elseif ($format === 'presentation') {
3994
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
3995
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
3996
            } elseif ($format === 'drawing') {
3997
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
3998
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
3999
            } elseif ($format === 'all') {
4000
                $extensionList = array_merge($extensionList, $extensionListFromText);
4001
                $extensionList = array_merge($extensionList, $extensionListToText);
4002
                $extensionList = array_merge($extensionList, $extensionListFromSpreadsheet);
4003
                $extensionList = array_merge($extensionList, $extensionListToSpreadsheet);
4004
                $extensionList = array_merge($extensionList, $extensionListFromPresentation);
4005
                $extensionList = array_merge($extensionList, $extensionListToPresentation);
4006
                $extensionList = array_merge($extensionList, $extensionListFromDrawing);
4007
                $extensionList = array_merge($extensionList, $extensionListToDrawing);
4008
            }
4009
        }
4010
4011
        return $extensionList;
4012
    }
4013
4014
    /**
4015
     * Get Format type list by extension and mode.
4016
     *
4017
     * @param string $mode Mode to search format type list
4018
     *
4019
     * @example 'from'
4020
     * @example 'to'
4021
     *
4022
     * @param string $extension file extension to check file type
4023
     *
4024
     * @return array
4025
     */
4026
    public static function getFormatTypeListConvertor($mode = 'from', $extension)
4027
    {
4028
        $formatTypesList = [];
4029
        $formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
4030
        foreach ($formatTypes as $formatType) {
4031
            if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
4032
                $formatTypesList[] = $formatType;
4033
            }
4034
        }
4035
4036
        return $formatTypesList;
4037
    }
4038
4039
    /**
4040
     * @param string $path
4041
     * @param bool   $is_certificate_mode
4042
     *
4043
     * @return bool
4044
     */
4045
    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
4046
    {
4047
        $foldersToAvoid = [
4048
            '/HotPotatoes_files',
4049
            '/certificates',
4050
        ];
4051
        $systemFolder = api_get_course_setting('show_system_folders');
4052
4053
        if ($systemFolder == 1) {
4054
            $foldersToAvoid = [];
4055
        }
4056
4057
        if (basename($path) == 'css') {
4058
            return true;
4059
        }
4060
4061
        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...
4062
            //Certificate results
4063
            if (strstr($path, 'certificates')) {
4064
                return true;
4065
            }
4066
        }
4067
4068
        // Admin setting for Hide/Show the folders of all users
4069
        if (api_get_setting('show_users_folders') == 'false') {
4070
            $foldersToAvoid[] = '/shared_folder';
4071
4072
            if (strstr($path, 'shared_folder_session_')) {
4073
                return true;
4074
            }
4075
        }
4076
4077
        // Admin setting for Hide/Show Default folders to all users
4078
        if (api_get_setting('show_default_folders') == 'false') {
4079
            $foldersToAvoid[] = '/images';
4080
            $foldersToAvoid[] = '/flash';
4081
            $foldersToAvoid[] = '/audio';
4082
            $foldersToAvoid[] = '/video';
4083
        }
4084
4085
        // Admin setting for Hide/Show chat history folder
4086
        if (api_get_setting('show_chat_folder') == 'false') {
4087
            $foldersToAvoid[] = '/chat_files';
4088
        }
4089
4090
        if (is_array($foldersToAvoid)) {
4091
            return in_array($path, $foldersToAvoid);
4092
        } else {
4093
            return false;
4094
        }
4095
    }
4096
4097
    /**
4098
     * @return array
4099
     */
4100
    public static function get_system_folders()
4101
    {
4102
        return [
4103
            '/certificates',
4104
            '/HotPotatoes_files',
4105
            '/chat_files',
4106
            '/images',
4107
            '/flash',
4108
            '/audio',
4109
            '/video',
4110
            '/shared_folder',
4111
            '/learning_path',
4112
        ];
4113
    }
4114
4115
    /**
4116
     * @return array
4117
     */
4118
    public static function getProtectedFolderFromStudent()
4119
    {
4120
        return [
4121
            '/certificates',
4122
            '/HotPotatoes_files',
4123
            '/chat_files',
4124
            '/shared_folder',
4125
            '/learning_path',
4126
        ];
4127
    }
4128
4129
    /**
4130
     * @param string $courseCode
4131
     *
4132
     * @return string 'visible' or 'invisible' string
4133
     */
4134
    public static function getDocumentDefaultVisibility($courseCode)
4135
    {
4136
        $settings = api_get_setting('tool_visible_by_default_at_creation');
4137
        $defaultVisibility = 'visible';
4138
4139
        if (isset($settings['documents'])) {
4140
            $portalDefaultVisibility = 'invisible';
4141
            if ($settings['documents'] == 'true') {
4142
                $portalDefaultVisibility = 'visible';
4143
            }
4144
4145
            $defaultVisibility = $portalDefaultVisibility;
4146
        }
4147
4148
        if (api_get_setting('documents_default_visibility_defined_in_course') == 'true') {
4149
            $courseVisibility = api_get_course_setting('documents_default_visibility', $courseCode);
4150
            if (!empty($courseVisibility) && in_array($courseVisibility, ['visible', 'invisible'])) {
4151
                $defaultVisibility = $courseVisibility;
4152
            }
4153
        }
4154
4155
        return $defaultVisibility;
4156
    }
4157
4158
    /**
4159
     * @param array  $courseInfo
4160
     * @param int    $id         doc id
4161
     * @param string $visibility visible/invisible
4162
     * @param int    $userId
4163
     */
4164
    public static function updateVisibilityFromAllSessions($courseInfo, $id, $visibility, $userId)
4165
    {
4166
        $sessionList = SessionManager::get_session_by_course($courseInfo['real_id']);
4167
4168
        if (!empty($sessionList)) {
4169
            foreach ($sessionList as $session) {
4170
                $sessionId = $session['id'];
4171
                api_item_property_update(
4172
                    $courseInfo,
4173
                    TOOL_DOCUMENT,
4174
                    $id,
4175
                    $visibility,
4176
                    $userId,
4177
                    null,
4178
                    null,
4179
                    null,
4180
                    null,
4181
                    $sessionId
4182
                );
4183
            }
4184
        }
4185
    }
4186
4187
    /**
4188
     * @param string $file
4189
     *
4190
     * @return string
4191
     */
4192
    public static function readNanogongFile($file)
4193
    {
4194
        $nanoGongJarFile = api_get_path(WEB_LIBRARY_PATH).'nanogong/nanogong.jar';
4195
        $html = '<applet id="applet" archive="'.$nanoGongJarFile.'" code="gong.NanoGong" width="160" height="95">';
4196
        $html .= '<param name="SoundFileURL" value="'.$file.'" />';
4197
        $html .= '<param name="ShowSaveButton" value="false" />';
4198
        $html .= '<param name="ShowTime" value="true" />';
4199
        $html .= '<param name="ShowRecordButton" value="false" />';
4200
        $html .= '</applet>';
4201
4202
        return $html;
4203
    }
4204
4205
    /**
4206
     * @param string $filePath
4207
     * @param string $path
4208
     * @param array  $courseInfo
4209
     * @param int    $sessionId
4210
     * @param string $whatIfFileExists overwrite|rename
4211
     * @param int    $userId
4212
     * @param int    $groupId
4213
     * @param int    $toUserId
4214
     * @param string $comment
4215
     *
4216
     * @return bool|path
4217
     */
4218
    public static function addFileToDocumentTool(
4219
        $filePath,
4220
        $path,
4221
        $courseInfo,
4222
        $sessionId,
4223
        $userId,
4224
        $whatIfFileExists = 'overwrite',
4225
        $groupId = null,
4226
        $toUserId = null,
4227
        $comment = null
4228
    ) {
4229
        if (!file_exists($filePath)) {
4230
            return false;
4231
        }
4232
4233
        $fileInfo = pathinfo($filePath);
4234
4235
        $file = [
4236
            'name' => $fileInfo['basename'],
4237
            'tmp_name' => $filePath,
4238
            'size' => filesize($filePath),
4239
            'from_file' => true,
4240
        ];
4241
4242
        $course_dir = $courseInfo['path'].'/document';
4243
        $baseWorkDir = api_get_path(SYS_COURSE_PATH).$course_dir;
4244
4245
        $filePath = handle_uploaded_document(
4246
            $courseInfo,
4247
            $file,
4248
            $baseWorkDir,
4249
            $path,
4250
            $userId,
4251
            $groupId,
4252
            $toUserId,
4253
            false,
4254
            $whatIfFileExists,
4255
            false,
4256
            false,
4257
            $comment,
4258
            $sessionId
4259
        );
4260
4261
        if ($filePath) {
4262
            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...
4263
                $courseInfo,
4264
                $filePath,
4265
                $sessionId
4266
            );
4267
        }
4268
4269
        return false;
4270
    }
4271
4272
    /**
4273
     * Converts wav to mp3 file.
4274
     * Requires the ffmpeg lib. In ubuntu: sudo apt-get install ffmpeg.
4275
     *
4276
     * @param string $wavFile
4277
     * @param bool   $removeWavFileIfSuccess
4278
     *
4279
     * @return bool
4280
     */
4281
    public static function convertWavToMp3($wavFile, $removeWavFileIfSuccess = false)
4282
    {
4283
        if (file_exists($wavFile)) {
4284
            try {
4285
                $ffmpeg = \FFMpeg\FFMpeg::create();
4286
                $video = $ffmpeg->open($wavFile);
4287
4288
                $mp3File = str_replace('wav', 'mp3', $wavFile);
4289
                $result = $video->save(new FFMpeg\Format\Audio\Mp3(), $mp3File);
4290
                if ($result && $removeWavFileIfSuccess) {
4291
                    unlink($wavFile);
4292
                }
4293
4294
                if (file_exists($mp3File)) {
4295
                    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...
4296
                }
4297
            } catch (Exception $e) {
4298
                error_log($e->getMessage());
4299
                error_log($e->getPrevious()->getMessage());
4300
            }
4301
        }
4302
4303
        return false;
4304
    }
4305
4306
    /**
4307
     * @param string $documentData     wav document information
4308
     * @param array  $courseInfo
4309
     * @param int    $sessionId
4310
     * @param int    $userId           user that adds the document
4311
     * @param string $whatIfFileExists
4312
     * @param bool   $deleteWavFile
4313
     *
4314
     * @return bool
4315
     */
4316
    public static function addAndConvertWavToMp3(
4317
        $documentData,
4318
        $courseInfo,
4319
        $sessionId,
4320
        $userId,
4321
        $whatIfFileExists = 'overwrite',
4322
        $deleteWavFile = false
4323
    ) {
4324
        if (empty($documentData)) {
4325
            return false;
4326
        }
4327
4328
        if (isset($documentData['absolute_path']) &&
4329
            file_exists($documentData['absolute_path'])
4330
        ) {
4331
            $mp3FilePath = self::convertWavToMp3($documentData['absolute_path']);
4332
            error_log($mp3FilePath);
4333
4334
            if (!empty($mp3FilePath) && file_exists($mp3FilePath)) {
4335
                $documentId = self::addFileToDocumentTool(
4336
                    $mp3FilePath,
4337
                    dirname($documentData['path']),
4338
                    $courseInfo,
4339
                    $sessionId,
4340
                    $userId,
4341
                    $whatIfFileExists,
4342
                    null,
4343
                    null,
4344
                    $documentData['comment']
4345
                );
4346
4347
                if (!empty($documentId)) {
4348
                    if ($deleteWavFile) {
4349
                        $coursePath = $courseInfo['directory'].'/document';
4350
                        $documentPath = api_get_path(SYS_COURSE_PATH).$coursePath;
4351
                        self::delete_document(
4352
                            $courseInfo,
4353
                            null,
4354
                            $documentPath,
4355
                            $sessionId,
4356
                            $documentData['id']
4357
                        );
4358
                    }
4359
4360
                    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...
4361
                }
4362
            }
4363
        }
4364
4365
        return false;
4366
    }
4367
4368
    /**
4369
     * Sets.
4370
     *
4371
     * @param string $file         ($document_data['path'])
4372
     * @param string $file_url_sys
4373
     *
4374
     * @return string
4375
     */
4376
    public static function generateAudioTempFile($file, $file_url_sys)
4377
    {
4378
        //make temp audio
4379
        $temp_folder = api_get_path(SYS_ARCHIVE_PATH).'temp/audio';
4380
        if (!file_exists($temp_folder)) {
4381
            @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

4381
            /** @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...
4382
        }
4383
4384
        //make htaccess with allow from all, and file index.html into temp/audio
4385
        $htaccess = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess';
4386
        if (!file_exists($htaccess)) {
4387
            $htaccess_content = "order deny,allow\r\nallow from all\r\nOptions -Indexes";
4388
            $fp = @fopen(api_get_path(SYS_ARCHIVE_PATH).'temp/audio/.htaccess', 'w');
4389
            if ($fp) {
4390
                fwrite($fp, $htaccess_content);
4391
                fclose($fp);
4392
            }
4393
        }
4394
4395
        //encript temp name file
4396
        $name_crip = sha1(uniqid()); //encript
4397
        $findext = explode(".", $file);
4398
        $extension = $findext[count($findext) - 1];
4399
        $file_crip = $name_crip.'.'.$extension;
4400
4401
        //copy file to temp/audio directory
4402
        $from_sys = $file_url_sys;
4403
        $to_sys = api_get_path(SYS_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4404
4405
        if (file_exists($from_sys)) {
4406
            copy($from_sys, $to_sys);
4407
        }
4408
4409
        // get file from tmp directory
4410
        Session::write('temp_audio_nanogong', $to_sys);
4411
4412
        return api_get_path(WEB_ARCHIVE_PATH).'temp/audio/'.$file_crip;
4413
    }
4414
4415
    /**
4416
     * Erase temp nanogong audio.
4417
     */
4418
    public static function removeGeneratedAudioTempFile()
4419
    {
4420
        $tempAudio = Session::read('temp_audio_nanogong');
4421
        if (!empty(isset($tempAudio)) && is_file($tempAudio)) {
4422
            unlink($tempAudio);
4423
            Session::erase('temp_audio_nanogong');
4424
        }
4425
    }
4426
4427
    /**
4428
     * Check if the past is used in this course.
4429
     *
4430
     * @param array  $courseInfo
4431
     * @param string $path
4432
     *
4433
     * @return array
4434
     */
4435
    public static function getDocumentByPathInCourse($courseInfo, $path)
4436
    {
4437
        $table = Database::get_course_table(TABLE_DOCUMENT);
4438
        $path = Database::escape_string($path);
4439
        $courseId = $courseInfo['real_id'];
4440
        if (empty($courseId)) {
4441
            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...
4442
        }
4443
        $sql = "SELECT * FROM $table WHERE c_id = $courseId AND path = '$path'";
4444
        $result = Database::query($sql);
4445
4446
        return Database::store_result($result, 'ASSOC');
4447
    }
4448
4449
    /**
4450
     * @param array $_course
4451
     *
4452
     * @return int
4453
     */
4454
    public static function createDefaultAudioFolder($_course)
4455
    {
4456
        if (!isset($_course['path'])) {
4457
            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...
4458
        }
4459
4460
        $audioId = null;
4461
        $path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
4462
        if (!is_dir($path.'audio')) {
4463
            mkdir($path.'audio', api_get_permissions_for_new_directories());
4464
            $audioId = add_document($_course, '/audio', 'folder', 0, 'Audio');
4465
            api_item_property_update(
4466
                $_course,
4467
                TOOL_DOCUMENT,
4468
                $audioId,
4469
                'FolderCreated',
4470
                api_get_user_id(),
4471
                null,
4472
                null,
4473
                null,
4474
                null,
4475
                api_get_session_id()
4476
            );
4477
        }
4478
4479
        return $audioId;
4480
    }
4481
4482
    /**
4483
     * Generate a default certificate for a courses.
4484
     *
4485
     * @todo move to certificate lib
4486
     *
4487
     * @global string $css CSS directory
4488
     * @global string $img_dir image directory
4489
     * @global string $default_course_dir Course directory
4490
     * @global string $js JS directory
4491
     *
4492
     * @param array $courseData     The course info
4493
     * @param bool  $fromBaseCourse
4494
     * @param int   $sessionId
4495
     */
4496
    public static function generateDefaultCertificate(
4497
        $courseData,
4498
        $fromBaseCourse = false,
4499
        $sessionId = 0
4500
    ) {
4501
        if (empty($courseData)) {
4502
            return false;
4503
        }
4504
4505
        global $css, $img_dir, $default_course_dir, $js;
4506
        $codePath = api_get_path(REL_CODE_PATH);
4507
        $dir = '/certificates';
4508
        $comment = null;
4509
        $title = get_lang('DefaultCertificate');
4510
        $fileName = api_replace_dangerous_char($title);
4511
        $filePath = api_get_path(SYS_COURSE_PATH)."{$courseData['directory']}/document$dir";
4512
4513
        if (!is_dir($filePath)) {
4514
            mkdir($filePath, api_get_permissions_for_new_directories());
4515
        }
4516
4517
        $fileFullPath = "$filePath/$fileName.html";
4518
        $fileType = 'file';
4519
        $templateContent = file_get_contents(api_get_path(SYS_CODE_PATH).'gradebook/certificate_template/template.html');
4520
4521
        $search = ['{CSS}', '{IMG_DIR}', '{REL_CODE_PATH}', '{COURSE_DIR}'];
4522
        $replace = [$css.$js, $img_dir, $codePath, $default_course_dir];
4523
4524
        $fileContent = str_replace($search, $replace, $templateContent);
4525
        $saveFilePath = "$dir/$fileName.html";
4526
4527
        if ($fromBaseCourse) {
4528
            $defaultCertificateId = self::get_default_certificate_id(
4529
                $courseData['code'],
4530
                0
4531
            );
4532
            if (!empty($defaultCertificateId)) {
4533
                // We have a certificate from the course base
4534
                $documentData = self::get_document_data_by_id(
4535
                    $defaultCertificateId,
4536
                    $courseData['code'],
4537
                    false,
4538
                    0
4539
                );
4540
4541
                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...
4542
                    $fileContent = file_get_contents($documentData['absolute_path']);
4543
                }
4544
            }
4545
        }
4546
4547
        if (file_exists($fileFullPath) === false) {
4548
            $result = file_put_contents($fileFullPath, $fileContent);
4549
            if ($result) {
4550
                $fileSize = filesize($fileFullPath);
4551
4552
                $documentId = add_document(
4553
                    $courseData,
4554
                    $saveFilePath,
4555
                    $fileType,
4556
                    $fileSize,
4557
                    $title,
4558
                    $comment,
4559
                    0, //$readonly = 0,
4560
                    true, //$save_visibility = true,
4561
                    null, //$group_id = null,
4562
                    $sessionId
4563
                );
4564
4565
                api_item_property_update(
4566
                    $courseData,
4567
                    TOOL_DOCUMENT,
4568
                    $documentId,
4569
                    'DocumentAdded',
4570
                    api_get_user_id(),
4571
                    null,
4572
                    null,
4573
                    null,
4574
                    null,
4575
                    $sessionId
4576
                );
4577
4578
                $defaultCertificateId = self::get_default_certificate_id(
4579
                    $courseData['code'],
4580
                    $sessionId
4581
                );
4582
4583
                if (!isset($defaultCertificateId)) {
4584
                    self::attach_gradebook_certificate(
4585
                        $courseData['code'],
4586
                        $documentId,
4587
                        $sessionId
4588
                    );
4589
                }
4590
            }
4591
        }
4592
    }
4593
4594
    /**
4595
     * Update the document name.
4596
     *
4597
     * @param int    $documentId The document id
4598
     * @param string $newName    The new name
4599
     */
4600
    public static function renameDocument($documentId, $newName)
4601
    {
4602
        $documentId = intval($documentId);
4603
        $newName = Database::escape_string($newName);
4604
        $docuentTable = Database::get_course_table(TABLE_DOCUMENT);
4605
4606
        $values = [
4607
            'title' => $newName,
4608
        ];
4609
4610
        $whereConditions = [
4611
            'id = ?' => $documentId,
4612
        ];
4613
4614
        Database::update($docuentTable, $values, $whereConditions);
4615
    }
4616
4617
    /**
4618
     * Get folder/file suffix.
4619
     *
4620
     * @param array $courseInfo
4621
     * @param int   $sessionId
4622
     * @param int   $groupId
4623
     *
4624
     * @return string
4625
     */
4626
    public static function getDocumentSuffix($courseInfo, $sessionId, $groupId)
4627
    {
4628
        // If no session or group, then no suffix.
4629
        if (empty($sessionId) && empty($groupId)) {
4630
            return '';
4631
        }
4632
4633
        return '__'.intval($sessionId).'__'.intval($groupId);
4634
    }
4635
4636
    /**
4637
     * Fix a document name adding session id and group id
4638
     * Turns picture.jpg -> picture__1__2.jpg
4639
     * Where 1 = session id and 2 group id
4640
     * Of session id and group id are empty then the function returns:
4641
     * picture.jpg ->  picture.jpg.
4642
     *
4643
     * @param string $name       folder or file name
4644
     * @param string $type       'folder' or 'file'
4645
     * @param array  $courseInfo
4646
     * @param int    $sessionId
4647
     * @param int    $groupId
4648
     *
4649
     * @return string
4650
     */
4651
    public static function fixDocumentName($name, $type, $courseInfo, $sessionId, $groupId)
4652
    {
4653
        $suffix = self::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4654
4655
        switch ($type) {
4656
            case 'folder':
4657
                $name = $name.$suffix;
4658
                break;
4659
            case 'file':
4660
                $name = self::addSuffixToFileName($name, $suffix);
4661
                break;
4662
        }
4663
4664
        return $name;
4665
    }
4666
4667
    /**
4668
     * Add a suffix to a file Example:
4669
     * /folder/picture.jpg => to /folder/picture_this.jpg
4670
     * where "_this" is the suffix.
4671
     *
4672
     * @param string $name
4673
     * @param string $suffix
4674
     *
4675
     * @return string
4676
     */
4677
    public static function addSuffixToFileName($name, $suffix)
4678
    {
4679
        $extension = pathinfo($name, PATHINFO_EXTENSION);
4680
        $fileName = pathinfo($name, PATHINFO_FILENAME);
4681
        $dir = pathinfo($name, PATHINFO_DIRNAME);
4682
4683
        if ($dir == '.') {
4684
            $dir = null;
4685
        }
4686
4687
        if (!empty($dir) && $dir != '/') {
4688
            $dir = $dir.'/';
4689
        }
4690
4691
        $name = $dir.$fileName.$suffix.'.'.$extension;
4692
4693
        return $name;
4694
    }
4695
4696
    /**
4697
     * Check if folder exist in the course base or in the session course.
4698
     *
4699
     * @param string $folder     Example: /folder/folder2
4700
     * @param array  $courseInfo
4701
     * @param int    $sessionId
4702
     * @param int    $groupId    group.id
4703
     *
4704
     * @return bool
4705
     */
4706
    public static function folderExists(
4707
        $folder,
4708
        $courseInfo,
4709
        $sessionId,
4710
        $groupId
4711
    ) {
4712
        $courseId = $courseInfo['real_id'];
4713
4714
        if (empty($courseId)) {
4715
            return false;
4716
        }
4717
4718
        $sessionId = intval($sessionId);
4719
        $folderWithSuffix = self::fixDocumentName(
4720
            $folder,
4721
            'folder',
4722
            $courseInfo,
4723
            $sessionId,
4724
            $groupId
4725
        );
4726
4727
        $folder = Database::escape_string($folder);
4728
        $folderWithSuffix = Database::escape_string($folderWithSuffix);
4729
4730
        // Check if pathname already exists inside document table
4731
        $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4732
        $sql = "SELECT id, path FROM $tbl_document
4733
                WHERE
4734
                    filetype = 'folder' AND
4735
                    c_id = $courseId AND
4736
                    (path = '$folder' OR path = '$folderWithSuffix') AND
4737
                    (session_id = 0 OR session_id = $sessionId)
4738
        ";
4739
4740
        $rs = Database::query($sql);
4741
        if (Database::num_rows($rs)) {
4742
            return true;
4743
        }
4744
4745
        return false;
4746
    }
4747
4748
    /**
4749
     * Check if file exist in the course base or in the session course.
4750
     *
4751
     * @param string $fileName   Example: /folder/picture.jpg
4752
     * @param array  $courseInfo
4753
     * @param int    $sessionId
4754
     * @param int    $groupId
4755
     *
4756
     * @return bool
4757
     */
4758
    public static function documentExists(
4759
        $fileName,
4760
        $courseInfo,
4761
        $sessionId,
4762
        $groupId
4763
    ) {
4764
        $courseId = $courseInfo['real_id'];
4765
4766
        if (empty($courseId)) {
4767
            return false;
4768
        }
4769
4770
        $sessionId = intval($sessionId);
4771
        $fileNameEscape = Database::escape_string($fileName);
4772
4773
        $fileNameWithSuffix = self::fixDocumentName(
4774
            $fileName,
4775
            'file',
4776
            $courseInfo,
4777
            $sessionId,
4778
            $groupId
4779
        );
4780
4781
        $fileNameWithSuffix = Database::escape_string($fileNameWithSuffix);
4782
4783
        // Check if pathname already exists inside document table
4784
        $table = Database::get_course_table(TABLE_DOCUMENT);
4785
        $sql = "SELECT id, path FROM $table
4786
                WHERE
4787
                    filetype = 'file' AND
4788
                    c_id = $courseId AND
4789
                    (
4790
                        path = '".$fileNameEscape."' OR
4791
                        path = '$fileNameWithSuffix'
4792
                    ) AND
4793
                    (session_id = 0 OR session_id = $sessionId)
4794
        ";
4795
        $rs = Database::query($sql);
4796
        if (Database::num_rows($rs)) {
4797
            return true;
4798
        }
4799
4800
        return false;
4801
    }
4802
4803
    /**
4804
     * Undo the suffix applied to a file example:
4805
     * turns picture__1__1.jpg to picture.jpg.
4806
     *
4807
     * @param string $name
4808
     * @param int    $courseId
4809
     * @param int    $sessionId
4810
     * @param int    $groupId
4811
     *
4812
     * @return string
4813
     */
4814
    public static function undoFixDocumentName(
4815
        $name,
4816
        $courseId,
4817
        $sessionId,
4818
        $groupId
4819
    ) {
4820
        if (empty($sessionId) && empty($groupId)) {
4821
            return $name;
4822
        }
4823
4824
        $suffix = self::getDocumentSuffix(
4825
            ['real_id' => $courseId],
4826
            $sessionId,
4827
            $groupId
4828
        );
4829
4830
        $name = str_replace($suffix, '', $name);
4831
4832
        return $name;
4833
    }
4834
4835
    /**
4836
     * @param string $path
4837
     * @param string $name
4838
     * @param array  $courseInfo
4839
     * @param int    $sessionId
4840
     * @param int    $groupId
4841
     *
4842
     * @return string
4843
     */
4844
    public static function getUniqueFileName($path, $name, $courseInfo, $sessionId, $groupId)
4845
    {
4846
        $counter = 1;
4847
        $filePath = $path.$name;
4848
        $uniqueName = $name;
4849
        while ($documentExists = self::documentExists(
4850
            $filePath,
4851
            $courseInfo,
4852
            $sessionId,
4853
            $groupId
4854
        )) {
4855
            $uniqueName = self::addSuffixToFileName($name, '_'.$counter);
4856
            $filePath = $path.$uniqueName;
4857
            $counter++;
4858
        }
4859
4860
        return $uniqueName;
4861
    }
4862
4863
    /**
4864
     * Builds the form that enables the user to
4865
     * select a directory to browse/upload in.
4866
     *
4867
     * @param array    An array containing the folders we want to be able to select
4868
     * @param string    The current folder (path inside of the "document" directory, including the prefix "/")
4869
     * @param string    Group directory, if empty, prevents documents to be uploaded
4870
     * (because group documents cannot be uploaded in root)
4871
     * @param bool    Whether to change the renderer (this will add a template <span>
4872
     * to the QuickForm object displaying the form)
4873
     *
4874
     * @return string html form
4875
     */
4876
    public static function build_directory_selector(
4877
        $folders,
4878
        $document_id,
4879
        $group_dir = '',
4880
        $change_renderer = false,
4881
        &$form = null,
4882
        $selectName = 'id'
4883
    ) {
4884
        $doc_table = Database::get_course_table(TABLE_DOCUMENT);
4885
        $course_id = api_get_course_int_id();
4886
        $folder_titles = [];
4887
4888
        if (is_array($folders)) {
4889
            $escaped_folders = [];
4890
            foreach ($folders as $key => &$val) {
4891
                $escaped_folders[$key] = Database::escape_string($val);
4892
            }
4893
            $folder_sql = implode("','", $escaped_folders);
4894
4895
            $sql = "SELECT path, title 
4896
                    FROM $doc_table
4897
                    WHERE 
4898
                        filetype = 'folder' AND 
4899
                        c_id = $course_id AND 
4900
                        path IN ('".$folder_sql."')";
4901
            $res = Database::query($sql);
4902
            $folder_titles = [];
4903
            while ($obj = Database::fetch_object($res)) {
4904
                $folder_titles[$obj->path] = $obj->title;
4905
            }
4906
        }
4907
4908
        $attributes = [];
4909
        if (empty($form)) {
4910
            $form = new FormValidator('selector', 'GET', api_get_self().'?'.api_get_cidreq());
4911
            $attributes = ['onchange' => 'javascript: document.selector.submit();'];
4912
        }
4913
        $form->addElement('hidden', 'cidReq', api_get_course_id());
4914
        $parent_select = $form->addSelect(
4915
            $selectName,
4916
            get_lang('CurrentDirectory'),
4917
            '',
4918
            $attributes
4919
        );
4920
4921
        // Group documents cannot be uploaded in the root
4922
        if (empty($group_dir)) {
4923
            $parent_select->addOption(get_lang('Documents'), '/');
4924
4925
            if (is_array($folders)) {
4926
                foreach ($folders as $folder_id => &$folder) {
4927
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4928
                    $path_parts = explode('/', $folder);
4929
                    $folder_titles[$folder] = cut($folder_titles[$folder], 80);
4930
                    $counter = count($path_parts) - 2;
4931
                    if ($counter > 0) {
4932
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', $counter).' &mdash; '.$folder_titles[$folder];
4933
                    } else {
4934
                        $label = ' &mdash; '.$folder_titles[$folder];
4935
                    }
4936
                    $parent_select->addOption($label, $folder_id);
4937
                    if ($selected != '') {
4938
                        $parent_select->setSelected($folder_id);
4939
                    }
4940
                }
4941
            }
4942
        } else {
4943
            if (!empty($folders)) {
4944
                foreach ($folders as $folder_id => &$folder) {
4945
                    $selected = ($document_id == $folder_id) ? ' selected="selected"' : '';
4946
                    $label = $folder_titles[$folder];
4947
                    if ($folder == $group_dir) {
4948
                        $label = get_lang('Documents');
4949
                    } else {
4950
                        $path_parts = explode('/', str_replace($group_dir, '', $folder));
4951
                        $label = cut($label, 80);
4952
                        $label = str_repeat('&nbsp;&nbsp;&nbsp;', count($path_parts) - 2).' &mdash; '.$label;
4953
                    }
4954
                    $parent_select->addOption($label, $folder_id);
4955
                    if ($selected != '') {
4956
                        $parent_select->setSelected($folder_id);
4957
                    }
4958
                }
4959
            }
4960
        }
4961
4962
        $html = $form->toHtml();
4963
4964
        return $html;
4965
    }
4966
4967
    /**
4968
     * Create a html hyperlink depending on if it's a folder or a file.
4969
     *
4970
     * @param array $document_data
4971
     * @param array $course_info
4972
     * @param bool  $show_as_icon      - if it is true, only a clickable icon will be shown
4973
     * @param int   $visibility        (1/0)
4974
     * @param int   $counter
4975
     * @param int   $size
4976
     * @param bool  $isAllowedToEdit
4977
     * @param bool  $isCertificateMode
4978
     *
4979
     * @return string url
4980
     */
4981
    public static function create_document_link(
4982
        $document_data,
4983
        $course_info,
4984
        $show_as_icon = false,
4985
        $counter = null,
4986
        $visibility,
4987
        $size = 0,
4988
        $isAllowedToEdit = false,
4989
        $isCertificateMode = false
4990
    ) {
4991
        global $dbl_click_id;
4992
4993
        $current_session_id = api_get_session_id();
4994
        $courseParams = api_get_cidreq();
4995
        $www = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document';
4996
        $webODFList = self::get_web_odf_extension_list();
4997
4998
        // Get the title or the basename depending on what we're using
4999
        if ($document_data['title'] != '') {
5000
            $title = $document_data['title'];
5001
        } else {
5002
            $title = basename($document_data['path']);
5003
        }
5004
5005
        $filetype = $document_data['filetype'];
5006
        $path = $document_data['path'];
5007
        $url_path = urlencode($document_data['path']);
5008
5009
        // Add class="invisible" on invisible files
5010
        $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...
5011
        $forcedownload_link = '';
5012
        $forcedownload_icon = '';
5013
        $prevent_multiple_click = '';
5014
        $force_download_html = '';
5015
5016
        if (!$show_as_icon) {
5017
            // Build download link (icon)
5018
            $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'];
5019
            // Folder download or file download?
5020
            $forcedownload_icon = ($filetype == 'folder') ? 'save_pack.png' : 'save.png';
5021
            // Prevent multiple clicks on zipped folder download
5022
            $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; }\"" : '';
5023
        }
5024
5025
        $target = '_self';
5026
        $is_browser_viewable_file = false;
5027
5028
        if ($filetype == 'file') {
5029
            // Check the extension
5030
            $ext = explode('.', $path);
5031
            $ext = strtolower($ext[sizeof($ext) - 1]);
5032
5033
            // HTML-files an some other types are shown in a frameset by default.
5034
            $is_browser_viewable_file = self::isBrowserViewable($ext);
5035
            if ($is_browser_viewable_file) {
5036
                if ($ext == 'pdf' || in_array($ext, $webODFList)) {
5037
                    $url = api_get_self().'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
5038
                } else {
5039
                    $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5040
                }
5041
            } else {
5042
                // url-encode for problematic characters (we may not call them dangerous characters...)
5043
                //$path = str_replace('%2F', '/', $url_path).'?'.$courseParams;
5044
                $url = $www.str_replace('%2F', '/', $url_path).'?'.$courseParams;
5045
            }
5046
        } else {
5047
            $url = api_get_self().'?'.$courseParams.'&id='.$document_data['id'];
5048
        }
5049
5050
        if ($isCertificateMode) {
5051
            $url .= '&certificate=true&selectcat='.(isset($_GET['selectcat']) ? $_GET['selectcat'] : '');
5052
        }
5053
5054
        // The little download icon
5055
        $tooltip_title = $title;
5056
        $tooltip_title_alt = $tooltip_title;
5057
5058
        if ($filetype == 'link') {
5059
            $tooltip_title_alt = $title;
5060
            $url = $document_data['comment'].'" target="_blank';
5061
        }
5062
5063
        if ($path == '/shared_folder') {
5064
            $tooltip_title_alt = get_lang('UserFolders');
5065
        } elseif (strstr($path, 'shared_folder_session_')) {
5066
            $tooltip_title_alt = get_lang('UserFolders').' ('.api_get_session_name(api_get_session_id()).')';
5067
        } elseif (strstr($tooltip_title, 'sf_user_')) {
5068
            $userinfo = api_get_user_info(substr($tooltip_title, 8));
5069
            $tooltip_title_alt = get_lang('UserFolder').' '.$userinfo['complete_name'];
5070
        } elseif ($path == '/chat_files') {
5071
            $tooltip_title_alt = get_lang('ChatFiles');
5072
        } elseif ($path == '/learning_path') {
5073
            $tooltip_title_alt = get_lang('LearningPaths');
5074
        } elseif ($path == '/video') {
5075
            $tooltip_title_alt = get_lang('Video');
5076
        } elseif ($path == '/audio') {
5077
            $tooltip_title_alt = get_lang('Audio');
5078
        } elseif ($path == '/flash') {
5079
            $tooltip_title_alt = get_lang('Flash');
5080
        } elseif ($path == '/images') {
5081
            $tooltip_title_alt = get_lang('Images');
5082
        } elseif ($path == '/images/gallery') {
5083
            $tooltip_title_alt = get_lang('DefaultCourseImages');
5084
        }
5085
5086
        $copyToMyFiles = $open_in_new_window_link = '';
5087
        $curdirpath = isset($_GET['curdirpath']) ? Security::remove_XSS($_GET['curdirpath']) : null;
5088
        $send_to = null;
5089
        $checkExtension = $path;
5090
5091
        if (!$show_as_icon) {
5092
            if ($filetype == 'folder') {
5093
                if ($isAllowedToEdit ||
5094
                    api_is_platform_admin() ||
5095
                    api_get_setting('students_download_folders') == 'true'
5096
                ) {
5097
                    // filter: when I am into a shared folder, I can only show "my shared folder" for donwload
5098
                    if (self::is_shared_folder($curdirpath, $current_session_id)) {
5099
                        if (preg_match('/shared_folder\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5100
                            preg_match('/shared_folder_session_'.$current_session_id.'\/sf_user_'.api_get_user_id().'$/', urldecode($forcedownload_link)) ||
5101
                            $isAllowedToEdit || api_is_platform_admin()
5102
                        ) {
5103
                            $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5104
                                Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5105
                        }
5106
                    } elseif (!preg_match('/shared_folder/', urldecode($forcedownload_link)) ||
5107
                        $isAllowedToEdit ||
5108
                        api_is_platform_admin()
5109
                    ) {
5110
                        $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5111
                            Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5112
                    }
5113
                }
5114
            } else {
5115
                $force_download_html = ($size == 0) ? '' : '<a href="'.$forcedownload_link.'" style="float:right"'.$prevent_multiple_click.' download="'.$document_data['basename'].'">'.
5116
                    Display::return_icon($forcedownload_icon, get_lang('Download'), [], ICON_SIZE_SMALL).'</a>';
5117
            }
5118
5119
            // Copy files to user's myfiles
5120
            if (api_get_setting('allow_my_files') === 'true' &&
5121
                api_get_setting('users_copy_files') === 'true' && api_is_anonymous() === false
5122
            ) {
5123
                $copy_myfiles_link = $filetype == 'file' ? api_get_self().'?'.$courseParams.'&action=copytomyfiles&id='.$document_data['id'] : api_get_self().'?'.$courseParams;
5124
                if ($filetype == 'file') {
5125
                    $copyToMyFiles = '<a href="'.$copy_myfiles_link.'" style="float:right"'.$prevent_multiple_click.'>'.
5126
                        Display::return_icon('briefcase.png', get_lang('CopyToMyFiles'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5127
5128
                    if (api_get_setting('allow_my_files') === 'false') {
5129
                        $copyToMyFiles = '';
5130
                    }
5131
                }
5132
            }
5133
5134
            $pdf_icon = '';
5135
            $extension = pathinfo($path, PATHINFO_EXTENSION);
5136
            if (!$isAllowedToEdit &&
5137
                api_get_setting('students_export2pdf') == 'true' &&
5138
                $filetype == 'file' &&
5139
                in_array($extension, ['html', 'htm'])
5140
            ) {
5141
                $pdf_icon = ' <a style="float:right".'.$prevent_multiple_click.' href="'.api_get_self().'?'.$courseParams.'&action=export_to_pdf&id='.$document_data['id'].'&curdirpath='.$curdirpath.'">'.
5142
                    Display::return_icon('pdf.png', get_lang('Export2PDF'), [], ICON_SIZE_SMALL).'</a> ';
5143
            }
5144
5145
            if ($is_browser_viewable_file) {
5146
                $open_in_new_window_link = '<a href="'.$www.str_replace('%2F', '/', $url_path).'?'.$courseParams.'" style="float:right"'.$prevent_multiple_click.' target="_blank">'.
5147
                    Display::return_icon('open_in_new_window.png', get_lang('OpenInANewWindow'), [], ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
5148
            }
5149
5150
            if ($filetype == 'file') {
5151
                // Sound preview with jplayer
5152
                if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5153
                    (preg_match('/wav$/i', urldecode($checkExtension))) ||
5154
                    preg_match('/ogg$/i', urldecode($checkExtension))
5155
                ) {
5156
                    return '<span style="float:left" '.$visibility_class.'>'.
5157
                    $title.
5158
                    '</span>'.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5159
                } elseif (
5160
                    // Show preview
5161
                    preg_match('/swf$/i', urldecode($checkExtension)) ||
5162
                    preg_match('/png$/i', urldecode($checkExtension)) ||
5163
                    preg_match('/gif$/i', urldecode($checkExtension)) ||
5164
                    preg_match('/jpg$/i', urldecode($checkExtension)) ||
5165
                    preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5166
                    preg_match('/bmp$/i', urldecode($checkExtension)) ||
5167
                    preg_match('/svg$/i', urldecode($checkExtension))
5168
                ) {
5169
                    // Simpler version of showinframesmin.php with no headers
5170
                    $url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
5171
                    $class = 'ajax';
5172
                    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...
5173
                        $class = "ajax text-muted";
5174
                    }
5175
5176
                    return Display::url(
5177
                        $title,
5178
                        $url,
5179
                        [
5180
                            'class' => $class,
5181
                            'title' => $tooltip_title_alt,
5182
                            'data-title' => $title,
5183
                            'style' => 'float:left;',
5184
                        ]
5185
                    )
5186
                    .$force_download_html.$send_to.$copyToMyFiles
5187
                    .$open_in_new_window_link.$pdf_icon;
5188
                } else {
5189
                    // For a "PDF Download" of the file.
5190
                    $pdfPreview = null;
5191
                    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...
5192
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5193
                    } else {
5194
                        $pdfPreview = Display::url(
5195
                            Display::return_icon('preview.png', get_lang('Preview'), null, ICON_SIZE_SMALL),
5196
                            api_get_path(WEB_CODE_PATH).'document/showinframes.php?'.$courseParams.'&id='.$document_data['id'],
5197
                            ['style' => 'float:right']
5198
                        );
5199
                    }
5200
                    // No plugin just the old and good showinframes.php page
5201
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" style="float:left" '.$visibility_class.' >'.$title.'</a>'.
5202
                    $pdfPreview.$force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5203
                }
5204
            } else {
5205
                return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.$title.'</a>'.
5206
                $force_download_html.$send_to.$copyToMyFiles.$open_in_new_window_link.$pdf_icon;
5207
            }
5208
            // end copy files to users myfiles
5209
        } else {
5210
            // Icon column
5211
            if (preg_match('/shared_folder/', urldecode($checkExtension)) &&
5212
                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...
5213
                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...
5214
            ) {
5215
                if ($filetype == 'file') {
5216
                    //Sound preview with jplayer
5217
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5218
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5219
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5220
                        $sound_preview = self::generate_media_preview($counter);
5221
5222
                        return $sound_preview;
5223
                    } elseif (
5224
                        // Show preview
5225
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5226
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5227
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5228
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5229
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5230
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5231
                        preg_match('/svg$/i', urldecode($checkExtension))
5232
                    ) {
5233
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
5234
5235
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5236
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5237
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5238
                        '</a>';
5239
                    } else {
5240
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5241
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5242
                            Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5243
                        '</a>';
5244
                    }
5245
                } else {
5246
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5247
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5248
                        Display::return_icon('shared.png', get_lang('ResourceShared'), []).
5249
                    '</a>';
5250
                }
5251
            } else {
5252
                if ($filetype == 'file') {
5253
                    // Sound preview with jplayer
5254
                    if (preg_match('/mp3$/i', urldecode($checkExtension)) ||
5255
                        (preg_match('/wav$/i', urldecode($checkExtension))) ||
5256
                        preg_match('/ogg$/i', urldecode($checkExtension))) {
5257
                        $sound_preview = self::generate_media_preview($counter);
5258
5259
                        return $sound_preview;
5260
                    } elseif (
5261
                        //Show preview
5262
                        preg_match('/html$/i', urldecode($checkExtension)) ||
5263
                        preg_match('/htm$/i', urldecode($checkExtension)) ||
5264
                        preg_match('/swf$/i', urldecode($checkExtension)) ||
5265
                        preg_match('/png$/i', urldecode($checkExtension)) ||
5266
                        preg_match('/gif$/i', urldecode($checkExtension)) ||
5267
                        preg_match('/jpg$/i', urldecode($checkExtension)) ||
5268
                        preg_match('/jpeg$/i', urldecode($checkExtension)) ||
5269
                        preg_match('/bmp$/i', urldecode($checkExtension)) ||
5270
                        preg_match('/svg$/i', urldecode($checkExtension))
5271
                    ) {
5272
                        $url = 'showinframes.php?'.$courseParams.'&id='.$document_data['id']; //without preview
5273
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5274
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5275
                        '</a>';
5276
                    } else {
5277
                        return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" '.$visibility_class.' style="float:left">'.
5278
                            self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5279
                        '</a>';
5280
                    }
5281
                } else {
5282
                    return '<a href="'.$url.'" title="'.$tooltip_title_alt.'" target="'.$target.'"'.$visibility_class.' style="float:left">'.
5283
                        self::build_document_icon_tag($filetype, $path, $isAllowedToEdit).
5284
                    '</a>';
5285
                }
5286
            }
5287
        }
5288
    }
5289
5290
    /**
5291
     * Builds an img html tag for the file type.
5292
     *
5293
     * @param string $type            (file/folder)
5294
     * @param string $path
5295
     * @param bool   $isAllowedToEdit
5296
     *
5297
     * @return string img html tag
5298
     */
5299
    public static function build_document_icon_tag($type, $path, $isAllowedToEdit = null)
5300
    {
5301
        $basename = basename($path);
5302
        $current_session_id = api_get_session_id();
5303
        if (is_null($isAllowedToEdit)) {
5304
            $isAllowedToEdit = api_is_allowed_to_edit(null, true);
5305
        }
5306
        $user_image = false;
5307
        if ($type == 'file') {
5308
            $icon = choose_image($basename);
5309
            $basename = substr(strrchr($basename, '.'), 1);
5310
        } elseif ($type == 'link') {
5311
            $icon = 'clouddoc.png';
5312
            $basename = get_lang('CloudFileLink');
5313
        } else {
5314
            if ($path == '/shared_folder') {
5315
                $icon = 'folder_users.png';
5316
                if ($isAllowedToEdit) {
5317
                    $basename = get_lang('HelpUsersFolder');
5318
                } else {
5319
                    $basename = get_lang('UserFolders');
5320
                }
5321
            } elseif (strstr($basename, 'sf_user_')) {
5322
                $userInfo = api_get_user_info(substr($basename, 8));
5323
                $icon = $userInfo['avatar_small'];
5324
                $basename = get_lang('UserFolder').' '.$userInfo['complete_name'];
5325
                $user_image = true;
5326
            } elseif (strstr($path, 'shared_folder_session_')) {
5327
                $sessionName = api_get_session_name($current_session_id);
5328
                if ($isAllowedToEdit) {
5329
                    $basename = '***('.$sessionName.')*** '.get_lang('HelpUsersFolder');
5330
                } else {
5331
                    $basename = get_lang('UserFolders').' ('.$sessionName.')';
5332
                }
5333
                $icon = 'folder_users.png';
5334
            } else {
5335
                $icon = 'folder_document.png';
5336
5337
                if ($path == '/audio') {
5338
                    $icon = 'folder_audio.png';
5339
                    if ($isAllowedToEdit) {
5340
                        $basename = get_lang('HelpDefaultDirDocuments');
5341
                    } else {
5342
                        $basename = get_lang('Audio');
5343
                    }
5344
                } elseif ($path == '/flash') {
5345
                    $icon = 'folder_flash.png';
5346
                    if ($isAllowedToEdit) {
5347
                        $basename = get_lang('HelpDefaultDirDocuments');
5348
                    } else {
5349
                        $basename = get_lang('Flash');
5350
                    }
5351
                } elseif ($path == '/images') {
5352
                    $icon = 'folder_images.png';
5353
                    if ($isAllowedToEdit) {
5354
                        $basename = get_lang('HelpDefaultDirDocuments');
5355
                    } else {
5356
                        $basename = get_lang('Images');
5357
                    }
5358
                } elseif ($path == '/video') {
5359
                    $icon = 'folder_video.png';
5360
                    if ($isAllowedToEdit) {
5361
                        $basename = get_lang('HelpDefaultDirDocuments');
5362
                    } else {
5363
                        $basename = get_lang('Video');
5364
                    }
5365
                } elseif ($path == '/images/gallery') {
5366
                    $icon = 'folder_gallery.png';
5367
                    if ($isAllowedToEdit) {
5368
                        $basename = get_lang('HelpDefaultDirDocuments');
5369
                    } else {
5370
                        $basename = get_lang('Gallery');
5371
                    }
5372
                } elseif ($path == '/chat_files') {
5373
                    $icon = 'folder_chat.png';
5374
                    if ($isAllowedToEdit) {
5375
                        $basename = get_lang('HelpFolderChat');
5376
                    } else {
5377
                        $basename = get_lang('ChatFiles');
5378
                    }
5379
                } elseif ($path == '/learning_path') {
5380
                    $icon = 'folder_learningpath.png';
5381
                    if ($isAllowedToEdit) {
5382
                        $basename = get_lang('HelpFolderLearningPaths');
5383
                    } else {
5384
                        $basename = get_lang('LearningPaths');
5385
                    }
5386
                }
5387
            }
5388
        }
5389
5390
        if ($user_image) {
5391
            return Display::img($icon, $basename, [], false);
5392
        }
5393
5394
        return Display::return_icon($icon, $basename, [], ICON_SIZE_SMALL);
5395
    }
5396
5397
    /**
5398
     * Creates the row of edit icons for a file/folder.
5399
     *
5400
     * @param array $document_data
5401
     * @param int   $id
5402
     * @param bool  $is_template
5403
     * @param int   $is_read_only
5404
     * @param int   $visibility    (1/0)
5405
     *
5406
     * @return string html img tags with hyperlinks
5407
     */
5408
    public static function build_edit_icons($document_data, $id, $is_template, $is_read_only = 0, $visibility)
5409
    {
5410
        $sessionId = api_get_session_id();
5411
        $courseParams = api_get_cidreq();
5412
        $document_id = $document_data['id'];
5413
        $type = $document_data['filetype'];
5414
        $is_read_only = $document_data['readonly'];
5415
        $path = $document_data['path'];
5416
5417
        if ($type == 'link') {
5418
            $parent_id = self::get_document_id(
5419
                api_get_course_info(),
5420
                rtrim($path, '/'),
5421
                0
5422
            );
5423
        } else {
5424
            $parent_id = self::get_document_id(
5425
                api_get_course_info(),
5426
                dirname($path),
5427
                0
5428
            );
5429
        }
5430
5431
        if (empty($parent_id) && !empty($sessionId)) {
5432
            $parent_id = self::get_document_id(
5433
                api_get_course_info(),
5434
                dirname($path),
5435
                $sessionId
5436
            );
5437
        }
5438
5439
        $curdirpath = dirname($document_data['path']);
5440
        $is_certificate_mode = self::is_certificate_mode($path);
5441
        $curdirpath = urlencode($curdirpath);
5442
        $extension = pathinfo($path, PATHINFO_EXTENSION);
5443
        //@todo Implement remote support for converter
5444
        $usePpt2lp = api_get_setting('service_ppt2lp', 'active') == 'true' && api_get_setting('service_ppt2lp', 'host') == 'localhost';
5445
        $formatTypeList = self::getFormatTypeListConvertor('from', $extension);
5446
        $formatType = current($formatTypeList);
5447
5448
        // If document is read only *or* we're in a session and the document
5449
        // is from a non-session context, hide the edition capabilities
5450
        $modify_icons = [];
5451
        $modify_icons[] = self::getButtonEdit($is_read_only, $document_data, $extension, $is_certificate_mode);
5452
        $modify_icons[] = self::getButtonMove($is_read_only, $document_data, $is_certificate_mode, $parent_id);
5453
        $modify_icons[] = self::getButtonVisibility(
5454
            $is_read_only,
5455
            $visibility,
5456
            $document_data,
5457
            $is_certificate_mode,
5458
            $parent_id
5459
        );
5460
        $modify_icons[] = self::getButtonDelete(
5461
            $is_read_only,
5462
            $document_data,
5463
            $is_certificate_mode,
5464
            $curdirpath,
5465
            $parent_id
5466
        );
5467
5468
        if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
5469
            // Add action to covert to PDF, will create a new document whit same filename but .pdf extension
5470
            // @TODO: add prompt to select a format target
5471
            if (!in_array($path, self::get_system_folders())) {
5472
                if ($usePpt2lp && $formatType) {
5473
                    $modify_icons[] = Display::url(
5474
                        Display::return_icon('convert.png', get_lang('Convert')),
5475
                        '#',
5476
                        ['class' => 'convertAction', 'data-documentId' => $document_id, 'data-formatType' => $formatType]
5477
                    );
5478
                }
5479
            }
5480
        }
5481
5482
        if ($type == 'file' && ($extension == 'html' || $extension == 'htm')) {
5483
            if ($is_template == 0) {
5484
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] != '/certificates') || !isset($_GET['curdirpath'])) {
5485
                    $modify_icons[] = Display::url(
5486
                        Display::return_icon('wizard.png', get_lang('AddAsTemplate')),
5487
                        api_get_self()."?$courseParams&curdirpath=$curdirpath&add_as_template=$id"
5488
                    );
5489
                }
5490
                if ((isset($_GET['curdirpath']) && $_GET['curdirpath'] == '/certificates') || $is_certificate_mode) {//allow attach certificate to course
5491
                    $visibility_icon_certificate = 'nocertificate';
5492
                    if (self::get_default_certificate_id(api_get_course_id()) == $id) {
5493
                        $visibility_icon_certificate = 'certificate';
5494
                        $certificate = get_lang('DefaultCertificate');
5495
                        $preview = get_lang('PreviewCertificate');
5496
                        $is_preview = true;
5497
                    } else {
5498
                        $is_preview = false;
5499
                        $certificate = get_lang('NoDefaultCertificate');
5500
                    }
5501
                    if (isset($_GET['selectcat'])) {
5502
                        $modify_icons[] = Display::url(
5503
                            Display::return_icon($visibility_icon_certificate.'.png', $certificate),
5504
                            api_get_self()."?$courseParams&curdirpath=$curdirpath&selectcat=".intval($_GET['selectcat'])."&set_certificate=$id"
5505
                        );
5506
                        if ($is_preview) {
5507
                            $modify_icons[] = Display::url(
5508
                                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...
5509
                                api_get_self()."?$courseParams&curdirpath=$curdirpath&set_preview=$id"
5510
                            );
5511
                        }
5512
                    }
5513
                }
5514
            } else {
5515
                $modify_icons[] = Display::url(
5516
                    Display::return_icon('wizard_na.png', get_lang('RemoveAsTemplate')),
5517
                    api_get_self()."?$courseParams&curdirpath=$curdirpath&remove_as_template=$id"
5518
                );
5519
            }
5520
5521
            $modify_icons[] = Display::url(
5522
                Display::return_icon('pdf.png', get_lang('Export2PDF')),
5523
                api_get_self()."?$courseParams&action=export_to_pdf&id=$id&curdirpath=$curdirpath"
5524
            );
5525
        }
5526
5527
        return implode(PHP_EOL, $modify_icons);
5528
    }
5529
5530
    /**
5531
     * @param $folders
5532
     * @param $curdirpath
5533
     * @param $move_file
5534
     * @param string $group_dir
5535
     *
5536
     * @return string
5537
     */
5538
    public static function build_move_to_selector($folders, $curdirpath, $move_file, $group_dir = '')
5539
    {
5540
        $form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
5541
5542
        // Form title
5543
        $form->addHidden('move_file', $move_file);
5544
5545
        $options = [];
5546
5547
        // Group documents cannot be uploaded in the root
5548
        if ($group_dir == '') {
5549
            if ($curdirpath != '/') {
5550
                $options['/'] = get_lang('Documents');
5551
            }
5552
5553
            if (is_array($folders)) {
5554
                foreach ($folders as &$folder) {
5555
                    // Hide some folders
5556
                    if ($folder == '/HotPotatoes_files' ||
5557
                        $folder == '/certificates' ||
5558
                        basename($folder) == 'css'
5559
                    ) {
5560
                        continue;
5561
                    }
5562
                    // Admin setting for Hide/Show the folders of all users
5563
                    if (api_get_setting('show_users_folders') == 'false' &&
5564
                        (strstr($folder, '/shared_folder') || strstr($folder, 'shared_folder_session_'))
5565
                    ) {
5566
                        continue;
5567
                    }
5568
5569
                    // Admin setting for Hide/Show Default folders to all users
5570
                    if (api_get_setting('show_default_folders') == 'false' &&
5571
                        (
5572
                            $folder == '/images' ||
5573
                            $folder == '/flash' ||
5574
                            $folder == '/audio' ||
5575
                            $folder == '/video' ||
5576
                            strstr($folder, '/images/gallery') ||
5577
                            $folder == '/video/flv'
5578
                        )
5579
                    ) {
5580
                        continue;
5581
                    }
5582
5583
                    // Admin setting for Hide/Show chat history folder
5584
                    if (api_get_setting('show_chat_folder') == 'false' &&
5585
                        $folder == '/chat_files') {
5586
                        continue;
5587
                    }
5588
5589
                    // You cannot move a file to:
5590
                    // 1. current directory
5591
                    // 2. inside the folder you want to move
5592
                    // 3. inside a subfolder of the folder you want to move
5593
                    if (($curdirpath != $folder) &&
5594
                        ($folder != $move_file) &&
5595
                        (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5596
                    ) {
5597
                        $path_displayed = $folder;
5598
                        // If document title is used, we have to display titles instead of real paths...
5599
                        $path_displayed = self::get_titles_of_path($folder);
5600
5601
                        if (empty($path_displayed)) {
5602
                            $path_displayed = get_lang('Untitled');
5603
                        }
5604
                        $options[$folder] = $path_displayed;
5605
                    }
5606
                }
5607
            }
5608
        } else {
5609
            foreach ($folders as $folder) {
5610
                if (($curdirpath != $folder) &&
5611
                    ($folder != $move_file) &&
5612
                    (substr($folder, 0, strlen($move_file) + 1) != $move_file.'/')
5613
                ) {
5614
                    // Cannot copy dir into his own subdir
5615
                    $path_displayed = self::get_titles_of_path($folder);
5616
                    $display_folder = substr($path_displayed, strlen($group_dir));
5617
                    $display_folder = ($display_folder == '') ? get_lang('Documents') : $display_folder;
5618
                    //$form .= '<option value="'.$folder.'">'.$display_folder.'</option>';
5619
                    $options[$folder] = $display_folder;
5620
                }
5621
            }
5622
        }
5623
        $form->addElement('select', 'move_to', get_lang('MoveTo'), $options);
5624
        $form->addButtonNext(get_lang('MoveElement'), 'move_file_submit');
5625
5626
        return $form->returnForm();
5627
    }
5628
5629
    /**
5630
     * Gets the path translated with title of docs and folders.
5631
     *
5632
     * @param string $path the real path
5633
     *
5634
     * @return the path which should be displayed
5635
     */
5636
    public static function get_titles_of_path($path)
5637
    {
5638
        global $tmp_folders_titles;
5639
        $course_id = api_get_course_int_id();
5640
        $nb_slashes = substr_count($path, '/');
5641
        $current_slash_pos = 0;
5642
        $path_displayed = '';
5643
        for ($i = 0; $i < $nb_slashes; $i++) {
5644
            // For each folder of the path, retrieve title.
5645
            $current_slash_pos = strpos($path, '/', $current_slash_pos + 1);
5646
            $tmp_path = substr($path, strpos($path, '/', 0), $current_slash_pos);
5647
5648
            if (empty($tmp_path)) {
5649
                // If empty, then we are in the final part of the path
5650
                $tmp_path = $path;
5651
            }
5652
5653
            if (!empty($tmp_folders_titles[$tmp_path])) {
5654
                // If this path has soon been stored here we don't need a new query
5655
                $path_displayed .= $tmp_folders_titles[$tmp_path];
5656
            } else {
5657
                $sql = 'SELECT title FROM '.Database::get_course_table(TABLE_DOCUMENT).'
5658
                        WHERE c_id = '.$course_id.' AND path LIKE BINARY "'.$tmp_path.'"';
5659
                $rs = Database::query($sql);
5660
                $tmp_title = '/'.Database::result($rs, 0, 0);
5661
                $path_displayed .= $tmp_title;
5662
                $tmp_folders_titles[$tmp_path] = $tmp_title;
5663
            }
5664
        }
5665
5666
        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...
5667
    }
5668
5669
    /**
5670
     * Creates form that asks for the directory name.
5671
     *
5672
     * @return string html-output text for the form
5673
     */
5674
    public static function create_dir_form($dirId)
5675
    {
5676
        global $document_id;
5677
        $form = new FormValidator('create_dir_form', 'post', api_get_self().'?'.api_get_cidreq());
5678
        $form->addElement('hidden', 'create_dir', 1);
5679
        $form->addElement('hidden', 'dir_id', intval($document_id));
5680
        $form->addElement('hidden', 'id', intval($dirId));
5681
        $form->addElement('header', get_lang('CreateDir'));
5682
        $form->addText('dirname', get_lang('NewDir'), ['autofocus' => 'autofocus']);
5683
        $form->addButtonCreate(get_lang('CreateFolder'));
5684
5685
        return $form->returnForm();
5686
    }
5687
5688
    /**
5689
     * Checks whether the user is in shared folder.
5690
     *
5691
     * @param string $curdirpath
5692
     * @param int    $current_session_id
5693
     *
5694
     * @return bool Return true when user is into shared folder
5695
     */
5696
    public static function is_shared_folder($curdirpath, $current_session_id)
5697
    {
5698
        $clean_curdirpath = Security::remove_XSS($curdirpath);
5699
        if ($clean_curdirpath == '/shared_folder') {
5700
            return true;
5701
        } elseif ($clean_curdirpath == '/shared_folder_session_'.$current_session_id) {
5702
            return true;
5703
        } else {
5704
            return false;
5705
        }
5706
    }
5707
5708
    /**
5709
     * Checks whether the user is into any user shared folder.
5710
     *
5711
     * @param string $path
5712
     * @param int    $sessionId
5713
     *
5714
     * @return bool Return true when user is in any user shared folder
5715
     */
5716
    public static function is_any_user_shared_folder($path, $sessionId)
5717
    {
5718
        $clean_path = Security::remove_XSS($path);
5719
        if (strpos($clean_path, 'shared_folder/sf_user_')) {
5720
            return true;
5721
        } elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
5722
            return true;
5723
        } else {
5724
            return false;
5725
        }
5726
    }
5727
5728
    /**
5729
     * Create users shared folder for course.
5730
     *
5731
     * @param int   $userId
5732
     * @param array $courseInfo
5733
     * @param int   $sessionId
5734
     */
5735
    public static function createUserSharedFolder($userId, array $courseInfo, $sessionId = 0)
5736
    {
5737
        $documentDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
5738
        $userInfo = api_get_user_info($userId);
5739
5740
        if (!$sessionId) {
5741
            //Create shared folder. Necessary for recycled courses.
5742
            if (!file_exists($documentDirectory.'/shared_folder')) {
5743
                create_unexisting_directory(
5744
                    $courseInfo,
5745
                    $userId,
5746
                    0,
5747
                    0,
5748
                    0,
5749
                    $documentDirectory,
5750
                    '/shared_folder',
5751
                    get_lang('UserFolders'),
5752
                    0
5753
                );
5754
            }
5755
            // Create dynamic user shared folder
5756
            if (!file_exists($documentDirectory.'/shared_folder/sf_user_'.$userId)) {
5757
                create_unexisting_directory(
5758
                    $courseInfo,
5759
                    $userId,
5760
                    0,
5761
                    0,
5762
                    0,
5763
                    $documentDirectory,
5764
                    '/shared_folder/sf_user_'.$userId,
5765
                    $userInfo['complete_name'],
5766
                    1
5767
                );
5768
            }
5769
5770
            return;
5771
        }
5772
5773
        // Create shared folder session.
5774
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId)) {
5775
            create_unexisting_directory(
5776
                $courseInfo,
5777
                api_get_user_id(),
5778
                $sessionId,
5779
                0,
5780
                0,
5781
                $documentDirectory,
5782
                '/shared_folder_session_'.$sessionId,
5783
                get_lang('UserFolders').' ('.api_get_session_name($sessionId).')',
5784
                0
5785
            );
5786
        }
5787
        //Create dynamic user shared folder into a shared folder session
5788
        if (!file_exists($documentDirectory.'/shared_folder_session_'.$sessionId.'/sf_user_'.$userId)) {
5789
            create_unexisting_directory(
5790
                $courseInfo,
5791
                $userId,
5792
                $sessionId,
5793
                0,
5794
                0,
5795
                $documentDirectory,
5796
                '/shared_folder_session_'.$sessionId.'/sf_user_'.$userId,
5797
                $userInfo['complete_name'].'('.api_get_session_name($sessionId).')',
5798
                1
5799
            );
5800
        }
5801
    }
5802
5803
    /**
5804
     * Checks whether the user is into his shared folder or into a subfolder.
5805
     *
5806
     * @param int    $user_id
5807
     * @param string $path
5808
     * @param int    $sessionId
5809
     *
5810
     * @return bool Return true when user is in his user shared folder or into a subfolder
5811
     */
5812
    public static function is_my_shared_folder($user_id, $path, $sessionId)
5813
    {
5814
        $clean_path = Security::remove_XSS($path).'/';
5815
        //for security does not remove the last slash
5816
        $main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
5817
        //for security does not remove the last slash
5818
        $main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
5819
5820
        if (preg_match($main_user_shared_folder, $clean_path)) {
5821
            return true;
5822
        } elseif (preg_match($main_user_shared_folder_session, $clean_path)) {
5823
            return true;
5824
        } else {
5825
            return false;
5826
        }
5827
    }
5828
5829
    /**
5830
     * Check if the file name or folder searched exist.
5831
     *
5832
     * @return bool Return true when exist
5833
     */
5834
    public static function search_keyword($document_name, $keyword)
5835
    {
5836
        if (api_strripos($document_name, $keyword) !== false) {
5837
            return true;
5838
        } else {
5839
            return false;
5840
        }
5841
    }
5842
5843
    /**
5844
     * Checks whether a document can be previewed by using the browser.
5845
     *
5846
     * @param string $file_extension the filename extension of the document (it must be in lower case)
5847
     *
5848
     * @return bool returns TRUE or FALSE
5849
     */
5850
    public static function isBrowserViewable($file_extension)
5851
    {
5852
        static $allowed_extensions = [
5853
            'htm', 'html', 'xhtml',
5854
            'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff',
5855
            'pdf', 'svg', 'swf',
5856
            'txt', 'log',
5857
            'mp4', 'ogg', 'ogv', 'ogx', 'mpg', 'mpeg', 'mov', 'avi', 'webm', 'wmv',
5858
            'mp3', 'oga', 'wav', 'au', 'wma', 'mid', 'kar',
5859
        ];
5860
5861
        /*
5862
          //TODO: make a admin switch to strict mode
5863
          1. global default $allowed_extensions
5864
          if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
5865
          return true;
5866
          }
5867
          2. check native support
5868
          3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
5869
         */
5870
5871
        if (!($result = in_array($file_extension, $allowed_extensions))) {
5872
            // Assignment + a logical check.
5873
            return false;
5874
        }
5875
5876
        //check native support (Explorer, Opera, Firefox, Chrome, Safari)
5877
        if ($file_extension == "pdf") {
5878
            return api_browser_support('pdf');
5879
        } elseif ($file_extension == "mp3") {
5880
            return api_browser_support('mp3');
5881
        } elseif ($file_extension == "mp4") {
5882
            return api_browser_support('mp4');
5883
        } elseif ($file_extension == "ogg" || $file_extension == "ogx" || $file_extension == "ogv" || $file_extension == "oga") {
5884
            return api_browser_support('ogg');
5885
        } elseif ($file_extension == "svg") {
5886
            return api_browser_support('svg');
5887
        } elseif ($file_extension == "mpg" || $file_extension == "mpeg") {
5888
            return api_browser_support('mpg');
5889
        } elseif ($file_extension == "mov") {
5890
            return api_browser_support('mov');
5891
        } elseif ($file_extension == "wav") {
5892
            return api_browser_support('wav');
5893
        } elseif ($file_extension == "mid" || $file_extension == "kar") {
5894
            return api_browser_support('mid');
5895
        } elseif ($file_extension == "avi") {
5896
            return api_browser_support('avi');
5897
        } elseif ($file_extension == "wma") {
5898
            return api_browser_support('wma');
5899
        } elseif ($file_extension == "wmv") {
5900
            return api_browser_support('wmv');
5901
        } elseif ($file_extension == "tif" || $file_extension == "tiff") {
5902
            return api_browser_support('tif');
5903
        } elseif ($file_extension == "mov") {
5904
            return api_browser_support('mov');
5905
        } elseif ($file_extension == "au") {
5906
            return api_browser_support('au');
5907
        } elseif ($file_extension == "webm") {
5908
            return api_browser_support('webm');
5909
        }
5910
5911
        return $result;
5912
    }
5913
5914
    /**
5915
     * @param array $courseInfo
5916
     * @param int   $sessionId
5917
     *
5918
     * @return array
5919
     */
5920
    public static function getDeletedDocuments($courseInfo, $sessionId = 0)
5921
    {
5922
        $table = Database::get_course_table(TABLE_DOCUMENT);
5923
        $courseId = $courseInfo['real_id'];
5924
        $sessionCondition = api_get_session_condition($sessionId);
5925
        $sql = "SELECT * FROM $table
5926
                WHERE
5927
                  path LIKE '%DELETED%' AND
5928
                  c_id = $courseId
5929
                  $sessionCondition
5930
                ORDER BY path
5931
        ";
5932
5933
        $result = Database::query($sql);
5934
        $files = [];
5935
        while ($document = Database::fetch_array($result, 'ASSOC')) {
5936
            $files[] = $document;
5937
        }
5938
5939
        return $files;
5940
    }
5941
5942
    /**
5943
     * @param int   $id
5944
     * @param array $courseInfo
5945
     * @param int   $sessionId
5946
     *
5947
     * @return array
5948
     */
5949
    public static function getDeletedDocument($id, $courseInfo, $sessionId = 0)
5950
    {
5951
        if (empty($courseInfo)) {
5952
            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...
5953
        }
5954
5955
        $table = Database::get_course_table(TABLE_DOCUMENT);
5956
        $courseId = $courseInfo['real_id'];
5957
        $sessionCondition = api_get_session_condition($sessionId);
5958
        $sql = "SELECT * FROM $table
5959
                WHERE
5960
                  path LIKE '%DELETED%' AND
5961
                  id = $id AND
5962
                  c_id = $courseId
5963
                  $sessionCondition
5964
                LIMIT 1
5965
        ";
5966
        $result = Database::query($sql);
5967
        if (Database::num_rows($result)) {
5968
            $result = Database::fetch_array($result, 'ASSOC');
5969
5970
            return $result;
5971
        }
5972
5973
        return [];
5974
    }
5975
5976
    /**
5977
     * @param int   $id
5978
     * @param array $courseInfo
5979
     * @param int   $sessionId
5980
     *
5981
     * @return bool
5982
     */
5983
    public static function purgeDocument($id, $courseInfo, $sessionId = 0)
5984
    {
5985
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
5986
        if (!empty($document)) {
5987
            $path = $document['path'];
5988
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
5989
            my_delete($coursePath.$path);
5990
            // Hard delete.
5991
            self::deleteDocumentFromDb($id, $courseInfo, $sessionId, true);
5992
5993
            return true;
5994
        }
5995
5996
        return false;
5997
    }
5998
5999
    /**
6000
     * @param array $courseInfo
6001
     * @param int   $sessionId
6002
     */
6003
    public static function purgeDocuments($courseInfo, $sessionId)
6004
    {
6005
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6006
        foreach ($files as $file) {
6007
            self::purgeDocument($file['id'], $courseInfo, $sessionId);
6008
        }
6009
    }
6010
6011
    /**
6012
     * @param int   $id
6013
     * @param array $courseInfo
6014
     * @param int   $sessionId
6015
     */
6016
    public static function downloadDeletedDocument($id, $courseInfo, $sessionId)
6017
    {
6018
        $document = self::getDeletedDocument($id, $courseInfo, $sessionId);
6019
        if (!empty($document)) {
6020
            $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/';
6021
6022
            if (Security::check_abs_path($coursePath.$document['path'], $coursePath)) {
6023
                self::file_send_for_download($coursePath.$document['path']);
6024
                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...
6025
            }
6026
        }
6027
    }
6028
6029
    /**
6030
     * @param array $courseInfo
6031
     * @param int   $sessionId
6032
     *
6033
     * @return bool
6034
     */
6035
    public static function downloadAllDeletedDocument($courseInfo, $sessionId)
6036
    {
6037
        $files = self::getDeletedDocuments($courseInfo, $sessionId);
6038
6039
        if (empty($files)) {
6040
            return false;
6041
        }
6042
6043
        $coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
6044
6045
        // Creating a ZIP file.
6046
        $tempZipFile = api_get_path(SYS_ARCHIVE_PATH).api_get_unique_id().".zip";
6047
        $zip = new PclZip($tempZipFile);
6048
        foreach ($files as $file) {
6049
            $zip->add(
6050
                $coursePath.$file['path'],
6051
                PCLZIP_OPT_REMOVE_PATH,
6052
                $coursePath
6053
            );
6054
        }
6055
6056
        if (Security::check_abs_path($tempZipFile, api_get_path(SYS_ARCHIVE_PATH))) {
6057
            self::file_send_for_download($tempZipFile, true);
6058
            @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

6058
            /** @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...
6059
            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...
6060
        }
6061
    }
6062
6063
    /**
6064
     * Delete documents from a session in a course.
6065
     *
6066
     * @param array $courseInfo
6067
     * @param int   $sessionId
6068
     *
6069
     * @return bool
6070
     */
6071
    public static function deleteDocumentsFromSession($courseInfo, $sessionId)
6072
    {
6073
        if (empty($courseInfo)) {
6074
            return false;
6075
        }
6076
6077
        if (empty($sessionId)) {
6078
            return false;
6079
        }
6080
6081
        $itemPropertyTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
6082
        $documentTable = Database::get_course_table(TABLE_DOCUMENT);
6083
6084
        $conditionSession = api_get_session_condition($sessionId, true, false, 'd.session_id');
6085
        $courseId = $courseInfo['real_id'];
6086
6087
        // get invisible folders
6088
        $sql = "SELECT DISTINCT d.id, path
6089
                FROM $itemPropertyTable i
6090
                INNER JOIN $documentTable d
6091
                ON (i.c_id = d.c_id)
6092
                WHERE
6093
                    d.id = i.ref AND
6094
                    i.tool = '".TOOL_DOCUMENT."'
6095
                    $conditionSession AND
6096
                    i.c_id = $courseId AND
6097
                    d.c_id = $courseId ";
6098
6099
        $result = Database::query($sql);
6100
        $documents = Database::store_result($result, 'ASSOC');
6101
        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...
6102
            $course_dir = $courseInfo['directory'].'/document';
6103
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
6104
            $base_work_dir = $sys_course_path.$course_dir;
6105
6106
            foreach ($documents as $document) {
6107
                $documentId = $document['id'];
6108
                self::delete_document(
6109
                    $courseInfo,
6110
                    null,
6111
                    $base_work_dir,
6112
                    $sessionId,
6113
                    $documentId
6114
                );
6115
            }
6116
        }
6117
6118
        $sql = "DELETE FROM $documentTable
6119
                WHERE c_id = $courseId AND session_id = $sessionId";
6120
        Database::query($sql);
6121
6122
        $sql = "DELETE FROM $itemPropertyTable
6123
                WHERE c_id = $courseId AND session_id = $sessionId AND tool = '".TOOL_DOCUMENT."'";
6124
        Database::query($sql);
6125
    }
6126
6127
    /**
6128
     * Update the file or directory path in the document db document table.
6129
     *
6130
     * @author - Hugues Peeters <[email protected]>
6131
     *
6132
     * @param string $action   - action type require : 'delete' or 'update'
6133
     * @param string $old_path - old path info stored to change
6134
     * @param string $new_path - new path info to substitute
6135
     *
6136
     * @desc Update the file or directory path in the document db document table
6137
     */
6138
    public static function updateDbInfo($action, $old_path, $new_path = '')
6139
    {
6140
        $dbTable = Database::get_course_table(TABLE_DOCUMENT);
6141
        $course_id = api_get_course_int_id();
6142
        $old_path = Database::escape_string($old_path);
6143
        switch ($action) {
6144
            case 'delete':
6145
                $query = "DELETE FROM $dbTable
6146
                          WHERE
6147
                            c_id = $course_id AND
6148
                            (
6149
                                path LIKE BINARY '".$old_path."' OR
6150
                                path LIKE BINARY '".$old_path."/%'
6151
                            )";
6152
                Database::query($query);
6153
                break;
6154
            case 'update':
6155
                if ($new_path[0] == '.') {
6156
                    $new_path = substr($new_path, 1);
6157
                }
6158
                $new_path = str_replace('//', '/', $new_path);
6159
6160
                // Attempt to update	- tested & working for root	dir
6161
                $new_path = Database::escape_string($new_path);
6162
                $query = "UPDATE $dbTable SET
6163
                            path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
6164
                          WHERE 
6165
                                c_id = $course_id AND 
6166
                                (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
6167
                Database::query($query);
6168
                break;
6169
        }
6170
    }
6171
6172
    /**
6173
     * This function calculates the resized width and resized heigt
6174
     * according to the source and target widths
6175
     * and heights, height so that no distortions occur
6176
     * parameters.
6177
     *
6178
     * @param $image = the absolute path to the image
0 ignored issues
show
Documentation Bug introduced by
The doc comment = at position 0 could not be parsed: Unknown type name '=' at position 0 in =.
Loading history...
6179
     * @param $target_width = how large do you want your resized image
6180
     * @param $target_height = how large do you want your resized image
6181
     * @param $slideshow (default=0) =
6182
     *      indicates weither we are generating images for a slideshow or not,
6183
     *		this overrides the $_SESSION["image_resizing"] a bit so that a thumbnail
6184
     *	    view is also possible when you choose not to resize the source images
6185
     *
6186
     * @return array
6187
     */
6188
    public static function resizeImageSlideShow(
6189
        $image,
6190
        $target_width,
6191
        $target_height,
6192
        $slideshow = 0
6193
    ) {
6194
        // Modifications by Ivan Tcholakov, 04-MAY-2009.
6195
        $result = [];
6196
        $imageResize = Session::read('image_resizing');
6197
        if ($imageResize == 'resizing' || $slideshow == 1) {
6198
            $new_sizes = api_resize_image($image, $target_width, $target_height);
6199
            $result[] = $new_sizes['height'];
6200
            $result[] = $new_sizes['width'];
6201
        } else {
6202
            $size = api_getimagesize($image);
6203
            $result[] = $size['height'];
6204
            $result[] = $size['width'];
6205
        }
6206
6207
        return $result;
6208
    }
6209
6210
    /**
6211
     * Calculates the total size of a directory by adding the sizes (that
6212
     * are stored in the database) of all files & folders in this directory.
6213
     *
6214
     * @param string $path
6215
     * @param bool   $can_see_invisible
6216
     *
6217
     * @return int Total size
6218
     */
6219
    public static function getTotalFolderSize($path, $can_see_invisible = false)
6220
    {
6221
        $table_itemproperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
6222
        $table_document = Database::get_course_table(TABLE_DOCUMENT);
6223
        $tool_document = TOOL_DOCUMENT;
6224
6225
        $course_id = api_get_course_int_id();
6226
        $session_id = api_get_session_id();
6227
        $session_condition = api_get_session_condition(
6228
            $session_id,
6229
            true,
6230
            true,
6231
            'props.session_id'
6232
        );
6233
6234
        if (empty($course_id)) {
6235
            return 0;
6236
        }
6237
6238
        $path = Database::escape_string($path);
6239
        $visibility_rule = ' props.visibility '.($can_see_invisible ? '<> 2' : '= 1');
6240
6241
        $sql = "SELECT SUM(table1.size) FROM (
6242
                SELECT props.ref, size
6243
                FROM $table_itemproperty AS props 
6244
                INNER JOIN $table_document AS docs
6245
                ON (docs.id = props.ref AND docs.c_id = props.c_id)
6246
                WHERE
6247
                    docs.c_id = $course_id AND                    
6248
                    docs.path LIKE '$path/%' AND
6249
                    props.c_id = $course_id AND
6250
                    props.tool = '$tool_document' AND
6251
                    $visibility_rule
6252
                    $session_condition
6253
                GROUP BY ref
6254
            ) as table1";
6255
6256
        $result = Database::query($sql);
6257
        if ($result && Database::num_rows($result) != 0) {
6258
            $row = Database::fetch_row($result);
6259
6260
            return $row[0] == null ? 0 : $row[0];
6261
        } else {
6262
            return 0;
6263
        }
6264
    }
6265
6266
    /**
6267
     * Adds a cloud link to the database.
6268
     *
6269
     * @author - Aquilino Blanco Cores <[email protected]>
6270
     *
6271
     * @param array  $_course
6272
     * @param string $path
6273
     * @param string $url
6274
     * @param string $name
6275
     *
6276
     * @return int id of document or 0 if already exists or there was a problem creating it
6277
     */
6278
    public static function addCloudLink($_course, $path, $url, $name)
6279
    {
6280
        $file_path = $path;
6281
        if (!self::cloudLinkExists($_course, $path, $url)) {
6282
            $doc_id = add_document($_course, $file_path, 'link', 0, $name, $url);
6283
            if ($doc_id) {
6284
                // Update document item_property
6285
                api_item_property_update(
6286
                    $_course,
6287
                    TOOL_DOCUMENT,
6288
                    $doc_id,
6289
                    'DocumentAdded',
6290
                    api_get_user_id(),
6291
                    api_get_group_id(),
6292
                    api_get_user_id(),
6293
                    null,
6294
                    null,
6295
                    api_get_session_id()
6296
                );
6297
            }
6298
6299
            // If the file is in a folder, we need to update all parent folders
6300
            item_property_update_on_folder($_course, $file_path, api_get_user_id());
6301
6302
            return $doc_id;
6303
        } else {
6304
            return 0;
6305
        }
6306
    }
6307
6308
    /**
6309
     * Deletes a cloud link from the database.
6310
     *
6311
     * @author - Aquilino Blanco Cores <[email protected]>
6312
     *
6313
     * @param array  $courseInfo
6314
     * @param string $documentId
6315
     *
6316
     * @return bool true if success / false if an error occurred
6317
     */
6318
    public static function deleteCloudLink($courseInfo, $documentId)
6319
    {
6320
        if (empty($documentId) || empty($courseInfo)) {
6321
            return false;
6322
        }
6323
6324
        $documentId = (int) $documentId;
6325
        $fileDeletedFromDb = false;
6326
        if (!empty($documentId)) {
6327
            self::deleteDocumentFromDb($documentId, $courseInfo, 0, true);
6328
            // checking
6329
            $table = Database::get_course_table(TABLE_DOCUMENT);
6330
            $courseId = $courseInfo['real_id'];
6331
            echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
6332
            $result = Database::query($sql);
6333
            $exists = Database::num_rows($result) > 0;
6334
            $fileDeletedFromDb = !$exists;
6335
        }
6336
6337
        return $fileDeletedFromDb;
6338
    }
6339
6340
    /**
6341
     * Gets the id of a cloud link with a given path.
6342
     *
6343
     * @author - Aquilino Blanco Cores <[email protected]>
6344
     *
6345
     * @param array  $courseInfo
6346
     * @param string $path
6347
     * @param string $url
6348
     *
6349
     * @return int link's id / false if no link found
6350
     */
6351
    public static function getCloudLinkId($courseInfo, $path, $url)
6352
    {
6353
        $table = Database::get_course_table(TABLE_DOCUMENT);
6354
6355
        if (empty($courseInfo)) {
6356
            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...
6357
        }
6358
6359
        $courseId = (int) $courseInfo['real_id'];
6360
        $path = Database::escape_string($path);
6361
6362
        if (substr($path, -1) != '/') {
6363
            // Add final slash to path if not present
6364
            $path .= '/';
6365
        }
6366
6367
        if (!empty($courseId) && !empty($path)) {
6368
            $sql = "SELECT id FROM $table 
6369
                    WHERE 
6370
                        c_id = $courseId AND 
6371
                        path LIKE BINARY '$path' AND 
6372
                        comment = '$url' AND 
6373
                        filetype = 'link' 
6374
                    LIMIT 1";
6375
            $result = Database::query($sql);
6376
            if ($result && Database::num_rows($result)) {
6377
                $row = Database::fetch_array($result);
6378
6379
                return intval($row[0]);
6380
            }
6381
        }
6382
6383
        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...
6384
    }
6385
6386
    /**
6387
     * Checks if a cloud link exists.
6388
     *
6389
     * @author - Aquilino Blanco Cores <[email protected]>
6390
     *
6391
     * @param array  $courseInfo
6392
     * @param string $path
6393
     * @param string $url
6394
     *
6395
     * @return bool true if it exists false in other case
6396
     */
6397
    public static function cloudLinkExists($courseInfo, $path, $url)
6398
    {
6399
        $exists = self::getCloudLinkId($courseInfo, $path, $url);
6400
6401
        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...
6402
    }
6403
6404
    /**
6405
     * Gets the wellformed URLs regular expression in order to use it on forms' verifications.
6406
     *
6407
     * @author Aquilino Blanco Cores <[email protected]>
6408
     *
6409
     * @return string the well formed URLs regular expressions string
6410
     */
6411
    public static function getWellFormedUrlRegex()
6412
    {
6413
        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';
6414
    }
6415
6416
    /**
6417
     * Gets the files hosting sites' whitelist.
6418
     *
6419
     * @author Aquilino Blanco Cores <[email protected]>
6420
     *
6421
     * @return array the sites list
6422
     */
6423
    public static function getFileHostingWhiteList()
6424
    {
6425
        return [
6426
            'asuswebstorage.com',
6427
            'dropbox.com',
6428
            'dropboxusercontent.com',
6429
            'fileserve.com',
6430
            'drive.google.com',
6431
            'icloud.com',
6432
            'mediafire.com',
6433
            'mega.nz',
6434
            'onedrive.live.com',
6435
            'slideshare.net',
6436
            'scribd.com',
6437
            'wetransfer.com',
6438
            'box.com',
6439
            'livefilestore.com', // OneDrive
6440
        ];
6441
    }
6442
6443
    /**
6444
     * Parse file information into a link.
6445
     *
6446
     * @param array  $userInfo        Current user info
6447
     * @param array  $course_info
6448
     * @param int    $session_id
6449
     * @param array  $resource
6450
     * @param int    $lp_id
6451
     * @param bool   $add_move_button
6452
     * @param string $target
6453
     * @param string $overwrite_url
6454
     *
6455
     * @return null|string
6456
     */
6457
    private static function parseFile(
6458
        $userInfo,
6459
        $course_info,
6460
        $session_id,
6461
        $resource,
6462
        $lp_id,
6463
        $add_move_button,
6464
        $target,
6465
        $overwrite_url
6466
    ) {
6467
        $img_sys_path = api_get_path(SYS_CODE_PATH).'img/';
6468
        $web_code_path = api_get_path(WEB_CODE_PATH);
6469
6470
        $documentId = $resource['id'];
6471
        $path = $resource['path'];
6472
6473
        if (empty($path)) {
6474
            $num = 0;
6475
        } else {
6476
            $num = substr_count($path, '/') - 1;
6477
        }
6478
6479
        // It's a file.
6480
        $icon = choose_image($path);
6481
        $position = strrpos($icon, '.');
6482
        $icon = substr($icon, 0, $position).'_small.gif';
6483
        $my_file_title = $resource['title'];
6484
        $visibility = $resource['visibility'];
6485
6486
        // If title is empty we try to use the path
6487
        if (empty($my_file_title)) {
6488
            $my_file_title = basename($path);
6489
        }
6490
6491
        // Show the "image name" not the filename of the image.
6492
        if ($lp_id) {
6493
            // LP URL
6494
            $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;
6495
            if (!empty($overwrite_url)) {
6496
                $url = $overwrite_url.'&cidReq='.$course_info['code'].'&id_session='.$session_id.'&document_id='.$documentId.'';
6497
            }
6498
        } else {
6499
            // Direct document URL
6500
            $url = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
6501
            if (!empty($overwrite_url)) {
6502
                $url = $overwrite_url.'&cidReq='.$course_info['code'].'&id_session='.$session_id.'&document_id='.$documentId;
6503
            }
6504
        }
6505
6506
        $img = Display::returnIconPath($icon);
6507
        if (!file_exists($img_sys_path.$icon)) {
6508
            $img = Display::returnIconPath('default_small.gif');
6509
        }
6510
6511
        $link = Display::url(
6512
            '<img alt="" src="'.$img.'" title="" />&nbsp;'.$my_file_title,
6513
            $url,
6514
            ['target' => $target, 'class' => 'moved']
6515
        );
6516
6517
        $directUrl = $web_code_path.'document/document.php?cidReq='.$course_info['code'].'&id_session='.$session_id.'&id='.$documentId;
6518
        $link .= '&nbsp;'.Display::url(
6519
            Display::return_icon('preview_view.png', get_lang('Preview')),
6520
            $directUrl,
6521
            ['target' => '_blank']
6522
        );
6523
6524
        $visibilityClass = null;
6525
        if ($visibility == 0) {
6526
            $visibilityClass = ' text-muted ';
6527
        }
6528
        $return = null;
6529
6530
        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...
6531
            $return .= '<li class="doc_resource '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6532
        } else {
6533
            $return .= '<li class="doc_resource lp_resource_element '.$visibilityClass.' " data_id="'.$documentId.'" data_type="document" title="'.$my_file_title.'" >';
6534
        }
6535
6536
        $return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
6537
        if ($add_move_button) {
6538
            $return .= '<a class="moved" href="#">';
6539
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
6540
            $return .= '</a> ';
6541
        }
6542
        $return .= $link;
6543
        $sessionStar = api_get_session_image($resource['session_id'], $userInfo['status']);
6544
        $return .= $sessionStar;
6545
6546
        $return .= '</div></li>';
6547
6548
        return $return;
6549
    }
6550
6551
    /**
6552
     * @param int   $folderId
6553
     * @param array $resource
6554
     * @param int   $lp_id
6555
     *
6556
     * @return null|string
6557
     */
6558
    private static function parseFolder($folderId, $resource, $lp_id)
6559
    {
6560
        $title = isset($resource['title']) ? $resource['title'] : null;
6561
        $path = isset($resource['path']) ? $resource['path'] : null;
6562
6563
        if (empty($path)) {
6564
            $num = 0;
6565
        } else {
6566
            $num = substr_count($path, '/');
6567
        }
6568
6569
        // It's a folder.
6570
        //hide some folders
6571
        if (in_array(
6572
            $path,
6573
            ['shared_folder', 'chat_files', 'HotPotatoes_files', 'css', 'certificates']
6574
        )) {
6575
            return null;
6576
        } elseif (preg_match('/_groupdocs/', $path)) {
6577
            return null;
6578
        } elseif (preg_match('/sf_user_/', $path)) {
6579
            return null;
6580
        } elseif (preg_match('/shared_folder_session_/', $path)) {
6581
            return null;
6582
        }
6583
6584
        $onclick = '';
6585
        // if in LP, hidden folder are displayed in grey
6586
        $folder_class_hidden = '';
6587
        if ($lp_id) {
6588
            if (isset($resource['visible']) && $resource['visible'] == 0) {
6589
                $folder_class_hidden = "doc_folder_hidden"; // in base.css
6590
            }
6591
            $onclick = 'onclick="javascript: testResources(\'res_'.$resource['id'].'\',\'img_'.$resource['id'].'\')"';
6592
        }
6593
        $return = null;
6594
6595
        if (empty($path)) {
6596
            $return = '<ul class="lp_resource">';
6597
        }
6598
6599
        $return .= '<li class="doc_folder '.$folder_class_hidden.'" id="doc_id_'.$resource['id'].'"  style="margin-left:'.($num * 18).'px; ">';
6600
6601
        $image = Display::returnIconPath('nolines_plus.gif');
6602
        if (empty($path)) {
6603
            $image = Display::returnIconPath('nolines_minus.gif');
6604
        }
6605
        $return .= '<img style="cursor: pointer;" src="'.$image.'" align="absmiddle" id="img_'.$resource['id'].'" '.$onclick.'>';
6606
        $return .= Display::return_icon('lp_folder.gif').'&nbsp;';
6607
        $return .= '<span '.$onclick.' style="cursor: pointer;" >'.$title.'</span>';
6608
        $return .= '</li>';
6609
6610
        if (empty($path)) {
6611
            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...
6612
                $return .= '<div id="res_'.$resource['id'].'" >';
6613
            } else {
6614
                $return .= '<div id="res_'.$resource['id'].'" style="display: none;" >';
6615
            }
6616
        }
6617
6618
        return $return;
6619
    }
6620
6621
    /**
6622
     * Get the button to edit document.
6623
     *
6624
     * @param bool   $isReadOnly
6625
     * @param array  $documentData
6626
     * @param string $extension
6627
     * @param bool   $isCertificateMode
6628
     *
6629
     * @return string
6630
     */
6631
    private static function getButtonEdit($isReadOnly, array $documentData, $extension, $isCertificateMode)
6632
    {
6633
        $iconEn = Display::return_icon('edit.png', get_lang('Modify'));
6634
        $iconDis = Display::return_icon('edit_na.png', get_lang('Modify'));
6635
        $courseParams = api_get_cidreq();
6636
        $webOdfExtensionList = self::get_web_odf_extension_list();
6637
        $path = $documentData['path'];
6638
        $document_id = $documentData['id'];
6639
6640
        if ($isReadOnly) {
6641
            if (!api_is_course_admin() && !api_is_platform_admin()) {
6642
                return $iconDis;
6643
            }
6644
6645
            if (
6646
                $extension == 'svg' && api_browser_support('svg') &&
6647
                api_get_setting('enabled_support_svg') == 'true'
6648
            ) {
6649
                return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
6650
            }
6651
6652
            if (
6653
                in_array($extension, $webOdfExtensionList) &&
6654
                api_get_configuration_value('enabled_support_odf') === true
6655
            ) {
6656
                return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
6657
            }
6658
6659
            if (
6660
                in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif']) ||
6661
                ($extension == 'pxd' && api_get_setting('enabled_support_pixlr') == 'true')
6662
            ) {
6663
                return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
6664
            }
6665
6666
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
6667
        }
6668
6669
        if (in_array($path, self::get_system_folders())) {
6670
            return $iconDis;
6671
        }
6672
6673
        if ($isCertificateMode) {
6674
            return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id&curdirpath=/certificates");
6675
        }
6676
6677
        $sessionId = api_get_session_id();
6678
6679
        if ($sessionId && $documentData['session_id'] != $sessionId) {
6680
            return $iconDis;
6681
        }
6682
6683
        if (
6684
            $extension == 'svg' && api_browser_support('svg') &&
6685
            api_get_setting('enabled_support_svg') == 'true'
6686
        ) {
6687
            return Display::url($iconEn, "edit_draw.php?$courseParams&id=$document_id");
6688
        }
6689
6690
        if (
6691
            in_array($extension, $webOdfExtensionList) &&
6692
            api_get_configuration_value('enabled_support_odf') === true
6693
        ) {
6694
            return Display::url($iconEn, "edit_odf.php?$courseParams&id=$document_id");
6695
        }
6696
6697
        if (
6698
            in_array($extension, ['png', 'jpg', 'jpeg', 'bmp', 'gif']) ||
6699
            ($extension == 'pxd' && api_get_setting('enabled_support_pixlr') == 'true')
6700
        ) {
6701
            return Display::url($iconEn, "edit_paint.php?$courseParams&id=$document_id");
6702
        }
6703
6704
        return Display::url($iconEn, "edit_document.php?$courseParams&id=$document_id");
6705
    }
6706
6707
    /**
6708
     * Get the button to move document.
6709
     *
6710
     * @param bool  $isReadOnly
6711
     * @param array $documentData
6712
     * @param bool  $isCertificateMode
6713
     * @param int   $parentId
6714
     *
6715
     * @return string
6716
     */
6717
    private static function getButtonMove($isReadOnly, array $documentData, $isCertificateMode, $parentId)
6718
    {
6719
        $iconEn = Display::return_icon('move.png', get_lang('Move'));
6720
        $iconDis = Display::return_icon('move_na.png', get_lang('Move'));
6721
6722
        if ($isReadOnly) {
6723
            return $iconDis;
6724
        }
6725
6726
        $path = $documentData['path'];
6727
        $document_id = $documentData['id'];
6728
        $sessionId = api_get_session_id();
6729
        $courseParams = api_get_cidreq();
6730
6731
        if ($isCertificateMode || in_array($path, self::get_system_folders())) {
6732
            return $iconDis;
6733
        }
6734
6735
        if ($sessionId) {
6736
            if ($documentData['session_id'] != $sessionId) {
6737
                return $iconDis;
6738
            }
6739
        }
6740
6741
        $urlMoveParams = http_build_query(['id' => $parentId, 'move' => $document_id]);
6742
6743
        return Display::url(
6744
            $iconEn,
6745
            api_get_self()."?$courseParams&$urlMoveParams"
6746
        );
6747
    }
6748
6749
    /**
6750
     * Get the button to set visibility to document.
6751
     *
6752
     * @param bool  $isReadOnly
6753
     * @param int   $visibility
6754
     * @param array $documentData
6755
     * @param bool  $isCertificateMode
6756
     * @param int   $parentId
6757
     *
6758
     * @return null|string
6759
     */
6760
    private static function getButtonVisibility(
6761
        $isReadOnly,
6762
        $visibility,
6763
        array $documentData,
6764
        $isCertificateMode,
6765
        $parentId
6766
    ) {
6767
        $visibility_icon = $visibility == 0 ? 'invisible' : 'visible';
6768
        $visibility_command = $visibility == 0 ? 'set_visible' : 'set_invisible';
6769
        $courseParams = api_get_cidreq();
6770
6771
        if ($isReadOnly) {
6772
            if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6773
                return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
6774
            }
6775
6776
            return null;
6777
        }
6778
6779
        if ($isCertificateMode) {
6780
            return Display::return_icon($visibility_icon.'.png', get_lang('VisibilityCannotBeChanged'));
6781
        }
6782
6783
        if (api_is_allowed_to_edit() || api_is_platform_admin()) {
6784
            $tip_visibility = $visibility_icon == 'invisible' ? get_lang('Show') : get_lang('Hide');
6785
6786
            return Display::url(
6787
                Display::return_icon($visibility_icon.'.png', $tip_visibility),
6788
                api_get_self()."?$courseParams&id=$parentId&$visibility_command={$documentData['id']}"
6789
            );
6790
        }
6791
6792
        return null;
6793
    }
6794
6795
    /**
6796
     * GEt the button to delete a document.
6797
     *
6798
     * @param bool   $isReadOnly
6799
     * @param array  $documentData
6800
     * @param bool   $isCertificateMode
6801
     * @param string $curDirPath
6802
     * @param int    $parentId
6803
     *
6804
     * @return string
6805
     */
6806
    private static function getButtonDelete(
6807
        $isReadOnly,
6808
        array $documentData,
6809
        $isCertificateMode,
6810
        $curDirPath,
6811
        $parentId
6812
    ) {
6813
        $iconEn = Display::return_icon('delete.png', get_lang('Delete'));
6814
        $iconDis = Display::return_icon('delete_na.png', get_lang('ThisFolderCannotBeDeleted'));
6815
        $path = $documentData['path'];
6816
        $id = $documentData['id'];
6817
        $courseParams = api_get_cidreq();
6818
6819
        if ($isReadOnly) {
6820
            return $iconDis;
6821
        }
6822
6823
        if (in_array($path, self::get_system_folders())) {
6824
            return $iconDis;
6825
        }
6826
6827
        $titleToShow = addslashes(basename($documentData['title']));
6828
        $urlDeleteParams = http_build_query([
6829
            'curdirpath' => $curDirPath,
6830
            'action' => 'delete_item',
6831
            'id' => $parentId,
6832
            'deleteid' => $documentData['id'],
6833
        ]);
6834
        $btn = Display::url(
6835
            $iconEn,
6836
            api_get_self()."?$courseParams&$urlDeleteParams",
6837
            ['onclick' => "return confirmation('$titleToShow');"]
6838
        );
6839
6840
        if (
6841
            isset($_GET['curdirpath']) &&
6842
            $_GET['curdirpath'] == '/certificates' &&
6843
            self::get_default_certificate_id(api_get_course_id()) == $id
6844
        ) {
6845
            return $btn;
6846
        }
6847
6848
        if ($isCertificateMode) {
6849
            return $btn;
6850
        }
6851
6852
        $sessionId = api_get_session_id();
6853
6854
        if ($sessionId) {
6855
            if ($documentData['session_id'] != $sessionId) {
6856
                return $iconDis;
6857
            }
6858
        }
6859
6860
        return $btn;
6861
    }
6862
}
6863