Completed
Push — master ( f8ff86...ec22b1 )
by Henry
03:57
created

MediaRequestHandler   F

Complexity

Total Complexity 115

Size/Duplication

Total Lines 665
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 317
dl 0
loc 665
rs 2
c 0
b 0
f 0
wmc 115

11 Methods

Rating   Name   Duplication   Size   Complexity  
C handleRequest() 0 76 14
A handleInfoRequest() 0 22 6
A handleCaptionRequest() 0 33 6
A handleBrowseRequest() 0 23 6
F handleMediaRequest() 0 115 24
B handleDownloadRequest() 0 38 9
B handleDeleteRequest() 0 34 8
A handleManageRequest() 0 6 1
B handleThumbnailRequest() 0 74 10
B handleMediaDeleteRequest() 0 34 9
D handleUploadRequest() 0 99 22

How to fix   Complexity   

Complex Class

Complex classes like MediaRequestHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MediaRequestHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Divergence\Controllers;
3
4
use Divergence\Helpers\JSON;
5
use Divergence\Models\Media\Media;
6
use Divergence\Models\ActiveRecord;
7
8
class MediaRequestHandler extends RecordsRequestHandler
9
{
10
    // RecordRequestHandler configuration
11
    public static $recordClass = Media::class;
12
    public static $accountLevelRead = false;
13
    public static $accountLevelBrowse = 'Staff';
14
    public static $accountLevelWrite = 'Staff';
15
    public static $accountLevelAPI = false;
16
    public static $browseLimit = 100;
17
    public static $browseOrder = ['ID' => 'DESC'];
18
19
    // configurables
20
    public static $defaultPage = 'browse';
21
    public static $defaultThumbnailWidth = 100;
22
    public static $defaultThumbnailHeight = 100;
23
    public static $uploadFileFieldName = 'mediaFile';
24
    public static $responseMode = 'html';
25
26
    public static $searchConditions = [
27
        'Caption' => [
28
            'qualifiers' => ['any','caption']
29
            ,'points' => 2
30
            ,'sql' => 'Caption LIKE "%%%s%%"',
31
        ]
32
        ,'CaptionLike' => [
33
            'qualifiers' => ['caption-like']
34
            ,'points' => 2
35
            ,'sql' => 'Caption LIKE "%s"',
36
        ]
37
        ,'CaptionNot' => [
38
            'qualifiers' => ['caption-not']
39
            ,'points' => 2
40
            ,'sql' => 'Caption NOT LIKE "%%%s%%"',
41
        ]
42
        ,'CaptionNotLike' => [
43
            'qualifiers' => ['caption-not-like']
44
            ,'points' => 2
45
            ,'sql' => 'Caption NOT LIKE "%s"',
46
        ],
47
    ];
48
49
    public static function handleRequest()
50
    {
51
        // handle json response mode
52
        if (static::peekPath() == 'json') {
53
            static::$responseMode = static::shiftPath();
54
        }
55
56
        // handle action
57
        switch ($action = static::shiftPath()) {
58
59
#    		case 'media':
60
#			{
61
#				return static::handleMediaRequest();
62
#			}
63
64
            case 'upload':
65
            {
66
                return static::handleUploadRequest();
67
            }
68
69
            case 'open':
70
            {
71
                $mediaID = static::shiftPath();
72
73
                return static::handleMediaRequest($mediaID);
74
            }
75
76
            case 'download':
77
            {
78
                $mediaID = static::shiftPath();
79
                $filename = urldecode(static::shiftPath());
80
81
                return static::handleDownloadRequest($mediaID, $filename);
82
            }
83
84
            case 'info':
85
            {
86
                $mediaID = static::shiftPath();
87
88
                return static::handleInfoRequest($mediaID);
89
            }
90
91
            case 'caption':
92
            {
93
                $mediaID = static::shiftPath();
94
95
                return static::handleCaptionRequest($mediaID);
96
            }
97
98
            case 'delete':
99
            {
100
                return static::handleDeleteRequest();
0 ignored issues
show
Bug introduced by
The call to Divergence\Controllers\M...::handleDeleteRequest() has too few arguments starting with Record. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
                return static::/** @scrutinizer ignore-call */ handleDeleteRequest();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
101
            }
102
103
            case 'thumbnail':
104
            {
105
                return static::handleThumbnailRequest();
106
            }
107
108
            case false:
109
            case '':
110
            case 'browse':
111
            {
112
                if ($_SERVER['REQUEST_METHOD'] == 'POST') {
113
                    return static::handleUploadRequest();
114
                }
115
116
                return static::handleBrowseRequest();
117
            }
118
119
            default:
120
            {
121
                if (ctype_digit($action)) {
122
                    return static::handleMediaRequest($action);
123
                } else {
124
                    return parent::handleRecordsRequest($action);
125
                }
126
            }
127
        }
128
    }
129
130
131
    public static function handleUploadRequest($options = [], $authenticationRequired = true)
132
    {
133
        // require authentication
134
        if ($authenticationRequired) {
135
            static::checkUploadAccess();
0 ignored issues
show
Bug introduced by
The method checkUploadAccess() does not exist on Divergence\Controllers\MediaRequestHandler. Did you maybe mean checkReadAccess()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
            static::/** @scrutinizer ignore-call */ 
136
                    checkUploadAccess();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
136
        }
137
138
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
139
            // init options
140
            $options = array_merge([
141
                'fieldName' => static::$uploadFileFieldName,
142
            ], $options);
143
144
145
            // check upload
146
            if (empty($_FILES[$options['fieldName']])) {
147
                return static::throwError('You did not select a file to upload');
0 ignored issues
show
Bug introduced by
The method throwError() does not exist on Divergence\Controllers\MediaRequestHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

147
                return static::/** @scrutinizer ignore-call */ throwError('You did not select a file to upload');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
148
            }
149
150
            // handle upload errors
151
            if ($_FILES[$options['fieldName']]['error'] != UPLOAD_ERR_OK) {
152
                switch ($_FILES[$options['fieldName']]['error']) {
153
                    case UPLOAD_ERR_NO_FILE:
154
                        return static::throwError('You did not select a file to upload');
155
156
                    case UPLOAD_ERR_INI_SIZE:
157
                    case UPLOAD_ERR_FORM_SIZE:
158
                        return static::throwError('Your file exceeds the maximum upload size. Please try again with a smaller file.');
159
160
                    case UPLOAD_ERR_PARTIAL:
161
                        return static::throwError('Your file was only partially uploaded, please try again.');
162
163
                    default:
164
                        return static::throwError('There was an unknown problem while processing your upload, please try again.');
165
                }
166
            }
167
168
            // init caption
169
            if (!isset($options['Caption'])) {
170
                if (!empty($_REQUEST['Caption'])) {
171
                    $options['Caption'] = $_REQUEST['Caption'];
172
                } else {
173
                    $options['Caption'] = preg_replace('/\.[^.]+$/', '', $_FILES[$options['fieldName']]['name']);
174
                }
175
            }
176
177
            // create media
178
            try {
179
                $Media = Media::createFromUpload($_FILES[$options['fieldName']]['tmp_name'], $options);
180
            } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The type Divergence\Controllers\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
181
                return static::throwInvalidRequestError('The file you uploaded is not of a supported media format');
0 ignored issues
show
Bug introduced by
The method throwInvalidRequestError() does not exist on Divergence\Controllers\MediaRequestHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
                return static::/** @scrutinizer ignore-call */ throwInvalidRequestError('The file you uploaded is not of a supported media format');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
182
            }
183
        } elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') {
