Passed
Push — master ( d08a98...cbd00e )
by Mihail
05:00
created

Content::actionGallerydelete()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 8
nop 2
dl 0
loc 31
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
namespace Apps\Controller\Api;
4
5
use Apps\ActiveRecord\App as AppRecord;
6
use Apps\ActiveRecord\Content as ContentRecord;
7
use Apps\Model\Api\Content\ContentRatingChange;
8
use Extend\Core\Arch\ApiController;
9
use Ffcms\Core\App;
10
use Ffcms\Core\Exception\ForbiddenException;
11
use Ffcms\Core\Exception\NativeException;
12
use Ffcms\Core\Exception\NotFoundException;
13
use Ffcms\Core\Helper\FileSystem\Directory;
14
use Ffcms\Core\Helper\FileSystem\File;
15
use Ffcms\Core\Helper\FileSystem\Normalize;
16
use Ffcms\Core\Helper\Type\Arr;
17
use Ffcms\Core\Helper\Type\Obj;
18
use Ffcms\Core\Helper\Type\Str;
19
use Gregwar\Image\Image;
20
21
class Content extends ApiController
22
{
23
    public $maxSize = 512000; // in bytes, 500 * 1024
24
    public $maxResize = 150;
25
26
    public $allowedExt = ['jpg', 'png', 'gif', 'jpeg', 'bmp', 'webp'];
27
28
    /**
29
     * Prepare configuratins before initialization
30
     */
31
    public function before()
32
    {
33
        parent::before();
34
        $configs = AppRecord::getConfigs('app', 'Content');
35
        // prevent null-type config data
36
        if ((int)$configs['gallerySize'] > 0) {
37
            $this->maxSize = (int)$configs['gallerySize'] * 1024;
38
        }
39
40
        if ((int)$configs['galleryResize'] > 0) {
41
            $this->maxResize = (int)$configs['galleryResize'];
42
        }
43
    }
44
    
45
    /**
46
     * Change content item rating action
47
     * @param string $type
48
     * @param int $id
49
     * @throws NativeException
50
     * @throws ForbiddenException
51
     * @throws NotFoundException
52
     * @return string
53
     */
54
    public function actionChangerate($type, $id)
55
    {
56
        // check input params
57 View Code Duplication
        if (!Arr::in($type, ['plus', 'minus']) || !Obj::isLikeInt($id)) {
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...
58
            throw new NativeException('Bad conditions');
59
        }
60
        
61
        // get current user and check is authed
62
        $user = App::$User->identity();
63
        if ($user === null || !App::$User->isAuth()) {
64
            throw new ForbiddenException(__('Authorization is required!'));
65
        }
66
67
        // set ignored content id to rate in session
68
        $ignored = App::$Session->get('content.rate.ignore');
69
        $ignored[] = $id;
70
        App::$Session->set('content.rate.ignore', $ignored);
71
        
72
        // find content record
73
        $record = ContentRecord::find($id);
74
        if ($record === null || $record->count() < 1) {
75
            throw new NotFoundException(__('Content item is not founded'));
76
        }
77
78
        // check if author rate him-self content
79
        if ($record->author_id === $user->getId()) {
80
            throw new ForbiddenException(__('You can not rate your own content'));
81
        }
82
        
83
        // initialize model
84
        $model = new ContentRatingChange($record, $type, $user);
0 ignored issues
show
Compatibility introduced by
$record of type object<Ffcms\Core\Arch\ActiveModel> is not a sub-type of object<Apps\ActiveRecord\Content>. It seems like you assume a child class of the class Ffcms\Core\Arch\ActiveModel to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
85
        // check if content items is already rated by this user
86
        if ($model->isAlreadyRated()) {;
87
            throw new ForbiddenException(__('You have already rate this!'));            
88
        }
89
        
90
        // make rate - add +1 to content rating and author rating
91
        $model->make();
92
        
93
        return json_encode([
94
            'status' => 1,
95
            'rating' => $model->getRating()
96
        ]);
97
    }
98
99
    /**
100
     * Upload new files to content item gallery
101
     * @param int $id
102
     * @return string
103
     * @throws ForbiddenException
104
     * @throws NativeException
105
     * @throws \Exception
106
     */
107
    public function actionGalleryupload($id)
108
    {
109
        // check if id is passed
110
        if (Str::likeEmpty($id)) {
111
            throw new NativeException('Wrong input data');
112
        }
113
114
        // check if user have permission to access there
115 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...
116
            throw new NativeException(__('Permissions to upload is denied'));
117
        }
118
119
        // check if directory exist
120
        if (!Directory::exist('/upload/gallery/' . $id)) {
121
            Directory::create('/upload/gallery/' . $id);
122
        }
123
124
        // get file object
125
        /** @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */
126
        $file = $this->request->files->get('file');
127
        if ($file === null || $file->getError() !== 0) {
128
            throw new NativeException(__('Unexpected error in upload process'));
129
        }
130
131
        // check file size
132
        if ($file->getSize() < 1 || $file->getSize() > $this->maxSize) {
133
            throw new ForbiddenException(__('File size is too big. Max size: %size%kb', ['size' => (int)($this->maxSize/1024)]));
134
        }
135
136
        // check file extension
137
        if (!Arr::in($file->guessExtension(), $this->allowedExt)) {
138
            throw new ForbiddenException(__('File extension is not allowed to upload. Allowed: %s%', ['s' => implode(', ', $this->allowedExt)]));
139
        }
140
141
        // create origin directory
142
        $originPath = '/upload/gallery/' . $id . '/orig/';
143
        if (!Directory::exist($originPath)) {
144
            Directory::create($originPath);
145
        }
146
147
        // lets make a new file name
148
        $fileName = App::$Security->simpleHash($file->getClientOriginalName() . $file->getSize());
149
        $fileNewName = $fileName . '.' . $file->guessExtension();
150
        // check if image is already loaded
151
        if (File::exist($originPath . $fileNewName)) {
152
            throw new ForbiddenException(__('File is always exists!'));
153
        }
154
        // save file from tmp to gallery origin directory
155
        $file->move(Normalize::diskFullPath($originPath), $fileNewName);
156
157
        // lets resize preview image for it
158
        $thumbPath = '/upload/gallery/' . $id . '/thumb/';
159
        if (!Directory::exist($thumbPath)) {
160
            Directory::create($thumbPath);
161
        }
162
163
        $thumb = new Image();
164
        $thumb->setCacheDir(root . '/Private/Cache/images');
165
166
        // open original file, resize it and save
167
        $thumbSaveName = Normalize::diskFullPath($thumbPath) . '/' . $fileName . '.jpg';
168
        $thumb->open(Normalize::diskFullPath($originPath) . DIRECTORY_SEPARATOR . $fileNewName)
169
            ->cropResize($this->maxResize)
170
            ->save($thumbSaveName, 'jpg', 90);
171
        $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...
172
173
        $this->setJsonHeader();
174
        return json_encode(['status' => 1, 'file' => [
175
            'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
176
            'url' => '/upload/gallery/' . $id . '/orig/' . $fileNewName,
177
            'name' => $fileNewName
178
        ]]);
179
    }
