Issues (3627)

app/bundles/AssetBundle/Entity/Asset.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2014 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\AssetBundle\Entity;
13
14
use Doctrine\ORM\Mapping as ORM;
15
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
16
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
17
use Mautic\CoreBundle\Entity\FormEntity;
18
use Mautic\CoreBundle\Helper\FileHelper;
19
use Symfony\Component\Filesystem\Filesystem;
20
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
21
use Symfony\Component\HttpFoundation\File\File;
22
use Symfony\Component\HttpFoundation\File\UploadedFile;
23
use Symfony\Component\Validator\Constraints as Assert;
24
use Symfony\Component\Validator\Context\ExecutionContextInterface;
25
use Symfony\Component\Validator\Mapping\ClassMetadata;
26
27
class Asset extends FormEntity
28
{
29
    /**
30
     * @var int
31
     */
32
    private $id;
33
34
    /**
35
     * @var string
36
     */
37
    private $title;
38
39
    /**
40
     * @var string
41
     */
42
    private $description;
43
44
    /**
45
     * @var string
46
     */
47
    private $storageLocation = 'local';
48
49
    /**
50
     * @var string
51
     */
52
    private $path;
53
54
    /**
55
     * @var string
56
     */
57
    private $remotePath;
58
59
    /**
60
     * @var string
61
     */
62
    private $originalFileName;
63
64
    /**
65
     * @var File
66
     */
67
    private $file;
68
69
    /**
70
     * Holds upload directory.
71
     */
72
    private $uploadDir;
73
74
    /**
75
     * Holds max size of uploaded file.
76
     */
77
    private $maxSize;
78
79
    /**
80
     * Temporary location when asset file is beeing updated.
81
     * We need to keep the old file till we are sure the new
82
     * one is stored correctly.
83
     */
84
    private $temp;
85
86
    /**
87
     * Temporary ID used for file upload and validations
88
     * before the actual ID is known.
89
     */
90
    private $tempId;
91
92
    /**
93
     * Temporary file name used for file upload and validations
94
     * before the actual ID is known.
95
     */
96
    private $tempName;
97
98
    /**
99
     * @var string
100
     */
101
    private $alias;
102
103
    /**
104
     * @var string
105
     */
106
    private $language = 'en';
107
108
    /**
109
     * @var \DateTime|null
110
     */
111
    private $publishUp;
112
113
    /**
114
     * @var \DateTime|null
115
     */
116
    private $publishDown;
117
118
    /**
119
     * @var int
120
     */
121
    private $downloadCount = 0;
122
123
    /**
124
     * @var int
125
     */
126
    private $uniqueDownloadCount = 0;
127
128
    /**
129
     * @var int
130
     */
131
    private $revision = 1;
132
133
    /**
134
     * @var \Mautic\CategoryBundle\Entity\Category
135
     **/
136
    private $category;
137
138
    /**
139
     * @var string
140
     */
141
    private $extension;
142
143
    /**
144
     * @var string
145
     */
146
    private $mime;
147
148
    /**
149
     * @var int
150
     */
151
    private $size;
152
153
    /**
154
     * @var string|null
155
     */
156
    private $downloadUrl;
157
158
    /**
159
     * @var bool
160
     */
161
    private $disallow = false;
162
163
    public static function loadMetadata(ORM\ClassMetadata $metadata)
164
    {
165
        $builder = new ClassMetadataBuilder($metadata);
166
167
        $builder->setTable('assets')
168
            ->setCustomRepositoryClass('Mautic\AssetBundle\Entity\AssetRepository')
169
            ->addIndex(['alias'], 'asset_alias_search');
170
171
        $builder->addIdColumns('title');
172
173
        $builder->addField('alias', 'string');
174
175
        $builder->createField('storageLocation', 'string')
176
            ->columnName('storage_location')
177
            ->nullable()
178
            ->build();
179
180
        $builder->createField('path', 'string')
181
            ->nullable()
182
            ->build();
183
184
        $builder->createField('remotePath', 'string')
185
            ->columnName('remote_path')
186
            ->nullable()
187
            ->build();
188
189
        $builder->createField('originalFileName', 'string')
190
            ->columnName('original_file_name')
191
            ->nullable()
192
            ->build();
193
194
        $builder->createField('language', 'string')
195
            ->columnName('lang')
196
            ->build();
197
198
        $builder->addPublishDates();
199
200
        $builder->createField('downloadCount', 'integer')
201
            ->columnName('download_count')
202
            ->build();
203
204
        $builder->createField('uniqueDownloadCount', 'integer')
205
            ->columnName('unique_download_count')
206
            ->build();
207
208
        $builder->addField('revision', 'integer');
209
210
        $builder->addCategory();
211
212
        $builder->createField('extension', 'string')
213
            ->nullable()
214
            ->build();
215
216
        $builder->createField('mime', 'string')
217
            ->nullable()
218
            ->build();
219
220
        $builder->createField('size', 'integer')
221
            ->nullable()
222
            ->build();
223
224
        $builder->createField('disallow', 'boolean')
225
            ->nullable()
226
            ->build();
227
    }
228
229
    /**
230
     * Prepares the metadata for API usage.
231
     *
232
     * @param $metadata
233
     */
234
    public static function loadApiMetadata(ApiMetadataDriver $metadata)
235
    {
236
        $metadata->setGroupPrefix('asset')
237
            ->addListProperties(
238
                [
239
                    'id',
240
                    'title',
241
                    'alias',
242
                    'category',
243
                    'description',
244
                ]
245
            )
246
            ->addProperties(
247
                [
248
                    'language',
249
                    'publishUp',
250
                    'publishDown',
251
                    'downloadCount',
252
                    'uniqueDownloadCount',
253
                    'revision',
254
                    'extension',
255
                    'mime',
256
                    'size',
257
                    'downloadUrl',
258
                    'storageLocation',
259
                    'disallow',
260
                ]
261
            )
262
            ->build();
263
    }
264
265
    /**
266
     * Clone magic function.
267
     */
268
    public function __clone()
269
    {
270
        $this->id = null;
271
272
        parent::__clone();
273
    }
274
275
    /**
276
     * Get id.
277
     *
278
     * @return int
279
     */
280
    public function getId()
281
    {
282
        return $this->id;
283
    }
284
285
    /**
286
     * Sets file.
287
     *
288
     * @param File $file
289
     */
290
    public function setFile(File $file = null)
291
    {
292
        $this->file = $file;
293
294
        // check if we have an old asset path
295
        if (isset($this->path)) {
296
            // store the old name to delete after the update
297
            $this->temp = $this->path;
298
            $this->path = null;
299
        }
300
    }
301
302
    /**
303
     * Get file.
304
     *
305
     * @return UploadedFile
306
     */
307
    public function getFile()
308
    {
309
        // if file is not set, try to find it at temp folder
310
        if ($this->isLocal() && empty($this->file)) {
311
            $tempFile = $this->loadFile(true);
312
313
            if ($tempFile) {
314
                $this->setFile($tempFile);
315
            }
316
        }
317
318
        return $this->file;
319
    }
320
321
    /**
322
     * Set title.
323
     *
324
     * @param string $title
325
     *
326
     * @return Asset
327
     */
328
    public function setTitle($title)
329
    {
330
        $this->isChanged('title', $title);
331
        $this->title = $title;
332
333
        return $this;
334
    }
335
336
    /**
337
     * Get title.
338
     *
339
     * @return string
340
     */
341
    public function getTitle()
342
    {
343
        return $this->title;
344
    }
345
346
    /**
347
     * @return mixed
348
     */
349
    public function getExtension()
350
    {
351
        return $this->extension;
352
    }
353
354
    /**
355
     * @param mixed $extension
356
     */
357
    public function setExtension($extension)
358
    {
359
        $this->extension = $extension;
360
    }
361
362
    /**
363
     * @return mixed
364
     */
365
    public function getMime()
366
    {
367
        return $this->mime;
368
    }
369
370
    /**
371
     * @param mixed $mime
372
     */
373
    public function setMime($mime)
374
    {
375
        $this->mime = $mime;
376
    }
377
378
    /**
379
     * Set originalFileName.
380
     *
381
     * @param string $originalFileName
382
     *
383
     * @return Asset
384
     */
385
    public function setOriginalFileName($originalFileName)
386
    {
387
        $this->isChanged('originalFileName', $originalFileName);
388
        $this->originalFileName = $originalFileName;
389
390
        return $this;
391
    }
392
393
    /**
394
     * Get originalFileName.
395
     *
396
     * @return string
397
     */
398
    public function getOriginalFileName()
399
    {
400
        return $this->originalFileName;
401
    }
402
403
    /**
404
     * Set storage location.
405
     *
406
     * @param string $storageLocation
407
     *
408
     * @return Asset
409
     */
410
    public function setStorageLocation($storageLocation)
411
    {
412
        $this->isChanged('storageLocation', $storageLocation);
413
        $this->storageLocation = $storageLocation;
414
415
        return $this;
416
    }
417
418
    /**
419
     * Get storage location.
420
     *
421
     * @return string
422
     */
423
    public function getStorageLocation()
424
    {
425
        if (null === $this->storageLocation) {
426
            $this->storageLocation = 'local';
427
        }
428
429
        return $this->storageLocation;
430
    }
431
432
    /**
433
     * Set path.
434
     *
435
     * @param string $path
436
     *
437
     * @return Asset
438
     */
439
    public function setPath($path)
440
    {
441
        $this->isChanged('path', $path);
442
        $this->path = $path;
443
444
        return $this;
445
    }
446
447
    /**
448
     * Get path.
449
     *
450
     * @return string
451
     */
452
    public function getPath()
453
    {
454
        return $this->path;
455
    }
456
457
    /**
458
     * Set remote path.
459
     *
460
     * @param string $remotePath
461
     *
462
     * @return Asset
463
     */
464
    public function setRemotePath($remotePath)
465
    {
466
        $this->isChanged('remotePath', $remotePath);
467
        $this->remotePath = $remotePath;
468
469
        return $this;
470
    }
471
472
    /**
473
     * Get remote path.
474
     *
475
     * @return string
476
     */
477
    public function getRemotePath()
478
    {
479
        return $this->remotePath;
480
    }
481
482
    /**
483
     * Set alias.
484
     *
485
     * @param string $alias
486
     *
487
     * @return Asset
488
     */
489
    public function setAlias($alias)
490
    {
491
        $this->isChanged('alias', $alias);
492
        $this->alias = $alias;
493
494
        return $this;
495
    }
496
497
    /**
498
     * Get alias.
499
     *
500
     * @return string
501
     */
502
    public function getAlias()
503
    {
504
        return $this->alias;
505
    }
506
507
    /**
508
     * Set publishUp.
509
     *
510
     * @param \DateTime $publishUp
511
     *
512
     * @return Asset
513
     */
514
    public function setPublishUp($publishUp)
515
    {
516
        $this->isChanged('publishUp', $publishUp);
517
        $this->publishUp = $publishUp;
518
519
        return $this;
520
    }
521
522
    /**
523
     * Get publishUp.
524
     *
525
     * @return \DateTime
526
     */
527
    public function getPublishUp()
528
    {
529
        return $this->publishUp;
530
    }
531
532
    /**
533
     * Set publishDown.
534
     *
535
     * @param \DateTime $publishDown
536
     *
537
     * @return Asset
538
     */
539
    public function setPublishDown($publishDown)
540
    {
541
        $this->isChanged('publishDown', $publishDown);
542
        $this->publishDown = $publishDown;
543
544
        return $this;
545
    }
546
547
    /**
548
     * Get publishDown.
549
     *
550
     * @return \DateTime
551
     */
552
    public function getPublishDown()
553
    {
554
        return $this->publishDown;
555
    }
556
557
    /**
558
     * Set downloadCount.
559
     *
560
     * @param int $downloadCount
561
     *
562
     * @return Asset
563
     */
564
    public function setDownloadCount($downloadCount)
565
    {
566
        $this->downloadCount = $downloadCount;
567
568
        return $this;
569
    }
570
571
    /**
572
     * Get downloadCount.
573
     *
574
     * @return int
575
     */
576
    public function getDownloadCount()
577
    {
578
        return $this->downloadCount;
579
    }
580
581
    /**
582
     * Set revision.
583
     *
584
     * @param int $revision
585
     *
586
     * @return Asset
587
     */
588
    public function setRevision($revision)
589
    {
590
        $this->revision = $revision;
591
592
        return $this;
593
    }
594
595
    /**
596
     * Get revision.
597
     *
598
     * @return int
599
     */
600
    public function getRevision()
601
    {
602
        return $this->revision;
603
    }
604
605
    /**
606
     * Set language.
607
     *
608
     * @param string $language
609
     *
610
     * @return Asset
611
     */
612
    public function setLanguage($language)
613
    {
614
        $this->isChanged('language', $language);
615
        $this->language = $language;
616
617
        return $this;
618
    }
619
620
    /**
621
     * Get language.
622
     *
623
     * @return string
624
     */
625
    public function getLanguage()
626
    {
627
        return $this->language;
628
    }
629
630
    /**
631
     * Set category.
632
     *
633
     * @param \Mautic\CategoryBundle\Entity\Category $category
634
     *
635
     * @return Asset
636
     */
637
    public function setCategory(\Mautic\CategoryBundle\Entity\Category $category = null)
638
    {
639
        $this->isChanged('category', $category);
640
        $this->category = $category;
641
642
        return $this;
643
    }
644
645
    /**
646
     * Get category.
647
     *
648
     * @return \Mautic\CategoryBundle\Entity\Category
649
     */
650
    public function getCategory()
651
    {
652
        return $this->category;
653
    }
654
655
    /**
656
     * Set uniqueDownloadCount.
657
     *
658
     * @param int $uniqueDownloadCount
659
     *
660
     * @return Asset
661
     */
662
    public function setUniqueDownloadCount($uniqueDownloadCount)
663
    {
664
        $this->uniqueDownloadCount = $uniqueDownloadCount;
665
666
        return $this;
667
    }
668
669
    /**
670
     * Get uniqueDownloadCount.
671
     *
672
     * @return int
673
     */
674
    public function getUniqueDownloadCount()
675
    {
676
        return $this->uniqueDownloadCount;
677
    }
678
679
    public function setFileNameFromRemote()
680
    {
681
        $fileName = basename($this->getRemotePath());
682
683
        $this->setOriginalFileName($fileName);
684
685
        // set the asset title as original file name if title is missing
686
        if (null === $this->getTitle()) {
687
            $this->setTitle($fileName);
688
        }
689
    }
690
691
    public function preUpload()
692
    {
693
        if (null !== $this->getFile()) {
694
            // set the asset title as original file name if title is missing
695
            if (null === $this->getTitle()) {
696
                $this->setTitle($this->file->getClientOriginalName());
697
            }
698
699
            $filename  = sha1(uniqid(mt_rand(), true));
700
            $extension = $this->getFile()->guessExtension();
701
702
            if (empty($extension)) {
703
                //get it from the original name
704
                $extension = pathinfo($this->originalFileName, PATHINFO_EXTENSION);
705
            }
706
            $this->path = $filename.'.'.$extension;
707
        } elseif ($this->isRemote() && null !== $this->getRemotePath()) {
708
            $this->setFileNameFromRemote();
709
        }
710
    }
711
712
    public function upload()
713
    {
714
        // the file property can be empty if the field is not required
715
        if (null === $this->getFile()) {
716
            // check for the remote and set type data
717
            if ($this->isRemote()) {
718
                $this->setFileInfoFromFile();
719
            }
720
721
            return;
722
        }
723
724
        // move takes the target directory and then the
725
        // target filename to move to
726
        $this->getFile()->move($this->getUploadDir(), $this->path);
727
        $filePath = $this->getUploadDir().'/'.$this->temp;
728
729
        $this->setFileInfoFromFile();
730
731
        // check if we have an old asset
732
        if (isset($this->temp) && file_exists($filePath)) {
733
            // delete the old asset
734
            unlink($filePath);
735
            // clear the temp asset path
736
            $this->temp = null;
737
        }
738
739
        // Remove temporary folder and files
740
        $fs = new Filesystem();
741
        $fs->remove($this->getAbsoluteTempDir());
742
743
        // clean up the file property as you won't need it anymore
744
        $this->file = null;
745
    }
746
747
    /**
748
     * Remove a file.
749
     */
750
    public function setFileInfoFromFile()
751
    {
752
        // get some basic information about the file type
753
        $fileInfo = $this->getFileInfo();
754
755
        if (!is_array($fileInfo)) {
756
            return;
757
        }
758
759
        // set the mime and extension column values
760
        $this->setExtension($fileInfo['extension']);
761
        $this->setMime($fileInfo['mime']);
762
        $this->setSize($fileInfo['size']);
763
    }
764
765
    /**
766
     * Remove a file.
767
     *
768
     * @param bool $temp >> regular uploaded file or temporary
769
     */
770
    public function removeUpload($temp = false)
771
    {
772
        if ($temp) {
773
            $file = $this->getAbsoluteTempPath();
774
        } else {
775
            $file = $this->getAbsolutePath();
776
        }
777
778
        if ($file && file_exists($file)) {
779
            unlink($file);
780
        }
781
    }
782
783
    /**
784
     * Returns absolute path to the file.
785
     *
786
     * @return string
787
     */
788
    public function getAbsolutePath()
789
    {
790
        return null === $this->path
791
            ? null
792
            : $this->getUploadDir().'/'.$this->path;
793
    }
794
795
    /**
796
     * Returns absolute path to temporary file.
797
     *
798
     * @return string
799
     */
800
    public function getAbsoluteTempPath()
801
    {
802
        return null === $this->tempId || null === $this->tempName
803
            ? null
804
            : $this->getAbsoluteTempDir().'/'.$this->tempName;
805
    }
806
807
    /**
808
     * Returns absolute path to temporary file.
809
     *
810
     * @return string
811
     */
812
    public function getAbsoluteTempDir()
813
    {
814
        return null === $this->tempId
815
            ? null
816
            : $this->getUploadDir().'/tmp/'.$this->tempId;
817
    }
818
819
    /**
820
     * Returns absolute path to upload dir.
821
     *
822
     * @return string
823
     */
824
    protected function getUploadDir()
825
    {
826
        if ($this->uploadDir) {
827
            return $this->uploadDir;
828
        }
829
830
        return 'media/files';
831
    }
832
833
    /**
834
     * Set uploadDir.
835
     *
836
     * @param string $uploadDir
837
     *
838
     * @return Asset
839
     */
840
    public function setUploadDir($uploadDir)
841
    {
842
        $this->uploadDir = $uploadDir;
843
844
        return $this;
845
    }
846
847
    /**
848
     * Returns maximal uploadable size in bytes.
849
     * If not set, 6000000 is default.
850
     *
851
     * @return string
852
     */
853
    protected function getMaxSize()
854
    {
855
        if ($this->maxSize) {
856
            return $this->maxSize;
857
        }
858
859
        return 6000000;
860
    }
861
862
    /**
863
     * Set max size.
864
     *
865
     * @param string $maxSize
866
     *
867
     * @return Asset
868
     */
869
    public function setMaxSize($maxSize)
870
    {
871
        $this->maxSize = $maxSize;
872
873
        return $this;
874
    }
875
876
    /**
877
     * Returns file extension.
878
     *
879
     * @return string
880
     */
881
    public function getFileType()
882
    {
883
        if (!empty($this->extension) && empty($this->changes['originalFileName'])) {
884
            return $this->extension;
885
        }
886
887
        if ($this->isRemote()) {
888
            return pathinfo(parse_url($this->getRemotePath(), PHP_URL_PATH), PATHINFO_EXTENSION);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pathinfo(parse_ur...ity\PATHINFO_EXTENSION) also could return the type array which is incompatible with the documented return type string.
Loading history...
889
        }
890
891
        if (null === $this->loadFile()) {
892
            return '';
893
        }
894
895
        return $this->loadFile()->guessExtension();
896
    }
897
898
    /**
899
     * Returns some file info.
900
     *
901
     * @return array
902
     */
903
    public function getFileInfo()
904
    {
905
        $fileInfo = [];
906
907
        if ($this->isRemote()) {
908
            $ch = curl_init($this->getRemotePath());
909
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
910
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
911
            curl_setopt($ch, CURLOPT_HEADER, 1);
912
            curl_setopt($ch, CURLOPT_NOBODY, 1);
913
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
914
            curl_exec($ch);
915
916
            // build an array of handy info
917
            $fileInfo['mime']      = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
918
            $fileInfo['extension'] = $this->getFileType();
919
            $fileInfo['size']      = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
920
921
            return $fileInfo;
922
        }
923
924
        if (null === $this->loadFile()) {
925
            return '';
926
        }
927
928
        // return an array of file type info
929
        $fileInfo['mime']      = $this->loadFile()->getMimeType();
930
        $fileInfo['extension'] = $this->getFileType();
931
        $fileInfo['size']      = $this->getSize(false, true);
932
933
        return $fileInfo;
934
    }
935
936
    /**
937
     * Returns file mime type.
938
     *
939
     * @return string
940
     */
941
    public function getFileMimeType()
942
    {
943
        if ($this->isRemote()) {
944
            $ch = curl_init($this->getRemotePath());
945
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
946
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
947
            curl_setopt($ch, CURLOPT_HEADER, 1);
948
            curl_setopt($ch, CURLOPT_NOBODY, 1);
949
            curl_exec($ch);
950
951
            return curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
952
        }
953
954
        if (null === $this->loadFile()) {
955
            return '';
956
        }
957
958
        return $this->loadFile()->getMimeType();
959
    }
960
961
    /**
962
     * Returns Font Awesome icon class based on file type.
963
     *
964
     * @return string
965
     */
966
    public function getIconClass()
967
    {
968
        $fileType = $this->getFileType();
969
970
        // return missing file icon if file type is empty
971
        if (!$fileType) {
972
            return 'fa fa-ban';
973
        }
974
975
        $fileTypes = $this->getFileExtensions();
976
977
        // Search for icon name by file extension.
978
        foreach ($fileTypes as $icon => $extensions) {
979
            if (in_array($fileType, $extensions)) {
980
                return 'fa fa-file-'.$icon.'-o';
981
            }
982
        }
983
984
        // File extension is unknown, display general file icon.
985
        return 'fa fa-file-o';
986
    }
987
988
    /**
989
     * Decides if an asset is image displayable by browser.
990
     *
991
     * @return bool
992
     */
993
    public function isImage()
994
    {
995
        $fileType = strtolower($this->getFileType());
996
997
        if (!$fileType) {
998
            return false;
999
        }
1000
1001
        $imageTypes = ['jpg', 'jpeg', 'png', 'gif'];
1002
1003
        if (in_array($fileType, $imageTypes)) {
1004
            return true;
1005
        }
1006
1007
        return false;
1008
    }
1009
1010
    /**
1011
     * Returns array of common extensions.
1012
     *
1013
     * @return string
1014
     */
1015
    public function getFileExtensions()
1016
    {
1017
        return [
1018
            'excel' => [
1019
                'xlsx',
1020
                'xlsm',
1021
                'xlsb',
1022
                'xltx',
1023
                'xltm',
1024
                'xls',
1025
                'xlt',
1026
            ],
1027
            'word' => [
1028
                'doc',
1029
                'docx',
1030
                'docm',
1031
                'dotx',
1032
            ],
1033
            'pdf' => [
1034
                'pdf',
1035
            ],
1036
            'audio' => [
1037
                'mp3',
1038
            ],
1039
            'archive' => [
1040
                'zip',
1041
                'rar',
1042
                'iso',
1043
                'tar',
1044
                'gz',
1045
                '7z',
1046
            ],
1047
            'image' => [
1048
                'jpg',
1049
                'jpeg',
1050
                'png',
1051
                'gif',
1052
                'ico',
1053
                'bmp',
1054
                'psd',
1055
            ],
1056
            'text' => [
1057
                'txt',
1058
                'pub',
1059
            ],
1060
            'code' => [
1061
                'php',
1062
                'js',
1063
                'json',
1064
                'yaml',
1065
                'xml',
1066
                'html',
1067
                'htm',
1068
                'sql',
1069
            ],
1070
            'powerpoint' => [
1071
                'ppt',
1072
                'pptx',
1073
                'pptm',
1074
                'xps',
1075
                'potm',
1076
                'potx',
1077
                'pot',
1078
                'pps',
1079
                'odp',
1080
            ],
1081
            'video' => [
1082
                'wmv',
1083
                'avi',
1084
                'mp4',
1085
                'mkv',
1086
                'mpeg',
1087
            ],
1088
        ];
1089
    }
1090
1091
    /**
1092
     * Load the file object from it's path.
1093
     *
1094
     * @return \Symfony\Component\HttpFoundation\File\File|null
1095
     */
1096
    public function loadFile($temp = false)
1097
    {
1098
        if ($temp) {
1099
            $path = $this->getAbsoluteTempPath();
1100
        } else {
1101
            $path = $this->getAbsolutePath();
1102
        }
1103
1104
        if (!$path || !file_exists($path)) {
1105
            return null;
1106
        }
1107
1108
        try {
1109
            $file = new File($path);
1110
        } catch (FileNotFoundException $e) {
1111
            $file = null;
1112
        }
1113
1114
        return $file;
1115
    }
1116
1117
    /**
1118
     * Load content of the file from it's path.
1119
     *
1120
     * @return string
1121
     */
1122
    public function getFileContents()
1123
    {
1124
        $path = $this->getFilePath();
1125
1126
        return file_get_contents($path);
1127
    }
1128
1129
    /**
1130
     * Get the path to the file; a URL if remote or full file path if local.
1131
     *
1132
     * @return string
1133
     */
1134
    public function getFilePath()
1135
    {
1136
        return $this->isRemote() ? $this->getRemotePath() : $this->getAbsolutePath();
1137
    }
1138
1139
    /**
1140
     * @return mixed
1141
     */
1142
    public function getDescription()
1143
    {
1144
        return $this->description;
1145
    }
1146
1147
    /**
1148
     * @param mixed $description
1149
     */
1150
    public function setDescription($description)
1151
    {
1152
        $this->description = $description;
1153
    }
1154
1155
    public static function loadValidatorMetadata(ClassMetadata $metadata)
1156
    {
1157
        // Add a constraint to manage the file upload data
1158
        $metadata->addConstraint(new Assert\Callback([__CLASS__, 'validateFile']));
1159
    }
1160
1161
    /**
1162
     * Validator to ensure proper data for the file fields.
1163
     *
1164
     * @param Asset                     $object  Entity object to validate
1165
     * @param ExecutionContextInterface $context Context object
1166
     */
1167
    public static function validateFile($object, ExecutionContextInterface $context)
1168
    {
1169
        if ($object->isLocal()) {
1170
            $tempName = $object->getTempName();
1171
            $path     = $object->getPath();
1172
1173
            // If the object is stored locally, we should have file data
1174
            if ($object->isNew() && null === $tempName && null === $path) {
1175
                $context->buildViolation('mautic.asset.asset.error.missing.file')
1176
                    ->atPath('tempName')
1177
                    ->setTranslationDomain('validators')
1178
                    ->addViolation();
1179
            }
1180
1181
            if (null === $object->getTitle()) {
1182
                $context->buildViolation('mautic.asset.asset.error.missing.title')
1183
                    ->atPath('title')
1184
                    ->setTranslationDomain('validators')
1185
                    ->addViolation();
1186
            }
1187
1188
            // Unset any remote file data
1189
            $object->setRemotePath(null);
1190
        } elseif ($object->isRemote()) {
1191
            // If the object is stored remotely, we should have a remote path
1192
            if (null === $object->getRemotePath()) {
1193
                $context->buildViolation('mautic.asset.asset.error.missing.remote.path')
1194
                    ->atPath('remotePath')
1195
                    ->setTranslationDomain('validators')
1196
                    ->addViolation();
1197
            }
1198
1199
            // Unset any local file data
1200
            $object->setPath(null);
1201
        }
1202
    }
1203
1204
    /**
1205
     * Set temporary ID.
1206
     *
1207
     * @param string $tempId
1208
     *
1209
     * @return Asset
1210
     */
1211
    public function setTempId($tempId)
1212
    {
1213
        $this->tempId = $tempId;
1214
1215
        return $this;
1216
    }
1217
1218
    /**
1219
     * Get temporary ID.
1220
     *
1221
     * @return string
1222
     */
1223
    public function getTempId()
1224
    {
1225
        return $this->tempId;
1226
    }
1227
1228
    /**
1229
     * Set temporary file name.
1230
     *
1231
     * @param string $tempName
1232
     *
1233
     * @return Asset
1234
     */
1235
    public function setTempName($tempName)
1236
    {
1237
        $this->tempName = $tempName;
1238
1239
        return $this;
1240
    }
1241
1242
    /**
1243
     * Get temporary file name.
1244
     *
1245
     * @return string
1246
     */
1247
    public function getTempName()
1248
    {
1249
        return $this->tempName;
1250
    }
1251
1252
    /**
1253
     * @param bool   $humanReadable
1254
     * @param bool   $forceUpdate
1255
     * @param string $inUnit
1256
     *
1257
     * @return float|string
1258
     */
1259
    public function getSize($humanReadable = true, $forceUpdate = false, $inUnit = '')
1260
    {
1261
        if (empty($this->size) || $forceUpdate) {
1262
            // Try to fetch it
1263
            if ($this->isRemote()) {
1264
                $ch = curl_init($this->getRemotePath());
1265
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1266
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
1267
                curl_setopt($ch, CURLOPT_HEADER, 1);
1268
                curl_setopt($ch, CURLOPT_NOBODY, 1);
1269
                curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
1270
1271
                curl_exec($ch);
1272
1273
                $this->setSize(round(curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD)));
1274
            }
1275
1276
            if (null === $this->loadFile()) {
1277
                return 0;
1278
            }
1279
1280
            $this->setSize(round($this->loadFile()->getSize()));
1281
        }
1282
1283
        return ($humanReadable) ? static::convertBytesToHumanReadable($this->size, $inUnit) : $this->size;
1284
    }
