MediaRequestHandler   F
last analyzed

Complexity

Total Complexity 120

Size/Duplication

Total Lines 676
Duplicated Lines 0 %

Test Coverage

Coverage 62.5%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 346
dl 0
loc 676
ccs 200
cts 320
cp 0.625
rs 2
c 5
b 0
f 0
wmc 120

17 Methods

Rating   Name   Duplication   Size   Complexity  
D handleUploadRequest() 0 90 20
A setCache() 0 6 1
A respondRangeNotSatisfiable() 0 7 1
C handle() 0 76 15
A handleInfoRequest() 0 22 6
A respondWithThumbnail() 0 17 2
A handleCaptionRequest() 0 33 6
C handleMediaRequest() 0 62 12
A handleBrowseRequest() 0 23 6
B handleDownloadRequest() 0 30 7
A respondEmpty() 0 9 2
B handleDeleteRequest() 0 34 8
A checkUploadAccess() 0 3 1
B handleThumbnailRequest() 0 50 10
B handleMediaDeleteRequest() 0 34 9
A throwUploadError() 0 6 1
C respondWithMedia() 0 77 13

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
/**
3
 * This file is part of the Divergence package.
4
 *
5
 * (c) Henry Paradiz <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Divergence\Controllers;
12
13
use Exception;
14
use Divergence\Models\Media\Media;
15
use Divergence\Models\ActiveRecord;
16
use Divergence\Responders\Response;
17
use Divergence\Responders\JsonBuilder;
18
use Divergence\Responders\EmptyBuilder;
19
use Divergence\Responders\EmptyResponse;
20
use Divergence\Responders\MediaBuilder;
21
use Psr\Http\Message\ResponseInterface;
22
use Divergence\Responders\MediaResponse;
23
use GuzzleHttp\Psr7\ServerRequest;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
class MediaRequestHandler extends RecordsRequestHandler
27
{
28
    // RecordRequestHandler configuration
29
    public static $recordClass = Media::class;
30
    public $accountLevelRead = false;
31
    public $accountLevelBrowse = 'User';
32
    public $accountLevelWrite = 'User';
33
    public $accountLevelAPI = false;
34
    public $browseLimit = 100;
35
    public $browseOrder = ['ID' => 'DESC'];
36
37
    // MediaRequestHandler configuration
38
    public $defaultPage = 'browse';
39
    public $defaultThumbnailWidth = 100;
40
    public $defaultThumbnailHeight = 100;
41
    public $uploadFileFieldName = 'mediaFile';
42
    public $responseMode = 'html';
43
44
    public static $inputStream = 'php://input'; // this is a setting so that unit tests can provide a fake stream :)
45
46
    public $searchConditions = [
47
        'Caption' => [
48
            'qualifiers' => ['any','caption']
49
            ,'points' => 2
50
            ,'sql' => 'Caption LIKE "%%%s%%"',
51
        ]
52
        ,'CaptionLike' => [
53
            'qualifiers' => ['caption-like']
54
            ,'points' => 2
55
            ,'sql' => 'Caption LIKE "%s"',
56
        ]
57
        ,'CaptionNot' => [
58
            'qualifiers' => ['caption-not']
59
            ,'points' => 2
60
            ,'sql' => 'Caption NOT LIKE "%%%s%%"',
61
        ]
62
        ,'CaptionNotLike' => [
63
            'qualifiers' => ['caption-not-like']
64
            ,'points' => 2
65
            ,'sql' => 'Caption NOT LIKE "%s"',
66
        ],
67
    ];
68
69
    private ?ServerRequest $request;
70
71 25
    public function handle(ServerRequestInterface $request): ResponseInterface
72
    {
73 25
        $this->request = $request;
74
75
        // handle json response mode
76 25
        if ($this->peekPath() == 'json') {
77 11
            $this->shiftPath();
78 11
            $this->responseBuilder = JsonBuilder::class;
79
        }
80
81
        // handle action
82 25
        switch ($action = $this->shiftPath()) {
83
84 25
            case 'upload':
85
                {
86 7
                    return $this->handleUploadRequest();
87
                }
88
89 18
            case 'open':
90
                {
91
                    $mediaID = $this->shiftPath();
92
93
                    return $this->handleMediaRequest($mediaID);
94
                }
95
96 18
            case 'download':
97
                {
98 1
                    $mediaID = $this->shiftPath();
99 1
                    if ($filename = $this->shiftPath()) {
100
                        $filename = urldecode($filename);
101
                    }
102 1
                    return $this->handleDownloadRequest($mediaID, $filename);
103
                }
104
105 17
            case 'info':
106
                {
107 1
                    $mediaID = $this->shiftPath();
108
109 1
                    return $this->handleInfoRequest($mediaID);
110
                }
111
112 16
            case 'caption':
113
                {
114
                    $mediaID = $this->shiftPath();
115
116
                    return $this->handleCaptionRequest($mediaID);
117
                }
118
119 16
            case 'delete':
120
                {
121
                    $mediaID = $this->shiftPath();
122
                    return $this->handleDeleteRequest($mediaID);
123
                }
124
125 16
            case 'thumbnail':
126
                {
127 3
                    return $this->handleThumbnailRequest();
128
                }
129
130
            case false:
131 12
            case '':
132 12
            case 'browse':
133
                {
134 1
                    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
135
                        return $this->handleUploadRequest();
136
                    }
137
138 1
                    return $this->handleBrowseRequest();
139
                }
140
141
            default:
142
                {
143 12
                    if (ctype_digit($action)) {
144 12
                        return $this->handleMediaRequest($action);
145
                    } else {
146
                        return parent::handleRecordsRequest($action);
147
                    }
148
                }
149
        }
150
    }
151
152
153 7
    public function handleUploadRequest($options = []): ResponseInterface
154
    {
155 7
        $this->checkUploadAccess();
156
157 7
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
158
            // init options
159 5
            $options = array_merge([
160 5
                'fieldName' => $this->uploadFileFieldName,
161 5
            ], $options);
162
163
164
            // check upload
165 5
            if (empty($_FILES[$options['fieldName']])) {
166 1
                return $this->throwUploadError('You did not select a file to upload');
167
            }
168
169
            // handle upload errors
170 4
            if ($_FILES[$options['fieldName']]['error'] != UPLOAD_ERR_OK) {
171 4
                switch ($_FILES[$options['fieldName']]['error']) {
172 4
                    case UPLOAD_ERR_NO_FILE:
173 1
                        return $this->throwUploadError('You did not select a file to upload');
174
175 3
                    case UPLOAD_ERR_INI_SIZE:
176 2
                    case UPLOAD_ERR_FORM_SIZE:
177 1
                        return $this->throwUploadError('Your file exceeds the maximum upload size. Please try again with a smaller file.');
178
179 2
                    case UPLOAD_ERR_PARTIAL:
180 1
                        return $this->throwUploadError('Your file was only partially uploaded, please try again.');
181
182
                    default:
183 1
                        return $this->throwUploadError('There was an unknown problem while processing your upload, please try again.');
184
                }
185
            }
186
187
            // init caption
188
            if (!isset($options['Caption'])) {
189
                if (!empty($_REQUEST['Caption'])) {
190
                    $options['Caption'] = $_REQUEST['Caption'];
191
                } else {
192
                    $options['Caption'] = preg_replace('/\.[^.]+$/', '', $_FILES[$options['fieldName']]['name']);
193
                }
194
            }
195
196
            // create media
197
            try {
198
                $Media = Media::createFromUpload($_FILES[$options['fieldName']]['tmp_name'], $options);
199
            } catch (Exception $e) {
200
                return $this->throwUploadError($e->getMessage());
201
            }
202 2
        } elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') {
203 2
            $put = fopen(static::$inputStream, 'r'); // open input stream
204
205 2
            $tmp = tempnam('/tmp', 'dvr');  // use PHP to make a temporary file
206 2
            $fp = fopen($tmp, 'w'); // open write stream to temp file
207
208
            // write
209 2
            while ($data = fread($put, 1024)) {
210 2
                fwrite($fp, $data);
211
            }
212
213
            // close handles
214 2
            fclose($fp);
215 2
            fclose($put);
216
217
            // create media
218
            try {
219 2
                $Media = Media::createFromFile($tmp, $options);
220
            } catch (Exception $e) {
221 2
                return $this->throwUploadError('The file you uploaded is not of a supported media format');
222
            }
223
        } else {
224
            return $this->respond('upload');
225
        }
226
227
        // assign context
228 2
        if (!empty($_REQUEST['ContextClass']) && !empty($_REQUEST['ContextID'])) {
229
            if (!is_subclass_of($_REQUEST['ContextClass'], ActiveRecord::class)
230
                || !in_array($_REQUEST['ContextClass']::getStaticRootClass(), Media::$fields['ContextClass']['values'])
231
                || !is_numeric($_REQUEST['ContextID'])) {
232
                return $this->throwUploadError('Context is invalid');
233
            } elseif (!$Media->Context = $_REQUEST['ContextClass']::getByID($_REQUEST['ContextID'])) {
234
                return $this->throwUploadError('Context class not found');
235
            }
236
237
            $Media->save();
238
        }
239
240 2
        return $this->respond('uploadComplete', [
241 2
            'success' => (bool)$Media
242 2
            ,'data' => $Media,
243 2
        ]);
244
    }
245
246 2
    public function respondRangeNotSatisfiable(string $responseID, int $start, int $end, int $size): Response
247
    {
248 2
        $this->responseBuilder = EmptyBuilder::class;
249
250 2
        return $this->respondEmpty($responseID)
251 2
            ->withStatus(416) // Range Not Satisfiable
252 2
            ->withHeader('Content-Range', "bytes $start-$end/$size");
253
    }
254
255
    /**
256
     * Set caching headers
257
     *
258
     * @param Response $response
259
     * @return Response
260
     */
