Passed
Push — master ( 7f9d4e...b9a584 )
by Julito
18:35 queued 06:54
created

unzip_uploaded_document()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 69
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 36
nc 11
nop 11
dl 0
loc 69
rs 8.0555
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ResourceLink;
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\CourseBundle\Entity\CDocument;
7
use Symfony\Component\HttpFoundation\File\UploadedFile;
8
9
/**
10
 * FILE UPLOAD LIBRARY.
11
 *
12
 * This is the file upload library for Chamilo.
13
 * Include/require it in your code to use its functionality.
14
 *
15
 * @todo test and reorganise
16
 */
17
18
/**
19
 * Changes the file name extension from .php to .phps
20
 * Useful for securing a site.
21
 *
22
 * @author Hugues Peeters <[email protected]>
23
 *
24
 * @param string $file_name Name of a file
25
 *
26
 * @return string the filename phps'ized
27
 */
28
function php2phps($file_name)
29
{
30
    return preg_replace('/\.(phar.?|php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
31
}
32
33
/**
34
 * Renames .htaccess & .HTACCESS to htaccess.txt.
35
 *
36
 * @param string $filename
37
 *
38
 * @return string
39
 */
40
function htaccess2txt($filename)
41
{
42
    return str_replace(['.htaccess', '.HTACCESS'], ['htaccess.txt', 'htaccess.txt'], $filename);
43
}
44
45
/**
46
 * This function executes our safety precautions
47
 * more functions can be added.
48
 *
49
 * @param string $filename
50
 *
51
 * @return string
52
 *
53
 * @see php2phps()
54
 * @see htaccess2txt()
55
 */
56
function disable_dangerous_file($filename)
57
{
58
    return htaccess2txt(php2phps($filename));
59
}
60
61
/**
62
 * Returns the name without extension, used for the title.
63
 *
64
 * @param string $name
65
 *
66
 * @return name without the extension
67
 */
68
function get_document_title($name)
69
{
70
    // If they upload .htaccess...
71
    $name = disable_dangerous_file($name);
72
    $ext = substr(strrchr($name, '.'), 0);
73
74
    if (empty($ext)) {
75
        return substr($name, 0, strlen($name));
76
    }
77
78
    return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
79
}
80
81
/**
82
 * This function checks if the upload succeeded.
83
 *
84
 * @return true if upload succeeded
85
 */
86
function process_uploaded_file($uploadedFileData, $show_output = true)
87
{
88
    $uploadedFile = [];
89
    if ($uploadedFileData instanceof UploadedFile) {
90
        $uploadedFile['error'] = $uploadedFileData->getError();
91
        $uploadedFile['tmp_name'] = $uploadedFileData->getPathname();
92
        $uploadedFile['size'] = $uploadedFileData->getSize();
93
    } else {
94
        $uploadedFile = $uploadedFileData;
95
    }
96
97
    // Checking the error code sent with the file upload.
98
    if (isset($uploadedFile['error'])) {
99
        switch ($uploadedFile['error']) {
100
            case 1:
101
                // The uploaded file exceeds the upload_max_filesize directive in php.ini.
102
                if ($show_output) {
103
                    Display::addFlash(
104
                        Display::return_message(
105
                            get_lang('The uploaded file exceeds the maximum filesize allowed by the server:').ini_get('upload_max_filesize'),
106
                            'error'
107
                        )
108
                    );
109
                }
110
111
                return false;
112
            case 2:
113
                // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
114
                // Not used at the moment, but could be handy if we want to limit the size of an upload
115
                // (e.g. image upload in html editor).
116
                $max_file_size = (int) $_POST['MAX_FILE_SIZE'];
117
                if ($show_output) {
118
                    Display::addFlash(
119
                        Display::return_message(
120
                            get_lang('The file size exceeds the maximum allowed setting:').format_file_size($max_file_size),
121
                            'error'
122
                        )
123
                    );
124
                }
125
126
                return false;
127
            case 3:
128
                // The uploaded file was only partially uploaded.
129
                if ($show_output) {
130
                    Display::addFlash(
131
                        Display::return_message(
132
                            get_lang('The uploaded file was only partially uploaded.').' '.get_lang('Please Try Again!'),
133
                            'error'
134
                        )
135
                    );
136
                }
137
138
                return false;
139
            case 4:
140
                // No file was uploaded.
141
                if ($show_output) {
142
                    Display::addFlash(
143
                        Display::return_message(
144
                            get_lang('No file was uploaded.').' '.get_lang('Please select a file before pressing the upload button.'),
145
                            'error'
146
                        )
147
                    );
148
                }
149
150
                return false;
151
        }
152
    }
153
154
    if (!file_exists($uploadedFile['tmp_name'])) {
155
        // No file was uploaded.
156
        if ($show_output) {
157
            Display::addFlash(Display::return_message(get_lang('The file upload has failed.'), 'error'));
158
        }
159
160
        return false;
161
    }
162
163
    if (file_exists($uploadedFile['tmp_name'])) {
164
        $filesize = filesize($uploadedFile['tmp_name']);
165
        if (empty($filesize)) {
166
            // No file was uploaded.
167
            if ($show_output) {
168
                Display::addFlash(
169
                    Display::return_message(
170
                        get_lang('The file upload has failed.SizeIsZero'),
171
                        'error'
172
                    )
173
                );
174
            }
175
176
            return false;
177
        }
178
    }
179
180
    $course_id = api_get_course_id();
181
182
    //Checking course quota if we are in a course
183
    if (!empty($course_id)) {
184
        $max_filled_space = DocumentManager::get_course_quota();
185
        // Check if there is enough space to save the file
186
        if (!DocumentManager::enough_space($uploadedFile['size'], $max_filled_space)) {
187
            if ($show_output) {
188
                Display::addFlash(
189
                    Display::return_message(
190
                        get_lang('There is not enough space to upload this file.'),
191
                        'error'
192
                    )
193
                );
194
            }
195
196
            return false;
197
        }
198
    }
199
200
    // case 0: default: We assume there is no error, the file uploaded with success.
201
    return true;
202
}
203
204
/**
205
 * This function does the save-work for the documents.
206
 * It handles the uploaded file and adds the properties to the database
207
 * If unzip=1 and the file is a zipfile, it is extracted
208
 * If we decide to save ALL kinds of documents in one database,
209
 * we could extend this with a $type='document', 'scormdocument',...
210
 *
211
 * @param array  $courseInfo
212
 * @param array  $uploadedFile            ($_FILES)
213
 *                                        array(
214
 *                                        'name' => 'picture.jpg',
215
 *                                        'tmp_name' => '...', // absolute path
216
 *                                        );
217
 * @param string $documentDir             Example: /var/www/chamilo/courses/ABC/document
218
 * @param string $uploadPath              Example: /folder1/folder2/
219
 * @param int    $userId
220
 * @param int    $groupId                 group.id
221
 * @param int    $toUserId                User ID, or NULL for everybody
222
 * @param int    $unzip                   1/0
223
 * @param string $whatIfFileExists        overwrite, rename or warn if exists (default)
224
 * @param bool   $output                  optional output parameter
225
 * @param bool   $onlyUploadFile
226
 * @param string $comment
227
 * @param int    $sessionId
228
 * @param bool   $treat_spaces_as_hyphens
229
 * @param string $uploadKey
230
 * @param int    $parentId
231
 * @param $content
232
 *
233
 * So far only use for unzip_uploaded_document function.
234
 * If no output wanted on success, set to false.
235
 *
236
 * @return CDocument|false
237
 */
238
function handle_uploaded_document(
239
    $courseInfo,
240
    $uploadedFile,
241
    $documentDir,
242
    $uploadPath,
243
    $userId,
244
    $groupId = 0,
245
    $toUserId = null,
246
    $unzip = 0,
247
    $whatIfFileExists = '',
248
    $output = true,
249
    $onlyUploadFile = false,
250
    $comment = null,
251
    $sessionId = null,
252
    $treat_spaces_as_hyphens = true,
253
    $uploadKey = '',
254
    $parentId = 0,
255
    $content = null
256
) {
257
    if (!$userId) {
258
        return false;
259
    }
260
261
    $userInfo = api_get_user_info();
262
    $uploadedFile['name'] = stripslashes($uploadedFile['name']);
263
    // Add extension to files without one (if possible)
264
    $uploadedFile['name'] = add_ext_on_mime($uploadedFile['name'], $uploadedFile['type']);
265
    $sessionId = (int) $sessionId;
266
    if (empty($sessionId)) {
267
        $sessionId = api_get_session_id();
268
    }
269
270
    $group = api_get_group_entity($groupId);
271
272
    // Just in case process_uploaded_file is not called
273
    $maxSpace = DocumentManager::get_course_quota();
274
275
    // Check if there is enough space to save the file
276
    if (!DocumentManager::enough_space($uploadedFile['size'], $maxSpace)) {
277
        if ($output) {
278
            Display::addFlash(
279
                Display::return_message(get_lang('There is not enough space to upload this file.'), 'error')
280
            );
281
        }
282
283
        return false;
284
    }
285
286
    $defaultVisibility = api_get_setting('course.active_tools_on_create');
287
    $defaultVisibility = in_array('document', $defaultVisibility) ? ResourceLink::VISIBILITY_PUBLISHED : ResourceLink::VISIBILITY_DRAFT;
288
289
    // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
290
    if (1 == $unzip && preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
291
        return unzip_uploaded_document(
292
            $courseInfo,
293
            $userInfo,
294
            $uploadedFile,
295
            $uploadPath,
296
            $documentDir,
297
            $maxSpace,
298
            $sessionId,
299
            $groupId,
300
            $output,
301
            $onlyUploadFile,
302
            $whatIfFileExists
303
        );
304
    } elseif (1 == $unzip && !preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
305
        // We can only unzip ZIP files (no gz, tar,...)
306
        if ($output) {
307
            Display::addFlash(
308
                Display::return_message(
309
                    get_lang('The file you selected was not a zip file.')." ".get_lang('Please Try Again!'),
310
                    'error'
311
                )
312
            );
313
        }
314
315
        return false;
316
    } else {
317
        // Clean up the name, only ASCII characters should stay. (and strict)
318
        $cleanName = api_replace_dangerous_char($uploadedFile['name'], $treat_spaces_as_hyphens);
319
320
        // No "dangerous" files
321
        $cleanName = disable_dangerous_file($cleanName);
322
323
        // Checking file extension
324
        if (!filter_extension($cleanName)) {
325
            if ($output) {
326
                Display::addFlash(
327
                    Display::return_message(
328
                        get_lang('File upload failed: this file extension or file type is prohibited'),
329
                        'error'
330
                    )
331
                );
332
            }
333
334
            return false;
335
        } else {
336
            // If the upload path differs from / (= root) it will need a slash at the end
337
            if ('/' !== $uploadPath) {
338
                $uploadPath = $uploadPath.'/';
339
            }
340
341
            // Full path to where we want to store the file with trailing slash
342
            $whereToSave = $documentDir.$uploadPath;
343
344
            // Just upload the file "as is"
345
            /*if ($onlyUploadFile) {
346
                $errorResult = moveUploadedFile($uploadedFile, $whereToSave.$cleanName);
347
                if ($errorResult) {
348
                    return $whereToSave.$cleanName;
349
                }
350
351
                return $errorResult;
352
            }*/
353
354
            /*
355
                Based in the clean name we generate a new filesystem name
356
                Using the session_id and group_id if values are not empty
357
            */
358
359
            // @todo fix clean name use hash instead of custom document name
360
            /*$fileSystemName = DocumentManager::fixDocumentName(
361
                $cleanName,
362
                'file',
363
                $courseInfo,
364
                $sessionId,
365
                $groupId
366
            );*/
367
368
            $fileSystemName = $cleanName;
369
370
            // Name of the document without the extension (for the title)
371
            $documentTitle = get_document_title($uploadedFile['name']);
372
373
            // Size of the uploaded file (in bytes)
374
            $fileSize = $uploadedFile['size'];
375
376
            // Example: /folder/picture.jpg
377
            /*$filePath = $uploadPath.$fileSystemName;
378
            $docId = DocumentManager::get_document_id(
379
                $courseInfo,
380
                $filePath,
381
                $sessionId
382
            );*/
383
384
            $courseEntity = api_get_course_entity($courseInfo['real_id']);
385
            if (empty($courseEntity)) {
386
                return false;
387
            }
388
389
            $sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
390
            $session = api_get_session_entity($sessionId);
391
            $group = api_get_group_entity($groupId);
392
            $documentRepo = Container::getDocumentRepository();
393
394
            /** @var \Chamilo\CoreBundle\Entity\AbstractResource $parentResource */
395
            $parentResource = $courseEntity;
396
            if (!empty($parentId)) {
397
                $parent = $documentRepo->find($parentId);
398
                if ($parent) {
399
                    $parentResource = $parent;
400
                }
401
            }
402
403
            $document = $documentRepo->findResourceByTitle(
404
                $documentTitle,
405
                $parentResource->getResourceNode(),
406
                $courseEntity,
407
                $session,
408
                $group
409
            );
410
411
            if (!($content instanceof UploadedFile)) {
412
                $request = Container::getRequest();
413
                $content = $request->files->get($uploadKey);
414
                if (is_array($content)) {
415
                    $content = $content[0];
416
                }
417
            }
418
419
            // What to do if the target file exists
420
            switch ($whatIfFileExists) {
421
                // Overwrite the file if it exists
422
                case 'overwrite':
423
                    if ($document) {
424
                        // Update file size
425
                        /*update_existing_document(
426
                            $courseInfo,
427
                            $document->getIid(),
428
                            $uploadedFile['size']
429
                        );*/
430
431
                        $document = DocumentManager::addFileToDocument(
432
                            $document,
433
                            null,
434
                            $content,
435
                            $defaultVisibility,
436
                            null,
437
                            $group
438
                        );
439
440
                        // Display success message with extra info to user
441
                        if ($document && $output) {
442
                            Display::addFlash(
443
                                Display::return_message(
444
                                    get_lang('File upload succeeded!').'<br /> '.
445
                                    $document->getTitle().' '.get_lang(' was overwritten.'),
446
                                    'confirmation',
447
                                    false
448
                                )
449
                            );
450
                        }
451
452
                        return $document;
453
                    } else {
454
                        // Put the document data in the database
455
                        $document = DocumentManager::addDocument(
456
                            $courseInfo,
457
                            null,
458
                            'file',
459
                            $fileSize,
460
                            $documentTitle,
461
                            $comment,
462
                            0,
463
                            $defaultVisibility,
464
                            $groupId,
465
                            $sessionId,
466
                            0,
467
                            true,
468
                            $content
469
                        );
470
471
                        // Display success message to user
472
                        if ($document) {
473
                            Display::addFlash(
474
                                Display::return_message(
475
                                    get_lang('File upload succeeded!').'<br /> '.$documentTitle,
476
                                    'confirmation',
477
                                    false
478
                                )
479
                            );
480
                        }
481
482
                        return $document;
483
                    }
484
                    break;
485
                case 'rename':
486
                    // Rename the file if it exists
487
                    $cleanName = DocumentManager::getUniqueFileName(
488
                        $uploadPath,
489
                        $cleanName,
490
                        $courseInfo,
491
                        $sessionId,
492
                        $groupId
493
                    );
494
495
                    $fileSystemName = DocumentManager::fixDocumentName(
496
                        $cleanName,
497
                        'file',
498
                        $courseInfo,
499
                        $sessionId,
500
                        $groupId
501
                    );
502
503
                    $documentTitle = disable_dangerous_file($cleanName);
504
                    $filePath = $uploadPath.$fileSystemName;
505
506
                    // Put the document data in the database
507
                    $document = DocumentManager::addDocument(
508
                        $courseInfo,
509
                        $filePath,
510
                        'file',
511
                        $fileSize,
512
                        $documentTitle,
513
                        $comment, // comment
514
                        0, // read only
515
                        $defaultVisibility, // save visibility
516
                        $groupId,
517
                        $sessionId,
518
                        0,
519
                        true,
520
                        $content,
521
                        $parentId
522
                    );
523
524
                    // Display success message to user
525
                    if ($output && $document) {
526
                        Display::addFlash(
527
                            Display::return_message(
528
                                get_lang('File upload succeeded!').'<br />'.
529
                                get_lang('File saved as').' '.$document->getTitle(),
530
                                'success',
531
                                false
532
                            )
533
                        );
534
                    }
535
536
                    return $document;
537
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
538
                case 'nothing':
539
                    if ($document) {
540
                        if ($output) {
541
                            Display::addFlash(
542
                                Display::return_message(
543
                                    $uploadPath.$cleanName.' '.get_lang(' already exists.'),
544
                                    'warning',
545
                                    false
546
                                )
547
                            );
548
                        }
549
                        break;
550
                    }
551
                    // no break
552
                default:
553
                    // Only save the file if it doesn't exist or warn user if it does exist
554
                    if ($document) {
555
                        if ($output) {
556
                            Display::addFlash(
557
                                Display::return_message($cleanName.' '.get_lang(' already exists.'), 'warning', false)
558
                            );
559
                        }
560
                    } else {
561
                        // Put the document data in the database
562
                        $document = DocumentManager::addDocument(
563
                            $courseInfo,
564
                            $filePath,
565
                            'file',
566
                            $fileSize,
567
                            $documentTitle,
568
                            $comment,
569
                            0,
570
                            $defaultVisibility,
571
                            $groupId,
572
                            $sessionId,
573
                            0,
574
                            true,
575
                            $content
576
                        );
577
578
                        if ($document) {
579
                            // Display success message to user
580
                            if ($output) {
581
                                Display::addFlash(
582
                                    Display::return_message(
583
                                        get_lang('File upload succeeded!').'<br /> '.$documentTitle,
584
                                        'confirm',
585
                                        false
586
                                    )
587
                                );
588
                            }
589
590
                            return $document;
591
                        } else {
592
                            if ($output) {
593
                                Display::addFlash(
594
                                    Display::return_message(
595
                                        get_lang('The uploaded file could not be saved (perhaps a permission problem?)'),
596
                                        'error',
597
                                        false
598
                                    )
599
                                );
600
                            }
601
                        }
602
                    }
603
                    break;
604
            }
605
        }
606
    }
607
}
608
609
/**
610
 * @param string $file
611
 * @param string $storePath
612
 *
613
 * @return bool
614
 */
615
function moveUploadedFile($file, $storePath)
616
{
617
    $handleFromFile = isset($file['from_file']) && $file['from_file'] ? true : false;
618
    $moveFile = isset($file['move_file']) && $file['move_file'] ? true : false;
619
    if ($moveFile) {
620
        $copied = copy($file['tmp_name'], $storePath);
621
622
        if (!$copied) {
623
            return false;
624
        }
625
    }
626
    if ($handleFromFile) {
627
        return file_exists($file['tmp_name']);
628
    } else {
629
        return move_uploaded_file($file['tmp_name'], $storePath);
630
    }
631
}
632
633
/**
634
 * Checks if there is enough place to add a file on a directory
635
 * on the base of a maximum directory size allowed
636
 * deprecated: use enough_space instead!
637
 *
638
 * @author Hugues Peeters <[email protected]>
639
 *
640
 * @param int    $file_size     Size of the file in byte
641
 * @param string $dir           Path of the directory where the file should be added
642
 * @param int    $max_dir_space Maximum size of the diretory in byte
643
 *
644
 * @return bool true if there is enough space, false otherwise
645
 *
646
 * @see enough_size() uses  dir_total_space() function
647
 */
648
function enough_size($file_size, $dir, $max_dir_space)
649
{
650
    // If the directory is the archive directory, safely ignore the size limit
651
    if (api_get_path(SYS_ARCHIVE_PATH) == $dir) {
652
        return true;
653
    }
654
655
    if ($max_dir_space) {
656
        $already_filled_space = dir_total_space($dir);
657
        if (($file_size + $already_filled_space) > $max_dir_space) {
658
            return false;
659
        }
660
    }
661
662
    return true;
663
}
664
665
/**
666
 * Computes the size already occupied by a directory and is subdirectories.
667
 *
668
 * @author Hugues Peeters <[email protected]>
669
 *
670
 * @param string $dir_path Size of the file in byte
671
 *
672
 * @return int Return the directory size in bytes
673
 */
674
function dir_total_space($dir_path)
675
{
676
    $save_dir = getcwd();
677
    chdir($dir_path);
678
    $handle = opendir($dir_path);
679
    $sumSize = 0;
680
    $dirList = [];
681
    while ($element = readdir($handle)) {
682
        if ('.' == $element || '..' == $element) {
683
            continue; // Skip the current and parent directories
684
        }
685
        if (is_file($element)) {
686
            $sumSize += filesize($element);
687
        }
688
        if (is_dir($element)) {
689
            $dirList[] = $dir_path.'/'.$element;
690
        }
691
    }
692
693
    closedir($handle);
694
695
    if (sizeof($dirList) > 0) {
696
        foreach ($dirList as $j) {
697
            $sizeDir = dir_total_space($j); // Recursivity
698
            $sumSize += $sizeDir;
699
        }
700
    }
701
    chdir($save_dir); // Return to initial position
702
703
    return $sumSize;
704
}
705
706
/**
707
 * Tries to add an extension to files without extension
708
 * Some applications on Macintosh computers don't add an extension to the files.
709
 * This subroutine try to fix this on the basis of the MIME type sent
710
 * by the browser.
711
 *
712
 * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
713
 *        We don't have solution for this kind of situation
714
 *
715
 * @author Hugues Peeters <[email protected]>
716
 * @author Bert Vanderkimpen
717
 *
718
 * @param string $file_name Name of the file
719
 * @param string $file_type Type of the file
720
 *
721
 * @return string File name
722
 */
723
function add_ext_on_mime($file_name, $file_type)
724
{
725
    // Check whether the file has an extension AND whether the browser has sent a MIME Type
726
727
    if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
728
        // Build a "MIME-types / extensions" connection table
729
        static $mime_type = [];
730
731
        $mime_type[] = 'application/msword';
732
        $extension[] = '.doc';
733
        $mime_type[] = 'application/rtf';
734
        $extension[] = '.rtf';
735
        $mime_type[] = 'application/vnd.ms-powerpoint';
736
        $extension[] = '.ppt';
737
        $mime_type[] = 'application/vnd.ms-excel';
738
        $extension[] = '.xls';
739
        $mime_type[] = 'application/pdf';
740
        $extension[] = '.pdf';
741
        $mime_type[] = 'application/postscript';
742
        $extension[] = '.ps';
743
        $mime_type[] = 'application/mac-binhex40';
744
        $extension[] = '.hqx';
745
        $mime_type[] = 'application/x-gzip';
746
        $extension[] = 'tar.gz';
747
        $mime_type[] = 'application/x-shockwave-flash';
748
        $extension[] = '.swf';
749
        $mime_type[] = 'application/x-stuffit';
750
        $extension[] = '.sit';
751
        $mime_type[] = 'application/x-tar';
752
        $extension[] = '.tar';
753
        $mime_type[] = 'application/zip';
754
        $extension[] = '.zip';
755
        $mime_type[] = 'application/x-tar';
756
        $extension[] = '.tar';
757
        $mime_type[] = 'text/html';
758
        $extension[] = '.html';
759
        $mime_type[] = 'text/plain';
760
        $extension[] = '.txt';
761
        $mime_type[] = 'text/rtf';
762
        $extension[] = '.rtf';
763
        $mime_type[] = 'img/gif';
764
        $extension[] = '.gif';
765
        $mime_type[] = 'img/jpeg';
766
        $extension[] = '.jpg';
767
        $mime_type[] = 'img/png';
768
        $extension[] = '.png';
769
        $mime_type[] = 'audio/midi';
770
        $extension[] = '.mid';
771
        $mime_type[] = 'audio/mpeg';
772
        $extension[] = '.mp3';
773
        $mime_type[] = 'audio/x-aiff';
774
        $extension[] = '.aif';
775
        $mime_type[] = 'audio/x-pn-realaudio';
776
        $extension[] = '.rm';
777
        $mime_type[] = 'audio/x-pn-realaudio-plugin';
778
        $extension[] = '.rpm';
779
        $mime_type[] = 'audio/x-wav';
780
        $extension[] = '.wav';
781
        $mime_type[] = 'video/mpeg';
782
        $extension[] = '.mpg';
783
        $mime_type[] = 'video/mpeg4-generic';
784
        $extension[] = '.mp4';
785
        $mime_type[] = 'video/quicktime';
786
        $extension[] = '.mov';
787
        $mime_type[] = 'video/x-msvideo';
788
        $extension[] = '.avi';
789
790
        $mime_type[] = 'video/x-ms-wmv';
791
        $extension[] = '.wmv';
792
        $mime_type[] = 'video/x-flv';
793
        $extension[] = '.flv';
794
        $mime_type[] = 'image/svg+xml';
795
        $extension[] = '.svg';
796
        $mime_type[] = 'image/svg+xml';
797
        $extension[] = '.svgz';
798
        $mime_type[] = 'video/ogg';
799
        $extension[] = '.ogv';
800
        $mime_type[] = 'audio/ogg';
801
        $extension[] = '.oga';
802
        $mime_type[] = 'application/ogg';
803
        $extension[] = '.ogg';
804
        $mime_type[] = 'application/ogg';
805
        $extension[] = '.ogx';
806
        $mime_type[] = 'application/x-freemind';
807
        $extension[] = '.mm';
808
809
        $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12';
810
        $extension[] = '.docm';
811
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
812
        $extension[] = '.docx';
813
        $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12';
814
        $extension[] = '.dotm';
815
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template';
816
        $extension[] = '.dotx';
817
        $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12';
818
        $extension[] = '.potm';
819
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template';
820
        $extension[] = '.potx';
821
        $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12';
822
        $extension[] = '.ppam';
823
        $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12';
824
        $extension[] = '.ppsm';
825
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow';
826
        $extension[] = '.ppsx';
827
        $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12';
828
        $extension[] = '.pptm';
829
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
830
        $extension[] = '.pptx';
831
        $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12';
832
        $extension[] = '.xlam';
833
        $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12';
834
        $extension[] = '.xlsb';
835
        $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12';
836
        $extension[] = '.xlsm';
837
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
838
        $extension[] = '.xlsx';
839
        $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12';
840
        $extension[] = '.xltm';
841
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template';
842
        $extension[] = '.xltx';
843
844
        // Test on PC (files with no extension get application/octet-stream)
845
        //$mime_type[] = 'application/octet-stream';      $extension[] = '.ext';
846
        // Check whether the MIME type sent by the browser is within the table
847
        foreach ($mime_type as $key => &$type) {
848
            if ($type == $file_type) {
849
                $file_name .= $extension[$key];
850
                break;
851
            }
852
        }
853
854
        unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
855
    }
856
857
    return $file_name;
858
}
859
860
/**
861
 * Manages all the unzipping process of an uploaded file.
862
 *
863
 * @author Hugues Peeters <[email protected]>
864
 *
865
 * @param array  $uploaded_file    - follows the $_FILES Structure
866
 * @param string $upload_path      - destination of the upload.
867
 *                                 This path is to append to $base_work_dir
868
 * @param string $base_work_dir    - base working directory of the module
869
 * @param int    $max_filled_space - amount of bytes to not exceed in the base
870
 *                                 working directory
871
 *
872
 * @return bool true if it succeeds false otherwise
873
 */
