Passed
Push — master ( ec22b1...4d2683 )
by Henry
01:59
created

MediaRequestHandler::throwUploadError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

101
                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...
102
            }
103
104
            case 'thumbnail':
105
            {
106
                return static::handleThumbnailRequest();
107
            }
108
109
            case false:
110
            case '':
111
            case 'browse':
112
            {
113
                if ($_SERVER['REQUEST_METHOD'] == 'POST') {
114
                    return static::handleUploadRequest();
115
                }
116
117
                return static::handleBrowseRequest();
118
            }
119
120
            default:
121
            {
122
                if (ctype_digit($action)) {
123
                    return static::handleMediaRequest($action);
124
                } else {
125
                    return parent::handleRecordsRequest($action);
126
                }
127
            }
128
        }
129
    }
130
131
132
    public static function handleUploadRequest($options = [], $authenticationRequired = true)
133
    {
134
        // require authentication
135
        if ($authenticationRequired) {
136
            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

136
            static::/** @scrutinizer ignore-call */ 
137
                    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...
137
        }
138
139
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
140
            // init options
141
            $options = array_merge([
142
                'fieldName' => static::$uploadFileFieldName,
143
            ], $options);
144
145
146
            // check upload
147
            if (empty($_FILES[$options['fieldName']])) {
148
                return static::throwUploadError('You did not select a file to upload');
149
            }
150
151
            // handle upload errors
152
            if ($_FILES[$options['fieldName']]['error'] != UPLOAD_ERR_OK) {
153
                switch ($_FILES[$options['fieldName']]['error']) {
154
                    case UPLOAD_ERR_NO_FILE:
155
                        return static::throwUploadError('You did not select a file to upload');
156
157
                    case UPLOAD_ERR_INI_SIZE:
158
                    case UPLOAD_ERR_FORM_SIZE:
159
                        return static::throwUploadError('Your file exceeds the maximum upload size. Please try again with a smaller file.');
160
161
                    case UPLOAD_ERR_PARTIAL:
162
                        return static::throwUploadError('Your file was only partially uploaded, please try again.');
163
164
                    default:
165
                        return static::throwUploadError('There was an unknown problem while processing your upload, please try again.');
166
                }
167
            }
168
169
            // init caption
170
            if (!isset($options['Caption'])) {
171
                if (!empty($_REQUEST['Caption'])) {
172
                    $options['Caption'] = $_REQUEST['Caption'];
173
                } else {
174
                    $options['Caption'] = preg_replace('/\.[^.]+$/', '', $_FILES[$options['fieldName']]['name']);
175
                }
176
            }
177
178
            // create media
179
            try {
180
                $Media = Media::createFromUpload($_FILES[$options['fieldName']]['tmp_name'], $options);
181
            } catch (Exception $e) {
182
                return static::throwUploadError('The file you uploaded is not of a supported media format');
183
            }
184
        } elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') {
185
            $put = fopen("php://input", "r"); // open input stream
186
187
            $tmp = tempnam("/tmp", "emr");  // use PHP to make a temporary file
188
            $fp = fopen($tmp, "w"); // open write stream to temp file
189
190
            // write
191
            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

191
            while ($data = fread(/** @scrutinizer ignore-type */ $put, 1024)) {
Loading history...
192
                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

192
                fwrite(/** @scrutinizer ignore-type */ $fp, $data);
Loading history...
193
            }
194
195
            // close handles
196
            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

196
            fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
197
            fclose($put);
198
199
            // create media
200
            try {
201
                $Media = Media::createFromFile($tmp, $options);
202
            } catch (Exception $e) {
203
                return static::throwUploadError('The file you uploaded is not of a supported media format');
204
            }
205
        } else {
206
            return static::respond('upload');
207
        }
208
209
        // assign tag
210
        /*if (!empty($_REQUEST['Tag']) && ($Tag = Tag::getByHandle($_REQUEST['Tag']))) {
211
            $Tag->assignItem('Media', $Media->ID);
212
        }*/
213
214
        // assign context
215
        if (!empty($_REQUEST['ContextClass']) && !empty($_REQUEST['ContextID'])) {
216
            if (!is_subclass_of($_REQUEST['ContextClass'], ActiveRecord::class)
217
                || !in_array($_REQUEST['ContextClass']::getStaticRootClass(), Media::$fields['ContextClass']['values'])
218
                || !is_numeric($_REQUEST['ContextID'])) {
219
                return static::throwUploadError('Context is invalid');
220
            } elseif (!$Media->Context = $_REQUEST['ContextClass']::getByID($_REQUEST['ContextID'])) {
221
                return static::throwUploadError('Context class not found');
222
            }
223
224
            $Media->save();
225
        }
