ModulesController::beforeRender()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace App\Controller;
14
15
use App\Utility\ApiConfigTrait;
16
use App\Utility\CacheTools;
17
use App\Utility\Message;
18
use App\Utility\PermissionsTrait;
19
use BEdita\SDK\BEditaClientException;
20
use BEdita\WebTools\Utility\ApiTools;
21
use Cake\Core\Configure;
22
use Cake\Event\Event;
23
use Cake\Event\EventInterface;
24
use Cake\Http\Exception\UnauthorizedException;
25
use Cake\Http\Response;
26
use Cake\I18n\I18n;
27
use Cake\Utility\Hash;
28
use Exception;
29
use Psr\Log\LogLevel;
30
31
/**
32
 * Modules controller: list, add, edit, remove objects
33
 *
34
 * @property \App\Controller\Component\CategoriesComponent $Categories
35
 * @property \App\Controller\Component\ChildrenComponent $Children
36
 * @property \App\Controller\Component\HistoryComponent $History
37
 * @property \App\Controller\Component\ObjectsEditorsComponent $ObjectsEditors
38
 * @property \App\Controller\Component\ParentsComponent $Parents
39
 * @property \App\Controller\Component\ProjectConfigurationComponent $ProjectConfiguration
40
 * @property \App\Controller\Component\PropertiesComponent $Properties
41
 * @property \App\Controller\Component\QueryComponent $Query
42
 * @property \App\Controller\Component\ThumbsComponent $Thumbs
43
 * @property \BEdita\WebTools\Controller\Component\ApiFormatterComponent $ApiFormatter
44
 */
