|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of Sulu. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) Sulu GmbH |
|
7
|
|
|
* |
|
8
|
|
|
* This source file is subject to the MIT license that is bundled |
|
9
|
|
|
* with this source code in the file LICENSE. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Sulu\Bundle\ArticleBundle\Controller; |
|
13
|
|
|
|
|
14
|
|
|
use FOS\RestBundle\Context\Context; |
|
15
|
|
|
use FOS\RestBundle\Controller\Annotations\Get; |
|
16
|
|
|
use FOS\RestBundle\Controller\Annotations\Post; |
|
17
|
|
|
use FOS\RestBundle\Routing\ClassResourceInterface; |
|
18
|
|
|
use FOS\RestBundle\View\ViewHandlerInterface; |
|
19
|
|
|
use ONGR\ElasticsearchBundle\Mapping\Caser; |
|
20
|
|
|
use ONGR\ElasticsearchBundle\Service\Manager; |
|
21
|
|
|
use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; |
|
22
|
|
|
use ONGR\ElasticsearchDSL\Query\FullText\MatchPhrasePrefixQuery; |
|
23
|
|
|
use ONGR\ElasticsearchDSL\Query\FullText\MatchQuery; |
|
24
|
|
|
use ONGR\ElasticsearchDSL\Query\MatchAllQuery; |
|
25
|
|
|
use ONGR\ElasticsearchDSL\Query\TermLevel\IdsQuery; |
|
26
|
|
|
use ONGR\ElasticsearchDSL\Query\TermLevel\RangeQuery; |
|
27
|
|
|
use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery; |
|
28
|
|
|
use ONGR\ElasticsearchDSL\Sort\FieldSort; |
|
29
|
|
|
use Sulu\Bundle\ArticleBundle\Admin\ArticleAdmin; |
|
30
|
|
|
use Sulu\Bundle\ArticleBundle\Document\ArticleDocument; |
|
31
|
|
|
use Sulu\Bundle\ArticleBundle\Document\Index\DocumentFactoryInterface; |
|
32
|
|
|
use Sulu\Bundle\ArticleBundle\ListBuilder\ElasticSearchFieldDescriptor; |
|
33
|
|
|
use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait; |
|
34
|
|
|
use Sulu\Component\Content\Form\Exception\InvalidFormException; |
|
35
|
|
|
use Sulu\Component\Content\Mapper\ContentMapperInterface; |
|
36
|
|
|
use Sulu\Component\DocumentManager\DocumentManagerInterface; |
|
37
|
|
|
use Sulu\Component\DocumentManager\MetadataFactoryInterface; |
|
38
|
|
|
use Sulu\Component\Hash\RequestHashCheckerInterface; |
|
39
|
|
|
use Sulu\Component\Rest\AbstractRestController; |
|
40
|
|
|
use Sulu\Component\Rest\Exception\MissingParameterException; |
|
41
|
|
|
use Sulu\Component\Rest\Exception\RestException; |
|
42
|
|
|
use Sulu\Component\Rest\ListBuilder\FieldDescriptorInterface; |
|
43
|
|
|
use Sulu\Component\Rest\ListBuilder\ListRepresentation; |
|
44
|
|
|
use Sulu\Component\Rest\ListBuilder\ListRestHelperInterface; |
|
45
|
|
|
use Sulu\Component\Rest\RequestParametersTrait; |
|
46
|
|
|
use Sulu\Component\Security\Authorization\PermissionTypes; |
|
47
|
|
|
use Sulu\Component\Security\Authorization\SecurityCheckerInterface; |
|
48
|
|
|
use Sulu\Component\Security\Authorization\SecurityCondition; |
|
49
|
|
|
use Sulu\Component\Security\SecuredControllerInterface; |
|
50
|
|
|
use Symfony\Component\Form\FormFactoryInterface; |
|
51
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
52
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
53
|
|
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* Provides API for articles. |
|
57
|
|
|
*/ |
|
58
|
|
|
class ArticleController extends AbstractRestController implements ClassResourceInterface, SecuredControllerInterface |
|
59
|
|
|
{ |
|
60
|
|
|
const DOCUMENT_TYPE = 'article'; |
|
61
|
|
|
|
|
62
|
|
|
use RequestParametersTrait; |
|
63
|
|
|
use ArticleViewDocumentIdTrait; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @var DocumentManagerInterface |
|
67
|
|
|
*/ |
|
68
|
|
|
private $documentManager; |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* @var ContentMapperInterface |
|
72
|
|
|
*/ |
|
73
|
|
|
private $contentMapper; |
|
74
|
|
|
|
|
75
|
|
|
/** |
|
76
|
|
|
* @var MetadataFactoryInterface |
|
77
|
|
|
*/ |
|
78
|
|
|
private $metadataFactory; |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* @var ListRestHelperInterface |
|
82
|
|
|
*/ |
|
83
|
|
|
private $restHelper; |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* @var Manager |
|
87
|
|
|
*/ |
|
88
|
|
|
private $manager; |
|
89
|
|
|
|
|
90
|
|
|
/** |
|
91
|
|
|
* @var DocumentFactoryInterface |
|
92
|
|
|
*/ |
|
93
|
|
|
private $documentFactory; |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* @var FormFactoryInterface |
|
97
|
|
|
*/ |
|
98
|
12 |
|
private $formFactory; |
|
99
|
|
|
|
|
100
|
12 |
|
/** |
|
101
|
|
|
* @var RequestHashCheckerInterface |
|
102
|
12 |
|
*/ |
|
103
|
|
|
private $requestHashChecker; |
|
104
|
|
|
|
|
105
|
12 |
|
/** |
|
106
|
12 |
|
* @var SecurityCheckerInterface |
|
107
|
12 |
|
*/ |
|
108
|
|
|
private $securityChecker; |
|
109
|
12 |
|
|
|
110
|
12 |
|
/** |
|
111
|
|
|
* @var bool |
|
112
|
12 |
|
*/ |
|
113
|
12 |
|
private $displayTabAll; |
|
114
|
|
|
|
|
115
|
|
|
public function __construct( |
|
116
|
12 |
|
ViewHandlerInterface $viewHandler, |
|
117
|
1 |
|
DocumentManagerInterface $documentManager, |
|
118
|
1 |
|
ContentMapperInterface $contentMapper, |
|
119
|
|
|
MetadataFactoryInterface $metadataFactory, |
|
120
|
|
|
ListRestHelperInterface $restHelper, |
|
121
|
12 |
|
Manager $manager, |
|
122
|
12 |
|
DocumentFactoryInterface $documentFactory, |
|
123
|
|
|
FormFactoryInterface $formFactory, |
|
124
|
2 |
|
RequestHashCheckerInterface $requestHashChecker, |
|
125
|
|
|
SecurityCheckerInterface $securityChecker, |
|
126
|
|
|
bool $displayTabAll, |
|
127
|
12 |
|
?TokenStorageInterface $tokenStorage = null |
|
128
|
10 |
|
) { |
|
129
|
|
|
parent::__construct($viewHandler, $tokenStorage); |
|
130
|
|
|
|
|
131
|
12 |
|
$this->documentManager = $documentManager; |
|
132
|
1 |
|
$this->contentMapper = $contentMapper; |
|
133
|
1 |
|
$this->metadataFactory = $metadataFactory; |
|
134
|
1 |
|
$this->restHelper = $restHelper; |
|
135
|
1 |
|
$this->manager = $manager; |
|
136
|
1 |
|
$this->documentFactory = $documentFactory; |
|
137
|
|
|
$this->formFactory = $formFactory; |
|
138
|
|
|
$this->requestHashChecker = $requestHashChecker; |
|
139
|
12 |
|
$this->securityChecker = $securityChecker; |
|
140
|
1 |
|
$this->displayTabAll = $displayTabAll; |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
12 |
|
/** |
|
144
|
|
|
* Create field-descriptor array. |
|
145
|
|
|
* |
|
146
|
|
|
* @return ElasticSearchFieldDescriptor[] |
|
147
|
12 |
|
*/ |
|
148
|
|
|
protected function getFieldDescriptors() |
|
149
|
12 |
|
{ |
|
150
|
1 |
|
return [ |
|
151
|
1 |
|
'uuid' => ElasticSearchFieldDescriptor::create('id', 'public.id') |
|
152
|
|
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
153
|
|
|
->build(), |
|
154
|
|
|
'typeTranslation' => ElasticSearchFieldDescriptor::create('typeTranslation', 'sulu_article.list.type') |
|
155
|
12 |
|
->setSortField('typeTranslation.raw') |
|
156
|
12 |
|
->setVisibility( |
|
157
|
|
|
$this->displayTabAll ? |
|
158
|
12 |
|
FieldDescriptorInterface::VISIBILITY_YES : |
|
159
|
12 |
|
FieldDescriptorInterface::VISIBILITY_NEVER |
|
160
|
10 |
|
) |
|
161
|
1 |
|
->build(), |
|
162
|
|
|
'title' => ElasticSearchFieldDescriptor::create('title', 'public.title') |
|
163
|
12 |
|
->setSortField('title.raw') |
|
164
|
|
|
->build(), |
|
165
|
|
|
'creatorFullName' => ElasticSearchFieldDescriptor::create('creatorFullName', 'sulu_article.list.creator') |
|
166
|
|
|
->setSortField('creatorFullName.raw') |
|
167
|
12 |
|
->build(), |
|
168
|
1 |
|
'changerFullName' => ElasticSearchFieldDescriptor::create('changerFullName', 'sulu_article.list.changer') |
|
169
|
1 |
|
->setSortField('changerFullName.raw') |
|
170
|
|
|
->build(), |
|
171
|
|
|
'authorFullName' => ElasticSearchFieldDescriptor::create('authorFullName', 'sulu_article.author') |
|
172
|
12 |
|
->setSortField('authorFullName.raw') |
|
173
|
12 |
|
->build(), |
|
174
|
12 |
|
'created' => ElasticSearchFieldDescriptor::create('created', 'public.created') |
|
175
|
12 |
|
->setSortField('authored') |
|
176
|
12 |
|
->setType('datetime') |
|
177
|
12 |
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
178
|
12 |
|
->build(), |
|
179
|
12 |
|
'changed' => ElasticSearchFieldDescriptor::create('changed', 'public.changed') |
|
180
|
12 |
|
->setSortField('authored') |
|
181
|
12 |
|
->setType('datetime') |
|
182
|
|
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
183
|
|
|
->build(), |
|
184
|
|
|
'authored' => ElasticSearchFieldDescriptor::create('authored', 'sulu_article.authored') |
|
185
|
|
|
->setSortField('authored') |
|
186
|
|
|
->setType('datetime') |
|
187
|
|
|
->build(), |
|
188
|
|
|
'localizationState' => ElasticSearchFieldDescriptor::create('localizationState') |
|
189
|
|
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
190
|
|
|
->build(), |
|
191
|
|
|
'published' => ElasticSearchFieldDescriptor::create('published') |
|
192
|
|
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
193
|
|
|
->build(), |
|
194
|
|
|
'publishedState' => ElasticSearchFieldDescriptor::create('publishedState') |
|
195
|
9 |
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
196
|
|
|
->build(), |
|
197
|
9 |
|
'routePath' => ElasticSearchFieldDescriptor::create('routePath') |
|
198
|
9 |
|
->setVisibility(FieldDescriptorInterface::VISIBILITY_NO) |
|
199
|
9 |
|
->build(), |
|
200
|
9 |
|
]; |
|
201
|
|
|
} |
|
202
|
9 |
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* Returns list of articles. |
|
205
|
|
|
*/ |
|
206
|
|
|
public function cgetAction(Request $request): Response |
|
207
|
9 |
|
{ |
|
208
|
9 |
|
$locale = $this->getRequestParameter($request, 'locale', true); |
|
209
|
9 |
|
|
|
210
|
9 |
|
$repository = $this->manager->getRepository($this->documentFactory->getClass('article')); |
|
211
|
9 |
|
$search = $repository->createSearch(); |
|
212
|
|
|
|
|
213
|
|
|
$limit = (int) $this->restHelper->getLimit(); |
|
214
|
|
|
$page = (int) $this->restHelper->getPage(); |
|
215
|
|
|
|
|
216
|
|
|
if (null !== $locale) { |
|
217
|
|
|
$search->addQuery(new TermQuery('locale', $locale)); |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
if (count($ids = array_filter(explode(',', $request->get('ids', ''))))) { |
|
221
|
|
|
$search->addQuery(new IdsQuery($this->getViewDocumentIds($ids, $locale))); |
|
222
|
|
|
$limit = count($ids); |
|
223
|
51 |
|
} |
|
224
|
|
|
|
|
225
|
51 |
|
$searchFields = $this->restHelper->getSearchFields(); |
|
226
|
51 |
|
if (0 === count($searchFields)) { |
|
227
|
51 |
|
$searchFields = ['title']; |
|
228
|
51 |
|
} |
|
229
|
|
|
|
|
230
|
51 |
|
$searchPattern = $this->restHelper->getSearchPattern(); |
|
231
|
51 |
|
if (!empty($searchPattern)) { |
|
232
|
51 |
|
$boolQuery = new BoolQuery(); |
|
233
|
|
|
foreach ($searchFields as $searchField) { |
|
234
|
51 |
|
$boolQuery->add(new MatchPhrasePrefixQuery($searchField, $searchPattern), BoolQuery::SHOULD); |
|
235
|
51 |
|
} |
|
236
|
51 |
|
$search->addQuery($boolQuery); |
|
237
|
51 |
|
} |
|
238
|
51 |
|
|
|
239
|
|
|
if (null !== ($typeString = $request->get('types'))) { |
|
240
|
|
|
$types = explode(',', $typeString); |
|
241
|
|
|
|
|
242
|
|
|
if (count($types) > 1) { |
|
243
|
|
|
$query = new BoolQuery(); |
|
244
|
|
|
|
|
245
|
|
|
foreach ($types as $type) { |
|
246
|
|
|
$query->add(new TermQuery('type', $type)); |
|
247
|
|
|
} |
|
248
|
|
|
} elseif ($types[0]) { |
|
249
|
|
|
$search->addQuery(new TermQuery('type', $types[0])); |
|
250
|
|
|
} |
|
251
|
9 |
|
} |
|
252
|
|
|
|
|
253
|
9 |
|
if ($contactId = $request->get('contactId')) { |
|
254
|
9 |
|
$boolQuery = new BoolQuery(); |
|
255
|
9 |
|
$boolQuery->add(new MatchQuery('changer_contact_id', $contactId), BoolQuery::SHOULD); |
|
256
|
|
|
$boolQuery->add(new MatchQuery('creator_contact_id', $contactId), BoolQuery::SHOULD); |
|
257
|
9 |
|
$boolQuery->add(new MatchQuery('author_id', $contactId), BoolQuery::SHOULD); |
|
258
|
9 |
|
$search->addQuery($boolQuery); |
|
259
|
9 |
|
} |
|
260
|
|
|
|
|
261
|
9 |
|
if ($categoryId = $request->get('categoryId')) { |
|
262
|
|
|
$search->addQuery(new TermQuery('excerpt.categories.id', $categoryId), BoolQuery::MUST); |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
if ($tagId = $request->get('tagId')) { |
|
266
|
9 |
|
$search->addQuery(new TermQuery('excerpt.tags.id', $tagId), BoolQuery::MUST); |
|
267
|
|
|
} |
|
268
|
9 |
|
|
|
269
|
9 |
|
if ($pageId = $request->get('pageId')) { |
|
270
|
9 |
|
$search->addQuery(new TermQuery('parent_page_uuid', $pageId), BoolQuery::MUST); |
|
271
|
|
|
} |
|
272
|
9 |
|
|
|
273
|
9 |
|
if ($workflowStage = $request->get('workflowStage')) { |
|
274
|
9 |
|
$search->addQuery(new TermQuery('published_state', 'published' === $workflowStage), BoolQuery::MUST); |
|
275
|
9 |
|
} |
|
276
|
9 |
|
|
|
277
|
|
|
if ($this->getBooleanRequestParameter($request, 'exclude-shadows', false, false)) { |
|
278
|
|
|
$search->addQuery(new TermQuery('localization_state.state', 'shadow'), BoolQuery::MUST_NOT); |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
if ($this->getBooleanRequestParameter($request, 'exclude-ghosts', false, false)) { |
|
282
|
|
|
$search->addQuery(new TermQuery('localization_state.state', 'ghost'), BoolQuery::MUST_NOT); |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
$authoredFrom = $request->get('authoredFrom'); |
|
286
|
|
|
$authoredTo = $request->get('authoredTo'); |
|
287
|
|
|
if ($authoredFrom || $authoredTo) { |
|
288
|
1 |
|
$search->addQuery($this->getRangeQuery('authored', $authoredFrom, $authoredTo), BoolQuery::MUST); |
|
289
|
|
|
} |
|
290
|
1 |
|
|
|
291
|
|
|
if (null === $search->getQueries()) { |
|
292
|
1 |
|
$search->addQuery(new MatchAllQuery()); |
|
293
|
1 |
|
} |
|
294
|
1 |
|
|
|
295
|
1 |
|
if (null !== $this->restHelper->getSortColumn() && |
|
296
|
1 |
|
$sortField = $this->getSortFieldName($this->restHelper->getSortColumn()) |
|
297
|
|
|
) { |
|
298
|
|
|
$search->addSort( |
|
299
|
1 |
|
new FieldSort($sortField, $this->restHelper->getSortOrder()) |
|
300
|
|
|
); |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
$fieldDescriptors = $this->getFieldDescriptors(); |
|
304
|
|
|
|
|
305
|
|
|
if ($limit) { |
|
306
|
|
|
$search->setSize($limit); |
|
307
|
|
|
$search->setFrom(($page - 1) * $limit); |
|
308
|
|
|
|
|
309
|
1 |
|
$fields = array_merge( |
|
310
|
|
|
$this->restHelper->getFields() ?: [], |
|
311
|
1 |
|
['id', 'localizationState', 'publishedState', 'published', 'title', 'routePath'] |
|
312
|
1 |
|
); |
|
313
|
1 |
|
$fieldDescriptors = array_filter( |
|
314
|
1 |
|
$fieldDescriptors, |
|
315
|
|
|
function(FieldDescriptorInterface $fieldDescriptor) use ($fields) { |
|
316
|
1 |
|
return in_array($fieldDescriptor->getName(), $fields); |
|
317
|
|
|
} |
|
318
|
|
|
); |
|
319
|
|
|
} else { |
|
320
|
|
|
$search->setSize(1000); |
|
321
|
|
|
$search->setScroll('1m'); |
|
322
|
|
|
} |
|
323
|
|
|
|
|
324
|
|
|
$searchResult = $repository->findRaw($search); |
|
325
|
|
|
$result = []; |
|
326
|
|
|
foreach ($searchResult as $document) { |
|
327
|
|
|
$documentData = $this->normalize($document['_source'], $fieldDescriptors); |
|
328
|
|
|
if (false !== ($index = array_search($documentData['id'], $ids))) { |
|
329
|
2 |
|
$result[$index] = $documentData; |
|
330
|
|
|
} else { |
|
331
|
|
|
$result[] = $documentData; |
|
332
|
2 |
|
} |
|
333
|
2 |
|
} |
|
334
|
|
|
|
|
335
|
|
|
if (count($ids)) { |
|
336
|
2 |
|
ksort($result); |
|
337
|
2 |
|
$result = array_values($result); |
|
338
|
2 |
|
} |
|
339
|
|
|
|
|
340
|
|
|
$count = $searchResult->count(); |
|
341
|
|
|
|
|
342
|
2 |
|
return $this->handleView( |
|
343
|
|
|
$this->view( |
|
344
|
|
|
new ListRepresentation( |
|
345
|
|
|
$result, |
|
346
|
|
|
'articles', |
|
347
|
|
|
'get_articles', |
|
348
|
|
|
$request->query->all(), |
|
349
|
2 |
|
$page, |
|
350
|
|
|
$limit, |
|
351
|
|
|
$count |
|
352
|
|
|
) |
|
353
|
|
|
) |
|
354
|
2 |
|
); |
|
355
|
1 |
|
} |
|
356
|
1 |
|
|
|
357
|
|
|
/** |
|
358
|
1 |
|
* @param FieldDescriptorInterface[] $fieldDescriptors |
|
359
|
1 |
|
*/ |
|
360
|
1 |
|
private function normalize(array $document, array $fieldDescriptors) |
|
361
|
1 |
|
{ |
|
362
|
1 |
|
$result = []; |
|
363
|
|
|
foreach ($fieldDescriptors as $fieldDescriptor) { |
|
364
|
|
|
$property = Caser::snake($fieldDescriptor->getName()); |
|
365
|
|
|
if ('id' === $property) { |
|
366
|
1 |
|
$property = 'uuid'; |
|
367
|
|
|
} |
|
368
|
1 |
|
|
|
369
|
1 |
|
$result[$fieldDescriptor->getName()] = array_key_exists($property, $document) ? $document[$property] : null; |
|
370
|
1 |
|
} |
|
371
|
|
|
|
|
372
|
|
|
return $result; |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
/** |
|
376
|
|
|
* Returns query to filter by given range. |
|
377
|
|
|
* |
|
378
|
1 |
|
* @param string $field |
|
379
|
1 |
|
* @param string $from |
|
380
|
1 |
|
* @param string $to |
|
381
|
1 |
|
* |
|
382
|
|
|
* @return RangeQuery |
|
383
|
1 |
|
*/ |
|
384
|
1 |
|
private function getRangeQuery($field, $from, $to) |
|
385
|
|
|
{ |
|
386
|
|
|
return new RangeQuery($field, array_filter(['gte' => $from, 'lte' => $to])); |
|
387
|
|
|
} |
|
388
|
|
|
|
|
389
|
|
|
/** |
|
390
|
2 |
|
* Returns single article. |
|
391
|
2 |
|
* |
|
392
|
2 |
|
* @Get(defaults={"id" = ""}) |
|
393
|
2 |
|
*/ |
|
394
|
2 |
|
public function getAction(Request $request, string $id): Response |
|
395
|
|
|
{ |
|
396
|
|
|
$locale = $this->getRequestParameter($request, 'locale', true); |
|
397
|
|
|
$document = $this->documentManager->find( |
|
398
|
|
|
$id, |
|
399
|
|
|
$locale |
|
400
|
2 |
|
); |
|
401
|
|
|
|
|
402
|
|
|
$context = new Context(); |
|
403
|
|
|
$context->setSerializeNull(true); |
|
404
|
|
|
$context->setGroups(['defaultPage', 'defaultArticle', 'smallArticlePage']); |
|
405
|
|
|
|
|
406
|
|
|
return $this->handleView( |
|
407
|
|
|
$this->view($document)->setContext($context) |
|
408
|
|
|
); |
|
409
|
1 |
|
} |
|
410
|
|
|
|
|
411
|
1 |
|
/** |
|
412
|
|
|
* Create article. |
|
413
|
1 |
|
*/ |
|
414
|
1 |
|
public function postAction(Request $request): Response |
|
415
|
1 |
|
{ |
|
416
|
|
|
$action = $request->get('action'); |
|
417
|
1 |
|
$document = $this->documentManager->create(self::DOCUMENT_TYPE); |
|
418
|
|
|
$locale = $this->getRequestParameter($request, 'locale', true); |
|
419
|
|
|
$data = $request->request->all(); |
|
420
|
|
|
|
|
421
|
|
|
$this->persistDocument($data, $document, $locale); |
|
422
|
52 |
|
$this->handleActionParameter($action, $document, $locale); |
|
423
|
|
|
$this->documentManager->flush(); |
|
424
|
52 |
|
|
|
425
|
|
|
$context = new Context(); |
|
426
|
|
|
$context->setSerializeNull(true); |
|
427
|
|
|
$context->setGroups(['defaultPage', 'defaultArticle', 'smallArticlePage']); |
|
428
|
|
|
|
|
429
|
|
|
return $this->handleView( |
|
430
|
|
|
$this->view($document)->setContext($context) |
|
431
|
|
|
); |
|
432
|
|
|
} |
|
433
|
|
|
|
|
434
|
|
|
/** |
|
435
|
|
|
* Update articles. |
|
436
|
|
|
*/ |
|
437
|
51 |
|
public function putAction(Request $request, string $id): Response |
|
438
|
|
|
{ |
|
439
|
51 |
|
$locale = $this->getRequestParameter($request, 'locale', true); |
|
440
|
51 |
|
$action = $request->get('action'); |
|
441
|
51 |
|
$data = $request->request->all(); |
|
442
|
|
|
|
|
443
|
|
|
$document = $this->documentManager->find( |
|
444
|
51 |
|
$id, |
|
445
|
|
|
$locale, |
|
446
|
|
|
[ |
|
447
|
51 |
|
'load_ghost_content' => false, |
|
448
|
|
|
'load_shadow_content' => false, |
|
449
|
51 |
|
] |
|
450
|
|
|
); |
|
451
|
|
|
|
|
452
|
|
|
$this->requestHashChecker->checkHash($request, $document, $document->getUuid()); |
|
453
|
51 |
|
|
|
454
|
|
|
$this->persistDocument($data, $document, $locale); |
|
455
|
|
|
$this->handleActionParameter($action, $document, $locale); |
|
456
|
|
|
$this->documentManager->flush(); |
|
457
|
51 |
|
|
|
458
|
51 |
|
$context = new Context(); |
|
459
|
51 |
|
$context->setSerializeNull(true); |
|
460
|
|
|
$context->setGroups(['defaultPage', 'defaultArticle', 'smallArticlePage']); |
|
461
|
51 |
|
|
|
462
|
|
|
return $this->handleView( |
|
463
|
|
|
$this->view($document)->setContext($context) |
|
464
|
|
|
); |
|
465
|
51 |
|
} |
|
466
|
|
|
|
|
467
|
|
|
/** |
|
468
|
|
|
* Deletes multiple documents. |
|
469
|
|
|
*/ |
|
470
|
|
|
public function cdeleteAction(Request $request): Response |
|
471
|
|
|
{ |
|
472
|
51 |
|
$ids = array_filter(explode(',', $request->get('ids', ''))); |
|
473
|
|
|
|
|
474
|
51 |
|
$documentManager = $this->documentManager; |
|
475
|
|
|
foreach ($ids as $id) { |
|
476
|
|
|
$document = $documentManager->find($id); |
|
477
|
|
|
$documentManager->remove($document); |
|
478
|
|
|
$documentManager->flush(); |
|
479
|
|
|
} |
|
480
|
1 |
|
|
|
481
|
|
|
return $this->handleView($this->view(null)); |
|
482
|
1 |
|
} |
|
483
|
|
|
|
|
484
|
|
|
/** |
|
485
|
|
|
* Deletes multiple documents. |
|
486
|
|
|
*/ |
|
487
|
|
|
public function deleteAction(string $id): Response |
|
488
|
|
|
{ |
|
489
|
|
|
$documentManager = $this->documentManager; |
|
490
|
|
|
$document = $documentManager->find($id); |
|
491
|
|
|
$documentManager->remove($document); |
|
492
|
51 |
|
$documentManager->flush(); |
|
493
|
|
|
|
|
494
|
|
|
return $this->handleView($this->view(null)); |
|
495
|
51 |
|
} |
|
496
|
17 |
|
|
|
497
|
17 |
|
/** |
|
498
|
|
|
* Trigger a action for given article specified over get-action parameter. |
|
499
|
51 |
|
* |
|
500
|
|
|
* @Post("/articles/{id}") |
|
501
|
|
|
*/ |
|
502
|
|
|
public function postTriggerAction(string $id, Request $request): Response |
|
503
|
|
|
{ |
|
504
|
|
|
// extract parameter |
|
505
|
|
|
$action = $this->getRequestParameter($request, 'action', true); |
|
506
|
|
|
$locale = $this->getRequestParameter($request, 'locale', true); |
|
507
|
|
|
|
|
508
|
1 |
|
// prepare vars |
|
509
|
|
|
$view = null; |
|
510
|
1 |
|
$data = null; |
|
511
|
1 |
|
$userId = $this->getUser()->getId(); |
|
512
|
1 |
|
|
|
513
|
1 |
|
try { |
|
514
|
|
|
switch ($action) { |
|
515
|
|
|
case 'unpublish': |
|
516
|
1 |
|
$document = $this->documentManager->find($id, $locale); |
|
517
|
|
|
$this->documentManager->unpublish($document, $locale); |
|
518
|
|
|
$this->documentManager->flush(); |
|
519
|
|
|
|
|
520
|
|
|
$data = $this->documentManager->find($id, $locale); |
|
521
|
|
|
|
|
522
|
|
|
break; |
|
523
|
|
|
case 'remove-draft': |
|
524
|
|
|
$data = $this->documentManager->find($id, $locale); |
|
525
|
|
|
$this->documentManager->removeDraft($data, $locale); |
|
526
|
|
|
$this->documentManager->flush(); |
|
527
|
|
|
|
|
528
|
|
|
break; |
|
529
|
|
|
case 'copy-locale': |
|
530
|
|
|
$destLocales = $this->getRequestParameter($request, 'dest', true); |
|
531
|
|
|
$destLocales = explode(',', $destLocales); |
|
532
|
|
|
|
|
533
|
|
|
foreach ($destLocales as $destLocale) { |
|
534
|
|
|
$this->securityChecker->checkPermission( |
|
535
|
|
|
new SecurityCondition($this->getSecurityContext(), $destLocale), |
|
536
|
|
|
PermissionTypes::EDIT |
|
537
|
|
|
); |
|
538
|
|
|
} |
|
539
|
|
|
|
|
540
|
|
|
$this->contentMapper->copyLanguage($id, $userId, null, $locale, $destLocales); |
|
541
|
|
|
|
|
542
|
|
|
$data = $this->documentManager->find($id, $locale); |
|
543
|
|
|
|
|
544
|
|
|
break; |
|
545
|
|
|
case 'copy': |
|
546
|
|
|
/** @var ArticleDocument $document */ |
|
547
|
|
|
$document = $this->documentManager->find($id, $locale); |
|
548
|
|
|
$copiedPath = $this->documentManager->copy($document, dirname($document->getPath())); |
|
549
|
|
|
$this->documentManager->flush(); |
|
550
|
|
|
|
|
551
|
|
|
$data = $this->documentManager->find($copiedPath, $locale); |
|
552
|
|
|
|
|
553
|
|
|
break; |
|
554
|
|
|
case 'order': |
|
555
|
|
|
$this->orderPages($this->getRequestParameter($request, 'pages', true), $locale); |
|
556
|
|
|
$this->documentManager->flush(); |
|
557
|
|
|
$this->documentManager->clear(); |
|
558
|
|
|
|
|
559
|
|
|
$data = $this->documentManager->find($id, $locale); |
|
560
|
|
|
|
|
561
|
|
|
break; |
|
562
|
|
|
default: |
|
563
|
|
|
throw new RestException('Unrecognized action: ' . $action); |
|
564
|
|
|
} |
|
565
|
|
|
|
|
566
|
|
|
// create context |
|
567
|
|
|
$context = new Context(); |
|
568
|
|
|
$context->setSerializeNull(true); |
|
569
|
|
|
$context->setGroups(['defaultPage', 'defaultArticle', 'smallArticlePage']); |
|
570
|
|
|
|
|
571
|
|
|
// prepare view |
|
572
|
|
|
$view = $this->view($data); |
|
573
|
|
|
$view->setContext($context); |
|
574
|
|
|
} catch (RestException $exc) { |
|
|
|
|
|
|
575
|
|
|
$view = $this->view($exc->toArray(), 400); |
|
576
|
|
|
} |
|
577
|
|
|
|
|
578
|
|
|
return $this->handleView($view); |
|
579
|
|
|
} |
|
580
|
|
|
|
|
581
|
|
|
/** |
|
582
|
|
|
* Ordering given pages. |
|
583
|
|
|
*/ |
|
584
|
|
|
private function orderPages(array $pages, string $locale): void |
|
585
|
|
|
{ |
|
586
|
|
|
$documentManager = $this->documentManager; |
|
587
|
|
|
|
|
588
|
|
|
for ($i = 0; $i < count($pages); ++$i) { |
|
|
|
|
|
|
589
|
|
|
$document = $documentManager->find($pages[$i], $locale); |
|
590
|
|
|
$documentManager->reorder($document, null); |
|
591
|
|
|
} |
|
592
|
|
|
} |
|
593
|
|
|
|
|
594
|
|
|
/** |
|
595
|
|
|
* {@inheritdoc} |
|
596
|
|
|
*/ |
|
597
|
|
|
public function getSecurityContext() |
|
598
|
|
|
{ |
|
599
|
|
|
return ArticleAdmin::SECURITY_CONTEXT; |
|
600
|
|
|
} |
|
601
|
|
|
|
|
602
|
|
|
/** |
|
603
|
|
|
* Persists the document using the given Formation. |
|
604
|
|
|
* |
|
605
|
|
|
* @param object $document |
|
606
|
|
|
* |
|
607
|
|
|
* @throws InvalidFormException |
|
608
|
|
|
* @throws MissingParameterException |
|
609
|
|
|
*/ |
|
610
|
|
|
private function persistDocument(array $data, $document, string $locale): void |
|
611
|
|
|
{ |
|
612
|
|
|
$formType = $this->metadataFactory->getMetadataForAlias('article')->getFormType(); |
|
613
|
|
|
$form = $this->formFactory->create( |
|
614
|
|
|
$formType, |
|
615
|
|
|
$document, |
|
616
|
|
|
[ |
|
617
|
|
|
// disable csrf protection, since we can't produce a token, because the form is cached on the client |
|
618
|
|
|
'csrf_protection' => false, |
|
619
|
|
|
] |
|
620
|
|
|
); |
|
621
|
|
|
$form->submit($data, false); |
|
622
|
|
|
|
|
623
|
|
|
if (!$form->isValid()) { |
|
624
|
|
|
throw new InvalidFormException($form); |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
|
|
if (array_key_exists('author', $data) && null === $data['author']) { |
|
628
|
|
|
$document->setAuthor(null); |
|
629
|
|
|
} |
|
630
|
|
|
|
|
631
|
|
|
if (array_key_exists('additionalWebspaces', $data) && null === $data['additionalWebspaces']) { |
|
632
|
|
|
$document->setAdditionalWebspaces(null); |
|
633
|
|
|
} |
|
634
|
|
|
|
|
635
|
|
|
if (array_key_exists('customizeWebspaceSettings', $data) && false === $data['customizeWebspaceSettings']) { |
|
636
|
|
|
$document->setMainWebspace(null); |
|
637
|
|
|
$document->setAdditionalWebspaces(null); |
|
638
|
|
|
} |
|
639
|
|
|
|
|
640
|
|
|
$this->documentManager->persist( |
|
641
|
|
|
$document, |
|
642
|
|
|
$locale, |
|
643
|
|
|
[ |
|
644
|
|
|
'user' => $this->getUser()->getId(), |
|
645
|
|
|
'clear_missing_content' => false, |
|
646
|
|
|
] |
|
647
|
|
|
); |
|
648
|
|
|
} |
|
649
|
|
|
|
|
650
|
|
|
/** |
|
651
|
|
|
* Delegates actions by given actionParameter, which can be retrieved from the request. |
|
652
|
|
|
* |
|
653
|
|
|
* @param object $document |
|
654
|
|
|
*/ |
|
655
|
|
|
private function handleActionParameter(?string $actionParameter, $document, string $locale): void |
|
656
|
|
|
{ |
|
657
|
|
|
switch ($actionParameter) { |
|
658
|
|
|
case 'publish': |
|
659
|
|
|
$this->documentManager->publish($document, $locale); |
|
660
|
|
|
|
|
661
|
|
|
break; |
|
662
|
|
|
} |
|
663
|
|
|
} |
|
664
|
|
|
|
|
665
|
|
|
private function getSortFieldName(string $sortBy): ?string |
|
666
|
|
|
{ |
|
667
|
|
|
$sortBy = Caser::snake($sortBy); |
|
668
|
|
|
$fieldDescriptors = $this->getFieldDescriptors(); |
|
669
|
|
|
|
|
670
|
|
|
if (array_key_exists($sortBy, $fieldDescriptors)) { |
|
671
|
|
|
return $fieldDescriptors[$sortBy]->getSortField(); |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
|
|
return null; |
|
675
|
|
|
} |
|
676
|
|
|
} |
|
677
|
|
|
|
Scrutinizer analyzes your
composer.json/composer.lockfile if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.