ImportController::initialize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
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 App\Utility\SchemaTrait;
16
use BEdita\SDK\BEditaClientException;
17
use Cake\Core\Configure;
18
use Cake\Event\EventInterface;
19
use Cake\Http\Exception\BadRequestException;
20
use Cake\Http\Response;
21
use Cake\Utility\Hash;
22
use Exception;
23
24
/**
25
 * Import controller: upload and load using filters
26
 */
27
class ImportController extends AppController
28
{
29
    use SchemaTrait;
30
31
    /**
32
     * @inheritDoc
33
     */
34
    public function initialize(): void
35
    {
36
        parent::initialize();
37
38
        $this->Security->setConfig('unlockedActions', ['file']);
39
    }
40
41
    /**
42
     * List of asyn service names to lookup
43
     *
44
     * @var array
45
     */
46
    protected $services = [];
47
48
    /**
49
     * @inheritDoc
50
     */
51
    public function beforeRender(EventInterface $event): ?Response
52
    {
53
        $this->set('moduleLink', ['_name' => 'import:index']);
54
55
        return parent::beforeRender($event);
56
    }
57
58
    /**
59
     * Display import page.
60
     *
61
     * @return void
62
     */
63
    public function index(): void
64
    {
65
        $result = $this->getRequest()->getSession()->consume('Import.result');
66
        $this->set(compact('result'));
67
        $this->loadFilters();
68
        /** @var \Authentication\Identity $user */
69
        $user = $this->Authentication->getIdentity();
70
        $this->set(
71
            'jobsAllow',
72
            (array)Hash::extract($this->getMeta($user), 'resources./async_jobs.hints.allow')
73
        );
74
    }
75
76
    /**
77
     * Get jobs rendering as json.
78
     *
79
     * @return void
80
     */
81
    public function jobs(): void
82
    {
83
        $this->viewBuilder()->setClassName('Json');
84
        $this->getRequest()->allowMethod('get');
85
        $this->loadFilters();
86
        $this->loadAsyncJobs();
87
        $this->setSerialize(['jobs']);
88
    }
89
90
    /**
91
     * Import data by filter/file.
92
     *
93
     * @return \Cake\Http\Response|null the Response.
94
     */
95
    public function file(): ?Response
96
    {
97
        try {
98
            $filter = $this->getRequest()->getData('filter');
99
            if (empty($filter)) {
100
                throw new BadRequestException(__('Import filter not selected'));
101
            }
102
            $importFilter = new $filter($this->apiClient);
103
104
            // see http://php.net/manual/en/features.file-upload.errors.php
105
            /** @var \Laminas\Diactoros\UploadedFile $file */
106
            $file = $this->getRequest()->getData('file');
107
            $fileError = $file->getError();
108
            if ($fileError > UPLOAD_ERR_OK) {
109
                throw new BadRequestException($this->uploadErrorMessage($fileError));
110
            }
111
112
            $result = $importFilter->import(
113
                $file->getClientFileName(),
114
                $file->getStream()->getMetadata('uri'),
115
                $this->getRequest()->getData('filter_options')
116
            );
117
            $this->getRequest()->getSession()->write(['Import.result' => $result]);
118
        } catch (Exception $e) {
119
            $this->Flash->error($e->getMessage(), ['params' => $e]);
120
        }
121
122
        return $this->redirect(['_name' => 'import:index']);
123
    }
124
125
    /**
126
     * Return a meaningful upload error message
127
     * see http://php.net/manual/en/features.file-upload.errors.php
128
     *
129
     * @param int $code Upload error code
130
     * @return string
131
     */
132
    protected function uploadErrorMessage(int $code): string
133
    {
134
        $errors = [
135
            UPLOAD_ERR_INI_SIZE => __('File is too big, max allowed size is {0}', ini_get('upload_max_filesize')),
136
            UPLOAD_ERR_FORM_SIZE => __('File is too big, form MAX_FILE_SIZE exceeded'),
137
            UPLOAD_ERR_PARTIAL => __('File only partially uploaded'),
138
            UPLOAD_ERR_NO_FILE => __('Missing import file'),
139
            UPLOAD_ERR_NO_TMP_DIR => __('Temporary folder missing'),
140
            UPLOAD_ERR_CANT_WRITE => __('Failed to write file to disk'),
141
            UPLOAD_ERR_EXTENSION => __('An extension stopped the file upload'),
142
        ];
143
144
        return (string)Hash::get($errors, (string)$code, __('Unknown upload error'));
145
    }
146
147
    /**
148
     * Load filters for view
149
     *
150
     * @return void
151
     */
152
    private function loadFilters(): void
153
    {
154
        $filters = [];
155
        $importFilters = Configure::read('Filters.import', []);
156
        foreach ($importFilters as $filter) {
157
            $accept = (array)Hash::get($filter, 'accept', ['text/xml', 'text/csv']);
158
            $name = (string)Hash::get($filter, 'name');
159
            $value = (string)Hash::get($filter, 'class');
160
            $text = (string)Hash::get($filter, 'label');
161
            $options = (array)Hash::get($filter, 'options');
162
            $filters[] = compact('accept', 'name', 'value', 'text', 'options');
163
            $this->updateServiceList($value);
164
        }
165
        $this->set('filters', $filters);
166
        $this->set('services', $this->services);
167
        $this->loadAsyncJobs();
168
    }
169
170
    /**
171
     * Update services list to lookup
172
     *
173
     * @param string $filterClass Filter class
174
     * @return void
175
     */
176
    protected function updateServiceList($filterClass): void
177
    {
178
        $service = call_user_func([$filterClass, 'getServiceName']);
179
        if (!empty($service) && !in_array($service, $this->services)) {
180
            $this->services[] = $service;
181
        }
182
    }
183
184
    /**
185
     * Load async jobs services to lookup
186
     *
187
     * @return void
188
     */
189
    protected function loadAsyncJobs(): void
190
    {
191
        if (empty($this->services)) {
192
            $this->set('jobs', []);
193
194
            return;
195
        }
196
        $query = [
197
            'sort' => '-created',
198
            'filter' => ['service' => implode(',', $this->services)],
199
        ];
200
        try {
201
            $response = $this->apiClient->get('/async_jobs', $query);
202
        } catch (BEditaClientException $e) {
203
            $this->log($e->getMessage(), 'error');
204
            $this->Flash->error($e->getMessage(), ['params' => $e]);
205
            $response = [];
206
        }
207
208
        $this->set('jobs', (array)Hash::get($response, 'data'));
209
    }
210
}
211