184
            $put = fopen("php://input", "r"); // open input stream
185
186
            $tmp = tempnam("/tmp", "emr");  // use PHP to make a temporary file
187
            $fp = fopen($tmp, "w"); // open write stream to temp file
188
189
            // write
190
            while ($data = fread($put, 1024)) {
0 ignored issues
show
Bug introduced by
It seems like $put can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
            while ($data = fread(/** @scrutinizer ignore-type */ $put, 1024)) {
Loading history...
191
                fwrite($fp, $data);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
                fwrite(/** @scrutinizer ignore-type */ $fp, $data);
Loading history...
192
            }
193
194
            // close handles
195
            fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

195
            fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
196
            fclose($put);
197
198
            // create media
199
            try {
200
                $Media = Media::createFromFile($tmp, $options);
201
            } catch (Exception $e) {
202
                return static::throwInvalidRequestError('The file you uploaded is not of a supported media format');
203
            }
204
        } else {
205
            return static::respond('upload');
206
        }
207
208
        // assign tag
209
        /*if (!empty($_REQUEST['Tag']) && ($Tag = Tag::getByHandle($_REQUEST['Tag']))) {
210
            $Tag->assignItem('Media', $Media->ID);
211
        }*/
212
213
        // assign context
214
        if (!empty($_REQUEST['ContextClass']) && !empty($_REQUEST['ContextID'])) {
215
            if (!is_subclass_of($_REQUEST['ContextClass'], ActiveRecord::class)
216
                || !in_array($_REQUEST['ContextClass']::getStaticRootClass(), Media::$fields['ContextClass']['values'])
217
                || !is_numeric($_REQUEST['ContextID'])) {
218
                return static::throwError('Context is invalid');
219
            } elseif (!$Media->Context = $_REQUEST['ContextClass']::getByID($_REQUEST['ContextID'])) {
220
                return static::throwError('Context class not found');
221
            }
222
223
            $Media->save();
224
        }
