Completed
Push — master ( 003c26...a1c350 )
by Donata
02:15
created

VideoSliderItem   C

Complexity

Total Complexity 37

Size/Duplication

Total Lines 333
Duplicated Lines 2.7 %

Coupling/Cohesion

Components 2
Dependencies 17

Importance

Changes 0
Metric Value
wmc 37
lcom 2
cbo 17
dl 9
loc 333
rs 6.7571
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A singular_name() 0 3 1
A plural_name() 0 3 1
A getSliderTypes() 9 9 2
A getCMSFields() 0 72 1
A fieldLabels() 0 14 1
A getVideoId() 0 9 3
C getEmbedLink() 0 22 8
B validate() 0 21 5
A getSliderImage() 0 7 2
A getBetterButtonsActions() 0 9 4
C fetchVideosPicture() 0 50 9

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * @author    Donatas Navidonskis <[email protected]>
5
 * @since     2017
6
 * @class     VideoSliderItem
7
 *
8
 * @property int     CoverID
9
 * @property int     Mp4ID
10
 * @property int     WebMID
11
 * @property int     OggID
12
 * @property string  Type
13
 * @property string  URL
14
 * @property boolean AutoPlay
15
 *
16
 * @method File Mp4
17
 * @method File WebM
18
 * @method File Ogg
19
 * @method Image Cover
20
 *
21
 * @TODO      implement https://github.com/xemle/html5-video-php package to convert webm and ogg videos when saving.
22
 */
23
class VideoSliderItem extends BaseSliderItem {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
24
25
    /**
26
     * @var array
27
     * @config
28
     */
29
    private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
30
        'Type'     => 'Enum(array("Youtube", "Vimeo", "File"), "File")',
31
        'URL'      => 'Varchar(128)',
32
        'AutoPlay' => 'Boolean(true)',
33
    ];
34
35
    /**
36
     * @var array
37
     * @config
38
     */
39
    private static $has_one = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
40
        'Mp4'   => 'File',
41
        'WebM'  => 'File',
42
        'Ogg'   => 'File',
43
        'Cover' => 'Image',
44
    ];
45
46
    /**
47
     * Allow to call those functions.
48
     *
49
     * @var array
50
     * @config
51
     */
52
    private static $better_buttons_actions = [
1 ignored issue
show
Unused Code introduced by
The property $better_buttons_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
53
        'fetchVideosPicture',
54
    ];
55
56
    /**
57
     * Set providers default embed link. The key value should
58
     * be equal within Type field value.
59
     * {VideoId} - will be replaced with actual video id
60
     * {AutoPlay} - will be replaced within key of AutoPlay if AutoPlay field is true.
61
     *
62
     * @var array
63
     * @config
64
     */
65
    private static $embed_links = [
0 ignored issues
show
Unused Code introduced by
The property $embed_links is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
66
        "Youtube" => [
67
            "Link"     => "https://www.youtube.com/embed/{VideoId}{AutoPlay}",
68
            "AutoPlay" => "?autoplay=1",
69
        ],
70
        "Vimeo"   => [
71
            "Link"     => "https://player.vimeo.com/video/{VideoId}{AutoPlay}",
72
            "AutoPlay" => "?autoplay=1",
73
        ],
74
        // Set your own providers options
75
    ];
76
77
    /**
78
     * @return string
79
     */
80
    public function singular_name() {
81
        return _t('VideoSliderItem.SINGULARNAME', 'Video slider');
82
    }
83
84
    /**
85
     * @return string
86
     */
87
    public function plural_name() {
88
        return _t('VideoSliderItem.PLURALNAME', 'Video sliders');
89
    }
90
91
    /**
92
     * @return array
93
     */
94 View Code Duplication
    public function getSliderTypes() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
95
        $types = [];
96
97
        foreach ($this->dbObject('Type')->enumValues() as $type) {
98
            $types[$type] = $this->fieldLabel($type);
99
        }
100
101
        return $types;
102
    }
103
104
    /**
105
     * @return \FieldList
106
     */