180
181
    /**
182
     * Show gallery images from upload directory
183
     * @param int $id
184
     * @return string
185
     * @throws NotFoundException
186
     * @throws NativeException
187
     */
188
    public function actionGallerylist($id)
189
    {
190
        // check if id is passed
191
        if (Str::likeEmpty($id)) {
192
            throw new NativeException('Wrong input data');
193
        }
194
195
        // check if user have permission to access there
196 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...
197
            throw new NativeException('Permission denied');
198
        }
199
200
        $thumbDir = Normalize::diskFullPath('/upload/gallery/' . $id . '/orig/');
201
        if (!Directory::exist($thumbDir)) {
202
            throw new NotFoundException('Nothing found');
203
        }
204
205
        $files = Directory::scan($thumbDir, null, true);
206
        if ($files === false || !Obj::isArray($files) || count($files) < 1) {
207
            throw new NotFoundException('Nothing found');
208
        }
209
210
        $output = [];
211
        foreach ($files as $file) {
212
            $fileExt = Str::lastIn($file, '.');
213
            $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 212 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...
214
            $output[] = [
215
                'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
216
                'url' => '/upload/gallery/' . $id . '/orig/' . $file,
217
                'name' => $file,
218
                'size' => File::size('/upload/gallery/' . $id . '/orig/' . $file)
219
            ];
220
        }
221
222
        $this->setJsonHeader();
223
        return json_encode(['status' => 1, 'files' => $output]);
224
    }
225
226
    /**
227
     * Remove items from gallery (preview+full)
228
     * @param int $id
229
     * @param string $file
230
     * @throws ForbiddenException
231
     * @throws NativeException
232
     * @return string
233
     */
234
    public function actionGallerydelete($id, $file = null)
235
    {
236
        if ($file === null || Str::likeEmpty($file)) {
237
            $file = (string)$this->request->query->get('file', null);
238
        }
239
        // check passed data
240
        if (Str::likeEmpty($file) || !Obj::isLikeInt($id)) {
241
            throw new NativeException('Wrong input data');
242
        }
243
244
        // check passed file extension
245
        $fileExt = Str::lastIn($file, '.', true);
246
        $fileName = Str::firstIn($file, '.');
247
        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 245 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...
248
            throw new ForbiddenException('Wrong file extension');
249
        }
250
251
        // generate path
252
        $thumb = '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg';
253
        $full = '/upload/gallery/' . $id . '/orig/' . $file;
254
255
        // check if file exists and remove
256
        if (File::exist($thumb) || File::exist($full)) {
257
            File::remove($thumb);
258
            File::remove($full);
259
        } else {
260
            throw new NativeException('Image is not founded');
261
        }
262
263
        return json_encode(['status' => 1, 'msg' => 'Image is removed']);
264
    }
265
}