874
function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space)
875
{
876
    $zip_file = new PclZip($uploaded_file['tmp_name']);
877
878
    // Check the zip content (real size and file extension)
879
    if (file_exists($uploaded_file['tmp_name'])) {
880
        $zip_content_array = $zip_file->listContent();
881
        $ok_scorm = false;
882
        $realFileSize = 0;
883
        foreach ($zip_content_array as &$this_content) {
884
            if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
885
                Display::addFlash(
886
                    Display::return_message(get_lang('The zip file can not contain .PHP files'))
887
                );
888
889
                return false;
890
            } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
891
                $ok_scorm = true;
892
            } elseif (stristr($this_content['filename'], 'LMS')) {
893
                $ok_plantyn_scorm1 = true;
894
            } elseif (stristr($this_content['filename'], 'REF')) {
895
                $ok_plantyn_scorm2 = true;
896
            } elseif (stristr($this_content['filename'], 'SCO')) {
897
                $ok_plantyn_scorm3 = true;
898
            } elseif (stristr($this_content['filename'], 'AICC')) {
899
                $ok_aicc_scorm = true;
900
            }
901
            $realFileSize += $this_content['size'];
902
        }
903
904
        if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
905
            $ok_scorm = true;
906
        }
907
908
        if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
909
            Display::addFlash(
910
                Display::return_message(get_lang('This is not a valid SCORM ZIP file !'))
911
            );
