Passed
Push — develop ( be70d2...34d182 )
by Henry
03:13
created

MediaRequestHandler::handle()   C

Complexity

Conditions 15
Paths 32

Size

Total Lines 76
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 18.8237

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 37
c 1
b 0
f 0
nc 32
nop 1
dl 0
loc 76
ccs 26
cts 35
cp 0.7429
crap 18.8237
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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")
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withHe...der('Pragma', 'public') could return the type GuzzleHttp\Psr7\MessageTrait&object which includes types incompatible with the type-hinted return Divergence\Responders\Response. Consider adding an additional type-check to rule them out.
Loading history...
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 5
            $response = $response->withStatus(206)
0 ignored issues
show
Bug introduced by
It seems like withStatus() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

331
            $response = $response->/** @scrutinizer ignore-call */ withStatus(206)
Loading history...
332 5
            ->withHeader('Content-Range', "bytes $start-$end/$size")
333 5
            ->withHeader('Content-Length', $length);
334
        } else {
335
            // range
336 2
            $filesize = filesize($Media->getFilesystemPath($variant));
337 2
            $end = $filesize - 1;
338 2
            $response = $response->withHeader('Content-Range', 'bytes 0-'.$end.'/'.$filesize)
339 2
                ->withHeader('Content-Length', $filesize);
340
        }
341
342 7
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response could return the type GuzzleHttp\Psr7\MessageTrait&object which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface. Consider adding an additional type-check to rule them out.
Loading history...
343
    }
344
345 3
    public function respondWithThumbnail(Media $Media, $variant, $responseID, $responseData = []): ResponseInterface
346
    {
347 3
        if ($this->responseBuilder != MediaBuilder::class) {
348
            throw new Exception('Media responses require MediaBuilder for putting together a response.');
349
        }
350 3
        $className = $this->responseBuilder;
351 3
        $responseBuilder = new $className($responseID, $responseData);
352
353 3
        $responseBuilder->setContentType($Media->ThumbnailMIMEType);
354
355 3
        $response = new MediaResponse($responseBuilder);
356 3
        $response = $this->setCache($response);
357
358 3
        $response = $response->withHeader('ETag', "media-$Media->ID-$variant")
359 3
            ->withHeader('Content-Length', filesize($responseID));
360
361 3
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response could return the type GuzzleHttp\Psr7\MessageTrait&object which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface. Consider adding an additional type-check to rule them out.
Loading history...
362
    }
363
364 4
    public function respondEmpty($responseID, $responseData = [])
365
    {
366 4
        if ($this->responseBuilder != EmptyBuilder::class) {
367
            throw new Exception('Media responses require MediaBuilder for putting together a response.');
368
        }
369 4
        $className = $this->responseBuilder;
370 4
        $responseBuilder = new $className($responseID, $responseData);
371 4
        $response = new EmptyResponse($responseBuilder);
372 4
        return $response;
373
    }
374
375
376 12
    public function handleMediaRequest($mediaID): ResponseInterface
377
    {
378 12
        if (empty($mediaID)) {
379
            return $this->throwNotFoundError();
380
        }
381
382
        // get media
383
        try {
384 12
            $Media = Media::getById($mediaID);
385
        } catch (Exception $e) {
386
            return $this->throwUnauthorizedError();
387
        }
388
389 12
        if (!$Media) {
390 1
            return $this->throwNotFoundError();
391
        }
392
393 11
        if (!$this->checkReadAccess($Media)) {
394
            return $this->throwNotFoundError();
395
        }
396
397
398 11
        $_server = $this->request->getServerParams();
399
400 11
        if (isset($_server['HTTP_ACCEPT'])) {
401
            if ($_server['HTTP_ACCEPT'] == 'application/json') {
402
                $this->responseBuilder = JsonBuilder::class;
403
            }
404
        }
405
406 11
        if ($this->responseBuilder == JsonBuilder::class) {
407 1
            return $this->respond('media', [
408 1
                'success' => true
409 1
                ,'data' => $Media,
410 1
            ]);
411
        } else {
412
413
            // determine variant
414 10
            if ($variant = $this->shiftPath()) {
415
                if (!$Media->isVariantAvailable($variant)) {
416
                    return $this->throwNotFoundError();
417
                }
418
            } else {
419 10
                $variant = 'original';
420
            }
421
422
            // initialize response
423 10
            $this->responseBuilder = MediaBuilder::class;
424 10
            set_time_limit(0);
425 10
            $filePath = $Media->getFilesystemPath($variant);
426
427
            // media are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
428 10
            if (!empty($_server['HTTP_IF_NONE_MATCH']) || !empty($_server['HTTP_IF_MODIFIED_SINCE'])) {
429 2
                $this->responseBuilder = EmptyBuilder::class;
430 2
                $response = $this->respondEmpty($filePath);
431 2
                $response->withDefaults(304);
432
433 2
                return $response;
434
            }
435
436
437 8
            return $this->respondWithMedia($Media, $variant, $filePath);
438
        }
439
    }