225
226
        return static::respond('uploadComplete', [
227
            'success' => (boolean)$Media
228
            ,'data' => $Media
229
            ,'TagID' => isset($Tag) ? $Tag->ID : null,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $Tag seems to never exist and therefore isset should always be false.
Loading history...
230
        ]);
231
    }
232
233
234
    public static function handleMediaRequest($mediaID)
235
    {
236
        if (empty($mediaID) || !is_numeric($mediaID)) {
237
            static::throwError('Missing or invalid media_id');
238
        }
239
240
        // get media
241
        try {
242
            $Media = Media::getById($mediaID);
243
        } catch (Exception $e) {
244
            return static::throwUnauthorizedError('You are not authorized to download this media');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...hrowUnauthorizedError() has too many arguments starting with 'You are not authorized to download this media'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

244
            return static::/** @scrutinizer ignore-call */ throwUnauthorizedError('You are not authorized to download this media');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
245
        }
246
247
        if (!$Media) {
248
            static::throwNotFoundError('Media ID #%u was not found', $media_id);
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media ID #%u was not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

248
            static::/** @scrutinizer ignore-call */ 
249
                    throwNotFoundError('Media ID #%u was not found', $media_id);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Comprehensibility Best Practice introduced by
The variable $media_id seems to be never defined.
Loading history...
249
        }
250
251
        if (!static::checkReadAccess($Media)) {
252
            return static::throwUnauthorizedError();
253
        }
254
255
        if (static::$responseMode == 'json' || $_SERVER['HTTP_ACCEPT'] == 'application/json') {
256
            JSON::translateAndRespond([
257
                'success' => true
258
                ,'data' => $Media,
259
            ]);
260
        } else {
261
262
            // determine variant
263
            if ($variant = static::shiftPath()) {
264
                if (!$Media->isVariantAvailable($variant)) {
0 ignored issues
show
Bug introduced by
The method isVariantAvailable() does not exist on Divergence\Models\ActiveRecord. It seems like you code against a sub-type of Divergence\Models\ActiveRecord such as Divergence\Models\Media\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

264
                if (!$Media->/** @scrutinizer ignore-call */ isVariantAvailable($variant)) {
Loading history...
265
                    return static::throwNotFoundError('Requested variant is not available');
266
                }
267
            } else {
268
                $variant = 'original';
269
            }
270
271
            // send caching headers
272
            $expires = 60*60*24*365;
273
            header("Cache-Control: public, max-age=$expires");
274
            header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time()+$expires));
275
            header('Pragma: public');
276
277
            // media are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
278
            if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
279
                header('HTTP/1.0 304 Not Modified');
280
                exit();
281
            }
282
283
            // initialize response
284
            set_time_limit(0);
285
            $filePath = $Media->getFilesystemPath($variant);
0 ignored issues
show
Bug introduced by
The method getFilesystemPath() does not exist on Divergence\Models\ActiveRecord. It seems like you code against a sub-type of Divergence\Models\ActiveRecord such as Divergence\Models\Media\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

285
            /** @scrutinizer ignore-call */ 
286
            $filePath = $Media->getFilesystemPath($variant);
Loading history...
286
            $fp = fopen($filePath, 'rb');
287
            $size = filesize($filePath);
288
            $length = $filesize;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $filesize seems to be never defined.
Loading history...
289
            $start = 0;
290
            $end = $size - 1;
291
292
            header('Content-Type: '.$Media->getMIMEType($variant));
0 ignored issues
show
Bug introduced by
The method getMIMEType() does not exist on Divergence\Models\ActiveRecord. It seems like you code against a sub-type of Divergence\Models\ActiveRecord such as Divergence\Models\Media\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