912
913
            return false;
914
        }
915
916
        if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
917
            Display::addFlash(
918
                Display::return_message(get_lang('The upload has failed. Either you have exceeded your maximum quota, or there is not enough disk space.'))
919
            );
920
921
            return false;
922
        }
923
924
        // It happens on Linux that $upload_path sometimes doesn't start with '/'
925
        if ('/' != $upload_path[0] && '/' != substr($base_work_dir, -1, 1)) {
926
            $upload_path = '/'.$upload_path;
927
        }
928
929
        if ('/' == $upload_path[strlen($upload_path) - 1]) {
930
            $upload_path = substr($upload_path, 0, -1);
931
        }
932
933
        /*	Uncompressing phase */
934
935
        $save_dir = getcwd();
936
        chdir($base_work_dir.$upload_path);
937
        $unzippingState = $zip_file->extract();
938
        for ($j = 0; $j < count($unzippingState); $j++) {
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...
939
            $state = $unzippingState[$j];
940
941
            // Fix relative links in html files
942
            $extension = strrchr($state['stored_filename'], '.');
943
        }
944
        if ($dir = @opendir($base_work_dir.$upload_path)) {
945
            while ($file = readdir($dir)) {
946
                if ('.' != $file && '..' != $file) {
947
                    $filetype = 'file';
948
                    if (is_dir($base_work_dir.$upload_path.'/'.$file)) {
949
                        $filetype = 'folder';
950
                    }
951
952
                    $safe_file = api_replace_dangerous_char($file);
953
                    @rename($base_work_dir.$upload_path.'/'.$file, $base_work_dir.$upload_path.'/'.$safe_file);
954
                    set_default_settings($upload_path, $safe_file, $filetype);
955
                }
956
            }
957
958
            closedir($dir);
959
        } else {
960
            error_log('Could not create directory '.$base_work_dir.$upload_path.' to unzip files');
961
        }
