Passed
Push — master ( 606c62...bcf895 )
by Dante
02:00
created

AdministrationBaseController::prepareBody()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 13
rs 9.9666
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2022 Atlas 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\Admin;
14
15
use App\Controller\AppController;
16
use BEdita\SDK\BEditaClientException;
17
use BEdita\WebTools\Utility\ApiTools;
18
use Cake\Event\EventInterface;
19
use Cake\Http\Exception\UnauthorizedException;
20
use Cake\Http\Response;
21
use Cake\Utility\Hash;
22
23
/**
24
 * Administration Controller
25
 */
26
abstract class AdministrationBaseController extends AppController
27
{
28
    /**
29
     * Endpoint
30
     *
31
     * @var string
32
     */
33
    protected $endpoint = '/admin';
34
35
    /**
36
     * Resource type in use
37
     *
38
     * @var string
39
     */
40
    protected $resourceType = null;
41
42
    /**
43
     * Readonly flag view.
44
     *
45
     * @var bool
46
     */
47
    protected $readonly = true;
48
49
    /**
50
     * Deleteonly flag view.
51
     *
52
     * @var bool
53
     */
54
    protected $deleteonly = false;
55
56
    /**
57
     * Properties to show in index columns
58
     *
59
     * @var array
60
     */
61
    protected $properties = [];
62
63
    /**
64
     * Properties to json decode before save
65
     *
66
     * @var array
67
     */
68
    protected $propertiesForceJson = [];
69
70
    /**
71
     * Properties that are secrets
72
     *
73
     * @var array
74
     */
75
    protected $propertiesSecrets = [];
76
77
    /**
78
     * Meta to show in index columns
79
     *
80
     * @var array
81
     */
82
    protected $meta = ['created', 'modified'];
83
84
    /**
85
     * @inheritDoc
86
     */
87
    public function initialize(): void
88
    {
89
        parent::initialize();
90
91
        $this->loadComponent('Properties');
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     *
97
     * Restrict `model` module access to `admin`
98
     */
99
    public function beforeFilter(EventInterface $event): ?Response
100
    {
101
        $res = parent::beforeFilter($event);
102
        if ($res !== null) {
103
            return $res;
104
        }
105
106
        /** @var \Authentication\Identity|null $user */
107
        $user = $this->Authentication->getIdentity();
108
        $roles = (array)$user->get('roles');
109
        if (empty($roles) || !in_array('admin', $roles)) {
110
            throw new UnauthorizedException(__('Module access not authorized'));
111
        }
112
113
        return null;
114
    }
115
116
    /**
117
     * Index method
118
     *
119
     * @return \Cake\Http\Response|null
120
     */
121
    public function index(): ?Response
122
    {
123
        $this->getRequest()->allowMethod(['get']);
124
        try {
125
            $response = $this->loadData();
126
        } catch (BEditaClientException $e) {
127
            $this->log($e->getMessage(), 'error');
128
            $this->Flash->error($e->getMessage(), ['params' => $e]);
129
130
            return $this->redirect(['_name' => 'dashboard']);
131
        }
132
133
        $this->set('resources', (array)Hash::get($response, 'data'));
134
        $this->set('meta', (array)Hash::get($response, 'meta'));
135
        $this->set('links', (array)Hash::get($response, 'links'));
136
        $this->set('resourceType', $this->resourceType);
137
        $this->set('properties', $this->properties);
138
        $this->set('propertiesSecrets', $this->propertiesSecrets);
139
        $this->set('metaColumns', $this->meta);
140
        $this->set('filter', []);
141
        $this->set('schema', (array)$this->Schema->getSchema($this->resourceType));
142
        $this->set('readonly', $this->readonly);
143
        $this->set('deleteonly', $this->deleteonly);
144
145
        return null;
146
    }
147
148
    /**
149
     * Save data
150
     *
151
     * @return \Cake\Http\Response|null
152
     */
153
    public function save(): ?Response
154
    {
155
        $this->getRequest()->allowMethod(['post']);
156
        $data = (array)$this->getRequest()->getData();
157
        $id = (string)Hash::get($data, 'id');
158
        $body = $this->prepareBody($data);
159
        $endpoint = $this->endpoint();
160
        try {
161
            if (empty($id)) {
162
                $this->apiClient->post($endpoint, json_encode($body));
163
            } else {
164
                $body['data']['id'] = $id;
165
                $this->apiClient->patch(sprintf('%s/%s', $endpoint, $id), json_encode($body));
166
            }
167
        } catch (BEditaClientException $e) {
168
            $this->log($e->getMessage(), 'error');
169
            $this->Flash->error($e->getMessage(), ['params' => $e]);
170
        }
171
172
        return $this->redirect(['_name' => sprintf('admin:list:%s', $this->resourceType)]);
173
    }
174
175
    /**
176
     * Remove roles by ID
177
     *
178
     * @param string $id The role ID
179
     * @return \Cake\Http\Response|null
180
     */
181
    public function remove(string $id): ?Response
182
    {
183
        $this->getRequest()->allowMethod(['post']);
184
        try {
185
            $this->apiClient->delete(sprintf('%s/%s', $this->endpoint(), $id));
186
        } catch (BEditaClientException $e) {
187
            $this->log($e->getMessage(), 'error');
188
            $this->Flash->error($e->getMessage(), ['params' => $e]);
189
        }
190
191
        return $this->redirect(['_name' => sprintf('admin:list:%s', $this->resourceType)]);
192
    }
193
194
    /**
195
     * Get endpoint by resource type and endpoint.
196
     * Roles => /roles
197
     * Other => /admin/:endpoint
198
     *
199
     * @return string
200
     */
201
    protected function endpoint(): string
202
    {
203
        if ($this->resourceType === 'roles') {
204
            return $this->endpoint;
205
        }
206
207
        return sprintf('%s/%s', $this->endpoint, $this->resourceType);
208
    }
209
210
    /**
211
     * Get all results iterating over pagination.
212
     *
213
     * @return array
214
     */
215
    protected function loadData(): array
216
    {
217
        $query = $this->getRequest()->getQueryParams();
218
        $resourceEndpoint = sprintf('%s/%s', $this->endpoint, $this->resourceType);
219
        $endpoint = $this->resourceType === 'roles' ? 'roles' : $resourceEndpoint;
220
        $resultResponse = ['data' => []];
221
        $pageCount = $page = 1;
222
        $total = 0;
223
        $limit = 500;
224
        while ($limit > $total && $page <= $pageCount) {
225
            $response = (array)$this->apiClient->get($endpoint, compact('page') + ['page_size' => 100]);
226
            $response = ApiTools::cleanResponse($response);
227
            $resultResponse['data'] = array_merge(
228
                $resultResponse['data'],
229
                (array)Hash::get($response, 'data'),
230
            );
231
            $resultResponse['meta'] = Hash::get($response, 'meta');
232
            $pageCount = (int)Hash::get($response, 'meta.pagination.page_count');
233
            $count = (int)Hash::get($response, 'meta.pagination.page_items');
234
            $total += $count;
235
            $page++;
236
        }
237
238
        return $resultResponse;
239
    }
240
241
    /**
242
     * Prepare body for request
243
     *
244
     * @param array $data The data
245
     * @return array
246
     */
247
    protected function prepareBody(array $data): array
248
    {
249
        foreach ($this->propertiesForceJson as $property) {
250
            $data[$property] = json_decode((string)Hash::get($data, $property), true);
251
        }
252
        $attributes = array_filter($data, function ($key) {
253
            return $key !== 'id';
254
        }, ARRAY_FILTER_USE_KEY);
255
256
        return [
257
            'data' => [
258
                'type' => $this->resourceType,
259
                'attributes' => $attributes,
260
            ],
261
        ];
262
    }
263
}
264