1285
1286
    /**
1287
     * @param mixed $size
1288
     *
1289
     * @return Asset
1290
     */
1291
    public function setSize($size)
1292
    {
1293
        $this->size = $size;
1294
1295
        return $this;
1296
    }
1297
1298
    /**
1299
     * Get value from PHP configuration with special handling of -1.
1300
     *
1301
     * @param string    $setting
1302
     * @param bool|true $convertToBytes
1303
     *
1304
     * @return int
1305
     */
1306
    public static function getIniValue($setting, $convertToBytes = true)
1307
    {
1308
        $value = ini_get($setting);
1309
1310
        if (-1 == $value || 0 === $value) {
1311
            return PHP_INT_MAX;
1312
        }
1313
1314
        if ($convertToBytes) {
1315
            $value = FileHelper::convertPHPSizeToBytes($value);
1316
        }
1317
1318
        return (int) $value;
1319
    }
1320
1321
    /**
1322
     * @param        $size
1323
     * @param string $unit
1324
     *
1325
     * @return string
1326
     */
1327
    public static function convertBytesToHumanReadable($size, $unit = '')
1328
    {
1329
        list($number, $unit) = self::convertBytesToUnit($size, $unit);
1330
1331
        // Format number
1332
        $number = number_format($number, 2);
1333
1334
        // Remove trailing .00
1335
        $number = false !== strpos($number, '.') ? rtrim(rtrim($number, '0'), '.') : $number;
1336
1337
        return $number.' '.$unit;
1338
    }
