Passed
Pull Request — master (#865)
by Stefano
03:23
created

TranslationsController   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 99
dl 0
loc 217
rs 10
c 0
b 0
f 0
wmc 22

7 Methods

Rating   Name   Duplication   Size   Complexity  
A typeFromUrl() 0 8 2
A add() 0 25 2
A initialize() 0 5 1
A setupJsonKeys() 0 9 1
A delete() 0 30 5
B edit() 0 36 7
A save() 0 43 4
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 BEdita\I18n\Core\I18nTrait;
16
use BEdita\SDK\BEditaClientException;
17
use Cake\Http\Exception\BadRequestException;
18
use Cake\Http\Exception\NotFoundException;
19
use Cake\Http\Response;
20
use Cake\Utility\Hash;
21
use Psr\Log\LogLevel;
22
23
/**
24
 * Translations controller: create, edit, remove translations
25
 *
26
 * @property \App\Controller\Component\HistoryComponent $History
27
 * @property \App\Controller\Component\ObjectsEditorsComponent $ObjectsEditors
28
 * @property \App\Controller\Component\PropertiesComponent $Properties
29
 * @property \App\Controller\Component\ProjectConfigurationComponent $ProjectConfiguration
30
 * @property \App\Controller\Component\QueryComponent $Query
31
 * @property \App\Controller\Component\ThumbsComponent $Thumbs
32
 * @property \BEdita\WebTools\Controller\Component\ApiFormatterComponent $ApiFormatter
33
 */
34
class TranslationsController extends ModulesController
35
{
36
    use I18nTrait;
37
38
    /**
39
     * @inheritDoc
40
     */
41
    public function initialize(): void
42
    {
43
        $this->setRequest($this->getRequest()->withParam('object_type', 'translations'));
44
        parent::initialize();
45
        $this->Query->setConfig('include', 'object');
46
    }
47
48
    /**
49
     * Display data to add a translation.
50
     *
51
     * @param string|int $id Object ID.
52
     * @return \Cake\Http\Response|null
53
     */
54
    public function add($id): ?Response
55
    {
56
        $this->getRequest()->allowMethod(['get']);
57
        $this->objectType = $this->typeFromUrl();
58
59
        try {
60
            $response = $this->apiClient->getObject($id, $this->objectType);
61
        } catch (BEditaClientException $e) {
62
            // Error! Back to index.
63
            $this->log($e->getMessage(), LogLevel::ERROR);
64
            $this->Flash->error($e->getMessage(), ['params' => $e]);
65
66
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]);
67
        }
68
        $this->ProjectConfiguration->read();
69
70
        $this->set('schema', $this->Schema->getSchema($this->objectType));
71
72
        $object = Hash::extract($response, 'data');
73
        $this->set('translation', []);
74
        $this->set('object', $object);
75
        // Use first available language as default new language
76
        $this->set('newLang', array_key_first($this->getLanguages()));
77
78
        return null;
79
    }
80
81
    /**
82
     * View single translation.
83
     *
84
     * @param string|int $id Object ID.
85
     * @param string $lang The lang code.
86
     * @return \Cake\Http\Response|null
87
     */
88
    public function edit($id, $lang): ?Response
89
    {
90
        $this->getRequest()->allowMethod(['get']);
91
        $this->objectType = $this->typeFromUrl();
92
93
        $translation = [];
94
        try {
95
            $response = $this->apiClient->getObject($id, $this->objectType, compact('lang'));
96
97
            // verify that exists a translation in lang $lang inside include
98
            if (!empty($response['included'])) {
99
                foreach ($response['included'] as $included) {
100
                    if ($included['type'] === 'translations' && $included['attributes']['lang'] == $lang) {
101
                        $translation = $included;
102
                    }
103
                }
104
            }
105
            if (empty($translation)) {
106
                throw new NotFoundException(sprintf('Translation not found per %s %s and lang %s', $this->objectType, $id, $lang));
107
            }
108
        } catch (\Exception $e) {
109
            // Error! Back to index.
110
            $this->log($e->getMessage(), LogLevel::ERROR);
111
            $this->Flash->error($e->getMessage(), ['params' => $e]);
112
113
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]);
114
        }
115
        $this->ProjectConfiguration->read();
116
117
        $this->set('schema', $this->Schema->getSchema($this->objectType));
