Passed
Push — master ( f2a413...87c0a1 )
by Julito
09:14
created

DocumentManager::is_folder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

    return false;
}

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

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

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

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

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

    return false;
}

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

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