1339
1340
    /**
1341
     * @param        $size
1342
     * @param string $unit
1343
     *
1344
     * @return array
1345
     */
1346
    public static function convertBytesToUnit($size, $unit = '')
1347
    {
1348
        $unit = strtoupper($unit);
1349
1350
        if ((!$unit && $size >= 1 << 30) || 'GB' == $unit || 'G' == $unit) {
1351
            return [$size / (1 << 30), 'GB'];
1352
        }
1353
        if ((!$unit && $size >= 1 << 20) || 'MB' == $unit || 'M' == $unit) {
1354
            return [$size / (1 << 20), 'MB'];
1355
        }
1356
        if ((!$unit && $size >= 1 << 10) || 'KB' == $unit || 'K' == $unit) {
1357
            return [$size / (1 << 10), 'KB'];
1358
        }
1359
1360
        // Add zero to remove useless .00
1361
        return [$size, 'bytes'];
1362
    }
1363
1364
    /**
1365
     * @return string|null
1366
     */
1367
    public function getDownloadUrl()
1368
    {
1369
        return $this->downloadUrl;
1370
    }
1371
1372
    /**
1373
     * @param string|null $downloadUrl
1374
     *
1375
     * @return Asset
1376
     */
1377
    public function setDownloadUrl($downloadUrl)
1378
    {
1379
        $this->downloadUrl = $downloadUrl;
1380
1381
        return $this;
1382
    }
1383
1384
    /**
1385
     * @return bool
1386
     */
1387
    public function isLocal()
1388
    {
1389
        return 'local' === $this->storageLocation;
1390
    }
1391
1392
    /**
1393
     * @return bool
1394
     */
1395
    public function isRemote()
1396
    {
1397
        return 'remote' === $this->storageLocation;
1398
    }
1399
1400
    /**
1401
     * @return bool
1402
     */
1403
    public function getDisallow()
1404
    {
1405
        return $this->disallow;
1406
    }
1407
1408
    /**
1409
     * @param mixed $disallow
1410
     */
1411
    public function setDisallow($disallow)
1412
    {
1413
        $this->disallow = $disallow;
1414
    }
1415
}
1416