292
            header('Content-Type: '.$Media->/** @scrutinizer ignore-call */ getMIMEType($variant));
Loading history...
293
            header('ETag: media-'.$Media->ID.'-'.$variant);
294
            header('Accept-Ranges: bytes');
295
296
            // interpret range requests
297
            if (!empty($_SERVER['HTTP_RANGE'])) {
298
                $chunkStart = $start;
0 ignored issues
show
Unused Code introduced by
The assignment to $chunkStart is dead and can be removed.
Loading history...
299
                $chunkEnd = $end;
300
301
                list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
302
303
                if (strpos($range, ',') !== false) {
304
                    header('HTTP/1.1 416 Requested Range Not Satisfiable');
305
                    header("Content-Range: bytes $start-$end/$size");
306
                    exit();
307
                }
308
309
                if ($range == '-') {
310
                    $chunkStart = $size - substr($range, 1);
311
                } else {
312
                    $range = explode('-', $range);
313
                    $chunkStart = $range[0];
314
                    $chunkEnd = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
315
                }
316
317
                $chunkEnd = ($chunkEnd > $end) ? $end : $chunkEnd;
318
                if ($chunkStart > $chunkEnd || $chunkStart > $size - 1 || $chunkEnd >= $size) {
319
                    header('HTTP/1.1 416 Requested Range Not Satisfiable');
320
                    header("Content-Range: bytes $start-$end/$size");
321
                    exit();
322
                }
323
324
                $start = $chunkStart;
325
                $end = $chunkEnd;
326
                $length = $end - $start + 1;
327
328
                fseek($fp, $start);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fseek() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
                fseek(/** @scrutinizer ignore-type */ $fp, $start);
Loading history...
Bug introduced by
It seems like $start can also be of type string; however, parameter $offset of fseek() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
                fseek($fp, /** @scrutinizer ignore-type */ $start);
Loading history...
329
                header('HTTP/1.1 206 Partial Content');
330
            }
331
332
            // finish response
333
            header("Content-Range: bytes $start-$end/$size");
334
            header("Content-Length: $length");
335
336
            $buffer = 1024 * 8;
337
            while (!feof($fp) && ($p = ftell($fp)) <= $end) {
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of ftell() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
            while (!feof($fp) && ($p = ftell(/** @scrutinizer ignore-type */ $fp)) <= $end) {
Loading history...
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of feof() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
            while (!feof(/** @scrutinizer ignore-type */ $fp) && ($p = ftell($fp)) <= $end) {
Loading history...
338
                if ($p + $buffer > $end) {
339
                    $buffer = $end - $p + 1;
340
                }
341
342
                echo fread($fp, $buffer);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

342
                echo fread(/** @scrutinizer ignore-type */ $fp, $buffer);
Loading history...
343
                flush();
344
            }
345
346
            fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

346
            fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
347
348
            Site::finishRequest();
349
        }
350
    }
351
352
    public static function handleInfoRequest($mediaID)
353
    {
354
        if (empty($mediaID) || !is_numeric($mediaID)) {
355
            static::throwError('Missing or invalid mediaID');
356
        }
357
358
        // get media
359
        try {
360
            $Media = Media::getById($mediaID);
361
        } catch (Exception $e) {
362
            return static::throwUnauthorizedError('You are not authorized to download this media');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...hrowUnauthorizedError() has too many arguments starting with 'You are not authorized to download this media'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

362
            return static::/** @scrutinizer ignore-call */ throwUnauthorizedError('You are not authorized to download this media');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
363
        }
364
365
        if (!$Media) {
366
            static::throwNotFoundError('Media ID #%u was not found', $mediaID);
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media ID #%u was not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

366
            static::/** @scrutinizer ignore-call */ 
367
                    throwNotFoundError('Media ID #%u was not found', $mediaID);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
367
        }
368
369
        if (!static::checkReadAccess($Media)) {
370
            return static::throwUnauthorizedError();
371
        }
372
373
        return parent::handleRecordRequest($Media);
374
    }
375
376
    public static function handleDownloadRequest($media_id, $filename = false)
