Passed
Push — master ( 8de1cf...dc39f2 )
by Stefano
01:22
created

ObjectTypesController   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 99
dl 0
loc 218
rs 10
c 0
b 0
f 0
wmc 18

8 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 6 1
A save() 0 9 2
A addCustomProperties() 0 19 4
A updateSchema() 0 15 2
A prepareProperties() 0 20 4
A tables() 0 17 1
A view() 0 33 2
A propertiesData() 0 17 2
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2019 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\Model;
14
15
use BEdita\SDK\BEditaClientException;
16
use Cake\Core\Configure;
17
use Cake\Http\Response;
18
use Cake\Utility\Hash;
19
use Psr\Log\LogLevel;
20
21
/**
22
 * Object Types Model Controller: list, add, edit, remove object types
23
 *
24
 * @property \App\Controller\Component\PropertiesComponent $Properties
25
 * @property \App\Controller\Component\SchemaComponent $Schema
26
 */
27
class ObjectTypesController extends ModelBaseController
28
{
29
    /**
30
     * Core tables list.
31
     *
32
     * @var array
33
     */
34
    public const TABLES = [
35
        'BEdita/Core.Folders',
36
        'BEdita/Core.Links',
37
        'BEdita/Core.Locations',
38
        'BEdita/Core.Media',
39
        'BEdita/Core.Objects',
40
        'BEdita/Core.Profiles',
41
        'BEdita/Core.Publications',
42
        'BEdita/Core.Users',
43
    ];
44
    /**
45
     * Resource type currently used
46
     *
47
     * @var string
48
     */
49
    protected $resourceType = 'object_types';
50
51
    /**
52
     * @inheritDoc
53
     */
54
    public function create(): ?Response
55
    {
56
        parent::create();
57
        $this->set('propertyTypesOptions', $this->Properties->typesOptions());
58
59
        return null;
60
    }
61
62
    /**
63
     * @inheritDoc
64
     */
65
    public function view($id): ?Response
66
    {
67
        parent::view($id);
68
69
        // retrieve additional data
70
        $resource = (array)$this->viewBuilder()->getVar('resource');
71
        $name = Hash::get($resource, 'attributes.name', 'undefined');
72
        $filter = ['object_type' => $name];
73
        try {
74
            $data = $this->propertiesData($filter);
75
        } catch (BEditaClientException $e) {
76
            $this->log($e->getMessage(), LogLevel::ERROR);
77
            $this->Flash->error($e->getMessage(), ['params' => $e]);
78
79
            return $this->redirect(['_name' => 'model:list:' . $this->resourceType]);
80
        }
81
82
        $objectTypeProperties = $this->prepareProperties($data, $name);
83
        $this->set(compact('objectTypeProperties'));
84
        $schema = $this->Schema->getSchema();
85
        $this->set('schema', $this->updateSchema($schema, $resource));
86
        $this->set('properties', $this->Properties->viewGroups($resource, $this->resourceType));
87
        $this->set('propertyTypesOptions', $this->Properties->typesOptions());
88
        $this->set('associationsOptions', $this->Properties->associationsOptions((array)Hash::get($resource, 'attributes.associations')));
89
        // setup `currentAttributes`
90
        $this->Modules->setupAttributes($resource);
91
92
        // get object schema
93
        $this->Schema->setConfig(['internalSchema' => false]);
94
        $this->set('objectTypeSchema', $this->Schema->getSchema($name));
95
        $this->Schema->setConfig(['internalSchema' => true]);
96
97
        return null;
98
    }
99
100
    /**
101
     * Read all properties via API, performing multiple calls if necessary.
102
     *
103
     * @param array $filter Properties filter
104
     * @return array
105
     */
106
    protected function propertiesData(array $filter): array
107
    {
108
        $data = [];
109
        $done = false;
110
        $page = 1;
111
        while (!$done) {
112
            $response = $this->apiClient->get(
113
                '/model/properties',
114
                compact('filter', 'page') + ['page_size' => 100]
115
            );
116
            $data = array_merge($data, (array)Hash::get($response, 'data'));
117
            $pageCount = (int)Hash::get($response, 'meta.pagination.page_count');
118
            $done = $pageCount === $page;
119
            $page++;
120
        }
121
122
        return $data;
123
    }
124
125
    /**
126
     * Update schema using resource.
127
     * If core type, skip.
128
     * Otherwise, set table and parent_name.
129
     *
130
     * @param array $schema The schema
131
     * @param array $resource The resource
132
     * @return array
133
     */
134
    protected function updateSchema(array $schema, array $resource): array
135
    {
136
        if ((bool)Hash::get($resource, 'meta.core_type')) {
137
            return $schema;
138
        }
139
        $schema['properties']['table'] = [
140
            'type' => 'string',
141
            'enum' => $this->tables($resource),
142
        ];
143
        $schema['properties']['parent_name'] = [
144
            'type' => 'string',
145
            'enum' => array_merge([''], $this->Schema->abstractTypes()),
146
        ];
147
148
        return $schema;
149
    }
150
151
    /**
152
     * Get available tables list
153
     *
154
     * @return array
155
     */
156
    protected function tables(array $resource): array
157
    {
158
        $tables = array_unique(
159
            array_merge(
160
                self::TABLES,
161
                (array)Configure::read('Model.objectTypesTables')
162
            )
163
        );
164
        $tables = array_unique(
165
            array_merge(
166
                $tables,
167
                (array)Hash::get($resource, 'attributes.table')
168
            )
169
        );
170
        sort($tables);
171
172
        return $tables;
173
    }
174
175
    /**
176
     * Separate properties between `inherited`, `core`  and `custom`
177
     *
178
     * @param array $data Property array
179
     * @param string $name Object type name
180
     * @return array
181
     */
182
    protected function prepareProperties(array $data, string $name): array
183
    {
184
        $inherited = $core = $custom = [];
185
        foreach ($data as $prop) {
186
            if (!is_numeric($prop['id'])) {
187
                $type = $prop['attributes']['object_type_name'];
188
                if ($type == $name) {
189
                    $core[] = $prop;
190
                } else {
191
                    $inherited[] = $prop;
192
                }
193
            } else {
194
                $custom[] = $prop;
195
            }
196
        }
197
198
        return [
199
            'core' => Hash::sort($core, '{n}.attributes.name'),
200
            'inherited' => Hash::sort($inherited, '{n}.attributes.name'),
201
            'custom' => Hash::sort($custom, '{n}.attributes.name'),
202
        ];
203
    }
204
205
    /**
206
     * Save object type.
207
     *
208
     * @return \Cake\Http\Response|null
209
     */
210
    public function save(): ?Response
211
    {
212
        $this->addCustomProperties();
213
        $this->request = $this->request->withoutData('addedProperties');
214
        if ($this->request->getData('associations') === '') {
215
            $this->request = $this->request->withData('associations', null);
216
        }
217
218
        return parent::save();
219
    }
220
221
    /**
222
     * Add custom property
223
     *
224
     * @return void
225
     */
226
    protected function addCustomProperties(): void
227
    {
228
        $added = json_decode((string)$this->request->getData('addedProperties'), true);
229
        if (empty($added) || !is_array($added)) {
230
            return;
231
        }
232
        $objectTypeName = $this->request->getData('name');
233
234
        foreach ($added as $prop) {
235
            $data = [
236
                'type' => 'properties',
237
                'attributes' => [
238
                    'description' => Hash::get($prop, 'description'),
239
                    'name' => Hash::get($prop, 'name'),
240
                    'property_type_name' => Hash::get($prop, 'type'),
241
                    'object_type_name' => $objectTypeName,
242
                ],
243
            ];
244
            $this->apiClient->post('/model/properties', json_encode(compact('data')));
245
        }
246
    }
247
}
248