1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4use ILIAS\File\Sanitation\FilePathSanitizer;
5use ILIAS\Filesystem\Exception\FileNotFoundException;
6use ILIAS\Filesystem\Util\LegacyPathHelper;
7use ILIAS\FileUpload\Location;
8
9require_once("Services/Object/classes/class.ilObject2.php");
10require_once('Modules/File/classes/class.ilFSStorageFile.php');
11
12/**
13 * Class ilObjFile
14 *
15 * @author  Sascha Hofmann <shofmann@databay.de>
16 * @author  Fabian Schmid <fs@studer-raimann.ch>
17 * @version $Id$
18 *
19 * @ingroup ModulesFile
20 */
21class ilObjFile extends ilObject2
22{
23    const MODE_FILELIST = "filelist";
24    const MODE_OBJECT = "object";
25    /**
26     * @var bool
27     */
28    protected $no_meta_data_creation;
29    /**
30     * @var string
31     */
32    protected $filename = '';
33    /**
34     * @var string
35     */
36    protected $filetype = '';
37    /**
38     * @var string
39     */
40    protected $filemaxsize = "20000000";    // not used yet
41    /**
42     * @var string
43     */
44    protected $filesize;
45    /**
46     * @var bool
47     */
48    public $raise_upload_error;
49    /**
50     * @var string
51     */
52    public $mode = self::MODE_OBJECT;
53    /**
54     * @var int
55     */
56    protected $page_count = 0;
57    /**
58     * @var bool
59     */
60    protected $rating = false;
61    /**
62     * @var \ilFSStorageFile
63     */
64    private $file_storage = null;
65    /**
66     * @var \ilLogger
67     */
68    protected $log = null;
69    /**
70     * @var int
71     */
72    protected $version = 1;
73    /**
74     * @var int
75     */
76    protected $max_version = 1;
77    /**
78     * @var string
79     */
80    protected $action = null;
81    /**
82     * @var int
83     */
84    protected $rollback_version = null;
85    /**
86     * @var int
87     */
88    protected $rollback_user_id = null;
89
90
91    /**
92     * ilObjFile constructor.
93     *
94     * @param int  $a_id                ID of the object, ref_id or obj_id possible
95     * @param bool $a_call_by_reference defines the $a_id a ref_id
96     */
97    public function __construct($a_id = 0, $a_call_by_reference = true)
98    {
99        $this->version = 0;
100        $this->max_version = 0;
101        $this->raise_upload_error = true;
102
103        $this->log = ilLoggerFactory::getLogger('file');
104
105        parent::__construct($a_id, $a_call_by_reference);
106
107        if ($this->getId()) {
108            $this->initFileStorage();
109        }
110    }
111
112
113    public function initType()
114    {
115        $this->type = "file";
116    }
117
118
119    /**
120     * create object
121     *
122     * @param bool upload mode (if enabled no entries in file_data will be done)
123     */
124    protected function doCreate($a_upload = false)
125    {
126        $this->createProperties($a_upload);
127    }
128
129
130    /**
131     * The basic properties of a file object are stored in table object_data.
132     * This is not sufficient for a file object. Therefore we create additional
133     * properties in table file_data.
134     * This method has been put into a separate operation, to allow a WebDAV Null resource
135     * (class.ilObjNull.php) to become a file object.
136     */
137    public function createProperties($a_upload = false)
138    {
139        global $DIC;
140
141        // Create file directory
142        $this->initFileStorage();
143        $this->file_storage->create();
144
145        if ($a_upload) {
146            return true;
147        }
148
149        // not upload mode
150        ilHistory::_createEntry($this->getId(), "create", $this->getFileName() . ",1" . ",1");
151        $this->addNewsNotification("file_created");
152
153        // New Item
154        $default_visibility = ilNewsItem::_getDefaultVisibilityForRefId($_GET['ref_id']);
155        if ($default_visibility == "public") {
156            ilBlockSetting::_write("news", "public_notifications", 1, 0, $this->getId());
157        }
158
159        // log creation
160        $this->log->debug("ilObjFile::createProperties, ID: " . $this->getId() . ", Name: "
161            . $this->getFileName() . ", Type: " . $this->getFileType() . ", Size: "
162            . $this->getFileSize() . ", Mode: " . $this->getMode() . ", Name(Bytes): "
163            . implode(":", ilStr::getBytesForString($this->getFileName())));
164        $this->log->logStack(ilLogLevel::DEBUG);
165
166        $DIC->database()->insert('file_data', $this->getArrayForDatabase());
167
168        //add metadata to database
169        $metadata = [
170            'meta_lifecycle_id' => ['integer', $DIC->database()->nextId('il_meta_lifecycle')],
171            'rbac_id' => ['integer', $this->getId()],
172            'obj_id' => ['integer', $this->getId()],
173            'obj_type' => ['text', "file"],
174            'meta_version' => ['integer', (int) $this->getVersion()],
175        ];
176        $DIC->database()->insert('il_meta_lifecycle', $metadata);
177
178        // no meta data handling for file list files
179        if ($this->getMode() != self::MODE_FILELIST) {
180            $this->createMetaData();
181        }
182    }
183
184
185    /**
186     * @param bool $a_status
187     */
188    public function setNoMetaDataCreation($a_status)
189    {
190        $this->no_meta_data_creation = (bool) $a_status;
191    }
192
193
194    protected function beforeCreateMetaData()
195    {
196        return !(bool) $this->no_meta_data_creation;
197    }
198
199
200    protected function beforeUpdateMetaData()
201    {
202        return !(bool) $this->no_meta_data_creation;
203    }
204
205
206    /**
207     * create file object meta data
208     */
209    protected function doCreateMetaData()
210    {
211        // add technical section with file size and format
212        $md_obj = new ilMD($this->getId(), 0, $this->getType());
213        $technical = $md_obj->addTechnical();
214        $technical->setSize($this->getFileSize());
215        $technical->save();
216        $format = $technical->addFormat();
217        $format->setFormat($this->getFileType());
218        $format->save();
219        $technical->update();
220    }
221
222
223    protected function beforeMDUpdateListener($a_element)
224    {
225        // Check file extension
226        // Removing the file extension is not allowed
227        include_once 'Services/MetaData/classes/class.ilMD.php';
228        $md = new ilMD($this->getId(), 0, $this->getType());
229        if (!is_object($md_gen = $md->getGeneral())) {
230            return false;
231        }
232        $title = $this->checkFileExtension($this->getFileName(), $md_gen->getTitle());
233        $md_gen->setTitle($title);
234        $md_gen->update();
235
236        return true;
237    }
238
239
240    protected function doMDUpdateListener($a_element)
241    {
242        // handling for technical section
243        include_once 'Services/MetaData/classes/class.ilMD.php';
244
245        switch ($a_element) {
246            case 'Technical':
247
248                // Update Format (size is not stored in db)
249                $md = new ilMD($this->getId(), 0, $this->getType());
250                if (!is_object($md_technical = $md->getTechnical())) {
251                    return false;
252                }
253
254                foreach ($md_technical->getFormatIds() as $id) {
255                    $md_format = $md_technical->getFormat($id);
256                    ilObjFile::_writeFileType($this->getId(), $md_format->getFormat());
257                    $this->setFileType($md_format->getFormat());
258                    break;
259                }
260
261                break;
262        }
263
264        return true;
265    }
266
267
268    /**
269     * @param int $a_version
270     *
271     * @return string
272     */
273    public function getDirectory($a_version = 0)
274    {
275        $version_subdir = "";
276
277        if ($a_version) {
278            // BEGIN WebDAV Avoid double slash before version subdirectory
279            $version_subdir = sprintf("%03d", $a_version);
280            // END WebDAV Avoid  double slash before version subdirectory
281        }
282
283        if (!is_object($this->file_storage)) {
284            $this->initFileStorage();
285        }
286
287        $str = $this->file_storage->getAbsolutePath() . '/' . $version_subdir;
288
289        return $str;
290    }
291
292
293    public function createDirectory()
294    {
295        ilUtil::makeDirParents($this->getDirectory());
296    }
297
298
299    public function raiseUploadError($a_raise = true)
300    {
301        $this->raise_upload_error = $a_raise;
302    }
303
304
305    /**
306     * @param      $a_upload_file
307     * @param      $a_filename
308     * @param bool $a_prevent_preview
309     *
310     * @return \ILIAS\FileUpload\DTO\UploadResult
311     * @throws \ILIAS\FileUpload\Collection\Exception\NoSuchElementException
312     * @throws \ILIAS\FileUpload\Exception\IllegalStateException
313     */
314    public function getUploadFile($a_upload_file, $a_filename, $a_prevent_preview = false)
315    {
316        global $DIC;
317
318        $upload = $DIC->upload();
319        $result = null;
320
321        if ($upload->hasUploads()) {
322            if (!$upload->hasBeenProcessed()) {
323                $upload->process();
324            }
325            /**
326             * @var $result \ILIAS\FileUpload\DTO\UploadResult
327             */
328            $result = $upload->getResults()[$a_upload_file];
329            if ($result->getStatus()->getCode() === \ILIAS\FileUpload\DTO\ProcessingStatus::OK) {
330                $metadata = $result->getMetaData();
331                if ($metadata->has(ilCountPDFPagesPreProcessors::PAGE_COUNT)) {
332                    $this->setPageCount($metadata->get(ilCountPDFPagesPreProcessors::PAGE_COUNT));
333                    $this->doUpdate();
334                }
335                $a_name = $result->getName();
336                $this->setFileName($a_name);
337
338                // bugfix mantis 26236:
339                // ensure that version and max_version are correct to prevent the upload file from being versioned incorrectly
340                if ($this->getVersion() > 0) {
341                    $file_hist_entries = (array) ilHistory::_getEntriesForObject($this->getId(), $this->getType());
342                    $highest_version = 0;
343                    foreach ($file_hist_entries as $file_hist_entry) {
344                        $version = $this->parseInfoParams($file_hist_entry)['version'];
345                        if ($version > $highest_version) {
346                            $highest_version = $version;
347                        }
348                    }
349                    if ($this->getVersion() < $highest_version) {
350                        $this->setVersion($highest_version);
351                    }
352                    if ($this->getVersion() > $this->getMaxVersion()) {
353                        $this->setMaxVersion($this->getVersion());
354                    }
355                }
356
357                $this->setVersion($this->getMaxVersion() + 1);
358                $this->setMaxVersion($this->getMaxVersion() + 1);
359
360                if (!is_dir($this->getDirectory($this->getVersion()))) {
361                    ilUtil::makeDirParents($this->getDirectory($this->getVersion()));
362                }
363
364                $target_directory = $this->getDirectory($this->getVersion()) . "/";
365                $relative_path_to_file = LegacyPathHelper::createRelativePath($target_directory);
366
367                $upload->moveOneFileTo($result, $relative_path_to_file, Location::STORAGE);
368
369                $this->handleQuotaUpdate($this);
370
371                // create preview?
372                if (!$a_prevent_preview) {
373                    $this->createPreview(false);
374                }
375            } else {
376                throw new ilFileException('not supported File');
377            }
378        }
379
380        return $result;
381    }
382
383
384    /**
385     * @param $a_upload_file
386     * @param $a_filename
387     *
388     * @return \ILIAS\FileUpload\DTO\UploadResult
389     * @throws \ILIAS\FileUpload\Collection\Exception\NoSuchElementException
390     * @throws \ILIAS\FileUpload\Exception\IllegalStateException
391     */
392    public function replaceFile($a_upload_file, $a_filename)
393    {
394        if ($result = $this->getUploadFile($a_upload_file, $a_filename, true)) {
395            ilHistory::_createEntry($this->getId(), "replace", $a_filename . "," . $this->getVersion() . "," . $this->getMaxVersion());
396            $this->addNewsNotification("file_updated");
397
398            // create preview
399            $this->createPreview(true);
400        }
401
402        return $result;
403    }
404
405
406    /**
407     * @param $a_upload_file
408     * @param $a_filename
409     *
410     * @return \ILIAS\FileUpload\DTO\UploadResult
411     * @throws \ILIAS\FileUpload\Collection\Exception\NoSuchElementException
412     * @throws \ILIAS\FileUpload\Exception\IllegalStateException
413     */
414    public function addFileVersion($a_upload_file, $a_filename)
415    {
416        if ($result = $this->getUploadFile($a_upload_file, $a_filename, true)) {
417            ilHistory::_createEntry($this->getId(), "new_version", $result->getName() . "," . $this->getVersion() . "," . $this->getMaxVersion());
418            $this->addNewsNotification("file_updated");
419
420            // create preview
421            $this->createPreview($this->getVersion() > 1);
422        }
423
424        return $result;
425    }
426
427
428    /**
429     * copy file
430     */
431    public function copy($a_source, $a_destination)
432    {
433        return copy($a_source, $this->getDirectory() . "/" . $a_destination);
434    }
435
436
437    /**
438     * clear data directory
439     */
440    public function clearDataDirectory()
441    {
442        ilUtil::delDir($this->getDirectory());
443        $this->createDirectory();
444    }
445
446
447    /**
448     * Deletes the specified history entries or all entries if no ids are specified.
449     *
450     * @param array $a_hist_entry_ids The ids of the entries to delete or null to delete all entries
451     */
452    public function deleteVersions($a_hist_entry_ids = null)
453    {
454        if ($a_hist_entry_ids == null || count($a_hist_entry_ids) < 1) {
455            $this->clearDataDirectory();
456
457            ilHistory::_removeEntriesForObject($this->getId());
458
459            self::handleQuotaUpdate($this);
460        } else {
461            $actualVersionDeleted = false;
462
463            // get all versions
464            $versions = $this->getVersions();
465
466            // delete each version
467            foreach ($a_hist_entry_ids as $hist_id) {
468                $entry = null;
469
470                // get version
471                foreach ($versions as $index => $version) {
472                    if ($version["hist_entry_id"] == $hist_id) {
473                        // remove each history entry
474                        ilHistory::_removeEntryByHistoryID($hist_id);
475
476                        // delete directory
477                        $version_dir = $this->getDirectory($version["version"]);
478                        ilUtil::delDir($version_dir);
479
480                        // is actual version?
481                        if ($version["version"] == $this->getVersion()) {
482                            $actualVersionDeleted = true;
483                        }
484
485                        // remove from array
486                        unset($versions[$index]);
487                        break;
488                    }
489                }
490            }
491
492            // update actual version if it was deleted before
493            if ($actualVersionDeleted) {
494                // get newest version (already sorted by getVersions)
495                $version = reset($versions);
496                $version['max_version'] = $this->getMaxVersion();
497                $this->updateWithVersion($version);
498            } else {
499                // updateWithVersion() will trigger quota, too
500                self::handleQuotaUpdate($this);
501            }
502        }
503    }
504
505
506    protected function doRead()
507    {
508        global $DIC;
509
510        $q = "SELECT * FROM file_data WHERE file_id = %s";
511        $r = $DIC->database()->queryF($q, ['integer'], [$this->getId()]);
512        $row = $r->fetchObject();
513
514        $this->setFileName($row->file_name);
515        $this->setFileType($row->file_type);
516        $this->setFileSize($row->file_size);
517        $this->setVersion($row->version ? $row->version : 1);
518        $this->setMaxVersion($row->max_version ? $row->max_version : 1);
519        $this->setMode($row->f_mode);
520        $this->setRating($row->rating);
521        $this->setPageCount($row->page_count);
522
523        $this->initFileStorage();
524    }
525
526
527    protected function beforeUpdate()
528    {
529        // no meta data handling for file list files
530        if ($this->getMode() != self::MODE_FILELIST) {
531            $this->updateMetaData();
532        }
533
534        return true;
535    }
536
537
538    protected function doUpdate()
539    {
540        global $DIC;
541
542        $a_columns = $this->getArrayForDatabase();
543        $DIC->database()->update('file_data', $a_columns, [
544            'file_id' => [
545                'integer',
546                $this->getId(),
547            ],
548        ]);
549
550        // update metadata with the current file version
551        $meta_version_column = ['meta_version' => ['integer', (int) $this->getVersion()]];
552        $DIC->database()->update('il_meta_lifecycle', $meta_version_column, [
553            'obj_id' => [
554                'integer',
555                $this->getId(),
556            ],
557        ]);
558
559        self::handleQuotaUpdate($this);
560
561        return true;
562    }
563
564
565    /**
566     * update meta data
567     */
568    protected function doUpdateMetaData()
569    {
570        // add technical section with file size and format
571        $md_obj = new ilMD($this->getId(), 0, $this->getType());
572        if (!is_object($technical = $md_obj->getTechnical())) {
573            $technical = $md_obj->addTechnical();
574            $technical->save();
575        }
576        $technical->setSize($this->getFileSize());
577
578        $format_ids = $technical->getFormatIds();
579        if (count($format_ids) > 0) {
580            $format = $technical->getFormat($format_ids[0]);
581            $format->setFormat($this->getFileType());
582            $format->update();
583        } else {
584            $format = $technical->addFormat();
585            $format->setFormat($this->getFileType());
586            $format->save();
587        }
588        $technical->update();
589    }
590
591
592    /**
593     * @param string $a_name
594     */
595    public function setFileName($a_name)
596    {
597        $this->filename = $a_name;
598    }
599
600
601    /**
602     * @return string
603     */
604    public function getFileName()
605    {
606        return $this->filename;
607    }
608
609
610    /**
611     * @param string $a_type
612     */
613    public function setFileType($a_type)
614    {
615        $this->filetype = $a_type;
616    }
617
618
619    /**
620     * @return string
621     */
622    public function getFileType()
623    {
624        return $this->filetype;
625    }
626
627
628    /**
629     * @param $a_size
630     */
631    public function setFileSize($a_size)
632    {
633        $this->filesize = $a_size;
634    }
635
636
637    public function getFileSize()
638    {
639        return $this->filesize;
640    }
641
642
643    public function setAction($a_action)
644    {
645        $this->action = $a_action;
646    }
647
648
649    public function getAction()
650    {
651        return $this->action;
652    }
653
654
655    public function setRollbackVersion($a_rollback_version)
656    {
657        $this->rollback_version = $a_rollback_version;
658    }
659
660
661    public function getRollbackVersion()
662    {
663        return $this->rollback_version;
664    }
665
666
667    public function setRollbackUserId($a_rollback_user_id)
668    {
669        $this->rollback_user_id = $a_rollback_user_id;
670    }
671
672
673    public function getRollbackUserId()
674    {
675        return $this->rollback_user_id;
676    }
677
678
679    /**
680     * Gets the disk usage of the object in bytes.
681     *
682     * @access    public
683     * @return    integer        the disk usage in bytes
684     */
685    public function getDiskUsage()
686    {
687        require_once("./Modules/File/classes/class.ilObjFileAccess.php");
688
689        return ilObjFileAccess::_lookupDiskUsage($this->id);
690    }
691
692
693    // END PATCH WebDAV Encapsulate file access in ilObjFile class.
694    public function getFile($a_hist_entry_id = null)
695    {
696        if (is_null($a_hist_entry_id)) {
697            $file = $this->getDirectory($this->getVersion()) . "/" . $this->getFileName();
698        } else {
699            require_once("./Services/History/classes/class.ilHistory.php");
700            $entry = ilHistory::_getEntryByHistoryID($a_hist_entry_id);
701
702            if ($entry === false) {
703                return false;
704            }
705
706            $data = $this->parseInfoParams($entry);
707            $file = $this->getDirectory($data["version"]) . "/" . $data["filename"];
708        }
709
710        return $file;
711    }
712
713
714    // END PATCH WebDAV Encapsulate file access in ilObjFile class.
715
716    public function setVersion($a_version)
717    {
718        $this->version = $a_version;
719    }
720
721
722    public function getVersion()
723    {
724        return $this->version;
725    }
726
727
728    public function setMaxVersion($a_max_version)
729    {
730        $this->max_version = $a_max_version;
731    }
732
733
734    public function getMaxVersion()
735    {
736        return $this->max_version;
737    }
738
739
740    /**
741     * mode is object or filelist
742     *
743     * @param string $a_mode mode
744     */
745    public function setMode($a_mode)
746    {
747        $this->mode = $a_mode;
748    }
749
750
751    /**
752     * mode is object or filelist
753     *
754     * @return    string        mode
755     */
756    public function getMode()
757    {
758        return $this->mode;
759    }
760
761
762    public static function _writeFileType($a_id, $a_format)
763    {
764        global $DIC;
765        $ilDB = $DIC['ilDB'];
766
767        $q = "UPDATE file_data SET " . " file_type = " . $ilDB->quote($a_format, 'text')
768            . " WHERE file_id = " . $ilDB->quote($a_id, 'integer');
769        $res = $ilDB->manipulate($q);
770    }
771
772
773    /**
774     * @param $a_id
775     *
776     * @return string
777     * @deprecated Static methods will be removed in a future version of ILIAS
778     */
779    public static function _lookupFileName($a_id)
780    {
781        global $DIC;
782        $ilDB = $DIC['ilDB'];
783
784        $q = "SELECT * FROM file_data WHERE file_id = " . $ilDB->quote($a_id, 'integer');
785        $r = $ilDB->query($q);
786        $row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
787
788        $strip_slashes = ilUtil::stripSlashes($row->file_name);
789
790        return $strip_slashes;
791    }
792
793
794    /** Lookups the file size of the file in bytes. */
795    public static function _lookupFileSize($a_id)
796    {
797        require_once("./Modules/File/classes/class.ilObjFileAccess.php");
798
799        return ilObjFileAccess::_lookupFileSize($a_id);
800    }
801
802
803    /**
804     * lookup version
805     */
806    public static function _lookupVersion($a_id)
807    {
808        require_once("./Modules/File/classes/class.ilObjFileAccess.php");
809
810        return ilObjFileAccess::_lookupVersion($a_id);
811    }
812
813
814    /**
815     * Determine File Size
816     */
817    public function determineFileSize($a_hist_entry_id = null)
818    {
819        if (is_null($a_hist_entry_id)) {
820            $file = $this->getDirectory($this->getVersion()) . "/" . $this->getFileName();
821        } else {
822            require_once("./Services/History/classes/class.ilHistory.php");
823            $entry = ilHistory::_getEntryByHistoryID($a_hist_entry_id);
824
825            if ($entry === false) {
826                return false;
827            }
828
829            $data = $this->parseInfoParams($entry);
830            $file = $this->getDirectory($data["version"]) . "/" . $data["filename"];
831        }
832        if (is_file($file)) {
833            $this->setFileSize(filesize($file));
834        }
835    }
836
837
838    /**
839     * @param null $a_hist_entry_id
840     *
841     * @return bool
842     */
843    public function sendFile($a_hist_entry_id = null)
844    {
845        $s = new FilePathSanitizer($this);
846        $s->sanitizeIfNeeded();
847
848        if (is_null($a_hist_entry_id)) {
849            $file = $this->getDirectory($this->getVersion()) . "/" . $this->getFileName();
850            $file = ilFileUtils::getValidFilename($file);
851        } else {
852            $entry = ilHistory::_getEntryByHistoryID($a_hist_entry_id);
853            $data = $this->parseInfoParams($entry);
854            $file = $this->getDirectory($data["version"]) . "/" . $data["filename"];
855        }
856
857
858        if ($this->file_storage->fileExists($file)) {
859            global $DIC;
860            $ilClientIniFile = $DIC['ilClientIniFile'];
861            /**
862             * @var $ilClientIniFile ilIniFile
863             */
864
865            $ilFileDelivery = new ilFileDelivery($file);
866            $ilFileDelivery->setDisposition($this->isInline() ? ilFileDelivery::DISP_INLINE : ilFileDelivery::DISP_ATTACHMENT);
867            $ilFileDelivery->setMimeType($this->guessFileType($file));
868            $ilFileDelivery->setConvertFileNameToAsci((bool) !$ilClientIniFile->readVariable('file_access', 'disable_ascii'));
869
870            // also returning the 'real' filename if a history file is delivered
871            if ($ilClientIniFile->readVariable('file_access', 'download_with_uploaded_filename')
872                != '1'
873                && is_null($a_hist_entry_id)
874            ) {
875                $ilFileDelivery->setDownloadFileName(ilFileUtils::getValidFilename($this->getTitle()));
876            } else {
877                // $download_file_name = basename($file);
878                /* FSX Info: basename has a Bug with Japanese and other characters, see:
879                 * http://stackoverflow.com/questions/32115609/basename-fail-when-file-name-start-by-an-accent
880                 * Therefore we can no longer use basename();
881                 */
882                $parts = explode(DIRECTORY_SEPARATOR, $file);
883                $download_file_name = end($parts);
884                $download_file_name = ilFileUtils::getValidFilename($download_file_name);
885                $ilFileDelivery->setDownloadFileName($download_file_name);
886            }
887            $ilFileDelivery->deliver();
888
889            return true;
890        }
891
892        throw new FileNotFoundException($this->lng->txt('file_not_found_sec'));
893    }
894
895
896    /**
897     * Returns the extension of the file name converted to lower-case.
898     * e.g. returns 'pdf' for 'document.pdf'.
899     */
900    public function getFileExtension()
901    {
902        require_once 'Modules/File/classes/class.ilObjFileAccess.php';
903
904        return ilObjFileAccess::_getFileExtension($this->getTitle());
905    }
906
907
908    /**
909     * Returns true, if this file should be displayed inline in a browser
910     * window. This is especially useful for PDF documents, HTML pages,
911     * and for images which are directly supported by the browser.
912     */
913    public function isInline()
914    {
915        require_once 'Modules/File/classes/class.ilObjFileAccess.php';
916
917        return ilObjFileAccess::_isFileInline($this->getTitle());
918    }
919
920
921    /**
922     * Returns true, if this file should be hidden in the repository view.
923     */
924    public function isHidden()
925    {
926        require_once 'Modules/File/classes/class.ilObjFileAccess.php';
927
928        return ilObjFileAccess::_isFileHidden($this->getTitle());
929    }
930    // END WebDAV: Get file extension, determine if file is inline, guess file type.
931
932
933    /**
934     * Guesses the file type based on the current values returned by getFileType()
935     * and getFileExtension().
936     * If getFileType() returns 'application/octet-stream', the file extension is
937     * used to guess a more accurate file type.
938     */
939    public function guessFileType($a_file = "")
940    {
941        $path = pathinfo($a_file);
942        if ($path["extension"] != "") {
943            $filename = $path["basename"];
944        } else {
945            $filename = "dummy." . $this->getFileExtension();
946        }
947        include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
948        $mime = ilMimeTypeUtil::getMimeType($a_file, $filename, $this->getFileType());
949
950        return $mime;
951    }
952
953
954    /**
955     * Clone
956     *
957     * @access public
958     *
959     * @param object clone
960     * @param int    target id
961     * @param int    copy id
962     *
963     */
964    protected function doCloneObject($a_new_obj, $a_target_id, $a_copy_id = 0)
965    {
966        global $DIC;
967        $ilDB = $DIC['ilDB'];
968
969        $a_new_obj->createDirectory();
970        $this->cloneMetaData($a_new_obj);
971
972        // Copy all file versions
973        ilUtil::rCopy($this->getDirectory(), $a_new_obj->getDirectory());
974
975        // object created now copy other settings
976        // bugfix mantis 26131
977        $DIC->database()->insert('file_data', $this->getArrayForDatabase($a_new_obj->getId()));
978
979        // copy all previews
980        require_once("./Services/Preview/classes/class.ilPreview.php");
981        ilPreview::copyPreviews($this->getId(), $a_new_obj->getId());
982
983        // copy history entries
984        require_once("./Services/History/classes/class.ilHistory.php");
985        ilHistory::_copyEntriesForObject($this->getId(), $a_new_obj->getId());
986
987        // Copy learning progress settings
988        include_once('Services/Tracking/classes/class.ilLPObjSettings.php');
989        $obj_settings = new ilLPObjSettings($this->getId());
990        $obj_settings->cloneSettings($a_new_obj->getId());
991        unset($obj_settings);
992
993        // add news notification
994        $a_new_obj->addNewsNotification("file_created");
995
996        return $a_new_obj;
997    }
998
999
1000    protected function beforeDelete()
1001    {
1002        global $DIC;
1003        $ilDB = $DIC['ilDB'];
1004
1005        // check, if file is used somewhere
1006        $usages = $this->getUsages();
1007        if (count($usages) == 0) {
1008            return true;
1009        }
1010
1011        return false;
1012    }
1013
1014
1015    protected function doDelete()
1016    {
1017        global $DIC;
1018        $ilDB = $DIC['ilDB'];
1019
1020        // delete file data entry
1021        $q = "DELETE FROM file_data WHERE file_id = " . $ilDB->quote($this->getId(), 'integer');
1022        $this->ilias->db->query($q);
1023
1024        // delete history entries
1025        require_once("./Services/History/classes/class.ilHistory.php");
1026        ilHistory::_removeEntriesForObject($this->getId());
1027
1028        self::handleQuotaUpdate($this);
1029
1030        // delete entire directory and its content
1031        if (@is_dir($this->getDirectory())) {
1032            ilUtil::delDir($this->getDirectory());
1033        }
1034
1035        // delete meta data
1036        if ($this->getMode() != self::MODE_FILELIST) {
1037            $this->deleteMetaData();
1038        }
1039
1040        // delete preview
1041        $this->deletePreview();
1042    }
1043
1044
1045    /**
1046     * export files of object to target directory
1047     * note: target directory must be the export target directory,
1048     * "/objects/il_<inst>_file_<file_id>/..." will be appended to this directory
1049     *
1050     * @param string $a_target_dir target directory
1051     */
1052    public function export($a_target_dir)
1053    {
1054        $subdir = "il_" . IL_INST_ID . "_file_" . $this->getId();
1055        ilUtil::makeDir($a_target_dir . "/objects/" . $subdir);
1056
1057        $filedir = $this->getDirectory($this->getVersion());
1058
1059        if (@!is_dir($filedir)) {
1060            $filedir = $this->getDirectory();
1061        }
1062
1063        ilUtil::rCopy($filedir, $a_target_dir . "/objects/" . $subdir);
1064    }
1065
1066
1067    /**
1068     * static delete all usages of
1069     */
1070    public static function _deleteAllUsages($a_type, $a_id, $a_usage_hist_nr = 0, $a_usage_lang = "-")
1071    {
1072        global $DIC;
1073        $ilDB = $DIC['ilDB'];
1074
1075        $and_hist = ($a_usage_hist_nr !== false) ? " AND usage_hist_nr = "
1076            . $ilDB->quote($a_usage_hist_nr, "integer") : "";
1077
1078        $file_ids = array();
1079        $set = $ilDB->query("SELECT id FROM file_usage" . " WHERE usage_type = "
1080            . $ilDB->quote($a_type, "text") . " AND usage_id= "
1081            . $ilDB->quote($a_id, "integer") . " AND usage_lang= "
1082            . $ilDB->quote($a_usage_lang, "text") . $and_hist);
1083        while ($row = $ilDB->fetchAssoc($set)) {
1084            $file_ids[] = $row["id"];
1085        }
1086
1087        $ilDB->manipulate("DELETE FROM file_usage WHERE usage_type = "
1088            . $ilDB->quote($a_type, "text") . " AND usage_id = "
1089            . $ilDB->quote((int) $a_id, "integer") . " AND usage_lang= "
1090            . $ilDB->quote($a_usage_lang, "text") . " AND usage_hist_nr = "
1091            . $ilDB->quote((int) $a_usage_hist_nr, "integer"));
1092
1093        foreach ($file_ids as $file_id) {
1094            self::handleQuotaUpdate(new self($file_id, false));
1095        }
1096    }
1097
1098
1099    /**
1100     * save usage
1101     */
1102    public static function _saveUsage($a_file_id, $a_type, $a_id, $a_usage_hist_nr = 0, $a_usage_lang = "-")
1103    {
1104        global $DIC;
1105        $ilDB = $DIC['ilDB'];
1106
1107        // check if file really exists
1108        if (ilObject::_lookupType($a_file_id) != "file") {
1109            return;
1110        }
1111        // #15143
1112        $ilDB->replace("file_usage", array(
1113            "id" => array("integer", (int) $a_file_id),
1114            "usage_type" => array("text", (string) $a_type),
1115            "usage_id" => array("integer", (int) $a_id),
1116            "usage_hist_nr" => array("integer", (int) $a_usage_hist_nr),
1117            "usage_lang" => array("text", $a_usage_lang),
1118        ), array());
1119
1120        self::handleQuotaUpdate(new self($a_file_id, false));
1121    }
1122
1123
1124    /**
1125     * get all usages of file object
1126     */
1127    public function getUsages()
1128    {
1129        global $DIC;
1130        $ilDB = $DIC['ilDB'];
1131
1132        // get usages in learning modules
1133        $q = "SELECT * FROM file_usage WHERE id = " . $ilDB->quote($this->getId(), "integer");
1134        $us_set = $ilDB->query($q);
1135        $ret = array();
1136        while ($us_rec = $ilDB->fetchAssoc($us_set)) {
1137            $ret[] = array(
1138                "type" => $us_rec["usage_type"],
1139                "id" => $us_rec["usage_id"],
1140                "lang" => $us_rec["usage_lang"],
1141                "hist_nr" => $us_rec["usage_hist_nr"],
1142            );
1143        }
1144
1145        return $ret;
1146    }
1147
1148
1149    /**
1150     * get all files of an object
1151     *
1152     * @param string $a_type object type (e.g. "lm:pg")
1153     * @param int    $a_id   object id
1154     *
1155     * @return    array        array of file ids
1156     */
1157    public static function _getFilesOfObject($a_type, $a_id, $a_usage_hist_nr = 0, $a_usage_lang = "-")
1158    {
1159        global $DIC;
1160        $ilDB = $DIC['ilDB'];
1161
1162        $lstr = "";
1163        if ($a_usage_lang != "") {
1164            $lstr = "usage_lang = " . $ilDB->quote((string) $a_usage_lang, "text") . " AND ";
1165        }
1166
1167        // get usages in learning modules
1168        $q = "SELECT * FROM file_usage WHERE " . "usage_id = " . $ilDB->quote((int) $a_id, "integer")
1169            . " AND " . "usage_type = " . $ilDB->quote((string) $a_type, "text") . " AND " . $lstr
1170            . "usage_hist_nr = " . $ilDB->quote((int) $a_usage_hist_nr, "integer");
1171        $file_set = $ilDB->query($q);
1172        $ret = array();
1173        while ($file_rec = $ilDB->fetchAssoc($file_set)) {
1174            $ret[$file_rec["id"]] = $file_rec["id"];
1175        }
1176
1177        return $ret;
1178    }
1179
1180
1181    // TODO: What is this function good for??
1182    public function getXMLZip()
1183    {
1184        global $DIC;
1185        $ilias = $DIC['ilias'];
1186
1187        $zip = PATH_TO_ZIP;
1188
1189        exec($zip . ' ' . ilUtil::escapeShellArg($this->getDirectory() . '/' . $this->getFileName())
1190            . " " . ilUtil::escapeShellArg($this->getDirectory() . '/' . '1.zip'));
1191
1192        return $this->getDirectory() . '/1.zip';
1193    }
1194
1195
1196    public function addNewsNotification($a_lang_var)
1197    {
1198        // BEGIN WebDAV Suppress news notification for hidden files
1199        if ($this->isHidden()) {
1200            return;
1201        }
1202        // END WebDAV Suppress news notification for hidden files
1203
1204        global $DIC;
1205        $ilUser = $DIC['ilUser'];
1206
1207        // Add Notification to news
1208        include_once("./Services/News/classes/class.ilNewsItem.php");
1209        include_once("./Modules/File/classes/class.ilObjFileAccess.php");
1210        $news_item = new ilNewsItem();
1211        $news_item->setContext($this->getId(), $this->getType());
1212        $news_item->setPriority(NEWS_NOTICE);
1213        $news_item->setTitle($a_lang_var);
1214        $news_item->setContentIsLangVar(true);
1215        if ($this->getDescription() != "") {
1216            $news_item->setContent("<p>" . $this->getDescription() . "</p>");
1217        }
1218        $news_item->setUserId($ilUser->getId());
1219        $news_item->setVisibility(NEWS_USERS);
1220        $news_item->create();
1221    }
1222
1223
1224    /**
1225     * init file storage object
1226     *
1227     * @access public
1228     *
1229     */
1230    public function initFileStorage()
1231    {
1232        $this->file_storage = new ilFSStorageFile($this->getId());
1233
1234        return true;
1235    }
1236
1237
1238    /**
1239     * storeUnzipedFile
1240     *
1241     * Stores Files unzipped from uploaded archive in filesystem
1242     *
1243     * @param string $a_upload_file
1244     * @param string $a_filename
1245     */
1246
1247    public function storeUnzipedFile($a_upload_file, $a_filename)
1248    {
1249        $this->setVersion($this->getVersion() + 1);
1250
1251        if (@!is_dir($this->getDirectory($this->getVersion()))) {
1252            ilUtil::makeDir($this->getDirectory($this->getVersion()));
1253        }
1254
1255        $file = $this->getDirectory($this->getVersion()) . "/" . $a_filename;
1256
1257        $file = ilFileUtils::getValidFilename($file);
1258
1259        ilFileUtils::rename($a_upload_file, $file);
1260
1261        // create preview
1262        $this->createPreview();
1263    }
1264
1265
1266    /**
1267     * @param      $obj_id
1268     * @param null $a_version
1269     *
1270     * @return bool|string
1271     * @throws \ILIAS\Filesystem\Exception\DirectoryNotFoundException
1272     */
1273    public static function _lookupAbsolutePath($obj_id, $a_version = null)
1274    {
1275        $file_object = new self($obj_id, false);
1276        $s = new FilePathSanitizer($file_object);
1277        $s->sanitizeIfNeeded();
1278
1279        return $file_object->getFile($a_version);
1280    }
1281
1282
1283    /**
1284     * Check if the file extension does still exist after an update of the title
1285     *
1286     * @return
1287     */
1288    public function checkFileExtension($new_filename, $new_title)
1289    {
1290        include_once './Modules/File/classes/class.ilObjFileAccess.php';
1291        $fileExtension = ilObjFileAccess::_getFileExtension($new_filename);
1292        $titleExtension = ilObjFileAccess::_getFileExtension($new_title);
1293        if ($titleExtension != $fileExtension && strlen($fileExtension) > 0) {
1294            // remove old extension
1295            $pi = pathinfo($this->getFileName());
1296            $suffix = $pi["extension"];
1297            if ($suffix != "") {
1298                if (substr($new_title, strlen($new_title) - strlen($suffix) - 1) == "." . $suffix) {
1299                    $new_title = substr($new_title, 0, strlen($new_title) - strlen($suffix) - 1);
1300                }
1301            }
1302            $new_title .= '.' . $fileExtension;
1303        }
1304
1305        return $new_title;
1306    }
1307
1308
1309    /**
1310     * Gets the file versions for this object.
1311     *
1312     * @param array $version_ids The file versions to get. If not specified all versions are
1313     *                           returned.
1314     *
1315     * @return array The file versions.
1316     *
1317     *               Example:  array (
1318     * 'date' => '2019-07-25 11:19:51',
1319     * 'user_id' => '6',
1320     * 'obj_id' => '287',
1321     * 'obj_type' => 'file',
1322     * 'action' => 'create',
1323     * 'info_params' => 'chicken_outlined.pdf,1,1',
1324     * 'user_comment' => '',
1325     * 'hist_entry_id' => '3',
1326     * 'title' => NULL,
1327     * )
1328     */
1329    public function getVersions($version_ids = null) : array
1330    {
1331        $versions = (array) ilHistory::_getEntriesForObject($this->getId(), $this->getType());
1332
1333        if ($version_ids != null && count($version_ids) > 0) {
1334            foreach ($versions as $index => $version) {
1335                if (!in_array($version["hist_entry_id"], $version_ids, true)) {
1336                    unset($versions[$index]);
1337                }
1338            }
1339        }
1340
1341        // add custom entries
1342        foreach ($versions as $index => $version) {
1343            $params = $this->parseInfoParams($version);
1344            $versions[$index] = array_merge($version, $params);
1345        }
1346
1347        // sort by version number (hist_entry_id will do for that)
1348        usort($versions, array($this, "compareVersions"));
1349
1350        return $versions;
1351    }
1352
1353
1354    /**
1355     * Gets a specific file version.
1356     *
1357     * @param int $version_id The version id to get.
1358     *
1359     * @return array The specific version or false if the version was not found.
1360     */
1361    public function getSpecificVersion($version_id)
1362    {
1363        include_once("./Services/History/classes/class.ilHistory.php");
1364        $version = ilHistory::_getEntryByHistoryID($version_id);
1365        if ($version === false) {
1366            return false;
1367        }
1368
1369        // ilHistory returns different keys in _getEntryByHistoryID and _getEntriesForObject
1370        // so this makes it the same
1371        $version["hist_entry_id"] = $version["id"];
1372        $version["user_id"] = $version["usr_id"];
1373        $version["date"] = $version["hdate"];
1374        unset($version["id"], $version["usr_id"], $version["hdate"]);
1375
1376        // parse params
1377        $params = $this->parseInfoParams($version);
1378
1379        return array_merge($version, $params);
1380    }
1381
1382
1383    /**
1384     * Makes the specified version the current one and returns theSummary of rollbackVersion
1385     *
1386     * @param int $version_id The id of the version to make the current one.
1387     *
1388     * @return array The new actual version.
1389     */
1390    public function rollback($version_id)
1391    {
1392        global $DIC;
1393        $ilDB = $DIC['ilDB'];
1394        $ilUser = $DIC['ilUser'];
1395
1396        $source = $this->getSpecificVersion($version_id);
1397        if ($source === false) {
1398            $this->ilErr->raiseError($this->lng->txt("obj_not_found"), $this->ilErr->MESSAGE);
1399        }
1400
1401        // get the new version number
1402        $new_version_nr = $this->getMaxVersion() + 1;
1403        $this->setMaxVersion($new_version_nr);
1404
1405        // copy file
1406        $source_path = $this->getDirectory($source["version"]) . "/" . $source["filename"];
1407        $dest_dir = $this->getDirectory($new_version_nr);
1408        if (@!is_dir($dest_dir)) {
1409            ilUtil::makeDir($dest_dir);
1410        }
1411
1412        copy($source_path, $dest_dir . "/" . $source["filename"]);
1413
1414        // create new history entry based on the old one
1415        include_once("./Services/History/classes/class.ilHistory.php");
1416        // bugfix mantis 26236: added rollback info to version instead of max_version to ensure compatibility with older ilias versions
1417        ilHistory::_createEntry($this->getId(), "rollback", $source["filename"] . ","
1418            . $new_version_nr . "|"
1419            . $source["version"] . "|"
1420            . $ilUser->getId() . ","
1421            . $this->getMaxVersion());
1422
1423        // get id of newest entry
1424        $entries = ilHistory::_getEntriesForObject($this->getId());
1425        $newest_entry_id = 0;
1426        foreach ($entries as $entry) {
1427            if ($entry["action"] == "rollback") {
1428                $newest_entry_id = $entry["hist_entry_id"];
1429            }
1430        }
1431        $new_version = $this->getSpecificVersion($newest_entry_id);
1432        $new_version['version'] = $new_version_nr;
1433        $new_version['max_version'] = $new_version_nr;
1434
1435        // change user back to the original uploader
1436        ilHistory::_changeUserId($new_version["hist_entry_id"], $source["user_id"]);
1437
1438        // update this file with the new version
1439        $this->updateWithVersion($new_version);
1440
1441        $this->addNewsNotification("file_updated");
1442
1443        return $new_version;
1444    }
1445
1446
1447    /**
1448     * Updates the file object with the specified file version.
1449     *
1450     * @param array $version The version to update the file object with.
1451     */
1452    protected function updateWithVersion($version)
1453    {
1454        // update title (checkFileExtension must be called before setFileName!)
1455        $this->setTitle($this->checkFileExtension($version["filename"], $this->getTitle()));
1456
1457        $this->setVersion($version["version"]);
1458        $this->setMaxVersion($version["max_version"]);
1459        $this->setFileName($version["filename"]);
1460
1461        // evaluate mime type (reset file type before)
1462        $this->setFileType("");
1463        $this->setFileType($this->guessFileType($version["filename"]));
1464
1465        // set filesize
1466        $this->determineFileSize();
1467
1468        $this->update();
1469
1470        // refresh preview
1471        $this->createPreview(true);
1472    }
1473
1474
1475    /**
1476     * Compares two file versions.
1477     *
1478     * @param array $v1 First file version to compare.
1479     * @param array $v2 Second file version to compare.
1480     *
1481     * @return int Returns an integer less than, equal to, or greater than zero if the first
1482     *             argument is considered to be respectively less than, equal to, or greater than
1483     *             the second.
1484     */
1485    public function compareVersions($v1, $v2)
1486    {
1487        // v2 - v1 because version should be descending
1488        return (int) $v2["version"] - (int) $v1["version"];
1489    }
1490
1491
1492    /**
1493     * Parses the info parameters ("info_params") of the specified history entry.
1494     *
1495     * @param array $entry The history entry.
1496     *
1497     * @return array Returns an array containing the "filename" and "version" contained within the
1498     *               "info_params".
1499     */
1500    private function parseInfoParams($entry)
1501    {
1502        $data = explode(",", $entry["info_params"]);
1503
1504        // bugfix: first created file had no version number
1505        // this is a workaround for all files created before the bug was fixed
1506        if (empty($data[1])) {
1507            $data[1] = "1";
1508        }
1509
1510        if (empty($data[2])) {
1511            $data[2] = "1";
1512        }
1513
1514// BEGIN bugfix #27391
1515        if (sizeof($data) > 3)
1516        {
1517          $last = sizeof($data) - 1;
1518          for ($n = 1; $n < $last - 1; $n++)
1519          {
1520            $data[0] .= "," . $data[$n];
1521          }
1522          $data[1] = $data[$last - 1];
1523          $data[2] = $data[$last];
1524        }
1525// END bugfix #27391
1526
1527        $result = array(
1528            "filename" => $data[0],
1529            "version" => $data[1],
1530            "max_version" => $data[2],
1531            "rollback_version" => "",
1532            "rollback_user_id" => "",
1533        );
1534
1535        // if rollback, the version contains the rollback version as well
1536        // bugfix mantis 26236: rollback info is read from version to ensure compatibility with older ilias versions
1537        if ($entry["action"] == "rollback") {
1538            $tokens = explode("|", $result["version"]);
1539            if (count($tokens) > 1) {
1540                $result["version"] = $tokens[0];
1541                $result["rollback_version"] = $tokens[1];
1542
1543                if (count($tokens) > 2) {
1544                    $result["rollback_user_id"] = $tokens[2];
1545                }
1546            }
1547        }
1548
1549        return $result;
1550    }
1551
1552
1553    protected static function handleQuotaUpdate(ilObjFile $a_file)
1554    {
1555        include_once "Services/MediaObjects/classes/class.ilObjMediaObject.php";
1556        $mob = new ilObjMediaObject();
1557
1558        // file itself could be workspace item
1559        $parent_obj_ids = array($a_file->getId());
1560
1561        foreach ($a_file->getUsages() as $item) {
1562            $parent_obj_id = $mob->getParentObjectIdForUsage($item);
1563            if ($parent_obj_id
1564                && !in_array($parent_obj_id, $parent_obj_ids)
1565            ) {
1566                $parent_obj_ids[] = $parent_obj_id;
1567            }
1568        }
1569
1570        include_once "Services/DiskQuota/classes/class.ilDiskQuotaHandler.php";
1571        ilDiskQuotaHandler::handleUpdatedSourceObject($a_file->getType(), $a_file->getId(), $a_file->getDiskUsage(), $parent_obj_ids);
1572    }
1573
1574
1575    /**
1576     * Creates a preview for the file object.
1577     *
1578     * @param bool $force true, to force the creation of the preview; false, to create the preview
1579     *                    only if the file is newer.
1580     */
1581    protected function createPreview($force = false)
1582    {
1583        // only normal files are supported
1584        if ($this->getMode() != self::MODE_OBJECT) {
1585            return;
1586        }
1587
1588        require_once("./Services/Preview/classes/class.ilPreview.php");
1589        ilPreview::createPreview($this, $force);
1590    }
1591
1592
1593    /**
1594     * Deletes the preview of the file object.
1595     */
1596    protected function deletePreview()
1597    {
1598        // only normal files are supported
1599        if ($this->getMode() != self::MODE_OBJECT) {
1600            return;
1601        }
1602
1603        require_once("./Services/Preview/classes/class.ilPreview.php");
1604        ilPreview::deletePreview($this->getId());
1605    }
1606
1607
1608    /**
1609     * @param bool $a_value
1610     */
1611    public function setRating($a_value)
1612    {
1613        $this->rating = (bool) $a_value;
1614    }
1615
1616
1617    /**
1618     * @return bool
1619     */
1620    public function hasRating()
1621    {
1622        return $this->rating;
1623    }
1624
1625
1626    /**
1627     * @return int
1628     */
1629    public function getPageCount()
1630    {
1631        return $this->page_count;
1632    }
1633
1634
1635    /**
1636     * @param int $page_count
1637     */
1638    public function setPageCount($page_count)
1639    {
1640        $this->page_count = $page_count;
1641    }
1642
1643
1644    /**
1645     * @param int $file_id (part of bugfix mantis 26131)
1646     *
1647     * @return array
1648     */
1649    private function getArrayForDatabase($file_id = 0)
1650    {
1651        if ($file_id == 0) {
1652            $file_id = $this->getId();
1653        }
1654        return [
1655            'file_id' => ['integer', $file_id],
1656            'file_name' => ['text', $this->getFileName()],
1657            'file_type' => ['text', $this->getFileType()],
1658            'file_size' => ['integer', (int) $this->getFileSize()],
1659            'version' => ['integer', (int) $this->getVersion()],
1660            'max_version' => ['integer', (int) $this->getMaxVersion()],
1661            'f_mode' => ['text', $this->getMode()],
1662            'page_count' => ['text', $this->getPageCount()],
1663            'rating' => ['integer', $this->hasRating()],
1664        ];
1665    }
1666}
1667