226
227
        return static::respond('uploadComplete', [
228
            'success' => (boolean)$Media
229
            ,'data' => $Media
230
            ,'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...
231
        ]);
232
    }
233
234
235
    public static function handleMediaRequest($mediaID)
236
    {
237
        if (empty($mediaID) || !is_numeric($mediaID)) {
238
            static::throwError('Missing or invalid media_id');
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

238
            static::/** @scrutinizer ignore-call */ 
239
                    throwError('Missing or invalid media_id');

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...
239
        }
240
241
        // get media
242
        try {
243
            $Media = Media::getById($mediaID);
244
        } catch (Exception $e) {
245
            return static::throwUnauthorizedError();
246
        }
247
248
        if (!$Media) {
249
            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

249
            static::/** @scrutinizer ignore-call */ 
250
                    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...
250
        }
251
252
        if (!static::checkReadAccess($Media)) {
253
            return static::throwNotFoundError();
254
        }
255
256
        if (static::$responseMode == 'json' || $_SERVER['HTTP_ACCEPT'] == 'application/json') {
257
            JSON::translateAndRespond([
258
                'success' => true
259
                ,'data' => $Media,
260
            ]);
261
        } else {
262
263
            // determine variant
264
            if ($variant = static::shiftPath()) {
265
                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

265
                if (!$Media->/** @scrutinizer ignore-call */ isVariantAvailable($variant)) {
Loading history...
266
                    return static::throwNotFoundError();
267
                }
268
            } else {
269
                $variant = 'original';
270
            }
271
272
            // send caching headers
273
            $expires = 60*60*24*365;
274
            header("Cache-Control: public, max-age=$expires");
275
            header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time()+$expires));
276
            header('Pragma: public');
277
278
            // media are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
279
            if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
280
                header('HTTP/1.0 304 Not Modified');
281
                exit();
282
            }
283
284
            // initialize response
285
            set_time_limit(0);
286
            $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

286
            /** @scrutinizer ignore-call */ 
287
            $filePath = $Media->getFilesystemPath($variant);
Loading history...
287
            $fp = fopen($filePath, 'rb');
288
            $size = filesize($filePath);
289
            $length = $filesize;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $filesize seems to be never defined.
Loading history...
290
            $start = 0;
291
            $end = $size - 1;
292
293
            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

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

329
                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

329
                fseek($fp, /** @scrutinizer ignore-type */ $start);
Loading history...
330
                header('HTTP/1.1 206 Partial Content');
331
            }
332
333
            // finish response
334
            header("Content-Range: bytes $start-$end/$size");
335
            header("Content-Length: $length");
336
337
            $buffer = 1024 * 8;
338
            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

338
            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

338
            while (!feof(/** @scrutinizer ignore-type */ $fp) && ($p = ftell($fp)) <= $end) {
Loading history...
339
                if ($p + $buffer > $end) {
340
                    $buffer = $end - $p + 1;
341
                }
342
343
                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

343
                echo fread(/** @scrutinizer ignore-type */ $fp, $buffer);
Loading history...
344
                flush();
345
            }
346
347
            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

347
            fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
348
349
            Site::finishRequest();
350
        }
351
    }
352
353
    public static function handleInfoRequest($mediaID)
354
    {
355
        if (empty($mediaID) || !is_numeric($mediaID)) {
356
            static::throwNotFoundError();
357
        }
358
359
        // get media
360
        try {
361
            $Media = Media::getById($mediaID);
362
        } catch (Exception $e) {
363
            return static::throwUnauthorizedError();
364
        }
365
366
        if (!$Media) {
367
            static::throwNotFoundError();
368
        }
369
370
        if (!static::checkReadAccess($Media)) {
371
            return static::throwUnauthorizedError();
372
        }
373
374
        return parent::handleRecordRequest($Media);
375
    }
376
377
    public static function handleDownloadRequest($media_id, $filename = false)