962
        chdir($save_dir); // Back to previous dir position
963
    }
964
965
    return true;
966
}
967
968
/**
969
 * Manages all the unzipping process of an uploaded document
970
 * This uses the item_property table for properties of documents.
971
 *
972
 * @author Hugues Peeters <[email protected]>
973
 * @author Bert Vanderkimpen
974
 *
975
 * @param array  $courseInfo
976
 * @param array  $userInfo
977
 * @param array  $uploaded_file    - follows the $_FILES Structure
978
 * @param string $uploadPath       - destination of the upload.
979
 *                                 This path is to append to $base_work_dir
980
 * @param string $base_work_dir    - base working directory of the module
981
 * @param int    $maxFilledSpace   - amount of bytes to not exceed in the base
982
 *                                 working directory
983
 * @param int    $sessionId
984
 * @param int    $groupId          group.id
985
 * @param bool   $output           Optional. If no output not wanted on success, set to false.
986
 * @param bool   $onlyUploadFile
987
 * @param string $whatIfFileExists (only works if $onlyUploadFile is false)
988
 *
989
 * @return bool true if it succeeds false otherwise
990
 */
991
function unzip_uploaded_document(
992
    $courseInfo,
993
    $userInfo,
994
    $uploaded_file,
995
    $uploadPath,
996
    $base_work_dir,
997
    $maxFilledSpace,
998
    $sessionId = 0,
999
    $groupId = 0,
1000
    $output = true,
1001
    $onlyUploadFile = false,
1002
    $whatIfFileExists = 'overwrite'
1003
) {
1004
    if (empty($courseInfo) || empty($userInfo) || empty($uploaded_file) || empty($uploadPath)) {
1005
        return false;
1006
    }
1007
    $zip = new PclZip($uploaded_file['tmp_name']);
1008
1009
    // Check the zip content (real size and file extension)
1010
    $zip_content_array = (array) $zip->listContent();
1011
    $realSize = 0;
1012
    foreach ($zip_content_array as &$this_content) {
1013
        $realSize += $this_content['size'];
1014
    }
1015
1016
    if (!DocumentManager::enough_space($realSize, $maxFilledSpace)) {
1017
        echo Display::return_message(get_lang('There is not enough space to upload this file.'), 'error');
1018
1019
        return false;
1020
    }
1021
1022
    $folder = api_get_unique_id();
1023
    $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
1024
    mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
1025
1026
    // Uncompress zip file
1027
    // We extract using a callback function that "cleans" the path
1028
    $zip->extract(
1029
        PCLZIP_OPT_PATH,
1030
        $destinationDir,
1031
        PCLZIP_CB_PRE_EXTRACT,
1032
        'clean_up_files_in_zip',
1033
        PCLZIP_OPT_REPLACE_NEWER
1034
    );
1035
1036
    if (false === $onlyUploadFile) {
1037
        // Add all documents in the unzipped folder to the database
1038
        add_all_documents_in_folder_to_database(
1039
            $courseInfo,
1040
            $userInfo,
1041
            $base_work_dir,
1042
            $destinationDir,
1043
            $sessionId,
1044
            $groupId,
1045
            $output,
1046
            ['path' => $uploadPath],
1047
            $whatIfFileExists
1048
        );
1049
    } else {
1050
        // Copy result
1051
        $fs = new \Symfony\Component\Filesystem\Filesystem();
1052
        $fs->mirror($destinationDir, $base_work_dir.$uploadPath, null, ['overwrite']);
1053
    }
1054
1055
    if (is_dir($destinationDir)) {
1056
        rmdirr($destinationDir);
1057
    }
1058
1059
    return true;
1060
}
1061
1062
/**
1063
 * This function is a callback function that is used while extracting a zipfile
1064
 * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract.
1065
 *
1066
 * @param array $p_event
1067
 * @param array $p_header
1068
 *
1069
 * @return int (If the function returns 1, then the extraction is resumed, if 0 the path was skipped)
1070
 */