45
class ModulesController extends AppController
46
{
47
    use ApiConfigTrait;
48
    use PermissionsTrait;
49
50
    /**
51
     * Object type currently used
52
     *
53
     * @var string
54
     */
55
    protected $objectType = null;
56
57
    /**
58
     * @inheritDoc
59
     */
60
    public function initialize(): void
61
    {
62
        parent::initialize();
63
64
        $this->loadComponent('Children');
65
        $this->loadComponent('History');
66
        $this->loadComponent('ObjectsEditors');
67
        $this->loadComponent('Parents');
68
        $this->loadComponent('Properties');
69
        $this->loadComponent('ProjectConfiguration');
70
        $this->loadComponent('Query');
71
        $this->loadComponent('Thumbs', Configure::read('Thumbs', []));
72
        $this->loadComponent('BEdita/WebTools.ApiFormatter');
73
        if ($this->getRequest()->getParam('object_type')) {
74
            $this->objectType = $this->getRequest()->getParam('object_type');
75
            $this->Modules->setConfig('currentModuleName', $this->objectType);
76
            $this->Schema->setConfig('type', $this->objectType);
77
        }
78
        $this->Security->setConfig('unlockedActions', ['save', 'setup']);
79
    }
80
81
    /**
82
     * {@inheritDoc}
83
     *
84
     * @codeCoverageIgnore
85
     */
86
    public function beforeRender(EventInterface $event): ?Response
87
    {
88
        $this->set('objectType', $this->objectType);
89
90
        return parent::beforeRender($event);
91
    }
92
93
    /**
94
     * Display resources list.
95
     *
96
     * @return \Cake\Http\Response|null
97
     */
98
    public function index(): ?Response
99
    {
100
        $this->getRequest()->allowMethod(['get']);
101
102
        // handle filter and query parameters using session
103
        $result = $this->applySessionFilter();
104
        if ($result != null) {
105
            return $result;
106
        }
107
108
        try {
109
            $params = $this->Query->index();
110
            $response = $this->apiClient->getObjects($this->objectType, $params);
111
            if (empty($params['q']) && empty($params['filter'])) {
112
                CacheTools::setModuleCount((array)$response, $this->Modules->getConfig('currentModuleName'));
113
            }
114
        } catch (BEditaClientException $e) {
115
            $this->log($e->getMessage(), LogLevel::ERROR);
116
            $this->Flash->error($e->getMessage(), ['params' => $e]);
117
            // remove session filter to avoid error repetition
118
            $session = $this->getRequest()->getSession();
119
            $session->delete(sprintf('%s.filter', $this->Modules->getConfig('currentModuleName')));
120
121
            return $this->redirect(['_name' => 'dashboard']);
122
        }
123
124
        $this->ProjectConfiguration->read();
125
126
        $response = $this->ApiFormatter->embedIncluded((array)$response);
127
        $objects = (array)Hash::get($response, 'data');
128
        $this->set('objects', $objects);
129
        $this->set('meta', (array)Hash::get($response, 'meta'));
130
        $this->set('links', (array)Hash::get($response, 'links'));
131
        $this->set('types', ['right' => $this->Schema->descendants($this->objectType)]);
132
133
        $this->set('properties', $this->Properties->indexList($this->objectType));
134
135
        // base/custom filters for filter view
136
        $this->set('filter', $this->Properties->filterList($this->objectType));
137
138
        // base/custom bulk actions for index view
139
        $this->set('bulkActions', $this->Properties->bulkList($this->objectType));
140
141
        // objectTypes schema
142
        $this->set('schema', $this->getSchemaForIndex($this->objectType));
143
        // custom properties
144
        $this->set('customProps', $this->Schema->customProps($this->objectType));
145
146
        // set prevNext for views navigations
147
        $this->setObjectNav($objects);
148
149
        return null;
150
    }
151
152
    /**
153
     * View single resource.
154
     *
155
     * @param string|int $id Resource ID.
156
     * @return \Cake\Http\Response|null
157
     */
158
    public function view($id): ?Response
159
    {
160
        $this->getRequest()->allowMethod(['get']);
161
162
        try {
163
            $query = ['count' => 'all'];
164
            $response = $this->apiClient->getObject($id, $this->objectType, $query);
165
        } catch (BEditaClientException $e) {
166
            // Error! Back to index.
167
            $this->log($e->getMessage(), LogLevel::ERROR);
168
            $this->Flash->error(__('Error retrieving the requested content'), ['params' => $e]);
169
170
            return $this->redirect(['_name' => 'modules:list', 'object_type' => $this->objectType]);
171
        }
172
        $this->ProjectConfiguration->read();
173
174
        $revision = Hash::get($response, 'meta.schema.' . $this->objectType . '.revision', null);
175
        $schema = $this->Schema->getSchema($this->objectType, $revision);
176
177
        $object = $response['data'];
178
179
        // setup `currentAttributes` and recover failure data from session.
180
        $this->Modules->setupAttributes($object);
181
182
        $included = !empty($response['included']) ? $response['included'] : [];
183
        $typeIncluded = Hash::combine($included, '{n}.id', '{n}', '{n}.type');
184
        $streams = Hash::get($typeIncluded, 'streams');
185
        $this->History->load($id, $object);
186
        $this->set(compact('object', 'included', 'schema', 'streams'));
187
        $this->set('properties', $this->Properties->viewGroups($object, $this->objectType));
188
        $this->set('foldersSchema', $this->Schema->getSchema('folders'));
189
190
        $computedRelations = array_reduce(
191
            array_keys($object['relationships']),
192
            function ($acc, $relName) use ($schema) {
193
                $acc[$relName] = (array)Hash::get($schema, sprintf('relations.%s', $relName), []);
194
195
                return $acc;
196
            },
197
            []
198
        );
199
        $this->setupViewRelations($computedRelations);
200
201
        // set objectNav
202
        $objectNav = $this->getObjectNav((string)$id);
203
        $this->set('objectNav', $objectNav);
204
205
        $this->ObjectsEditors->update((string)$id);
206
207
        return null;
208
    }
209
210
    /**
211
     * View single resource by id, doing a proper redirect (302) to resource module view by type.
212
     * If no resource found by ID, redirect to referer.
213
     *
214
     * @param string|int $id Resource ID.
215
     * @return \Cake\Http\Response|null
216
     */
217
    public function uname($id): ?Response
218
    {
219
        try {
220
            $response = $this->apiClient->get(sprintf('/objects/%s', $id));
221
        } catch (BEditaClientException $e) {
222
            $msg = $e->getMessage();
223
            $msgNotFound = sprintf(__('Resource "%s" not found', true), $id);
224
            $msgNotAvailable = sprintf(__('Resource "%s" not available. Error: %s', true), $id, $msg);
225
            $error = $e->getCode() === 404 ? $msgNotFound : $msgNotAvailable;
226
            $this->Flash->error($error);
227
228
            return $this->redirect($this->referer());
229
        }
230
        $_name = 'modules:view';
231
        $object_type = $response['data']['type'];
232
        $id = $response['data']['id'];
233
234
        return $this->redirect(compact('_name', 'object_type', 'id'));
235
    }
236
237
    /**
238
     * Display new resource form.
239
     *
240
     * @return \Cake\Http\Response|null
241
     */
242
    public function create(): ?Response
243
    {
244
        $this->viewBuilder()->setTemplate('view');
245
246
        // Create stub object with empty `attributes`.
247
        $schema = $this->Schema->getSchema();
248
        if (!is_array($schema)) {
249
            $this->Flash->error(__('Cannot create abstract objects or objects without schema'));
250
251
            return $this->redirect(['_name' => 'modules:list', 'object_type' => $this->objectType]);
252
        }
253
        $attributes = array_fill_keys(
254
            array_keys(
255
                array_filter(
256
                    $schema['properties'],
257
                    function ($schema) {
258
                        return empty($schema['readOnly']);
259
                    }
260
                )
261
            ),
262
            null
263
        );
264
        $object = [
265
            'type' => $this->objectType,
266
            'attributes' => $attributes,
267
        ];
268
269
        $this->set(compact('object', 'schema'));
270
        $this->set('properties', $this->Properties->viewGroups($object, $this->objectType));
271
        $this->ProjectConfiguration->read();
272
273
        $this->setupViewRelations((array)Hash::get($schema, 'relations'));
274
275
        return null;
276
    }
277
278
    /**
279
     * Create new object from ajax request.
280
     *
281
     * @return void
282
     */
283
    public function save(): void
284
    {
285
        $this->viewBuilder()->setClassName('Json'); // force json response
286
        $this->getRequest()->allowMethod(['post']);
287
        $requestData = $this->prepareRequest($this->objectType);
288
        unset($requestData['_csrfToken']);
289
        // extract related objects data
290
        $relatedData = (array)Hash::get($requestData, '_api');
291
        unset($requestData['_api']);
292
293
        try {
294
            $uname = Hash::get($requestData, 'uname');
295
            if (!empty($uname) && is_numeric($uname)) {
296
                $this->set(['error' => __('Invalid numeric uname. Change it to a valid string')]);
297
                $this->setSerialize(['error']);
298
299
                return;
300
            }
301
            $id = (string)Hash::get($requestData, 'id');
302
            // skip save if no data changed
303
            $schema = (array)$this->Schema->getSchema($this->objectType);
304
            $permissions = (array)Hash::get($requestData, 'permissions');
305
            $skipSaveObject = $this->Modules->skipSaveObject($id, $requestData);
306
            $skipSaveRelated = $this->Modules->skipSaveRelated($id, $relatedData);
307
            $skipSavePermissions = $this->Modules->skipSavePermissions($id, $permissions, $schema);
308
            if ($skipSaveObject && $skipSaveRelated && $skipSavePermissions) {
309
                $response = $this->apiClient->getObject($id, $this->objectType, ['count' => 'all']);
310
                $this->Thumbs->urls($response);
311
                $this->set((array)$response);
312
                $this->setSerialize(array_keys($response));
313
314
                return;
315
            }
316
317
            // upload file (if available)
318
            $this->Modules->upload($requestData);
319
320
            // save data
321
            $lang = I18n::getLocale();
322
            $headers = ['Accept-Language' => $lang];
323
            if (!$skipSaveObject) {
324
                $response = $this->apiClient->save($this->objectType, $requestData, $headers);
325
            } else {
326
                $response = $this->apiClient->getObject($id, $this->objectType);
327
            }
328
            if (!$skipSavePermissions) {
329
                try {
330
                    $this->savePermissions(
331
                        (array)$response,
332
                        $schema,
333
                        $permissions
334
                    );
335
                } catch (BEditaClientException $error) {
336
                    $this->handleError($error);
337
                }
338
            }
339
            $id = (string)Hash::get($response, 'data.id');
340
            if (!$skipSaveRelated) {
341
                try {
342
                    $this->Modules->saveRelated($id, $this->objectType, $relatedData);
343
                } catch (BEditaClientException $error) {
344
                    $this->handleError($error);
345
                }
346
            }
347
            $options = [
348
                'id' => Hash::get($response, 'data.id'),
349
                'type' => $this->objectType,
350
                'data' => $requestData,
351
            ];
352
            $event = new Event('Controller.afterSave', $this, $options);
353
            $this->getEventManager()->dispatch($event);
354
        } catch (BEditaClientException $error) {
355
            $this->handleError($error);
356
357
            return;
358
        }
359
        if ($response['data']) {
360
            $response['data'] = [ $response['data'] ];
361
        }
362
363
        $this->Thumbs->urls($response);
364
365
        $this->set((array)$response);
366
        $this->setSerialize(array_keys($response));
367
    }
368
369
    /**
370
     * Handle exception error: log, flash and set.
371
     *
372
     * @param \BEdita\SDK\BEditaClientException $exception The exception
373
     * @return void
374
     */
375
    protected function handleError(BEditaClientException $exception): void
376
    {
377
        $message = new Message($exception);
378
        $this->log($message->get(), LogLevel::ERROR);
379
        $this->Flash->error($message->get(), ['params' => $exception]);
380
        $error = $this->viewBuilder()->getVar('error') ?? [];
381
        $error = is_array($error) ? array_merge($error, [$message->get()]) : [$error, $message->get()];
382
        $this->set(['error' => $error]);
383
        $this->setSerialize(['error']);
384
    }
385
386
    /**
387
     * Clone single object.
388
     *
389
     * @param string|int $id Object ID.
390
     * @return \Cake\Http\Response|null
391
     */
392
    public function clone($id): ?Response
393
    {
394
        $this->viewBuilder()->setTemplate('view');
395
        $schema = $this->Schema->getSchema();
396
        if (!is_array($schema)) {
397
            $this->Flash->error(__('Cannot create abstract objects or objects without schema'));
398
399
            return $this->redirect(['_name' => 'modules:list', 'object_type' => $this->objectType]);
400
        }
401
        try {
402
            $modified = [
403
                'title' => $this->getRequest()->getQuery('title'),
404
                'status' => 'draft',
405
            ];
406
            $reset = (array)Configure::read(sprintf('Clone.%s.reset', $this->objectType));
407
            foreach ($reset as $field) {
408
                $modified[$field] = null;
409
            }
410
            $included = [];
411
            foreach (['relationships', 'translations'] as $attribute) {
412
                if ($this->getRequest()->getQuery($attribute) === 'true') {
413
                    $included[] = $attribute;
414
                }
415
            }
416
            $clone = $this->apiClient->clone($this->objectType, $id, $modified, $included);
417
            $id = (string)Hash::get($clone, 'data.id');
418
        } catch (BEditaClientException $e) {
419
            $this->log($e->getMessage(), LogLevel::ERROR);
420
            $this->Flash->error($e->getMessage(), ['params' => $e]);
421
        }
422
423
        return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]);
