DocumentManager   F
last analyzed

Complexity

Total Complexity 1075

Size/Duplication

Total Lines 7535
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 2
Metric Value
wmc 1075
eloc 3959
c 5
b 0
f 2
dl 0
loc 7535
rs 0.8

107 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A get_course_quota() 0 20 5
B smartReadFile() 0 54 11
B file_get_mime_type() 0 214 4
A searchKeyword() 0 7 2
F is_visible_by_id() 0 118 25
A updateVisibilityFromAllSessions() 0 18 3
A getDeletedDocument() 0 26 3
A get_titles_of_path() 0 31 4
A undoFixDocumentName() 0 19 3
F create_document_link() 0 332 96
A attach_gradebook_certificate() 0 18 4
A generateAudioJavascript() 0 3 1
F getJodconverterExtensionList() 0 111 19
D getButtonEdit() 0 75 24
A set_document_as_template() 0 21 1
A get_default_certificate_id() 0 26 5
A getDocumentDefaultVisibility() 0 22 6
B create_directory_certificate_in_course() 0 55 5
A generateAudioPreview() 0 10 2
B write_resources_tree() 0 62 8
D replaceUrlWithNewCourseCode() 0 160 23
A replace_user_info_into_html() 0 53 4
A get_document_id_of_directory_certificate() 0 13 2
F parseFolder() 0 72 15
D parse_HTML_attributes() 0 82 21
A downloadDeletedDocument() 0 9 3
D build_document_icon_tag() 0 96 24
A is_my_shared_folder() 0 15 3
B getButtonDelete() 0 62 11
A cloudLinkExists() 0 5 1
C check_readonly() 0 84 16
A removeGeneratedAudioTempFile() 0 6 3
D isBrowserViewable() 0 62 24
F build_edit_icons() 0 171 29
A export_to_pdf() 0 34 4
A isBasicCourseFolder() 0 6 1
A addFileToDocumentTool() 0 52 3
A generateAudioTempFile() 0 37 5
D build_move_to_selector() 0 93 31
F writeContentIntoDocument() 0 165 22
A enough_space() 0 10 3
A getDocumentByPathInCourse() 0 12 2
A createDefaultAudioFolder() 0 26 3
A renameDocument() 0 15 1
A updateDbInfo() 0 35 5
A convertWavToMp3() 0 23 6
B generateDefaultCertificate() 0 92 9
A getUniqueFileName() 0 17 2
A documentExists() 0 43 3
A getDeletedDocuments() 0 20 2
A folderExists() 0 40 3
A getFileHostingWhiteList() 0 25 3
F getAllDocumentData() 0 187 34
A generateMediaPreview() 0 18 4
B get_document_id() 0 58 11
A getSessionFolderFilters() 0 15 3
A get_system_folders() 0 12 1
D get_text_content() 0 72 20
A is_any_user_shared_folder() 0 10 3
A addSuffixToFileName() 0 17 4
A resizeImageSlideShow() 0 20 3
F parseFile() 0 114 12
A purgeDocument() 0 14 2
A is_folder() 0 13 2
F is_visible() 0 115 20
A getFormatTypeListConvertor() 0 11 3
F index_document() 0 220 31
A remove_attach_certificate() 0 23 6
A unset_document_as_template() 0 26 1
A displaySimpleQuota() 0 10 1
A getHideDownloadIcon() 0 10 3
A getDocumentSuffix() 0 8 3
A deleteDocumentsFromSession() 0 54 5
A downloadAllDeletedDocument() 0 25 4
A getWellFormedUrlRegex() 0 3 1
A getProtectedFolderFromStudent() 0 8 1
A get_web_odf_extension_list() 0 3 1
A getAllDocumentsCreatedByUser() 0 36 3
A is_shared_folder() 0 10 3
F get_all_document_folders() 0 203 24
B getButtonVisibility() 0 34 10
B documents_total_space() 0 43 6
A deleteDocumentFromDb() 0 48 4
C check_visibility_tree() 0 67 14
B getTotalFolderSize() 0 57 9
F get_document_preview() 0 288 45
A deleteCloudLink() 0 20 4
F get_resources_from_source_html() 0 364 66
A create_dir_form() 0 12 1
B addAndConvertWavToMp3() 0 49 8
C get_document_data_by_id() 0 94 16
B getCloudLinkId() 0 33 7
C upload_document() 0 115 17
A includeMathJaxScript() 0 13 2
D build_directory_selector() 0 116 18
A purgeDocuments() 0 5 2
B getButtonMove() 0 33 8
A delete_document_from_search_engine() 0 20 3
A is_certificate_mode() 0 11 5
B is_folder_to_avoid() 0 48 10
A addCloudLink() 0 27 3
F get_all_info_to_certificate() 0 179 13
B createUserSharedFolder() 0 72 6
F delete_document() 0 197 30
A fixDocumentName() 0 14 3
F file_send_for_download() 0 148 37

How to fix   Complexity   

Complex Class

Complex classes like DocumentManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DocumentManager, and based on these observations, apply Extract Interface, too.

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