440
441 1
    public function handleInfoRequest($mediaID): ResponseInterface
442
    {
443 1
        if (empty($mediaID) || !is_numeric($mediaID)) {
444
            $this->throwNotFoundError();
445
        }
446
447
        // get media
448
        try {
449 1
            $Media = Media::getById($mediaID);
450
        } catch (Exception $e) {
451
            return $this->throwUnauthorizedError();
452
        }
453
454 1
        if (!$Media) {
455
            return $this->throwNotFoundError();
456
        }
457
458 1
        if (!$this->checkReadAccess($Media)) {
459
            return $this->throwUnauthorizedError();
460
        }
461
462 1
        return parent::handleRecordRequest($Media);
463
    }
464
465 1
    public function handleDownloadRequest($media_id, $filename = false): ResponseInterface
466
    {
467 1
        if (empty($media_id) || !is_numeric($media_id)) {
468
            $this->throwNotFoundError();
469
        }
470
471
        // get media
472
        try {
473 1
            $Media = Media::getById($media_id);
474
        } catch (Exception $e) {
475
            return $this->throwUnauthorizedError();
476
        }
477
478
479 1
        if (!$Media) {
480
            return $this->throwNotFoundError();
481
        }
482
483 1
        if (!$this->checkReadAccess($Media)) {
484
            return $this->throwUnauthorizedError();
485
        }
486
487 1
        $filePath = $Media->getFilesystemPath('original');
488
489 1
        $this->responseBuilder = MediaBuilder::class;
490 1
        $response = $this->respondWithMedia($Media, 'original', $filePath);
491
492 1
        $response = $response->withHeader('Content-Disposition', 'attachment; filename="'.($filename ? $filename : $filePath).'"');
493
494 1
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response could return the type GuzzleHttp\Psr7\MessageTrait&object which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface. Consider adding an additional type-check to rule them out.
Loading history...
495
    }
496
497
    public function handleCaptionRequest($media_id): ResponseInterface
498
    {
499
        // require authentication
500
        $GLOBALS['Session']->requireAccountLevel('Staff');
501
502
        if (empty($media_id) || !is_numeric($media_id)) {
503
            return $this->throwNotFoundError();
504
        }
505
506
        // get media
507
        try {
508
            $Media = Media::getById($media_id);
509
        } catch (Exception $e) {
510
            return $this->throwUnauthorizedError();
511
        }
512
513
514
        if (!$Media) {
515
            $this->throwNotFoundError();
516
        }
517
518
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
519
            $Media->Caption = $_REQUEST['Caption'];
520
            $Media->save();
521
522
            return $this->respond('mediaCaptioned', [
523
                'success' => true
524
                ,'data' => $Media,
525
            ]);
526
        }
527
528
        return $this->respond('mediaCaption', [
529
            'data' => $Media,
530
        ]);
531
    }
532
533
    public function handleDeleteRequest(ActiveRecord $Record): ResponseInterface
534
    {
535
        // require authentication
536
        $GLOBALS['Session']->requireAccountLevel('Staff');
537
538
        if ($mediaID = $this->peekPath()) {
539
            $mediaIDs = [$mediaID];
540
        } elseif (!empty($_REQUEST['mediaID'])) {
541
            $mediaIDs = [$_REQUEST['mediaID']];
542
        } elseif (is_array($_REQUEST['media'])) {
543
            $mediaIDs = $_REQUEST['media'];
544
        }
545
546
        $deleted = [];
547
        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...
548
            if (!is_numeric($mediaID)) {
549
                continue;
550
            }
551
552
            // get media
553
            $Media = Media::getByID($mediaID);
554
555
            if (!$Media) {
556
                return $this->throwNotFoundError();
557
            }
558
559
            if ($Media->destroy()) {
560
                $deleted[] = $Media;
561
            }
562
        }
563
564
        return $this->respond('mediaDeleted', [
565
            'success' => true
566
            ,'data' => $deleted,
567
        ]);
568
    }
569
570 3
    public function handleThumbnailRequest(Media $Media = null): ResponseInterface
571
    {
572
        // get media
573 3
        if (!$Media) {
574 3
            if (!$mediaID = $this->shiftPath()) {
575
                return $this->throwNotFoundError();
576 3
            } elseif (!$Media = Media::getByID($mediaID)) {
577
                return $this->throwNotFoundError();
578
            }
579
        }
580
581 3
        $_server = $this->request->getServerParams();
582
583
        // thumbnails are immutable for a given URL, so no need to actually check anything if the browser wants to revalidate its cache
584 3
        if (!empty($_server['HTTP_IF_NONE_MATCH']) || !empty($_server['HTTP_IF_MODIFIED_SINCE'])) {
585
            $this->responseBuilder = EmptyBuilder::class;
586
            $response = $this->respondEmpty($Media->ID);
587
            $response->withDefaults(304);
588
589
            return $response;
590
        }
591
592
        // get format
593 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

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