424
    }
425
426
    /**
427
     * Delete single resource.
428
     *
429
     * @return \Cake\Http\Response|null
430
     */
431
    public function delete(): ?Response
432
    {
433
        $this->getRequest()->allowMethod(['post']);
434
        $id = $this->getRequest()->getData('id');
435
        $ids = $this->getRequest()->getData('ids');
436
        $ids = is_string($ids) ? explode(',', $ids) : $ids;
437
        $ids = empty($ids) ? [$id] : $ids;
438
        try {
439
            $this->apiClient->deleteObjects($ids, $this->objectType);
440
            $eventManager = $this->getEventManager();
441
            foreach ($ids as $id) {
442
                $event = new Event('Controller.afterDelete', $this, ['id' => $id, 'type' => $this->objectType]);
443
                $eventManager->dispatch($event);
444
            }
445
        } catch (BEditaClientException $e) {
446
            $this->log($e->getMessage(), LogLevel::ERROR);
447
            $this->Flash->error($e->getMessage(), ['params' => $e]);
448
            $id = $this->getRequest()->getData('id');
449
            $options = empty($id) ? $this->referer() : ['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id];
450
451
            return $this->redirect($options);
452
        }
453
        $this->Flash->success(__('Object(s) deleted'));
454
455
        return $this->redirect([
456
            '_name' => 'modules:list',
457
            'object_type' => $this->objectType,
458
        ]);
459
    }
460
461
    /**
462
     * Relation data load via API => `GET /:object_type/:id/related/:relation`
463
     *
464
     * @param string|int $id The object ID.
465
     * @param string $relation The relation name.
466
     * @return void
467
     */
468
    public function related($id, string $relation): void
469
    {
470
        if ($id === 'new') {
471
            $this->set('data', []);
472
            $this->setSerialize(['data']);
473
474
            return;
475
        }
476
477
        $this->getRequest()->allowMethod(['get']);
478
        $query = $this->Query->prepare($this->getRequest()->getQueryParams());
479
        try {
480
            $response = $this->apiClient->getRelated($id, $this->objectType, $relation, $query);
481
            $response = $this->ApiFormatter->embedIncluded((array)$response);
482
        } catch (BEditaClientException $error) {
483
            $this->handleError($error);
484
485
            return;
486
        }
487
488
        $this->Thumbs->urls($response);
489
490
        $this->set((array)$response);
491
        $this->setSerialize(array_keys($response));
492
    }
493
494
    /**
495
     * Load resources of $type callig api `GET /:type/`
496
     * Json response
497
     *
498
     * @param string|int $id the object identifier.
499
     * @param string $type the resource type name.
500
     * @return void
501
     */
502
    public function resources($id, string $type): void
503
    {
504
        $this->getRequest()->allowMethod(['get']);
505
        $query = $this->Query->prepare($this->getRequest()->getQueryParams());
506
        try {
507
            $response = $this->apiClient->get($type, $query);
508
        } catch (BEditaClientException $error) {
509
            $this->handleError($error);
510
511
            return;
512
        }
513
514
        $this->set((array)$response);
515
        $this->setSerialize(array_keys($response));
516
    }
517
518
    /**
519
     * Relation data load calling api `GET /:object_type/:id/relationships/:relation`
520
     * Json response
521
     *
522
     * @param string|int $id The object ID.
523
     * @param string $relation The relation name.
524
     * @return void
525
     */
526
    public function relationships($id, string $relation): void
527
    {
528
        $this->getRequest()->allowMethod(['get']);
529
        $available = $this->availableRelationshipsUrl($relation);
530
531
        try {
532
            $query = $this->Query->prepare($this->getRequest()->getQueryParams());
533
            $response = $this->apiClient->get($available, $query);
534
535
            $this->Thumbs->urls($response);
536
        } catch (BEditaClientException $ex) {
537
            $this->log($ex->getMessage(), LogLevel::ERROR);
538
539
            $this->set('error', $ex->getMessage());
540
            $this->setSerialize(['error']);
541
542
            return;
543
        }
544
545
        $this->set((array)$response);
546
        $this->setSerialize(array_keys($response));
547
    }
548
549
    /**
550
     * Retrieve URL to get objects available for a relation
551
     *
552
     * @param string $relation The relation name.
553
     * @return string
554
     */
555
    protected function availableRelationshipsUrl(string $relation): string
556
    {
557
        $defaults = [
558
            'children' => '/objects',
559
            'parent' => '/folders',
560
            'parents' => '/folders',
561
        ];
562
        $defaultUrl = (string)Hash::get($defaults, $relation);
563
        if (!empty($defaultUrl)) {
564
            return $defaultUrl;
565
        }
566
567
        $relationsSchema = $this->Schema->getRelationsSchema();
568
        $types = $this->Modules->relatedTypes($relationsSchema, $relation);
569
570
        return count($types) === 1 ? sprintf('/%s', $types[0]) : '/objects?filter[type][]=' . implode('&filter[type][]=', $types);
571
    }
572
573
    /**
574
     * get object properties and format them for index
575
     *
576
     * @param string $objectType objecte type name
577
     * @return array $schema
578
     */
579
    public function getSchemaForIndex($objectType): array
580
    {
581
        $schema = (array)$this->Schema->getSchema($objectType);
582
583
        // if prop is an enum then prepend an empty string for select element
584
        if (!empty($schema['properties'])) {
585
            foreach ($schema['properties'] as &$property) {
586
                if (isset($property['enum'])) {
587
                    array_unshift($property['enum'], '');
588
                }
589
            }
590
        }
591
592
        return $schema;
593
    }
594
595
    /**
596
     * Get objectType
597
     *
598
     * @return string|null
599
     */
600
    public function getObjectType(): ?string
601
    {
602
        return $this->objectType;
603
    }
604
605
    /**
606
     * Set objectType
607
     *
608
     * @param string|null $objectType The object type
609
     * @return void
610
     */
611
    public function setObjectType(?string $objectType): void
612
    {
613
        $this->objectType = $objectType;
614
    }
615
616
    /**
617
     * Set schemasByType and filtersByType, considering relations and schemas.
618
     *
619
     * @param array $relations The relations
620
     * @return void
621
     */
622
    private function setupViewRelations(array $relations): void
623
    {
624
        // setup relations schema
625
        $relationsSchema = $this->Schema->getRelationsSchema();
626
        $this->set('relationsSchema', $relationsSchema);
627
628
        // setup relations metadata
629
        $this->Modules->setupRelationsMeta(
630
            $relationsSchema,
631
            $relations,
632
            $this->Properties->relationsList($this->objectType),
633
            $this->Properties->hiddenRelationsList($this->objectType),
634
            $this->Properties->readonlyRelationsList($this->objectType)
635
        );
636
637
        // set right types, considering the object type relations
638
        $rel = (array)$this->viewBuilder()->getVar('relationsSchema');
639
        $rightTypes = \App\Utility\Schema::rightTypes($rel);
640
        $this->set('rightTypes', $rightTypes);
641
642
        // set schemas for relations right types
643
        $schemasByType = $this->Schema->getSchemasByType($rightTypes);
644
        $this->set('schemasByType', $schemasByType);
645
        $this->set('filtersByType', $this->Properties->filtersByType($rightTypes));
646
    }
647
648
    /**
649
     * Get list of users / no email, no relationships, no links, no schema, no included.
650
     *
651
     * @return void
652
     */
653
    public function users(): void
654
    {
655
        $this->viewBuilder()->setClassName('Json');
656
        $this->getRequest()->allowMethod('get');
657
        $query = array_merge(
658
            $this->getRequest()->getQueryParams(),
659
            ['fields' => 'id,title,username,name,surname,last_login']
660
        );
661
        $response = (array)$this->apiClient->get('users', $query);
662
        $response = ApiTools::cleanResponse($response);
663
        $data = (array)Hash::get($response, 'data');
664
        $meta = (array)Hash::get($response, 'meta');
665
        $this->set(compact('data', 'meta'));
666
        $this->setSerialize(['data', 'meta']);
667
    }
668
669
    /**
670
     * Get single resource, minimal data / no relationships, no links, no schema, no included.
671
     *
672
     * @param string $id The object ID
673
     * @return void
674
     */
675
    public function get(string $id): void
676
    {
677
        $this->viewBuilder()->setClassName('Json');
678
        $this->getRequest()->allowMethod('get');
679
        $data = $meta = [];
680
        $query = $this->getRequest()->getQueryParams();
681
        $type = (string)Hash::get($query, 'type');
682
        $fields = array_unique(array_merge(
683
            explode(',', 'id,title,description,uname,status,media_url'),
684
            explode(',', (string)Hash::get($query, 'fields', ''))
685
        ));
686
        $query['fields'] = implode(',', $fields);
687
        if ($type == null) {
688
            $response = (array)$this->apiClient->getObject($id, 'objects', $query);
689
            $type = (string)Hash::get($response, 'data.type');
690
        }
691
        $filter = (array)$this->getRequest()->getQuery('filter');
692
        $types = (string)Hash::get($filter, 'type');
693
        $filterType = !empty($types) ? explode(',', (string)Hash::get($filter, 'type')) : [];
694
        if (count($filterType) === 0 || in_array($type, $filterType)) {
695
            $response = (array)$this->apiClient->getObject($id, $type, $query);
696
            $response = $this->ApiFormatter->embedIncluded($response);
697
            $stream = (array)Hash::get($response, 'data.relationships.streams.data.0', []);
698
            $response = ApiTools::cleanResponse($response);
699
            $data = (array)Hash::get($response, 'data');
700
            $data['attributes'] = array_merge($data['attributes'], (array)Hash::get($stream, 'attributes', []));
701
            $data['attributes'] = array_filter($data['attributes'], fn($key) => in_array($key, $fields), ARRAY_FILTER_USE_KEY);
702
            $meta = (array)Hash::get($response, 'meta');
703
            $meta = array_merge($meta, (array)Hash::get($stream, 'meta', []));
704
            $meta = array_filter($meta, fn($key) => in_array($key, $fields), ARRAY_FILTER_USE_KEY);
705
        }
706
        $this->set(compact('data', 'meta'));
707
        $this->setSerialize(['data', 'meta']);
708
    }
709
710
    /**
711
     * Setup module.
712
     *
713
     * @return \Cake\Http\Response|null
714
     */
715
    public function setup(): ?Response
716
    {
717
        /** @var \Authentication\Identity|null $user */
718
        $user = $this->Authentication->getIdentity();
719
        $roles = (array)$user->get('roles');
720
        if (!in_array('admin', $roles)) {
721
            throw new UnauthorizedException(__('You are not authorized to access here'));
722
        }
723
        $this->getRequest()->allowMethod(['get', 'post']);
724
        if ($this->getRequest()->is('post')) {
725
            try {
726
                $requestData = $this->getRequest()->getData();
727
                $configurationKey = $requestData['configurationKey'] ?? null;
728
                unset($requestData['configurationKey']);
729
                $propertyName = explode('.', $configurationKey)[0];
730
                $subkey = explode('.', $configurationKey)[1];
731
                $propertyValue = (array)Configure::read($propertyName);
732
                $propertyValue = (array)Hash::insert($propertyValue, $subkey, $requestData);
733
                $this->saveApiConfig($propertyName, $propertyValue);
734
                $response = 'Configuration saved';
735
                $this->set('response', $response);
736
                $this->setSerialize(['response']);
737
            } catch (Exception $e) {
738
                $error = $e->getMessage();
739
                $this->set('error', $error);
740
                $this->setSerialize(['error']);
741
            }
742
        }
743
744
        return null;
745
    }
746
}
747