107
    public function getCMSFields() {
108
        $fields = parent::getCMSFields();
109
        $fields->removeByName(['Type', 'Mp4', 'WebM', 'Ogg', 'URL', 'AutoPlay', 'Cover']);
110
        $fields->findOrMakeTab('Root.Media', $this->fieldLabel('Media'));
111
112
        $fields->addFieldsToTab('Root.Media', [
113
            $coverField = UploadField::create('Cover', $this->fieldLabel('Cover')),
114
            DropdownField::create('AutoPlay', $this->fieldLabel('TurnOnAutoPlayMode'), BlocksUtility::localized_answers()),
115
            $videoType = OptionsetField::create('Type', $this->fieldLabel('Type'), $this->getSliderTypes(), 'File'),
116
            $uploadFieldContainer = DisplayLogicWrapper::create(
117
                $mp4UploadField = UploadField::create('Mp4', $this->fieldLabel('VideoMp4')),
118
                $webMUploadField = UploadField::create('WebM', $this->fieldLabel('VideoWebM')),
119
                $oggUploadField = UploadField::create('Ogg', $this->fieldLabel('VideoOgg'))
120
            ),
121
            $urlAddressField = TextField::create('URL', $this->fieldLabel('URLAddress'))->setRightTitle(
122
                $this->fieldLabel('SetVideoURLAddress')
123
            ),
124
        ]);
125
126
        $coverField
127
            ->setAllowedMaxFileNumber(1)
128
            ->setAllowedFileCategories('image')
129
            ->setRightTitle(
130
                _t('VideoSliderItem.SET_VIDEO_COVER_IMAGE', 'Set video cover image')
131
            )
132
            ->setFolderName(
133
                sprintf('%s/Sliders', BaseBlock::config()->upload_directory)
134
            );
135
136
        $mp4UploadField
137
            ->setAllowedMaxFileNumber(1)
138
            ->setAllowedExtensions('mp4')
0 ignored issues
show
Documentation introduced by
'mp4' is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
139
            ->setRightTitle(
140
                _t('VideoSliderItem.ALLOWED_FILE_EXTENSIONS', 'Allowed file extensions: {extensions}', [
0 ignored issues
show
Documentation introduced by
array('extensions' => '.mp4') is of type array<string,string,{"extensions":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
141
                    'extensions' => '.mp4',
142
                ])
143
            )
144
            ->setFolderName(
145
                sprintf('%s/Video-Sliders', BaseBlock::config()->upload_directory)
146
            );
147
148
        $webMUploadField
149
            ->setAllowedMaxFileNumber(1)
150
            ->setAllowedExtensions('webm')
0 ignored issues
show
Documentation introduced by
'webm' is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
            ->setRightTitle(
152
                _t('VideoSliderItem.ALLOWED_FILE_EXTENSIONS', 'Allowed file extensions: {extensions}', [
0 ignored issues
show
Documentation introduced by
array('extensions' => '.webm') is of type array<string,string,{"extensions":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
153
                    'extensions' => '.webm',
154
                ])
155
            )
156
            ->setFolderName(
157
                sprintf('%s/Video-Sliders', BaseBlock::config()->upload_directory)
158
            );
159
160
        $oggUploadField
161
            ->setAllowedMaxFileNumber(1)
162
            ->setAllowedExtensions('ogg')
0 ignored issues
show
Documentation introduced by
'ogg' is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
163
            ->setRightTitle(
164
                _t('VideoSliderItem.ALLOWED_FILE_EXTENSIONS', 'Allowed file extensions: {extensions}', [
0 ignored issues
show
Documentation introduced by
array('extensions' => '.ogg') is of type array<string,string,{"extensions":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
165
                    'extensions' => '.ogg',
166
                ])
167
            )
168
            ->setFolderName(
169
                sprintf('%s/Video-Sliders', BaseBlock::config()->upload_directory)
170
            );
171
172
        $uploadFieldContainer->displayIf('Type')->isEqualTo('File');
173
        $urlAddressField->displayIf('Type')->isNotEqualTo('File');
174
175
        $this->extend('updateCMSFields', $fields);
176
177
        return $fields;
178
    }
179
180
    /**
181
     * @param bool $includeRelations
182
     *
183
     * @return array
184
     */
185
    public function fieldLabels($includeRelations = true) {
186
        return array_merge(parent::fieldLabels($includeRelations), [
187
            'Type'               => _t('VideoSliderItem.TYPE', 'Type'),
188
            'Youtube'            => _t('VideoSliderItem.YOUTUBE', 'Youtube'),
189
            'Vimeo'              => _t('VideoSliderItem.VIMEO', 'Vimeo'),
190
            'File'               => _t('VideoSliderItem.FILE', 'File'),
191
            'URLAddress'         => _t('VideoSliderItem.URL_ADDRESS', 'URL address'),
192
            'SetVideoURLAddress' => _t('VideoSliderItem.SET_VIDEO_URL_ADDRESS', 'Set video URL address'),
193
            'VideoMp4'           => _t('SliderItem.VIDEO_MP4', 'Video Mp4'),
194
            'VideoWebM'          => _t('SliderItem.VIDEO_WEBM', 'Video WebM'),
195
            'VideoOgg'           => _t('SliderItem.VIDEO_OGG', 'Video Ogg'),
196
            'TurnOnAutoPlayMode' => _t('SliderItem.TURN_ON_AUTO_PLAY_MODE', 'Turn on auto play mode?'),
197
        ]);
198
    }
199
200
    /**
201
     * This will get an id of the URL address or false
202
     * if can't parsed, object type not one of supported
203
     * providers or just empty url address field.
204
     *
205
     * @return string|false
206
     * @throws ProviderNotFound
207
     */
208
    public function getVideoId() {
209
        if (! empty($this->URL) && $this->Type != 'File') {
210
            $videoId = BlocksUtility::parse_video_id($this->URL, $this->Type);
211
212
            return $videoId;
213
        }
214
215
        return false;
216
    }
217
218
    /**
219
     * Get embed link by the set of Type field. Method depends by
220
     * static::$embed_links property.
221
     *
222
     * @return bool|string
223
     */
224
    public function getEmbedLink() {
225
        if (! empty($this->URL) && $this->Type != 'File') {
226
            try {
227
                $videoId = BlocksUtility::parse_video_id($this->URL, $this->Type);
228
            } catch (ProviderNotFound $ex) {
229
                return false;
230
            }
231
232
            if ($videoId && array_key_exists($this->Type, ($options = static::config()->embed_links))) {
233
                $options = $options[$this->Type];
234
                $autoPlay = array_key_exists("AutoPlay", $options) && ! empty($options["AutoPlay"]) ? $options["AutoPlay"] : '';
235
236
                return str_replace(
237
                    ['{VideoId}', '{AutoPlay}'],
238
                    [$videoId, $autoPlay],
239
                    $options["Link"]
240
                );
241
            }
242
        }
243
244
        return false;
245
    }
246
247
    /**
248
     * @return \ValidationResult
249
     */
250
    protected function validate() {
251
        $validation = parent::validate();
252
253
        if (! empty($this->URL) && $this->Type != 'File') {
254
            try {
255
                $result = $this->getVideoId();
256
            } catch (ProviderNotFound $ex) {
257
                $validation->error($ex->getMessage());
258
259
                return $validation;
260
            }
261
262
            // if we can't parse url address, return an error with bad url address or
263
            // the type is not of the url address providers.
264
            if (! $result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
265
                $validation->error(_t('VideoSliderItem.INVALID_URL_ADDRESS_OR_THE_TYPE', 'Invalid URL address or the type'));
266
            }
267
        }
268
269
        return $validation;
270
    }
271
272
    /**
273
     * @return Image|false
274
     */
275
    public function getSliderImage() {
276
        if ($this->Cover()->exists()) {
0 ignored issues
show
Documentation Bug introduced by
The method Cover does not exist on object<VideoSliderItem>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
277
            return $this->Cover();
0 ignored issues
show
Documentation Bug introduced by
The method Cover does not exist on object<VideoSliderItem>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
278
        }
279
280
        return false;
281
    }
282
283
    /**
284
     * Creating a button to fetch videos picture if cover image not exists.
285
     *
286
     * @return FieldList
287
     */
288
    public function getBetterButtonsActions() {
289
        $fields = parent::getBetterButtonsActions();
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class BaseSliderItem as the method getBetterButtonsActions() does only exist in the following sub-classes of BaseSliderItem: VideoSliderItem. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
290
291
        if ($this->Type != 'File' && ! $this->Cover()->exists() && ! empty($this->URL)) {
0 ignored issues
show
Documentation Bug introduced by
The method Cover does not exist on object<VideoSliderItem>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
292
            $fields->push(BetterButtonCustomAction::create('fetchVideosPicture', _t('VideoSliderItem.FETCH_VIDEOS_PICTURE', 'Fetch videos picture')));
293
        }
294
295
        return $fields;
296
    }
297
298
    /**
299
     * Fetching/downloading picture from the providers url address and
300
     * saving as Image object.
301
     *
302
     * @return false
303
     */
304
    public function fetchVideosPicture() {
305
        try {
306
            $videoId = $this->getVideoId();
307
        } catch (ProviderNotFound $ex) {
308
            return false;
309
        }
310
311
        $directoryPath = sprintf("%s/Sliders", BaseBlock::config()->upload_directory);
312
        $folder = Folder::find_or_make($directoryPath);
313
314
        if (empty($this->URL)) {
315
            return false;
316
        }
317
318
        $title = ! empty($this->Title) ? FileNameFilter::create()->filter($this->Title)."-{$this->ID}" : "video-{$this->ID}";
319
        $fileName = strtolower(sprintf("%s.jpg", $title));
320
        $baseFolder = Director::baseFolder()."/".$folder->getFilename();
321
322
        switch (strtolower($this->Type)) {
323
            case 'youtube':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
324
325
                $fileContent = file_get_contents("https://img.youtube.com/vi/{$videoId}/maxresdefault.jpg");
326
327
                break;
328
329
            case 'vimeo':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
330
331
                $hash = unserialize(file_get_contents("http://vimeo.com/api/v2/video/{$videoId}.php"));
332
                $fileContent = file_get_contents($hash[0]['thumbnail_large']);
333
334
                break;
335
        }
336
337
        if ($fileContent) {
338
            if (file_put_contents($absoluteFileName = ($baseFolder.$fileName), $fileContent)) {
0 ignored issues
show
Bug introduced by
The variable $fileContent does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
339
                $image = Image::create([
340
                    "Filename" => $folder->getFilename().$fileName,
341
                    "Title"    => $this->Title,
342
                    "Name"     => $fileName,
343
                    "ParentID" => $folder->ID,
344
                    "OwnerID"  => Member::currentUserID(),
345
                ]);
346
347
                if ($image->write()) {
348
                    $this->CoverID = $image->ID;
349
                    $this->write();
350
                }
351
            }
352
        }
353
    }
354
355
}