TranslationsController::initialize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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