378
    {
379
        if (empty($media_id) || !is_numeric($media_id)) {
380
            static::throwNotFoundError();
381
        }
382
383
        // get media
384
        try {
385
            $Media = Media::getById($media_id);
386
        } catch (Exception $e) {
387
            return static::throwUnauthorizedError();
388
        }
389
390
391
        if (!$Media) {
392
            static::throwNotFoundError();
393
        }
394
395
        if (!static::checkReadAccess($Media)) {
396
            return static::throwUnauthorizedError();
397
        }
398
399
        // determine filename
400
        if (empty($filename)) {
401
            $filename = $Media->Caption ? $Media->Caption : sprintf('%s_%u', $Media->ContextClass, $Media->ContextID);
0 ignored issues
show
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...
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 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

401
            $filename = $Media->Caption ? $Media->Caption : sprintf('%s_%u', /** @scrutinizer ignore-type */ $Media->ContextClass, $Media->ContextID);
Loading history...
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...
402
        }
403
404
        if (strpos($filename, '.') === false) {
405
            // add extension
406
            $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...
407
        }
408
409
        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...
410
        header('Content-Disposition: attachment; filename="'.str_replace('"', '', $filename).'"');
411
        header('Content-Length: '.filesize($Media->FilesystemPath));
0 ignored issues
show
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

411
        header('Content-Length: '.filesize(/** @scrutinizer ignore-type */ $Media->FilesystemPath));
Loading history...
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...
412
413
        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

413
        readfile(/** @scrutinizer ignore-type */ $Media->FilesystemPath);
Loading history...
414
        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...
415
    }
416
417
    public static function handleCaptionRequest($media_id)
418
    {
419
        // require authentication
420
        $GLOBALS['Session']->requireAccountLevel('Staff');
421
422
        if (empty($media_id) || !is_numeric($media_id)) {
423
            static::throwNotFoundError();
424
        }
425
426
        // get media
427
        try {
428
            $Media = Media::getById($media_id);
429
        } catch (Exception $e) {
430
            return static::throwUnauthorizedError();
431
        }
432
433
434
        if (!$Media) {
435
            static::throwNotFoundError();
436
        }
437
438
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
439
            $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...
440
            $Media->save();
441
442
            return static::respond('mediaCaptioned', [
443
                'success' => true
444
                ,'data' => $Media,
445
            ]);
446
        }
447
448
        return static::respond('mediaCaption', [
449
            'data' => $Media,
450
        ]);
451
    }
452
453
    public static function handleDeleteRequest(ActiveRecord $Record)
454
    {
455
        // require authentication
456
        $GLOBALS['Session']->requireAccountLevel('Staff');
457
458
        if ($mediaID = static::peekPath()) {
459
            $mediaIDs = [$mediaID];
460
        } elseif (!empty($_REQUEST['mediaID'])) {
461
            $mediaIDs = [$_REQUEST['mediaID']];
462
        } elseif (is_array($_REQUEST['media'])) {
463
            $mediaIDs = $_REQUEST['media'];
464
        }
465
466
        $deleted = [];
467
        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...
468
            if (!is_numeric($mediaID)) {
469
                continue;
470
            }
471
472
            // get media
473
            $Media = Media::getByID($mediaID);
474
475
            if (!$Media) {
476
                static::throwNotFoundError();
477
            }
478
479
            if ($Media->destroy()) {
480
                $deleted[] = $Media;
481
            }
482
        }
483
484
        return static::respond('mediaDeleted', [
485
            'success' => true
486
            ,'data' => $deleted,
487
        ]);
488
    }
489
490
491
492
493
494
495
    public static function handleThumbnailRequest(Media $Media = null)
496
    {
497
        // send caching headers
498
        $expires = 60*60*24*365;
499
        header("Cache-Control: public, max-age=$expires");
500
        header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time()+$expires));
501
        header('Pragma: public');
502
503
504
        // thumbnails are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
505
        if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
506
            header('HTTP/1.0 304 Not Modified');
507
            exit();
508
        }
509
510
511
        // get media
512
        if (!$Media) {
513
            if (!$mediaID = static::shiftPath()) {
514
                return static::throwNotFoundError();
515
            } elseif (!$Media = Media::getByID($mediaID)) {
516
                return static::throwNotFoundError();
517
            }
518
        }
519
520
521
        // get format
522
        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

522
        if (preg_match('/^(\d+)x(\d+)(x([0-9A-F]{6})?)?$/i', /** @scrutinizer ignore-type */ static::peekPath(), $matches)) {
Loading history...
523
            static::shiftPath();
524
            $maxWidth = $matches[1];
525
            $maxHeight = $matches[2];
526
            $fillColor = !empty($matches[4]) ? $matches[4] : false;
527
        } else {
528
            $maxWidth = static::$defaultThumbnailWidth;
529
            $maxHeight = static::$defaultThumbnailHeight;
530
            $fillColor = false;
531
        }
532
533
        if (static::peekPath() == 'cropped') {
534
            static::shiftPath();
535
            $cropped = true;
536
        } else {
537
            $cropped = false;
538
        }
539
540
541
        // fetch thumbnail