261 10
    public function setCache(Response $response): Response
262
    {
263 10
        $expires = 60*60*24*365;
264 10
        return $response->withHeader('Cache-Control', "public, max-age= $expires")
265 10
        ->withHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time()+$expires))
266 10
        ->withHeader('Pragma', 'public');
267
    }
268
269 9
    public function respondWithMedia(Media $Media, $variant, $responseID, $responseData = []): ResponseInterface
270
    {
271 9
        if ($this->responseBuilder != MediaBuilder::class) {
272
            throw new Exception('Media responses require MediaBuilder for putting together a response.');
273
        }
274 9
        $className = $this->responseBuilder;
275 9
        $responseBuilder = new $className($responseID, $responseData);
276
277 9
        $responseBuilder->setContentType($Media->MIMEType);
278
279
280 9
        $size = filesize($responseID);
281 9
        $length = $size;
282 9
        $start = 0;
283 9
        $end = $size - 1;
284
285
        // interpret range requests
286 9
        $_server = $this->request->getServerParams();
0 ignored issues
show
Bug introduced by
The method getServerParams() does not exist on null. ( 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
        $_server = $this->request->getServerParams();

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...
287 9
        if (!empty($_server['HTTP_RANGE'])) {
288 7
            $chunkStart = $start;
0 ignored issues
show
Unused Code introduced by
The assignment to $chunkStart is dead and can be removed.
Loading history...
289 7
            $chunkEnd = $end;
0 ignored issues
show
Unused Code introduced by
The assignment to $chunkEnd is dead and can be removed.
Loading history...
290
291 7
            list(, $range) = explode('=', $_server['HTTP_RANGE'], 2);
292
293
            // comma indicates multiple ranges which we currently do not support
294 7
            if (strpos($range, ',') !== false) {
295 1
                return $this->respondRangeNotSatisfiable($responseID, $start, $end, $size);
296
            }
297
298 6
            if ($range == '-') { // missing range start and end
299 1
                $range = '0-';
300
            }
301
302 6
            $range = explode('-', $range);
303 6
            $chunkStart = $range[0];
304 6
            $chunkEnd = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
305
306
307 6
            $chunkEnd = ($chunkEnd > $end) ? $end : $chunkEnd;
308
309
            // requested content out of bounds
310 6
            if ($chunkStart > $chunkEnd || $chunkStart > $size - 1 || $chunkEnd >= $size) {
311 1
                return $this->respondRangeNotSatisfiable($responseID, $start, $end, $size);
312
            }
313
314 5
            $start = intval($chunkStart);
315 5
            $end = intval($chunkEnd);
316 5
            $length = $end - $start + 1;
317 5
            $responseBuilder->setRange($start, $end, $length);
318
        }
319
320
321 7
        $response = new MediaResponse($responseBuilder);
322 7
        $response = $this->setCache($response);
323
324
        // tell browser ranges are accepted
325 7
        $response = $response->withHeader('Accept-Ranges', 'bytes')
326 7
        // provide a unique ID for this media
327 7
        ->withHeader('ETag', 'media-'.$Media->ID.'-'.$variant);
328
329
        // if partial content provide proper response header
330 7
        if (isset($chunkStart)) {
331
            // only send 206 if response is less than the whole file
332 5
            if ($end-$start+1<$size) {
333 3
                $response = $response->withStatus(206);
334
            }
335 5
            $response = $response->withHeader('Content-Range', "bytes $start-$end/$size")
336 5
            ->withHeader('Content-Length', $length);
337
        } else {
338
            // range
339 2
            $filesize = filesize($Media->getFilesystemPath($variant));
340 2
            $end = $filesize - 1;
341 2
            $response = $response->withHeader('Content-Range', 'bytes 0-'.$end.'/'.$filesize)
342 2
                ->withHeader('Content-Length', $filesize);
343
        }
344
345 7
        return $response;
346
    }
347
348 3
    public function respondWithThumbnail(Media $Media, $variant, $responseID, $responseData = []): ResponseInterface
349
    {
350 3
        if ($this->responseBuilder != MediaBuilder::class) {
351
            throw new Exception('Media responses require MediaBuilder for putting together a response.');
352
        }
353 3
        $className = $this->responseBuilder;
354 3
        $responseBuilder = new $className($responseID, $responseData);
355
356 3
        $responseBuilder->setContentType($Media->ThumbnailMIMEType);
357
358 3
        $response = new MediaResponse($responseBuilder);
359 3
        $response = $this->setCache($response);
360
361 3
        $response = $response->withHeader('ETag', "media-$Media->ID-$variant")
362 3
            ->withHeader('Content-Length', filesize($responseID));
363
364 3
        return $response;
365
    }
366
367 4
    public function respondEmpty($responseID, $responseData = [])
368
    {
369 4
        if ($this->responseBuilder != EmptyBuilder::class) {
370
            throw new Exception('Media responses require MediaBuilder for putting together a response.');
371
        }
372 4
        $className = $this->responseBuilder;
373 4
        $responseBuilder = new $className($responseID, $responseData);
374 4
        $response = new EmptyResponse($responseBuilder);
375 4
        return $response;
376
    }
377
378
379 12
    public function handleMediaRequest($mediaID): ResponseInterface
380
    {
381 12
        if (empty($mediaID)) {
382
            return $this->throwNotFoundError();
383
        }
384
385
        // get media
386
        try {
387 12
            $Media = Media::getById($mediaID);
388
        } catch (Exception $e) {
389
            return $this->throwUnauthorizedError();
390
        }
391
392 12
        if (!$Media) {
393 1
            return $this->throwNotFoundError();
394
        }
395
396 11
        if (!$this->checkReadAccess($Media)) {
397
            return $this->throwNotFoundError();
398
        }
399
400
401 11
        $_server = $this->request->getServerParams();
402
403 11
        if (isset($_server['HTTP_ACCEPT'])) {
404
            if ($_server['HTTP_ACCEPT'] == 'application/json') {
405
                $this->responseBuilder = JsonBuilder::class;
406
            }
407
        }
408
409 11
        if ($this->responseBuilder == JsonBuilder::class) {
410 1
            return $this->respond('media', [
411 1
                'success' => true
412 1
                ,'data' => $Media,
413 1
            ]);
414
        } else {
415
416
            // determine variant
417 10
            if ($variant = $this->shiftPath()) {
418
                if (!$Media->isVariantAvailable($variant)) {
419
                    return $this->throwNotFoundError();
420
                }
421
            } else {
422 10
                $variant = 'original';
423
            }
424
425
            // initialize response
426 10
            $this->responseBuilder = MediaBuilder::class;
427 10
            set_time_limit(0);
428 10
            $filePath = $Media->getFilesystemPath($variant);
429
430
            // media are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
431 10
            if (!empty($_server['HTTP_IF_NONE_MATCH']) || !empty($_server['HTTP_IF_MODIFIED_SINCE'])) {
432 2
                $this->responseBuilder = EmptyBuilder::class;
433 2
                $response = $this->respondEmpty($filePath);
434 2
                $response->withDefaults(304);
435
436 2
                return $response;
437
            }
438
439
440 8
            return $this->respondWithMedia($Media, $variant, $filePath);
441
        }
442
    }
443
444 1
    public function handleInfoRequest($mediaID): ResponseInterface
445
    {
446 1
        if (empty($mediaID) || !is_numeric($mediaID)) {
447
            $this->throwNotFoundError();
448
        }
449
450
        // get media
451
        try {
452 1
            $Media = Media::getById($mediaID);
453
        } catch (Exception $e) {
454
            return $this->throwUnauthorizedError();
455
        }
456
457 1
        if (!$Media) {
458
            return $this->throwNotFoundError();
459
        }
460
461 1
        if (!$this->checkReadAccess($Media)) {
462
            return $this->throwUnauthorizedError();
463
        }
464
465 1
        return parent::handleRecordRequest($Media);
466
    }
467
468 1
    public function handleDownloadRequest($media_id, $filename = false): ResponseInterface
469
    {
470 1
        if (empty($media_id) || !is_numeric($media_id)) {
471
            $this->throwNotFoundError();
472
        }
473
474
        // get media
475
        try {
476 1
            $Media = Media::getById($media_id);
477
        } catch (Exception $e) {
478
            return $this->throwUnauthorizedError();
479
        }
480
481
482 1
        if (!$Media) {
483
            return $this->throwNotFoundError();
484
        }
485
486 1
        if (!$this->checkReadAccess($Media)) {
487
            return $this->throwUnauthorizedError();
488
        }
489
490 1
        $filePath = $Media->getFilesystemPath('original');
491
492 1
        $this->responseBuilder = MediaBuilder::class;
493 1
        $response = $this->respondWithMedia($Media, 'original', $filePath);
494
495 1
        $response = $response->withHeader('Content-Disposition', 'attachment; filename="'.($filename ? $filename : $filePath).'"');
496
497 1
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
498
    }
499
500
    public function handleCaptionRequest($media_id): ResponseInterface
501
    {
502
        // require authentication
503
        $GLOBALS['Session']->requireAccountLevel('Staff');
504
505
        if (empty($media_id) || !is_numeric($media_id)) {
506
            return $this->throwNotFoundError();
507
        }
508
509
        // get media
510
        try {
511
            $Media = Media::getById($media_id);
512
        } catch (Exception $e) {
513
            return $this->throwUnauthorizedError();
514
        }
515
516
517
        if (!$Media) {
518
            $this->throwNotFoundError();
519
        }
520
521
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
522
            $Media->Caption = $_REQUEST['Caption'];
523
            $Media->save();
524
525
            return $this->respond('mediaCaptioned', [
526
                'success' => true
527
                ,'data' => $Media,
528
            ]);
529
        }
530
531
        return $this->respond('mediaCaption', [
532
            'data' => $Media,
533
        ]);
534
    }
