Passed
Push — master ( c2a29b...80000e )
by Mihail
04:08
created

Content   C

Complexity

Total Complexity 38

Size/Duplication

Total Lines 239
Duplicated Lines 3.77 %

Coupling/Cohesion

Components 1
Dependencies 21

Importance

Changes 6
Bugs 3 Features 0
Metric Value
wmc 38
c 6
b 3
f 0
lcom 1
cbo 21
dl 9
loc 239
rs 5.1333

5 Methods

Rating   Name   Duplication   Size   Complexity  
A before() 0 13 3
D actionChangerate() 3 43 9
C actionGalleryupload() 3 74 12
C actionGallerylist() 3 36 8
B actionGallerydelete() 0 28 6

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
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
use Apps\ActiveRecord\Content as ContentRecord;
18
use Apps\ActiveRecord\ContentRating;
19
use Apps\Model\Api\Content\ContentRatingChange;
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 JsonException
50
     * @return string
51
     */
52
    public function actionChangerate($type, $id)
53
    {
54
        // check input params
55 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...
56
            throw new JsonException('Bad conditions');
57
        }
58
        
59
        // get current user and check is authed
60
        $user = App::$User->identity();
61
        if ($user === null || !App::$User->isAuth()) {
62
            throw new JsonException(__('Authorization is required!'));
63
        }
64
        
65
        // find content record
66
        $record = ContentRecord::find($id);
67
        if ($record === null || $record->count() < 1) {
68
            throw new JsonException(__('Content item is not founded'));
69
        }
70
        
71
        // initialize model
72
        $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...
73
        // check if content items is already rated by this user
74
        if ($model->isAlreadyRated()) {
75
            // set ignored content id to rate in session
76
            $ignored = App::$Session->get('content.rate.ignore');
77
            $ignored[] = $id;
78
            App::$Session->set('content.rate.ignore', $ignored);
79
            throw new JsonException(__('You have already rate this!'));            
80
        }
81
        
82
        // make rate - add +1 to content rating and author rating
83
        if ($model->make()) {
84
            // set ignored content id to rate in session
85
            $ignored = App::$Session->get('content.rate.ignore');
86
            $ignored[] = $id;
87
            App::$Session->set('content.rate.ignore', $ignored);            
88
        }
89
        
90
        return json_encode([
91
            'status' => 1,
92
            'rating' => $model->getRating() // @todo this
93
        ]);
94
    }
95
96
    /**
97
     * Upload new files to content item gallery
98
     * @param int $id
99
     * @return string
100
     * @throws JsonException
101
     * @throws NativeException
102
     * @throws \Exception
103
     */
104
    public function actionGalleryupload($id)
105
    {
106
        // check if id is passed
107
        if (Str::likeEmpty($id)) {
108
            throw new JsonException('Wrong input data');
109
        }
110
111
        // check if user have permission to access there
112 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...
113
            throw new NativeException(__('Permissions to upload is denied'));
114
        }
115
116
        // check if directory exist
117
        if (!Directory::exist('/upload/gallery/' . $id)) {
118
            Directory::create('/upload/gallery/' . $id);
119
        }
120
121
        // get file object
122
        /** @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */
123
        $file = App::$Request->files->get('gallery-files');
124
        if ($file === null || $file->getError() !== 0) {
125
            throw new JsonException(__('Unexpected error in upload process'));
126
        }
127
128
        // check file size
129
        if ($file->getSize() < 1 || $file->getSize() > $this->maxSize) {
130
            throw new JsonException(__('File size is too big. Max size: %size%kb', ['size' => intval($this->maxSize/1024)]));
131
        }
132
133
        // check file extension
134
        if (!Arr::in($file->guessExtension(), $this->allowedExt)) {
135
            throw new JsonException(__('File extension is not allowed to upload. Allowed: %s%', ['s' => implode(', ', $this->allowedExt)]));
136
        }
137
138
        // create origin directory
139
        $originPath = '/upload/gallery/' . $id . '/orig/';
140
        if (!Directory::exist($originPath)) {
141
            Directory::create($originPath);
142
        }