1071
function clean_up_files_in_zip($p_event, &$p_header)
1072
{
1073
    $originalStoredFileName = $p_header['stored_filename'];
1074
    $baseName = basename($originalStoredFileName);
1075
    // Skip files
1076
    $skipFiles = [
1077
        '__MACOSX',
1078
        '.Thumbs.db',
1079
        'Thumbs.db',
1080
    ];
1081
1082
    if (in_array($baseName, $skipFiles)) {
1083
        return 0;
1084
    }
1085
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
1086
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
1087
1088
    return 1;
1089
}
1090
1091
function cleanZipFilesNoRename($p_event, &$p_header)
1092
{
1093
    $originalStoredFileName = $p_header['stored_filename'];
1094
    $baseName = basename($originalStoredFileName);
1095
    // Skip files
1096
    $skipFiles = [
1097
        '__MACOSX',
1098
        '.Thumbs.db',
1099
        'Thumbs.db',
1100
    ];
1101
1102
    if (in_array($baseName, $skipFiles)) {
1103
        return 0;
1104
    }
1105
    $modifiedStoredFileName = clean_up_path($originalStoredFileName, false);
1106
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
1107
1108
    return 1;
1109
}
1110
1111
/**
1112
 * Allow .htaccess file.
1113
 *
1114
 * @param $p_event
1115
 * @param $p_header
1116
 *
1117
 * @return int
1118
 */
1119
function cleanZipFilesAllowHtaccess($p_event, &$p_header)
1120
{
1121
    $originalStoredFileName = $p_header['stored_filename'];
1122
    $baseName = basename($originalStoredFileName);
1123
1124
    $allowFiles = ['.htaccess'];
1125
    if (in_array($baseName, $allowFiles)) {
1126
        return 1;
1127
    }
1128
1129
    // Skip files
1130
    $skipFiles = [
1131
        '__MACOSX',
1132
        '.Thumbs.db',
1133
        'Thumbs.db',
1134
    ];
1135
1136
    if (in_array($baseName, $skipFiles)) {
1137
        return 0;
1138
    }
1139
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
1140
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
1141
1142
    return 1;
1143
}
1144
1145
/**
1146
 * This function cleans up a given path
1147
 * by eliminating dangerous file names and cleaning them.
1148
 *
1149
 * @param string $path
1150
 * @param bool   $replaceName
1151
 *
1152
 * @return string
1153
 *
1154
 * @see disable_dangerous_file()
1155
 * @see api_replace_dangerous_char()
1156
 */
1157
function clean_up_path($path, $replaceName = true)
1158
{
1159
    // Split the path in folders and files
1160
    $path_array = explode('/', $path);
1161
    // Clean up every folder and filename in the path
1162
    foreach ($path_array as $key => &$val) {
1163
        // We don't want to lose the dots in ././folder/file (cfr. zipfile)
1164
        if ('.' != $val) {
1165
            if ($replaceName) {
1166
                $val = api_replace_dangerous_char($val);
1167
            }
1168
            $val = disable_dangerous_file($val);
1169
        }
1170
    }
1171
    // Join the "cleaned" path (modified in-place as passed by reference)
1172
    $path = implode('/', $path_array);
1173
    filter_extension($path);
1174
1175
    return $path;
1176
}
1177
1178
/**
1179
 * Checks if the file is dangerous, based on extension and/or mimetype.
1180
 * The list of extensions accepted/rejected can be found from
1181
 * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include').
1182
 *
1183
 * @param string $filename passed by reference. The filename will be modified
1184
 *                         if filter rules say so! (you can include path but the filename should look like 'abc.html')
1185
 *
1186
 * @return int 0 to skip file, 1 to keep file
1187
 */