118
119
        $object = Hash::extract($response, 'data');
120
        $this->set('translation', $translation);
121
        $this->set('object', $object);
122
123
        return null;
124
    }
125
126
    /**
127
     * Create or edit single translation.
128
     *
129
     * @return void
130
     */
131
    public function save(): void
132
    {
133
        $this->request->allowMethod(['post']);
134
        $this->objectType = $this->typeFromUrl();
135
        $this->setupJsonKeys();
136
        $requestData = $this->prepareRequest($this->objectType);
137
        $objectId = $requestData['object_id'];
138
        if (!empty($requestData['id'])) {
139
            unset($requestData['object_id']);
140
        }
141
        $lang = $requestData['lang'];
142
        try {
143
            $this->apiClient->save('translations', $requestData);
144
        } catch (BEditaClientException $e) {
145
            // Error! Back to object view or index.
146
            $this->log($e->getMessage(), LogLevel::ERROR);
147
            $this->Flash->error($e->getMessage(), ['params' => $e]);
148
149
            if ($this->getRequest()->getData('id')) {
150
                $this->redirect([
151
                    '_name' => 'translations:edit',
152
                    'object_type' => $this->objectType,
153
                    'id' => $objectId,
154
                    'lang' => $lang,
155
                ]);
156
157
                return;
158
            }
159
160
            $this->redirect([
161
                '_name' => 'translations:add',
162
                'object_type' => $this->objectType,
163
                'id' => $objectId,
164
            ]);
165
166
            return;
167
        }
168
169
        $this->redirect([
170
            '_name' => 'translations:edit',
171
            'object_type' => $this->objectType,
172
            'id' => $objectId,
173
            'lang' => $lang,
174
        ]);
175
    }
176
177
    /**
178
     * Setup internal `_jsonKeys`, add `translated_fields.` prefix
179
     * to create the correct path to the single translated field.
180
     *
181
     * @return void
182
     */
183
    protected function setupJsonKeys(): void
184
    {
185
        $jsonKeys = (array)array_map(
186
            function ($v) {
187
                return sprintf('translated_fields.%s', $v);
188
            },
189
            explode(',', (string)$this->request->getData('_jsonKeys'))
190
        );
191
        $this->request = $this->request->withData('_jsonKeys', implode(',', $jsonKeys));
192
    }
193
194
    /**
195
     * Delete single translation.
196
     * Expected request:
197
     *     data: [
198
     *         {
199
     *             id: <translation id>,
200
     *             object_id: <translated object id>,
201
     *         }
202
     *     ]
203
     *
204
     * @return \Cake\Http\Response
205
     */
206
    public function delete(): Response
207
    {
208
        $this->getRequest()->allowMethod(['post']);
209
        $this->objectType = $this->typeFromUrl();
210
        $requestData = $this->getRequest()->getData();
211
        $translation = [];
212
        try {
213
            if (empty($requestData[0])) {
214
                throw new BadRequestException(__('Empty request data'));
215
            }
216
            $translation = $requestData[0];
217
            if (empty($translation['id'])) {
218
                throw new BadRequestException(__('Empty translation "id"'));
219
            }
220
            if (empty($translation['object_id'])) {
221
                throw new BadRequestException(__('Empty translation "object_id"'));
222
            }
223
            // remove completely the translation
224
            $this->apiClient->delete(sprintf('/translations/%s', $translation['id']));
225
        } catch (BEditaClientException $e) {
226
            $this->log($e->getMessage(), LogLevel::ERROR);
227
            $this->Flash->error($e->getMessage(), ['params' => $e]);
228
229
            // redir to main object view
230
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $translation['object_id']]);
231
        }
232
        $this->Flash->success(__('Translation(s) deleted'));
233
234
        // redir to main object view
235
        return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $translation['object_id']]);
236
    }
237
238
    /**
239
     * Get type from url, if objectType is 'translations'
240
     *
241
     * @return string
242
     */
243
    protected function typeFromUrl(): string
244
    {
245
        if ($this->objectType !== 'translations') {
246
            return $this->objectType;
247
        }
248
        $here = (string)$this->getRequest()->getAttribute('here');
249
250
        return substr($here, 1, strpos(substr($here, 1), '/'));
251
    }
252
}
253