377
    {
378
        if (empty($media_id) || !is_numeric($media_id)) {
379
            static::throwError('Missing or invalid media_id');
380
        }
381
382
        // get media
383
        try {
384
            $Media = Media::getById($media_id);
385
        } catch (Exception $e) {
386
            return static::throwUnauthorizedError('You are not authorized to download this media');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...hrowUnauthorizedError() has too many arguments starting with 'You are not authorized to download this media'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

386
            return static::/** @scrutinizer ignore-call */ throwUnauthorizedError('You are not authorized to download this media');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
387
        }
388
389
390
        if (!$Media) {
391
            static::throwNotFoundError('Media ID #%u was not found', $media_id);
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media ID #%u was not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

391
            static::/** @scrutinizer ignore-call */ 
392
                    throwNotFoundError('Media ID #%u was not found', $media_id);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
392
        }
393
394
        if (!static::checkReadAccess($Media)) {
395
            return static::throwUnauthorizedError();
396
        }
397
398
        // determine filename
399
        if (empty($filename)) {
400
            $filename = $Media->Caption ? $Media->Caption : sprintf('%s_%u', $Media->ContextClass, $Media->ContextID);
0 ignored issues
show
Bug Best Practice introduced by
The property ContextID does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
It seems like $Media->ContextClass can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

400
            $filename = $Media->Caption ? $Media->Caption : sprintf('%s_%u', /** @scrutinizer ignore-type */ $Media->ContextClass, $Media->ContextID);
Loading history...
Bug Best Practice introduced by
The property Caption does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property ContextClass does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
401
        }
402
403
        if (strpos($filename, '.') === false) {
404
            // add extension
405
            $filename .= '.'.$Media->Extension;
0 ignored issues
show
Bug Best Practice introduced by
The property Extension does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
406
        }
407
408
        header('Content-Type: '.$Media->MIMEType);
0 ignored issues
show
Bug Best Practice introduced by
The property MIMEType does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
409
        header('Content-Disposition: attachment; filename="'.str_replace('"', '', $filename).'"');
410
        header('Content-Length: '.filesize($Media->FilesystemPath));
0 ignored issues
show
Bug Best Practice introduced by
The property FilesystemPath does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
It seems like $Media->FilesystemPath can also be of type array; however, parameter $filename of filesize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

410
        header('Content-Length: '.filesize(/** @scrutinizer ignore-type */ $Media->FilesystemPath));
Loading history...
411
412
        readfile($Media->FilesystemPath);
0 ignored issues
show
Bug introduced by
It seems like $Media->FilesystemPath can also be of type array; however, parameter $filename of readfile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

412
        readfile(/** @scrutinizer ignore-type */ $Media->FilesystemPath);
Loading history...
413
        exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
414
    }
415
416
    public static function handleCaptionRequest($media_id)
417
    {
418
        // require authentication
419
        $GLOBALS['Session']->requireAccountLevel('Staff');
420
421
        if (empty($media_id) || !is_numeric($media_id)) {
422
            static::throwError('Missing or invalid media_id');
423
        }
424
425
        // get media
426
        try {
427
            $Media = Media::getById($media_id);
428
        } catch (Exception $e) {
429
            return static::throwUnauthorizedError('You are not authorized to download this media');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...hrowUnauthorizedError() has too many arguments starting with 'You are not authorized to download this media'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

429
            return static::/** @scrutinizer ignore-call */ throwUnauthorizedError('You are not authorized to download this media');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
430
        }
431
432
433
        if (!$Media) {
434
            static::throwNotFoundError('Media ID #%u was not found', $media_id);
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media ID #%u was not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

434
            static::/** @scrutinizer ignore-call */ 
435
                    throwNotFoundError('Media ID #%u was not found', $media_id);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
435
        }
436
437
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
438
            $Media->Caption = $_REQUEST['Caption'];
0 ignored issues
show
Bug Best Practice introduced by
The property Caption does not exist on Divergence\Models\ActiveRecord. Since you implemented __set, consider adding a @property annotation.
Loading history...
439
            $Media->save();
440
441
            return static::respond('mediaCaptioned', [
442
                'success' => true
443
                ,'data' => $Media,
444
            ]);
445
        }
446
447
        return static::respond('mediaCaption', [
448
            'data' => $Media,
449
        ]);
450
    }
451
452
    public static function handleDeleteRequest(ActiveRecord $Record)