1188
function filter_extension(&$filename)
1189
{
1190
    if ('/' == substr($filename, -1)) {
1191
        return 1; // Authorize directories
1192
    }
1193
    $blacklist = api_get_setting('upload_extensions_list_type');
1194
    if ('whitelist' != $blacklist) { // if = blacklist
1195
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
1196
1197
        $skip = api_get_setting('upload_extensions_skip');
1198
        $ext = strrchr($filename, '.');
1199
        $ext = substr($ext, 1);
1200
        if (empty($ext)) {
1201
            return 1; // We're in blacklist mode, so accept empty extensions
1202
        }
1203
        if (in_array(strtolower($ext), $extensions)) {
1204
            if ('true' == $skip) {
1205
                return 0;
1206
            } else {
1207
                $new_ext = api_get_setting('upload_extensions_replace_by');
1208
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1209
1210
                return 1;
1211
            }
1212
        } else {
1213
            return 1;
1214
        }
1215
    } else {
1216
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_whitelist')));
1217
        $skip = api_get_setting('upload_extensions_skip');
1218
        $ext = strrchr($filename, '.');
1219
        $ext = substr($ext, 1);
1220
        if (empty($ext)) {
1221
            return 1; // Accept empty extensions
1222
        }
1223
        if (!in_array(strtolower($ext), $extensions)) {
1224
            if ('true' == $skip) {
1225
                return 0;
1226
            } else {
1227
                $new_ext = api_get_setting('upload_extensions_replace_by');
1228
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1229
1230
                return 1;
1231
            }
1232
        } else {
1233
            return 1;
1234
        }
1235
    }
1236
}
1237
1238
/**
1239
 * Updates an existing document in the database
1240
 * as the file exists, we only need to change the size.
1241
 *
1242
 * @param array $_course
1243
 * @param int   $documentId
1244
 * @param int   $filesize
1245
 * @param int   $readonly
1246
 *
1247
 * @return bool true /false
1248
 */
1249
function update_existing_document($_course, $documentId, $filesize, $readonly = 0)
1250
{
1251
    $document_table = Database::get_course_table(TABLE_DOCUMENT);
1252
    $documentId = intval($documentId);
1253
    $filesize = intval($filesize);
1254
    $readonly = intval($readonly);
1255
    $course_id = $_course['real_id'];
1256
1257
    $sql = "UPDATE $document_table SET
1258
            size = '$filesize',
1259
            readonly = '$readonly'
1260
			WHERE c_id = $course_id AND id = $documentId";
1261
    if (Database::query($sql)) {
1262
        return true;
1263
    } else {
1264
        return false;
1265
    }
1266
}
1267
1268
/**
1269
 * This function updates the last_edit_date, last edit user id on all folders in a given path.
1270
 *
1271
 * @param array  $_course
1272
 * @param string $path
1273
 * @param int    $user_id
1274
 */
1275
function item_property_update_on_folder($_course, $path, $user_id)
1276
{
1277
    // If we are in the root, just return... no need to update anything
1278
    if ('/' == $path) {
1279
        return;
1280
    }
1281
1282
    $user_id = intval($user_id);
1283
1284
    // If the given path ends with a / we remove it
1285
    $endchar = substr($path, strlen($path) - 1, 1);
1286
    if ('/' == $endchar) {
1287
        $path = substr($path, 0, strlen($path) - 1);
1288
    }
1289
1290
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
1291
1292
    // Get the time
1293
    $time = api_get_utc_datetime();
1294
1295
    // Det all paths in the given path
1296
    // /folder/subfolder/subsubfolder/file
1297
    // if file is updated, subsubfolder, subfolder and folder are updated
1298
    $exploded_path = explode('/', $path);
1299
    $course_id = api_get_course_int_id();
1300
    $newpath = '';
1301
    foreach ($exploded_path as $key => &$value) {
1302
        // We don't want a slash before our first slash
1303
        if (0 != $key) {
1304
            $newpath .= '/'.$value;
1305
            // Select ID of given folder
1306
            $folder_id = DocumentManager::get_document_id($_course, $newpath);
1307
1308
            if ($folder_id) {
1309
                $sql = "UPDATE $table SET
1310
				        lastedit_date = '$time',
1311
				        lastedit_type = 'DocumentInFolderUpdated',
1312
				        lastedit_user_id='$user_id'
1313
						WHERE
1314
						    c_id = $course_id AND
1315
						    tool='".TOOL_DOCUMENT."' AND
1316
						    ref = '$folder_id'";
1317
                Database::query($sql);
1318
            }
1319
        }
1320
    }
1321
}
1322
1323
/**
1324
 * Adds file to document table in database
1325
 * deprecated: use file_set_default_settings instead.
1326
 *
1327
 * @author	Olivier Cauberghe <[email protected]>
1328
 *
1329
 * @param	path,filename
1330
 * action:	Adds an entry to the document table with the default settings
1331
 */
1332
function set_default_settings($upload_path, $filename, $filetype = 'file')
1333
{
1334
    $dbTable = Database::get_course_table(TABLE_DOCUMENT);
1335
    global $default_visibility;
1336
1337
    if (!$default_visibility) {
1338
        $default_visibility = 'v';
1339
    }
1340
    $filetype = Database::escape_string($filetype);
1341
1342
    $upload_path = str_replace('\\', '/', $upload_path);
1343
    $upload_path = str_replace('//', '/', $upload_path);
1344
1345
    if ('/' == $upload_path) {
1346
        $upload_path = '';
1347
    } elseif (!empty($upload_path) && '/' != $upload_path[0]) {
1348
        $upload_path = "/$upload_path";
1349
    }
1350
1351
    $endchar = substr($filename, strlen($filename) - 1, 1);
1352
1353
    if ('/' == $endchar) {
1354
        $filename = substr($filename, 0, -1);
1355
    }
1356
    $filename = Database::escape_string($filename);
1357
    $query = "SELECT count(*) as bestaat FROM $dbTable
1358
              WHERE path='$upload_path/$filename'";
1359
    $result = Database::query($query);
1360
    $row = Database::fetch_array($result);
1361
    if ($row['bestaat'] > 0) {
1362
        $query = "UPDATE $dbTable SET
1363
		            path='$upload_path/$filename',
1364
		            visibility='$default_visibility',
1365
		            filetype='$filetype'
1366
		          WHERE path='$upload_path/$filename'";
1367
    } else {
1368
        $query = "INSERT INTO $dbTable (path,visibility,filetype)
1369
		          VALUES('$upload_path/$filename','$default_visibility','$filetype')";
1370
    }
1371
    Database::query($query);
1372
}
1373
1374
/**
1375
 * Retrieves the image path list in a html file.
1376
 *
1377
 * @author Hugues Peeters <[email protected]>
1378
 *
1379
 * @param string $html_file
1380
 *
1381
 * @return array -  images path list
1382
 */
1383
function search_img_from_html($html_file)
1384
{
1385
    $img_path_list = [];
1386
1387
    if (!$fp = fopen($html_file, 'r')) {
1388
        return;
1389
    }
1390
1391
    // Aearch and store occurences of the <img> tag in an array
1392
    $size_file = (0 === filesize($html_file)) ? 1 : filesize($html_file);
1393
    if (isset($fp) && false !== $fp) {
1394
        $buffer = fread($fp, $size_file);
1395
        if (strlen($buffer) >= 0 && false !== $buffer) {
1396
        } else {
1397
            exit('<center>Can not read file.</center>');
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...
1398
        }
1399
    } else {
1400
        exit('<center>Can not read file.</center>');
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...
1401
    }
1402
    $matches = [];
1403
    if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
1404
        $img_tag_list = $matches[0];
1405
    }
1406
1407
    fclose($fp);
1408
    unset($buffer);
1409
1410
    // Search the image file path from all the <IMG> tag detected
1411
1412
    if (sizeof($img_tag_list) > 0) {
1413
        foreach ($img_tag_list as &$this_img_tag) {
1414
            if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
1415
                $img_path_list[] = $matches[1];
1416
            }
1417
        }