143
144
        // lets make a new file name
145
        $fileName = App::$Security->simpleHash($file->getFilename() . $file->getSize());
146
        $fileNewName = $fileName . '.' . $file->guessExtension();
147
        // save file from tmp to gallery origin directory
148
        $file->move(Normalize::diskFullPath($originPath), $fileNewName);
149
150
        // lets resize preview image for it
151
        $thumbPath = '/upload/gallery/' . $id . '/thumb/';
152
        if (!Directory::exist($thumbPath)) {
153
            Directory::create($thumbPath);
154
        }
155
156
        $thumb = new Image();
157
        $thumb->setCacheDir(root . '/Private/Cache/images');
158
159
        // open original file, resize it and save
160
        $thumbSaveName = Normalize::diskFullPath($thumbPath) . '/' . $fileName . '.jpg';
161
        $thumb->open(Normalize::diskFullPath($originPath) . DIRECTORY_SEPARATOR . $fileNewName)
162
            ->cropResize($this->maxResize)
163
            ->save($thumbSaveName, 'jpg', 90);
164
        $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...
165
166
        // dont ask me why there is 2nd lvl array (can contains multiply items to frontend response)
167
        $output = [
168
            'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
169
            'url' => '/upload/gallery/' . $id . '/orig/' . $fileNewName,
170
            'name' => $fileNewName
171
        ];
172
173
        $this->setJsonHeader();
174
175
        // generate success response
176
        return json_encode(['status' => 1, 'message' => 'ok', 'files' => [$output]]);
177
    }
178
179
    /**
180
     * Show gallery images from upload directory
181
     * @param int $id
182
     * @return string
183
     * @throws JsonException
184
     * @throws NativeException
185
     */
186
    public function actionGallerylist($id)
187
    {
188
        // check if id is passed
189
        if (Str::likeEmpty($id)) {
190
            throw new JsonException('Wrong input data');
191
        }
192
193
        // check if user have permission to access there
194 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...
195
            throw new NativeException('Permission denied');
196
        }
197
198
        $thumbDir = Normalize::diskFullPath('/upload/gallery/' . $id . '/orig/');
199
        if (!Directory::exist($thumbDir)) {
200
            throw new JsonException('Nothing found');
201
        }
202
203
        $files = Directory::scan($thumbDir, null, true);
204
        if (!Obj::isArray($files) || count($files) < 1) {
205
            throw new JsonException('Nothing found');
206
        }
207
208
        $output = [];
209
        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...
210
            $fileExt = Str::lastIn($file, '.');
211
            $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 210 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...
212
            $output[] = [
213
                'thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg',
214
                'url' => '/upload/gallery/' . $id . '/orig/' . $file,
215
                'name' => $file
216
            ];
217
        }
218
219
        $this->setJsonHeader();
220
        return json_encode(['files' => $output]);
221
    }
222
223
    /**
224
     * Remove items from gallery (preview+full)
225
     * @param int $id
226
     * @param string $file
227
     * @throws JsonException
228
     * @throws NativeException
229
     * @return string
230
     */
231
    public function actionGallerydelete($id, $file)
232
    {
233
        // check passed data
234
        if (Str::likeEmpty($file) || !Obj::isLikeInt($id)) {
235
            throw new JsonException('Wrong input data');
236
        }
237
238
        // check passed file extension
239
        $fileExt = Str::lastIn($file, '.', true);
240
        $fileName = Str::firstIn($file, '.');
241
        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 239 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...
242
            throw new JsonException('Wrong file extension');
243
        }
244
245
        // generate path
246
        $thumb = '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg';
247
        $full = '/upload/gallery/' . $id . '/orig/' . $file;
248
249
        // check if file exists and remove
250
        if (File::exist($thumb) || File::exist($full)) {
251
            File::remove($thumb);
252
            File::remove($full);
253
        } else {
254
            throw new NativeException('Image is not founded');
255
        }
256
257
        return json_encode(['status' => 1, 'msg' => 'Image is removed']);
258
    }
259
}