453
    {
454
        // require authentication
455
        $GLOBALS['Session']->requireAccountLevel('Staff');
456
457
        if ($mediaID = static::peekPath()) {
458
            $mediaIDs = [$mediaID];
459
        } elseif (!empty($_REQUEST['mediaID'])) {
460
            $mediaIDs = [$_REQUEST['mediaID']];
461
        } elseif (is_array($_REQUEST['media'])) {
462
            $mediaIDs = $_REQUEST['media'];
463
        }
464
465
        $deleted = [];
466
        foreach ($mediaIDs as $mediaID) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mediaIDs does not seem to be defined for all execution paths leading up to this point.
Loading history...
467
            if (!is_numeric($mediaID)) {
468
                static::throwError('Invalid mediaID');
469
            }
470
471
            // get media
472
            $Media = Media::getByID($mediaID);
473
474
            if (!$Media) {
475
                static::throwNotFoundError('Media ID #%u was not found', $mediaID);
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media ID #%u was not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

475
                static::/** @scrutinizer ignore-call */ 
476
                        throwNotFoundError('Media ID #%u was not found', $mediaID);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
476
            }
477
478
            if ($Media->destroy()) {
479
                $deleted[] = $Media;
480
            }
481
        }
482
483
        return static::respond('mediaDeleted', [
484
            'success' => true
485
            ,'data' => $deleted,
486
        ]);
487
    }
488
489
490
491
492
493
494
    public static function handleThumbnailRequest(Media $Media = null)
495
    {
496
        // send caching headers
497
        $expires = 60*60*24*365;
498
        header("Cache-Control: public, max-age=$expires");
499
        header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time()+$expires));
500
        header('Pragma: public');
501
502
503
        // thumbnails are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
504
        if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
505
            header('HTTP/1.0 304 Not Modified');
506
            exit();
507
        }
508
509
510
        // get media
511
        if (!$Media) {
512
            if (!$mediaID = static::shiftPath()) {
513
                return static::throwError('Invalid request');
514
            } elseif (!$Media = Media::getByID($mediaID)) {
515
                return static::throwNotFoundError('Media not found');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Media not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

515
                return static::/** @scrutinizer ignore-call */ throwNotFoundError('Media not found');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
516
            }
517
        }
518
519
520
        // get format
521
        if (preg_match('/^(\d+)x(\d+)(x([0-9A-F]{6})?)?$/i', static::peekPath(), $matches)) {
0 ignored issues
show
Bug introduced by
It seems like static::peekPath() can also be of type false; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

521
        if (preg_match('/^(\d+)x(\d+)(x([0-9A-F]{6})?)?$/i', /** @scrutinizer ignore-type */ static::peekPath(), $matches)) {
Loading history...
522
            static::shiftPath();
523
            $maxWidth = $matches[1];
524
            $maxHeight = $matches[2];
525
            $fillColor = !empty($matches[4]) ? $matches[4] : false;
526
        } else {
527
            $maxWidth = static::$defaultThumbnailWidth;
528
            $maxHeight = static::$defaultThumbnailHeight;
529
            $fillColor = false;
530
        }
531
532
        if (static::peekPath() == 'cropped') {
533
            static::shiftPath();
534
            $cropped = true;
535
        } else {
536
            $cropped = false;
537
        }
538
539
540
        // fetch thumbnail
541
        try {
542
            // get thumbnail
543
            $thumbPath = $Media->getThumbnail($maxWidth, $maxHeight, $fillColor, $cropped);
0 ignored issues
show
Bug introduced by
The method getThumbnail() does not exist on Divergence\Models\ActiveRecord. It seems like you code against a sub-type of Divergence\Models\ActiveRecord such as Divergence\Models\Media\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

543
            /** @scrutinizer ignore-call */ 
544
            $thumbPath = $Media->getThumbnail($maxWidth, $maxHeight, $fillColor, $cropped);
Loading history...
544
        } catch (Exception $e) {
545
            /*\Emergence\Logger::general_warning('Caught exception while creating thumbnail for media, returning server error', array(
546
                'exceptionClass' => get_class($e)
547
                ,'exceptionMessage' => $e->getMessage()
548
                ,'exceptionCode' => $e->getCode()
549
                ,'recordData' => $Media->getData()
550
                ,'thumbFormat' => array(
551
                    'maxWidth' => $maxWidth
552
                    ,'maxHeight' => $maxHeight
553
                    ,'fillColor' => $fillColor
554
                    ,'cropped' => $cropped
555
                )
556
            ));*/
557
558
            return static::throwServerError('Thumbnail unavailable');
0 ignored issues
show
Bug introduced by
The method throwServerError() does not exist on Divergence\Controllers\MediaRequestHandler. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

558
            return static::/** @scrutinizer ignore-call */ throwServerError('Thumbnail unavailable');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
559
        }
560
561
562
        // dump it out
563
        header("ETag: media-$Media->ID-$maxWidth-$maxHeight-$fillColor-$cropped");
564
        header("Content-Type: $Media->ThumbnailMIMEType");
0 ignored issues
show
Bug Best Practice introduced by
The property ThumbnailMIMEType does not exist on Divergence\Models\ActiveRecord. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property ThumbnailMIMEType does not exist on Divergence\Models\Media\Media. Since you implemented __get, consider adding a @property annotation.
Loading history...
565
        header('Content-Length: '.filesize($thumbPath));
566
        readfile($thumbPath);
567
        exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
568
    }