542
        try {
543
            // get thumbnail
544
            $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

544
            /** @scrutinizer ignore-call */ 
545
            $thumbPath = $Media->getThumbnail($maxWidth, $maxHeight, $fillColor, $cropped);
Loading history...
545
        } catch (Exception $e) {
546
            /*\Emergence\Logger::general_warning('Caught exception while creating thumbnail for media, returning server error', array(
547
                'exceptionClass' => get_class($e)
548
                ,'exceptionMessage' => $e->getMessage()
549
                ,'exceptionCode' => $e->getCode()
550
                ,'recordData' => $Media->getData()
551
                ,'thumbFormat' => array(
552
                    'maxWidth' => $maxWidth
553
                    ,'maxHeight' => $maxHeight
554
                    ,'fillColor' => $fillColor
555
                    ,'cropped' => $cropped
556
                )
557
            ));*/
558
559
            return static::throwNotFoundError();
560
        }
561
562
563
        // dump it out
564
        header("ETag: media-$Media->ID-$maxWidth-$maxHeight-$fillColor-$cropped");
565
        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...
566
        header('Content-Length: '.filesize($thumbPath));
567
        readfile($thumbPath);
568
        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...
569
    }
570
571
572
573
    public static function handleManageRequest()
574
    {
575
        // access control
576
        $GLOBALS['Session']->requireAccountLevel('Staff');
577
578
        return static::respond('manage');
579
    }
580
581
582
583
    public static function handleBrowseRequest($options = [], $conditions = [], $responseID = null, $responseData = [])
584
    {
585
        // apply tag filter
586
        if (!empty($_REQUEST['tag'])) {
587
            // get tag
588
            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...
589
                return static::throwNotFoundError();
590
            }
591
592
            $conditions[] = 'ID IN (SELECT ContextID FROM tag_items WHERE TagID = '.$Tag->ID.' AND ContextClass = "Product")';
593
        }
594
595
596
        // apply context filter
597
        if (!empty($_REQUEST['ContextClass'])) {
598
            $conditions['ContextClass'] = $_REQUEST['ContextClass'];
599
        }
600
601
        if (!empty($_REQUEST['ContextID']) && is_numeric($_REQUEST['ContextID'])) {
602
            $conditions['ContextID'] = $_REQUEST['ContextID'];
603
        }
604
605
        return parent::handleBrowseRequest($options, $conditions, $responseID, $responseData);
606
    }
607
608
609
610
    #	public static function handleMediaRequest()
611
    #	{
612
    #		if(static::peekPath() == 'delete')
613
    #		{
614
    #			return static::handleMediaDeleteRequest();
615
    #		}
616
    #
617
    #
618
    #		// get media
619
    #		$media = JSON::translateRecords(Media::getAll(), true);
620
    #
621
    #		// get tag media assignments
622
    #		$media_tags = Tag::getAllItems('media');
623
    #
624
    #		// inject album assignments to photo records
625
    #		foreach($media_tags AS $media_id => $tags)
626
    #		{
627
    #			foreach($tags AS $tag)
628
    #			{
629
    #				$media[$media_id]['tags'][] = $tag['tag_id'];
630
    #			}
631
    #		}
632
    #
633
    #		return static::respond('media', array(
634
    #			'success' => true
635
    #			,'data' => array_values($media)
636
    #		));
637
    #	}
638
639
640
    public static function handleMediaDeleteRequest()
641
    {
642
        // sanity check
643
        if (empty($_REQUEST['media']) || !is_array($_REQUEST['media'])) {
644
            static::throwNotFoundError();
645
        }
646
647
        // retrieve photos
648
        $media_array = [];
649
        foreach ($_REQUEST['media'] as $media_id) {
650
            if (!is_numeric($media_id)) {
651
                static::throwNotFoundError();
652
            }
653
654
            if ($Media = Media::getById($media_id)) {
655
                $media_array[$Media->ID] = $Media;
656
657
                if (!static::checkWriteAccess($Media)) {
658
                    return static::throwUnauthorizedError();
659
                }
660
            }
661
        }
662
663
        // delete
664
        $deleted = [];
665
        foreach ($media_array as $media_id => $Media) {
666
            if ($Media->delete()) {
667
                $deleted[] = $media_id;
668
            }
669
        }
670
671
        return static::respond('mediaDeleted', [
672
            'success' => true
673
            ,'deleted' => $deleted,
674
        ]);
675
    }
676
677
    public static function throwUploadError($error)
678
    {
679
        return static::respond('error', [
680
            'success' => false,
681
            'failed' => [
682
                'errors'	=>	$error,
683
            ],
684
        ]);
685
    }
686
}
687