Passed
Pull Request — master (#901)
by Stefano
02:39
created

TranslationsController::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 3
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
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
     * @inheritDoc
50
     */
51
    public function index(): ?Response
52
    {
53
        parent::index();
54
        $this->set('types', ['right' => $this->Modules->objectTypes(false)]);
55
56
        return null;
57
    }
58
59
    /**
60
     * Display data to add a translation.
61
     *
62
     * @param string|int $id Object ID.
63
     * @return \Cake\Http\Response|null
64
     */
65
    public function add($id): ?Response
66
    {
67
        $this->getRequest()->allowMethod(['get']);
68
        $this->objectType = $this->typeFromUrl();
69
70
        try {
71
            $response = $this->apiClient->getObject($id, $this->objectType);
72
        } catch (BEditaClientException $e) {
73
            // Error! Back to index.
74
            $this->log($e->getMessage(), LogLevel::ERROR);
75
            $this->Flash->error($e->getMessage(), ['params' => $e]);
76
77
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]);
78
        }
79
        $this->ProjectConfiguration->read();
80
81
        $this->set('schema', $this->Schema->getSchema($this->objectType));
82
83
        $object = Hash::extract($response, 'data');
84
        $this->set('translation', []);
85
        $this->set('object', $object);
86
        // Use first available language as default new language
87
        $this->set('newLang', array_key_first($this->getLanguages()));
88
89
        return null;
90
    }
91
92
    /**
93
     * View single translation.
94
     *
95
     * @param string|int $id Object ID.
96
     * @param string $lang The lang code.
97
     * @return \Cake\Http\Response|null
98
     */
99
    public function edit($id, $lang): ?Response
100
    {
101
        $this->getRequest()->allowMethod(['get']);
102
        $this->objectType = $this->typeFromUrl();
103
104
        $translation = [];
105
        try {
106
            $response = $this->apiClient->getObject($id, $this->objectType, compact('lang'));
107
108
            // verify that exists a translation in lang $lang inside include
109
            if (!empty($response['included'])) {
110
                foreach ($response['included'] as $included) {
111
                    if ($included['type'] === 'translations' && $included['attributes']['lang'] == $lang) {
112
                        $translation = $included;
113
                    }
114
                }
115
            }
116
            if (empty($translation)) {
117
                throw new NotFoundException(sprintf('Translation not found per %s %s and lang %s', $this->objectType, $id, $lang));
118
            }
119
        } catch (\Exception $e) {
120
            // Error! Back to index.
121
            $this->log($e->getMessage(), LogLevel::ERROR);
122
            $this->Flash->error($e->getMessage(), ['params' => $e]);
123
124
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]);
125
        }
126
        $this->ProjectConfiguration->read();
127
128
        $this->set('schema', $this->Schema->getSchema($this->objectType));
129
130
        $object = Hash::extract($response, 'data');
131
        $this->set('translation', $translation);
132
        $this->set('object', $object);
133
134
        return null;
135
    }
136
137
    /**
138
     * Create or edit single translation.
139
     *
140
     * @return void
141
     */
142
    public function save(): void
143
    {
144
        $this->request->allowMethod(['post']);
145
        $this->objectType = $this->typeFromUrl();
146
        $this->setupJsonKeys();
147
        $requestData = $this->prepareRequest($this->objectType);
148
        $objectId = $requestData['object_id'];
149
        if (!empty($requestData['id'])) {
150
            unset($requestData['object_id']);
151
        }
152
        $lang = $requestData['lang'];
153
        try {
154
            $this->apiClient->save('translations', $requestData);
155
        } catch (BEditaClientException $e) {
156
            // Error! Back to object view or index.
157
            $this->log($e->getMessage(), LogLevel::ERROR);
158
            $this->Flash->error($e->getMessage(), ['params' => $e]);
159
160
            if ($this->getRequest()->getData('id')) {
161
                $this->redirect([
162
                    '_name' => 'translations:edit',
163
                    'object_type' => $this->objectType,
164
                    'id' => $objectId,
165
                    'lang' => $lang,
166
                ]);
167
168
                return;
169
            }
170
171
            $this->redirect([
172
                '_name' => 'translations:add',
173
                'object_type' => $this->objectType,
174
                'id' => $objectId,
175
            ]);
176
177
            return;
178
        }
179
180
        $this->redirect([
181
            '_name' => 'translations:edit',
182
            'object_type' => $this->objectType,
183
            'id' => $objectId,
184
            'lang' => $lang,
185
        ]);
186
    }
187
188
    /**
189
     * Setup internal `_jsonKeys`, add `translated_fields.` prefix
190
     * to create the correct path to the single translated field.
191
     *
192
     * @return void
193
     */
194
    protected function setupJsonKeys(): void
195
    {
196
        $jsonKeys = (array)array_map(
197
            function ($v) {
198
                return sprintf('translated_fields.%s', $v);
199
            },
200
            explode(',', (string)$this->request->getData('_jsonKeys'))
201
        );
202
        $this->request = $this->request->withData('_jsonKeys', implode(',', $jsonKeys));
203
    }
204
205
    /**
206
     * Delete single translation.
207
     * Expected request:
208
     *     data: [
209
     *         {
210
     *             id: <translation id>,
211
     *             object_id: <translated object id>,
212
     *         }
213
     *     ]
214
     *
215
     * @return \Cake\Http\Response
216
     */
217
    public function delete(): Response
218
    {
219
        $this->getRequest()->allowMethod(['post']);
220
        $this->objectType = $this->typeFromUrl();
221
        $requestData = $this->getRequest()->getData();
222
        $translation = [];
223
        try {
224
            if (empty($requestData[0])) {
225
                throw new BadRequestException(__('Empty request data'));
226
            }
227
            $translation = $requestData[0];
228
            if (empty($translation['id'])) {
229
                throw new BadRequestException(__('Empty translation "id"'));
230
            }
231
            if (empty($translation['object_id'])) {
232
                throw new BadRequestException(__('Empty translation "object_id"'));
233
            }
234
            // remove completely the translation
235
            $this->apiClient->delete(sprintf('/translations/%s', $translation['id']));
236
        } catch (BEditaClientException $e) {
237
            $this->log($e->getMessage(), LogLevel::ERROR);
238
            $this->Flash->error($e->getMessage(), ['params' => $e]);
239
240
            // redir to main object view
241
            return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $translation['object_id']]);
242
        }
243
        $this->Flash->success(__('Translation(s) deleted'));
244
245
        // redir to main object view
246
        return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $translation['object_id']]);
247
    }
248
249
    /**
250
     * Get type from url, if objectType is 'translations'
251
     *
252
     * @return string
253
     */
254
    protected function typeFromUrl(): string
255
    {
256
        if ($this->objectType !== 'translations') {
257
            return $this->objectType;
258
        }
259
        $here = (string)$this->getRequest()->getAttribute('here');
260
261
        return substr($here, 1, strpos(substr($here, 1), '/'));
262
    }
263
}
264