1418
        $img_path_list = array_unique($img_path_list); // Remove duplicate entries
1419
    }
1420
1421
    return $img_path_list;
1422
}
1423
1424
/**
1425
 * Creates a new directory trying to find a directory name
1426
 * that doesn't already exist.
1427
 *
1428
 * @author  Hugues Peeters <[email protected]>
1429
 * @author  Bert Vanderkimpen
1430
 *
1431
 * @param array  $_course                 current course information
1432
 * @param int    $user_id                 current user id
1433
 * @param int    $session_id
1434
 * @param int    $to_group_id             group.id
1435
 * @param int    $to_user_id
1436
 * @param string $base_work_dir           /var/www/chamilo/courses/ABC/document
1437
 * @param string $desired_dir_name        complete path of the desired name
1438
 *                                        Example: /folder1/folder2
1439
 * @param string $title                   "folder2"
1440
 * @param int    $visibility              (0 for invisible, 1 for visible, 2 for deleted)
1441
 * @param bool   $generateNewNameIfExists
1442
 * @param bool   $sendNotification        depends in conf setting "send_notification_when_document_added"
1443
 * @param array  $parentInfo
1444
 *
1445
 * @return CDocument|false
1446
 */
1447
function create_unexisting_directory(
1448
    $_course,
1449
    $user_id,
1450
    $session_id,
1451
    $to_group_id,
1452
    $to_user_id,
1453
    $base_work_dir,
1454
    $desired_dir_name,
1455
    $title = '',
1456
    $visibility = '',
1457
    $generateNewNameIfExists = false,
1458
    $sendNotification = true,
1459
    $parentInfo = null
1460
) {
1461
    $course_id = $_course['real_id'];
1462
    $session_id = (int) $session_id;
1463
1464
    $parentId = 0;
1465
    if (!empty($parentInfo)) {
1466
        if (is_array($parentInfo) && isset($parentInfo['iid'])) {
1467
            $parentId = $parentInfo['iid'];
1468
        }
1469
        if ($parentInfo instanceof CDocument) {
1470
            $parentId = $parentInfo->getIid();
1471
        }
1472
    }
1473
1474
    $document = DocumentManager::addDocument(
1475
        $_course,
1476
        $desired_dir_name,
1477
        'folder',
1478
        0,
1479
        $title,
1480
        null,
1481
        0,
1482
        true,
1483
        $to_group_id,
1484
        $session_id,
1485
        $user_id,
1486
        $sendNotification,
1487
        '',
1488
        $parentId
1489
    );
1490
1491
    if ($document) {
1492
        return $document;
1493
    }
1494
1495
    $folderExists = DocumentManager::folderExists(
1496
        $desired_dir_name,
1497
        $_course,
1498
        $session_id,
1499
        $to_group_id
1500
    );
1501
1502
    if (true === $folderExists) {
1503
        if ($generateNewNameIfExists) {
1504
            $counter = 1;
1505
            while (1) {
1506
                $folderExists = DocumentManager::folderExists(
1507
                    $desired_dir_name.'_'.$counter,
1508
                    $_course,
1509
                    $session_id,
1510
                    $to_group_id
1511
                );
1512
1513
                if (false === $folderExists) {
1514
                    break;
1515
                }
1516
                $counter++;
1517
            }
1518
            $desired_dir_name = $desired_dir_name.'_'.$counter;
1519
        }
1520
    }
1521
1522
    $systemFolderName = $desired_dir_name;
1523
1524
    // Adding suffix
1525
    $suffix = DocumentManager::getDocumentSuffix(
1526
        $_course,
1527
        $session_id,
1528
        $to_group_id
1529
    );
1530
1531
    $systemFolderName .= $suffix;
1532
1533
    if (null == $title) {
1534
        $title = basename($desired_dir_name);
1535
    }
1536
1537
    // Check if pathname already exists inside document table
1538
    $table = Database::get_course_table(TABLE_DOCUMENT);
1539
    $sql = "SELECT iid, path FROM $table
1540
            WHERE
1541
                c_id = $course_id AND
1542
                path = '".Database::escape_string($systemFolderName)."'";
1543
    $rs = Database::query($sql);
1544
1545
    $parentId = 0;
1546
    if (!empty($parentInfo) && isset($parentInfo['iid'])) {
1547
        $parentId = $parentInfo['iid'];
1548
    }
1549
1550
    if (0 == Database::num_rows($rs)) {
1551
        $document = DocumentManager::addDocument(
1552
            $_course,
1553
            $systemFolderName,
1554
            'folder',
1555
            0,
1556
            $title,
1557
            null,
1558
            0,
1559
            true,
1560
            $to_group_id,
1561
            $session_id,
1562
            $user_id,
1563
            $sendNotification,
1564
            '',
1565
            $parentId
1566
        );
1567
1568
        if ($document) {
1569
            return $document;
1570
        }
1571
    } else {
1572
        $document = Database::fetch_array($rs);
1573
        $documentData = DocumentManager::get_document_data_by_id(
1574
            $document['iid'],
1575
            $_course['code'],
1576
            false,
1577
            $session_id
1578
        );
1579
1580
        if ($documentData) {
1581
            $document = Container::getDocumentRepository()->find($documentData['iid']);
1582
1583
            return $document;
1584
        }
1585
    }
1586
1587
    return false;
1588
}
1589
1590
/**
1591
 * Handles uploaded missing images.
1592
 *
1593
 * @author Hugues Peeters <[email protected]>
1594
 * @author Bert Vanderkimpen
1595
 *
1596
 * @param array  $_course
1597
 * @param array  $uploaded_file_collection - follows the $_FILES Structure
1598
 * @param string $base_work_dir
1599
 * @param string $missing_files_dir
1600
 * @param int    $user_id
1601
 * @param int    $to_group_id              group.id
1602
 */
1603
function move_uploaded_file_collection_into_directory(
1604
    $_course,
1605
    $uploaded_file_collection,
1606
    $base_work_dir,
1607
    $missing_files_dir,
1608
    $user_id,
1609
    $to_group_id,
1610
    $to_user_id,
1611
    $max_filled_space
1612
) {
1613
    $number_of_uploaded_images = count($uploaded_file_collection['name']);
1614
    $list = [];
1615
    for ($i = 0; $i < $number_of_uploaded_images; $i++) {
1616
        $missing_file['name'] = $uploaded_file_collection['name'][$i];
1617
        $missing_file['type'] = $uploaded_file_collection['type'][$i];
1618
        $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
1619
        $missing_file['error'] = $uploaded_file_collection['error'][$i];
1620
        $missing_file['size'] = $uploaded_file_collection['size'][$i];
1621
1622
        $upload_ok = process_uploaded_file($missing_file);
1623
        if ($upload_ok) {
1624
            $list[] = handle_uploaded_document(
1625
                $_course,
1626
                $missing_file,
1627
                $base_work_dir,
1628
                $missing_files_dir,
1629
                $user_id,
1630
                $to_group_id,
1631
                $to_user_id,
1632
                $max_filled_space,
1633
                0,
1634
                'overwrite'
1635
            );
1636
        }
1637
        unset($missing_file);
1638
    }
1639
1640
    return $list;
1641
}
1642
1643
/**
1644
 * Opens the old html file and replace the src path into the img tag
1645
 * This also works for files in subdirectories.
1646
 *
1647
 * @param $original_img_path is an array
1648
 * @param $new_img_path is an array
1649
 */
1650
function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file)
1651
{
1652
    // Open the file
1653
    $fp = fopen($html_file, 'r');
1654
    $buffer = fread($fp, filesize($html_file));
1655
    $new_html_content = '';
1656
1657
    // Fix the image tags
1658
    for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb; $i++) {
1659
        $replace_what = $original_img_path[$i];
1660
        // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
1661
        $exploded_file_path = explode('/', $new_img_path[$i]);
1662
        $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
1663
        $buffer = str_replace($replace_what, $replace_by, $buffer);
1664
    }
