Passed
Push — master ( 336a24...a4f66d )
by Mihail
14:10
created

Content::actionGallerydelete()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 28
rs 8.439
cc 6
eloc 15
nc 4
nop 2
1
<?php
2
3
namespace Apps\Controller\Api;
4
5
use Extend\Core\Arch\ApiController;
6
use Apps\ActiveRecord\App as AppRecord;
7
use Ffcms\Core\App;
8
use Ffcms\Core\Exception\JsonException;
9
use Ffcms\Core\Exception\NativeException;
10
use Ffcms\Core\Helper\FileSystem\Directory;
11
use Ffcms\Core\Helper\FileSystem\File;
12
use Ffcms\Core\Helper\FileSystem\Normalize;
13
use Ffcms\Core\Helper\Type\Arr;
14
use Ffcms\Core\Helper\Type\Obj;
15
use Ffcms\Core\Helper\Type\Str;
16
use Gregwar\Image\Image;
17
18
class Content extends ApiController
19
{
20
    public $maxSize = 512000; // in bytes, 500 * 1024
21
    public $maxResize = 150;
22
23
    public $allowedExt = ['jpg', 'png', 'gif', 'jpeg', 'bmp', 'webp'];
24
25
    public function before()
26
    {
27
        parent::before();
28
        $configs = AppRecord::getConfigs('app', 'Content');
29
        // prevent null-type config data
30
        if ((int)$configs['gallerySize'] > 0) {
31
            $this->maxSize = (int)$configs['gallerySize'] * 1024;
32
        }
33
34
        if ((int)$configs['galleryResize'] > 0) {
35
            $this->maxResize = (int)$configs['galleryResize'];
36
        }
37
    }
38
39
    /**
40
     * Upload new files to content item gallery
41
     * @param $id
42
     * @return string
43
     * @throws JsonException
44
     * @throws NativeException
45
     * @throws \Exception
46
     */
47
    public function actionGalleryupload($id)
48
    {
49
        // check if id is passed
50
        if (Str::likeEmpty($id)) {
51
            throw new JsonException('Wrong input data');
52
        }
53
54
        // check if user have permission to access there
55 View Code Duplication
        if (!App::$User->isAuth() || !App::$User->identity()->getRole()->can('global/file')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
56
            throw new NativeException(__('Permissions to upload is denied'));
57
        }
58
59
        // check if directory exist
60
        if (!Directory::exist('/upload/gallery/' . $id)) {
61
            Directory::create('/upload/gallery/' . $id);
62
        }
63
64
        // get file object
65
        /** @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */
66
        $file = App::$Request->files->get('gallery-files');
67
        if ($file === null || $file->getError() !== 0) {
68
            throw new JsonException(__('Unexpected error in upload process'));
69
        }
70
71
        // check file size
72
        if ($file->getSize() < 1 || $file->getSize() > $this->maxSize) {
73
            throw new JsonException(__('File size is too big. Max size: %size%kb', ['size' => intval($this->maxSize/1024)]));
74
        }
75
76
        // check file extension
77
        if (!Arr::in($file->guessExtension(), $this->allowedExt)) {
78
            throw new JsonException(__('File extension is not allowed to upload. Allowed: %s%', ['s' => implode(', ', $this->allowedExt)]));
79
        }
80
81
        // create origin directory
82
        $originPath = '/upload/gallery/' . $id . '/orig/';
83
        if (!Directory::exist($originPath)) {
84
            Directory::create($originPath);
85
        }
86
87
        // lets make a new file name
88
        $fileName = App::$Security->simpleHash($file->getFilename() . $file->getSize());
89
        $fileNewName = $fileName . '.' . $file->guessExtension();
90
        // save file from tmp to gallery origin directory
91
        $file->move(Normalize::diskFullPath($originPath), $fileNewName);
92
93
        // lets resize preview image for it
94
        $thumbPath = '/upload/gallery/' . $id . '/thumb/';
95
        if (!Directory::exist($thumbPath)) {
96
            Directory::create($thumbPath);
97
        }
98
99
        $thumb = new Image();
100
        $thumb->setCacheDir(root . '/Private/Cache/images');
101
102
        // open original file, resize it and save
103
        $thumbSaveName = Normalize::diskFullPath($thumbPath) . '/' . $fileName . '.jpg';
104
        $thumb->open(Normalize::diskFullPath($originPath) . DIRECTORY_SEPARATOR . $fileNewName)
105
            ->cropResize($this->maxResize)
106
            ->save($thumbSaveName, 'jpg', 90);
107
        $thumb = null;
0 ignored issues
show
Unused Code introduced by
$thumb is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
109
        $output[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$output was never initialized. Although not strictly required by PHP, it is generally a good practice to add $output = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
110
            'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
111
            'url' => '/upload/gallery/' . $id . '/orig/' . $fileNewName,
112
            'name' => $fileNewName
113
        ];
114
115
        $this->setJsonHeader();
116
117
        // generate success response
118
        return json_encode(['status' => 1, 'message' => 'ok', 'files' => $output]);
119
    }
120
121
    /**
122
     * Show gallery images from upload directory
123
     * @param int $id
124
     * @return string
125
     * @throws JsonException
126
     * @throws NativeException
127
     */
128
    public function actionGallerylist($id)
129
    {
130
        // check if id is passed
131
        if (Str::likeEmpty($id)) {
132
            throw new JsonException('Wrong input data');
133
        }
134
135
        // check if user have permission to access there
136 View Code Duplication
        if (!App::$User->isAuth() || !App::$User->identity()->getRole()->can('global/file')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
137
            throw new NativeException('Permission denied');
138
        }
139
140
        $thumbDir = Normalize::diskFullPath('/upload/gallery/' . $id . '/orig/');
141
        if (!Directory::exist($thumbDir)) {
142
            throw new JsonException('Nothing found');
143
        }
144
145
        $files = Directory::scan($thumbDir, null, true);
146
        if (!Obj::isArray($files) || count($files) < 1) {
147
            throw new JsonException('Nothing found');
148
        }
149
150
        $output = [];
151
        foreach ($files as $file) {
0 ignored issues
show
Bug introduced by
The expression $files of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
152
            $fileExt = Str::lastIn($file, '.');
153
            $fileName = Str::sub($file, 0, -Str::length($fileExt));
0 ignored issues
show
Security Bug introduced by
It seems like $fileExt defined by \Ffcms\Core\Helper\Type\Str::lastIn($file, '.') on line 152 can also be of type false; however, Ffcms\Core\Helper\Type\Str::length() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
154
            $output[] = [
155
                'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
156
                'url' => '/upload/gallery/' . $id . '/orig/' . $file,
157
                'name' => $file
158
            ];
159
        }
160
161
        $this->setJsonHeader();
162
        return json_encode(['files' => $output]);
163
    }
164
165
    public function actionGallerydelete($id, $file)
166
    {
167
        // check passed data
168
        if (Str::likeEmpty($file) || !Obj::isLikeInt($id)) {
169
            throw new JsonException('Wrong input data');
170
        }
171
172
        // check passed file extension
173
        $fileExt = Str::lastIn($file, '.', true);
174
        $fileName = Str::firstIn($file, '.');
175
        if (!Arr::in($fileExt, $this->allowedExt)) {
0 ignored issues
show
Security Bug introduced by
It seems like $fileExt defined by \Ffcms\Core\Helper\Type\...astIn($file, '.', true) on line 173 can also be of type false; however, Ffcms\Core\Helper\Type\Arr::in() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
176
            throw new JsonException('Wrong file extension');
177
        }
178
179
        // generate path
180
        $thumb = '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg';
181
        $full = '/upload/gallery/' . $id . '/orig/' . $file;
182
183
        // check if file exists and remove
184
        if (File::exist($thumb) || File::exist($full)) {
185
            File::remove($thumb);
186
            File::remove($full);
187
        } else {
188
            throw new NativeException('Image is not founded');
189
        }
190
191
        return json_encode(['status' => 1, 'msg' => 'Image is removed']);
192
    }
193
}