569
570
571
572
    public static function handleManageRequest()
573
    {
574
        // access control
575
        $GLOBALS['Session']->requireAccountLevel('Staff');
576
577
        return static::respond('manage');
578
    }
579
580
581
582
    public static function handleBrowseRequest($options = [], $conditions = [], $responseID = null, $responseData = [])
583
    {
584
        // apply tag filter
585
        if (!empty($_REQUEST['tag'])) {
586
            // get tag
587
            if (!$Tag = Tag::getByHandle($_REQUEST['tag'])) {
0 ignored issues
show
Bug introduced by
The type Divergence\Controllers\Tag was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
588
                return static::throwNotFoundError('Tag not found');
0 ignored issues
show
Unused Code introduced by
The call to Divergence\Controllers\R...r::throwNotFoundError() has too many arguments starting with 'Tag not found'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

588
                return static::/** @scrutinizer ignore-call */ throwNotFoundError('Tag not found');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
589
            }
590
591
            $conditions[] = 'ID IN (SELECT ContextID FROM tag_items WHERE TagID = '.$Tag->ID.' AND ContextClass = "Product")';
592
        }
593
594
595
        // apply context filter
596
        if (!empty($_REQUEST['ContextClass'])) {
597
            $conditions['ContextClass'] = $_REQUEST['ContextClass'];
598
        }
599
600
        if (!empty($_REQUEST['ContextID']) && is_numeric($_REQUEST['ContextID'])) {
601
            $conditions['ContextID'] = $_REQUEST['ContextID'];
602
        }
603
604
        return parent::handleBrowseRequest($options, $conditions, $responseID, $responseData);
605
    }
606
607
608
609
    #	public static function handleMediaRequest()
610
    #	{
611
    #		if(static::peekPath() == 'delete')
612
    #		{
613
    #			return static::handleMediaDeleteRequest();
614
    #		}
615
    #
616
    #
617
    #		// get media
618
    #		$media = JSON::translateRecords(Media::getAll(), true);
619
    #
620
    #		// get tag media assignments
621
    #		$media_tags = Tag::getAllItems('media');
622
    #
623
    #		// inject album assignments to photo records
624
    #		foreach($media_tags AS $media_id => $tags)
625
    #		{
626
    #			foreach($tags AS $tag)
627
    #			{
628
    #				$media[$media_id]['tags'][] = $tag['tag_id'];
629
    #			}
630
    #		}
631
    #
632
    #		return static::respond('media', array(
633
    #			'success' => true
634
    #			,'data' => array_values($media)
635
    #		));
636
    #	}
637
638
639
    public static function handleMediaDeleteRequest()
640
    {
641
        // sanity check
642
        if (empty($_REQUEST['media']) || !is_array($_REQUEST['media'])) {
643
            static::throwError('Invalid request');
644
        }
645
646
        // retrieve photos
647
        $media_array = [];
648
        foreach ($_REQUEST['media'] as $media_id) {
649
            if (!is_numeric($media_id)) {
650
                static::throwError('Invalid request');
651
            }
652
653
            if ($Media = Media::getById($media_id)) {
654
                $media_array[$Media->ID] = $Media;
655
656
                if (!static::checkWriteAccess($Media)) {
657
                    return static::throwUnauthorizedError();
658
                }
659
            }
660
        }
661
662
        // delete
663
        $deleted = [];
664
        foreach ($media_array as $media_id => $Media) {
665
            if ($Media->delete()) {
666
                $deleted[] = $media_id;
667
            }
668
        }
669
670
        return static::respond('mediaDeleted', [
671
            'success' => true
672
            ,'deleted' => $deleted,
673
        ]);
674
    }
675
}
676