535
536
    public function handleDeleteRequest(ActiveRecord $Record): ResponseInterface
537
    {
538
        // require authentication
539
        $GLOBALS['Session']->requireAccountLevel('Staff');
540
541
        if ($mediaID = $this->peekPath()) {
542
            $mediaIDs = [$mediaID];
543
        } elseif (!empty($_REQUEST['mediaID'])) {
544
            $mediaIDs = [$_REQUEST['mediaID']];
545
        } elseif (is_array($_REQUEST['media'])) {
546
            $mediaIDs = $_REQUEST['media'];
547
        }
548
549
        $deleted = [];
550
        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...
551
            if (!is_numeric($mediaID)) {
552
                continue;
553
            }
554
555
            // get media
556
            $Media = Media::getByID($mediaID);
557
558
            if (!$Media) {
559
                return $this->throwNotFoundError();
560
            }
561
562
            if ($Media->destroy()) {
563
                $deleted[] = $Media;
564
            }
565
        }
566
567
        return $this->respond('mediaDeleted', [
568
            'success' => true
569
            ,'data' => $deleted,
570
        ]);
571
    }
572
573 3
    public function handleThumbnailRequest(Media $Media = null): ResponseInterface
574
    {
575
        // get media
576 3
        if (!$Media) {
577 3
            if (!$mediaID = $this->shiftPath()) {
578
                return $this->throwNotFoundError();
579 3
            } elseif (!$Media = Media::getByID($mediaID)) {
580
                return $this->throwNotFoundError();
581
            }
582
        }
583
584 3
        $_server = $this->request->getServerParams();
585
586
        // thumbnails are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
587 3
        if (!empty($_server['HTTP_IF_NONE_MATCH']) || !empty($_server['HTTP_IF_MODIFIED_SINCE'])) {
588
            $this->responseBuilder = EmptyBuilder::class;
589
            $response = $this->respondEmpty($Media->ID);
590
            $response->withDefaults(304);
591
592
            return $response;
593
        }
594
595
        // get format
596 3
        if (preg_match('/^(\d+)x(\d+)(x([0-9A-F]{6})?)?$/i', $this->peekPath(), $matches)) {
0 ignored issues
show
Bug introduced by
It seems like $this->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

596
        if (preg_match('/^(\d+)x(\d+)(x([0-9A-F]{6})?)?$/i', /** @scrutinizer ignore-type */ $this->peekPath(), $matches)) {
Loading history...
597 2
            $this->shiftPath();
598 2
            $maxWidth = $matches[1];
599 2
            $maxHeight = $matches[2];
600 2
            $fillColor = !empty($matches[4]) ? $matches[4] : false;
601
        } else {
602 1
            $maxWidth = $this->defaultThumbnailWidth;
603 1
            $maxHeight = $this->defaultThumbnailHeight;
604 1
            $fillColor = false;
605
        }
606
607 3
        if ($this->peekPath() == 'cropped') {
608
            $this->shiftPath();
609
            $cropped = true;
610
        } else {
611 3
            $cropped = false;
612
        }
613
614
        // get thumbnail media
615
        try {
616 3
            $thumbPath = $Media->getThumbnail($maxWidth, $maxHeight, $fillColor, $cropped);
617
618 3
            $this->responseBuilder = MediaBuilder::class;
619 3
            $response = $this->respondWithThumbnail($Media, "$maxWidth-$maxHeight-$fillColor-$cropped", $thumbPath);
620 3
            return $response;
621
        } catch (Exception $e) {
622
            return $this->throwNotFoundError();
623
        }
624
    }
625
626
627 1
    public function handleBrowseRequest($options = [], $conditions = [], $responseID = null, $responseData = []): ResponseInterface
628
    {
629
        // apply tag filter
630 1
        if (!empty($_REQUEST['tag'])) {
631
            // get tag
632
            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...
633
                return $this->throwNotFoundError();
634
            }
635
636
            $conditions[] = 'ID IN (SELECT ContextID FROM tag_items WHERE TagID = '.$Tag->ID.' AND ContextClass = "Product")';
637
        }
638
639
640
        // apply context filter
641 1
        if (!empty($_REQUEST['ContextClass'])) {
642
            $conditions['ContextClass'] = $_REQUEST['ContextClass'];
643
        }
644
645 1
        if (!empty($_REQUEST['ContextID']) && is_numeric($_REQUEST['ContextID'])) {
646
            $conditions['ContextID'] = $_REQUEST['ContextID'];
647
        }
648
649 1
        return parent::handleBrowseRequest($options, $conditions, $responseID, $responseData);
650
    }
