Passed
Push — master ( cf75f9...66ca98 )
by Julito
09:35
created

DocumentManager::export_to_pdf()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 30
nc 6
nop 4
dl 0
loc 52
rs 8.8177
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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