1665
1666
    $new_html_content .= $buffer;
1667
1668
    @fclose($fp);
1669
1670
    // Write the resulted new file
1671
1672
    if (!$fp = fopen($html_file, 'w')) {
1673
        return;
1674
    }
1675
1676
    if (!fwrite($fp, $new_html_content)) {
1677
        return;
1678
    }
1679
}
1680
1681
/**
1682
 * Checks the extension of a file, if it's .htm or .html
1683
 * we use search_img_from_html to get all image paths in the file.
1684
 *
1685
 * @param string $file
1686
 *
1687
 * @return array paths
1688
 *
1689
 * @see check_for_missing_files() uses search_img_from_html()
1690
 */
1691
function check_for_missing_files($file)
1692
{
1693
    if ('.htm' == strrchr($file, '.') || '.html' == strrchr($file, '.')) {
1694
        $img_file_path = search_img_from_html($file);
1695
1696
        return $img_file_path;
1697
    }
1698
1699
    return false;
1700
}
1701
1702
/**
1703
 * This function builds a form that asks for the missing images in a html file
1704
 * maybe we should do this another way?
1705
 *
1706
 * @param array  $missing_files
1707
 * @param string $upload_path
1708
 * @param string $file_name
1709
 *
1710
 * @return string the form
1711
 */
1712
function build_missing_files_form($missing_files, $upload_path, $file_name)
1713
{
1714
    // Do we need a / or not?
1715
    $added_slash = ('/' == $upload_path) ? '' : '/';
1716
    $folder_id = DocumentManager::get_document_id(api_get_course_info(), $upload_path);
1717
    // Build the form
1718
    $form = "<p><strong>".get_lang('Missing images detected')."</strong></p>"
1719
        ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">"
1720
        // Related_file is the path to the file that has missing images
1721
        ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />"
1722
        ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />"
1723
        ."<input type=\"hidden\" name=\"id\" value=\"".$folder_id."\" />"
1724
        ."<table border=\"0\">";
1725
    foreach ($missing_files as &$this_img_file_path) {
1726
        $form .= "<tr>"
1727
            ."<td>".basename($this_img_file_path)." : </td>"
1728
            ."<td>"
1729
            ."<input type=\"file\" name=\"img_file[]\"/>"
1730
            ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
1731
            ."</td>"
1732
            ."</tr>";
1733
    }
1734
    $form .= "</table>"
1735
        ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
1736
        ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Validate')."\" class=\"save\">".get_lang('Validate')."</button>"
1737
        ."</form>";
1738
1739
    return $form;
1740
}
1741
1742
/**
1743
 * This recursive function can be used during the upgrade process form older
1744
 * versions of Chamilo
1745
 * It crawls the given directory, checks if the file is in the DB and adds
1746
 * it if it's not.
1747
 *
1748
 * @param array  $courseInfo
1749
 * @param array  $userInfo
1750
 * @param string $base_work_dir    course document dir
1751
 * @param string $folderPath       folder to read
1752
 * @param int    $sessionId
1753
 * @param int    $groupId          group.id
1754
 * @param bool   $output
1755
 * @param array  $parent
1756
 * @param string $whatIfFileExists
1757
 *
1758
 * @return bool
1759
 */
1760
function add_all_documents_in_folder_to_database(
1761
    $courseInfo,
1762
    $userInfo,
1763
    $base_work_dir,
1764
    $folderPath,
1765
    $sessionId = 0,
1766
    $groupId = 0,
1767
    $output = false,
1768
    $parent = [],
1769
    $whatIfFileExists = 'overwrite'
1770
) {
1771
    if (empty($userInfo) || empty($courseInfo)) {
1772
        return false;
1773
    }
1774
1775
    $userId = $userInfo['user_id'];
1776
1777
    // Open dir
1778
    $handle = opendir($folderPath);
1779
1780
    if (is_dir($folderPath)) {
1781
        // Run trough
1782
        while ($file = readdir($handle)) {
1783
            if ('.' == $file || '..' == $file) {
1784
                continue;
1785
            }
1786
1787
            $parentPath = '';
1788
            if (!empty($parent) && isset($parent['path'])) {
1789
                $parentPath = $parent['path'];
1790
                if ('/' == $parentPath) {
1791
                    $parentPath = '';
1792
                }
1793
            }
1794
1795
            $completePath = $parentPath.'/'.$file;
1796
            $sysFolderPath = $folderPath.'/'.$file;
1797
1798
            // Is directory?
1799
            if (is_dir($sysFolderPath)) {
1800
                $folderExists = DocumentManager::folderExists(
1801
                    $completePath,
1802
                    $courseInfo,
1803
                    $sessionId,
1804
                    $groupId
1805
                );
1806
1807
                if (true === $folderExists) {
1808
                    switch ($whatIfFileExists) {
1809
                        case 'overwrite':
1810
                            $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
1811
                            if ($documentId) {
1812
                                $newFolderData = DocumentManager::get_document_data_by_id(
1813
                                    $documentId,
1814
                                    $courseInfo['code'],
1815
                                    false,
1816
                                    $sessionId
1817
                                );
1818
                            }
1819
                            break;
1820
                        case 'rename':
1821
                            $newFolderData = create_unexisting_directory(
1822
                                $courseInfo,
1823
                                $userId,
1824
                                $sessionId,
1825
                                $groupId,
1826
                                null,
1827
                                $base_work_dir,
1828
                                $completePath,
1829
                                null,
1830
                                null,
1831
                                true
1832
                            );
1833
                            break;
1834
                        case 'nothing':
1835
                            if ($output) {
1836
                                $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
1837
                                if ($documentId) {
1838
                                    $folderData = DocumentManager::get_document_data_by_id(
1839
                                        $documentId,
1840
                                        $courseInfo['code'],
1841
                                        false,
1842
                                        $sessionId
1843
                                    );
1844
                                    Display::addFlash(
1845
                                        Display::return_message(
1846
                                            $folderData['path'].' '.get_lang(' already exists.'),
1847
                                            'warning'
1848
                                        )
1849
                                    );
1850
                                }
1851
                            }
1852
                            continue 2;
1853
                            break;
1854
                    }
1855
                } else {
1856
                    $newFolderData = create_unexisting_directory(
1857
                        $courseInfo,
1858
                        $userId,
1859
                        $sessionId,
1860
                        $groupId,
1861
                        null,
1862
                        $base_work_dir,
1863
                        $completePath,
1864
                        null,
1865
                        null,
1866
                        false
1867
                    );
1868
                }
1869
1870
                // Recursive
1871
                add_all_documents_in_folder_to_database(
1872
                    $courseInfo,
1873
                    $userInfo,
1874
                    $base_work_dir,
1875
                    $sysFolderPath,
1876
                    $sessionId,
1877
                    $groupId,
1878
                    $output,
1879
                    $newFolderData,
1880
                    $whatIfFileExists
1881
                );
1882
            } else {
1883
                // Rename
1884
                $uploadedFile = [
1885
                    'name' => $file,
1886
                    'tmp_name' => $sysFolderPath,
1887
                    'size' => filesize($sysFolderPath),
1888
                    'type' => null,
1889
                    'from_file' => true,
1890
                    'move_file' => true,
1891
                ];
1892
1893
                handle_uploaded_document(
1894
                    $courseInfo,
1895
                    $uploadedFile,
1896
                    $base_work_dir,
1897
                    $parentPath,
1898
                    $userId,
1899
                    $groupId,
1900
                    null,
1901
                    0,
1902
                    $whatIfFileExists,
1903
                    $output,
1904
                    false,
1905
                    null,
1906
                    $sessionId
1907
                );
1908
            }
1909
        }
1910
    }
1911
}
1912