651
652
653
654
    public function handleMediaDeleteRequest(): ResponseInterface
655
    {
656
        // sanity check
657
        if (empty($_REQUEST['media']) || !is_array($_REQUEST['media'])) {
658
            return $this->throwNotFoundError();
659
        }
660
661
        // retrieve photos
662
        $media_array = [];
663
        foreach ($_REQUEST['media'] as $media_id) {
664
            if (!is_numeric($media_id)) {
665
                return $this->throwNotFoundError();
666
            }
667
668
            if ($Media = Media::getById($media_id)) {
669
                $media_array[$Media->ID] = $Media;
670
671
                if (!$this->checkWriteAccess($Media)) {
672
                    return $this->throwUnauthorizedError();
673
                }
674
            }
675
        }
676
677
        // delete
678
        $deleted = [];
679
        foreach ($media_array as $media_id => $Media) {
680
            if ($Media->delete()) {
681
                $deleted[] = $media_id;
682
            }
683
        }
684
685
        return $this->respond('mediaDeleted', [
686
            'success' => true
687
            ,'deleted' => $deleted,
688
        ]);
689
    }
690
691 7
    public function checkUploadAccess()
692
    {
693 7
        return true;
694
    }
695
696 5
    public function throwUploadError($error): ResponseInterface
697
    {
698 5
        return $this->respond('error', [
699 5
            'success' => false,
700 5
            'failed' => [
701 5
                'errors'	=>	$error,
702 5
            ],
703 5
        ]);
704
    }
705
}
706