1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* File containing the Content controller class. |
5
|
|
|
* |
6
|
|
|
* @copyright Copyright (C) eZ Systems AS. All rights reserved. |
7
|
|
|
* @license For full copyright and license information view LICENSE file distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
namespace eZ\Publish\Core\REST\Server\Controller; |
10
|
|
|
|
11
|
|
|
use eZ\Publish\Core\REST\Common\Message; |
12
|
|
|
use eZ\Publish\Core\REST\Common\Exceptions; |
13
|
|
|
use eZ\Publish\Core\REST\Server\Values; |
14
|
|
|
use eZ\Publish\Core\REST\Server\Controller as RestController; |
15
|
|
|
use eZ\Publish\API\Repository\Values\Content\Relation; |
16
|
|
|
use eZ\Publish\API\Repository\Values\Content\VersionInfo; |
17
|
|
|
use eZ\Publish\API\Repository\Exceptions\NotFoundException; |
18
|
|
|
use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException; |
19
|
|
|
use eZ\Publish\API\Repository\Exceptions\ContentValidationException; |
20
|
|
|
use eZ\Publish\Core\REST\Server\Exceptions\ForbiddenException; |
21
|
|
|
use eZ\Publish\Core\REST\Server\Exceptions\BadRequestException; |
22
|
|
|
use eZ\Publish\Core\REST\Server\Exceptions\ContentFieldValidationException as RESTContentFieldValidationException; |
23
|
|
|
use eZ\Publish\Core\REST\Server\Values\RestContentCreateStruct; |
24
|
|
|
use Symfony\Component\HttpFoundation\Request; |
25
|
|
|
use Symfony\Component\HttpKernel\HttpKernelInterface; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Content controller. |
29
|
|
|
*/ |
30
|
|
|
class Content extends RestController |
31
|
|
|
{ |
32
|
|
|
/** |
33
|
|
|
* Loads a content info by remote ID. |
34
|
|
|
* |
35
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\BadRequestException |
36
|
|
|
* |
37
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\TemporaryRedirect |
38
|
|
|
*/ |
39
|
|
View Code Duplication |
public function redirectContent(Request $request) |
40
|
|
|
{ |
41
|
|
|
if (!$request->query->has('remoteId')) { |
42
|
|
|
throw new BadRequestException("'remoteId' parameter is required."); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfoByRemoteId( |
46
|
|
|
$request->query->get('remoteId') |
47
|
|
|
); |
48
|
|
|
|
49
|
|
|
return new Values\TemporaryRedirect( |
50
|
|
|
$this->router->generate( |
51
|
|
|
'ezpublish_rest_loadContent', |
52
|
|
|
array( |
53
|
|
|
'contentId' => $contentInfo->id, |
54
|
|
|
) |
55
|
|
|
) |
56
|
|
|
); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Loads a content info, potentially with the current version embedded. |
61
|
|
|
* |
62
|
|
|
* @param mixed $contentId |
63
|
|
|
* @param \Symfony\Component\HttpFoundation\Request $request |
64
|
|
|
* |
65
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\RestContent |
66
|
|
|
*/ |
67
|
|
|
public function loadContent($contentId, Request $request) |
68
|
|
|
{ |
69
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
70
|
|
|
|
71
|
|
|
$mainLocation = null; |
72
|
|
|
if (!empty($contentInfo->mainLocationId)) { |
73
|
|
|
$mainLocation = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); |
77
|
|
|
|
78
|
|
|
$contentVersion = null; |
79
|
|
|
$relations = null; |
80
|
|
|
if ($this->getMediaType($request) === 'application/vnd.ez.api.content') { |
81
|
|
|
$languages = null; |
82
|
|
|
if ($request->query->has('languages')) { |
83
|
|
|
$languages = explode(',', $request->query->get('languages')); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$contentVersion = $this->repository->getContentService()->loadContent($contentId, $languages); |
87
|
|
|
$relations = $this->repository->getContentService()->loadRelations($contentVersion->getVersionInfo()); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
$restContent = new Values\RestContent( |
91
|
|
|
$contentInfo, |
92
|
|
|
$mainLocation, |
93
|
|
|
$contentVersion, |
94
|
|
|
$contentType, |
95
|
|
|
$relations, |
96
|
|
|
$request->getPathInfo() |
97
|
|
|
); |
98
|
|
|
|
99
|
|
|
if ($contentInfo->mainLocationId === null) { |
100
|
|
|
return $restContent; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
return new Values\CachedValue( |
|
|
|
|
104
|
|
|
$restContent, |
105
|
|
|
array('locationId' => $contentInfo->mainLocationId) |
106
|
|
|
); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Updates a content's metadata. |
111
|
|
|
* |
112
|
|
|
* @param mixed $contentId |
113
|
|
|
* |
114
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\RestContent |
115
|
|
|
*/ |
116
|
|
|
public function updateContentMetadata($contentId, Request $request) |
117
|
|
|
{ |
118
|
|
|
$updateStruct = $this->inputDispatcher->parse( |
119
|
|
|
new Message( |
120
|
|
|
array('Content-Type' => $request->headers->get('Content-Type')), |
121
|
|
|
$request->getContent() |
|
|
|
|
122
|
|
|
) |
123
|
|
|
); |
124
|
|
|
|
125
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
126
|
|
|
|
127
|
|
|
// update section |
128
|
|
|
if ($updateStruct->sectionId !== null) { |
|
|
|
|
129
|
|
|
$section = $this->repository->getSectionService()->loadSection($updateStruct->sectionId); |
|
|
|
|
130
|
|
|
$this->repository->getSectionService()->assignSection($contentInfo, $section); |
131
|
|
|
$updateStruct->sectionId = null; |
|
|
|
|
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
// @todo Consider refactoring! ContentService::updateContentMetadata throws the same exception |
135
|
|
|
// in case the updateStruct is empty and if remoteId already exists. Since REST version of update struct |
136
|
|
|
// includes section ID in addition to other fields, we cannot throw exception if only sectionId property |
137
|
|
|
// is set, so we must skip updating content in that case instead of allowing propagation of the exception. |
138
|
|
|
foreach ($updateStruct as $propertyName => $propertyValue) { |
|
|
|
|
139
|
|
|
if ($propertyName !== 'sectionId' && $propertyValue !== null) { |
140
|
|
|
// update content |
141
|
|
|
$this->repository->getContentService()->updateContentMetadata($contentInfo, $updateStruct); |
|
|
|
|
142
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
143
|
|
|
break; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
try { |
148
|
|
|
$locationInfo = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); |
149
|
|
|
} catch (NotFoundException $e) { |
150
|
|
|
$locationInfo = null; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
return new Values\RestContent( |
154
|
|
|
$contentInfo, |
155
|
|
|
$locationInfo |
156
|
|
|
); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Loads a specific version of a given content object. |
161
|
|
|
* |
162
|
|
|
* @param mixed $contentId |
163
|
|
|
* |
164
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\TemporaryRedirect |
165
|
|
|
*/ |
166
|
|
View Code Duplication |
public function redirectCurrentVersion($contentId) |
167
|
|
|
{ |
168
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
169
|
|
|
|
170
|
|
|
return new Values\TemporaryRedirect( |
171
|
|
|
$this->router->generate( |
172
|
|
|
'ezpublish_rest_loadContentInVersion', |
173
|
|
|
array( |
174
|
|
|
'contentId' => $contentId, |
175
|
|
|
'versionNumber' => $contentInfo->currentVersionNo, |
176
|
|
|
) |
177
|
|
|
) |
178
|
|
|
); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Loads a specific version of a given content object. |
183
|
|
|
* |
184
|
|
|
* @param mixed $contentId |
185
|
|
|
* @param int $versionNumber |
186
|
|
|
* |
187
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\Version |
188
|
|
|
*/ |
189
|
|
|
public function loadContentInVersion($contentId, $versionNumber, Request $request) |
190
|
|
|
{ |
191
|
|
|
$languages = null; |
192
|
|
|
if ($request->query->has('languages')) { |
193
|
|
|
$languages = explode(',', $request->query->get('languages')); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
$content = $this->repository->getContentService()->loadContent( |
197
|
|
|
$contentId, |
198
|
|
|
$languages, |
199
|
|
|
$versionNumber |
200
|
|
|
); |
201
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType( |
202
|
|
|
$content->getVersionInfo()->getContentInfo()->contentTypeId |
203
|
|
|
); |
204
|
|
|
|
205
|
|
|
$versionValue = new Values\Version( |
206
|
|
|
$content, |
207
|
|
|
$contentType, |
208
|
|
|
$this->repository->getContentService()->loadRelations($content->getVersionInfo()), |
209
|
|
|
$request->getPathInfo() |
210
|
|
|
); |
211
|
|
|
|
212
|
|
|
if ($content->contentInfo->mainLocationId === null || $content->versionInfo->status === VersionInfo::STATUS_DRAFT) { |
213
|
|
|
return $versionValue; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return new Values\CachedValue( |
|
|
|
|
217
|
|
|
$versionValue, |
218
|
|
|
array('locationId' => $content->contentInfo->mainLocationId) |
219
|
|
|
); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Creates a new content draft assigned to the authenticated user. |
224
|
|
|
* If a different userId is given in the input it is assigned to the |
225
|
|
|
* given user but this required special rights for the authenticated |
226
|
|
|
* user (this is useful for content staging where the transfer process |
227
|
|
|
* does not have to authenticate with the user which created the content |
228
|
|
|
* object in the source server). The user has to publish the content if |
229
|
|
|
* it should be visible. |
230
|
|
|
* |
231
|
|
|
* @param \Symfony\Component\HttpFoundation\Request $request |
232
|
|
|
* |
233
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\CreatedContent |
234
|
|
|
*/ |
235
|
|
|
public function createContent(Request $request) |
236
|
|
|
{ |
237
|
|
|
$contentCreate = $this->parseCreateContentRequest($request); |
238
|
|
|
|
239
|
|
|
return $this->doCreateContent($request, $contentCreate); |
|
|
|
|
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* The content is deleted. If the content has locations (which is required in 4.x) |
244
|
|
|
* on delete all locations assigned the content object are deleted via delete subtree. |
245
|
|
|
* |
246
|
|
|
* @param mixed $contentId |
247
|
|
|
* |
248
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
249
|
|
|
*/ |
250
|
|
|
public function deleteContent($contentId) |
251
|
|
|
{ |
252
|
|
|
$this->repository->getContentService()->deleteContent( |
253
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId) |
254
|
|
|
); |
255
|
|
|
|
256
|
|
|
return new Values\NoContent(); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Creates a new content object as copy under the given parent location given in the destination header. |
261
|
|
|
* |
262
|
|
|
* @param mixed $contentId |
263
|
|
|
* |
264
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\ResourceCreated |
265
|
|
|
*/ |
266
|
|
|
public function copyContent($contentId, Request $request) |
267
|
|
|
{ |
268
|
|
|
$destination = $request->headers->get('Destination'); |
269
|
|
|
|
270
|
|
|
$parentLocationParts = explode('/', $destination); |
271
|
|
|
$copiedContent = $this->repository->getContentService()->copyContent( |
272
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId), |
273
|
|
|
$this->repository->getLocationService()->newLocationCreateStruct(array_pop($parentLocationParts)) |
274
|
|
|
); |
275
|
|
|
|
276
|
|
|
return new Values\ResourceCreated( |
277
|
|
|
$this->router->generate( |
278
|
|
|
'ezpublish_rest_loadContent', |
279
|
|
|
array('contentId' => $copiedContent->id) |
280
|
|
|
) |
281
|
|
|
); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Deletes a translation from all the Versions of the given Content Object. |
286
|
|
|
* |
287
|
|
|
* If any non-published Version contains only the Translation to be deleted, that entire Version will be deleted |
288
|
|
|
* |
289
|
|
|
* @param int $contentId |
290
|
|
|
* @param string $languageCode |
291
|
|
|
* |
292
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
293
|
|
|
* |
294
|
|
|
* @throws \Exception |
295
|
|
|
*/ |
296
|
|
|
public function deleteContentTranslation($contentId, $languageCode) |
297
|
|
|
{ |
298
|
|
|
$contentService = $this->repository->getContentService(); |
299
|
|
|
|
300
|
|
|
$this->repository->beginTransaction(); |
301
|
|
|
try { |
302
|
|
|
$contentInfo = $contentService->loadContentInfo($contentId); |
303
|
|
|
$contentService->deleteTranslation( |
304
|
|
|
$contentInfo, |
305
|
|
|
$languageCode |
306
|
|
|
); |
307
|
|
|
|
308
|
|
|
$this->repository->commit(); |
309
|
|
|
|
310
|
|
|
return new Values\NoContent(); |
311
|
|
|
} catch (\Exception $e) { |
312
|
|
|
$this->repository->rollback(); |
313
|
|
|
throw $e; |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Returns a list of all versions of the content. This method does not |
319
|
|
|
* include fields and relations in the Version elements of the response. |
320
|
|
|
* |
321
|
|
|
* @param mixed $contentId |
322
|
|
|
* |
323
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\VersionList |
324
|
|
|
*/ |
325
|
|
|
public function loadContentVersions($contentId, Request $request) |
326
|
|
|
{ |
327
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
328
|
|
|
|
329
|
|
|
return new Values\VersionList( |
330
|
|
|
$this->repository->getContentService()->loadVersions($contentInfo), |
331
|
|
|
$request->getPathInfo() |
332
|
|
|
); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* The version is deleted. |
337
|
|
|
* |
338
|
|
|
* @param mixed $contentId |
339
|
|
|
* @param mixed $versionNumber |
340
|
|
|
* |
341
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\ForbiddenException |
342
|
|
|
* |
343
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
344
|
|
|
*/ |
345
|
|
View Code Duplication |
public function deleteContentVersion($contentId, $versionNumber) |
346
|
|
|
{ |
347
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo( |
348
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId), |
349
|
|
|
$versionNumber |
350
|
|
|
); |
351
|
|
|
|
352
|
|
|
if ($versionInfo->isPublished()) { |
353
|
|
|
throw new ForbiddenException('Version in status PUBLISHED cannot be deleted'); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$this->repository->getContentService()->deleteVersion( |
357
|
|
|
$versionInfo |
358
|
|
|
); |
359
|
|
|
|
360
|
|
|
return new Values\NoContent(); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Remove the given Translation from the given Version Draft. |
365
|
|
|
* |
366
|
|
|
* @param int $contentId |
367
|
|
|
* @param int $versionNumber |
368
|
|
|
* @param string $languageCode |
369
|
|
|
* |
370
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
371
|
|
|
* |
372
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\ForbiddenException |
373
|
|
|
*/ |
374
|
|
|
public function deleteTranslationFromDraft($contentId, $versionNumber, $languageCode) |
375
|
|
|
{ |
376
|
|
|
$contentService = $this->repository->getContentService(); |
377
|
|
|
$versionInfo = $contentService->loadVersionInfoById($contentId, $versionNumber); |
378
|
|
|
|
379
|
|
|
if (!$versionInfo->isDraft()) { |
380
|
|
|
throw new ForbiddenException('Translation can be deleted from DRAFT Version only'); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
$contentService->deleteTranslationFromDraft($versionInfo, $languageCode); |
384
|
|
|
|
385
|
|
|
return new Values\NoContent(); |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* The system creates a new draft version as a copy from the given version. |
390
|
|
|
* |
391
|
|
|
* @param mixed $contentId |
392
|
|
|
* @param mixed $versionNumber |
393
|
|
|
* |
394
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\CreatedVersion |
395
|
|
|
*/ |
396
|
|
View Code Duplication |
public function createDraftFromVersion($contentId, $versionNumber) |
397
|
|
|
{ |
398
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
399
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); |
400
|
|
|
$contentDraft = $this->repository->getContentService()->createContentDraft( |
401
|
|
|
$contentInfo, |
402
|
|
|
$this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) |
403
|
|
|
); |
404
|
|
|
|
405
|
|
|
return new Values\CreatedVersion( |
406
|
|
|
array( |
407
|
|
|
'version' => new Values\Version( |
408
|
|
|
$contentDraft, |
409
|
|
|
$contentType, |
410
|
|
|
$this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) |
411
|
|
|
), |
412
|
|
|
) |
413
|
|
|
); |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* The system creates a new draft version as a copy from the current version. |
418
|
|
|
* |
419
|
|
|
* @param mixed $contentId |
420
|
|
|
* |
421
|
|
|
* @throws ForbiddenException if the current version is already a draft |
422
|
|
|
* |
423
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\CreatedVersion |
424
|
|
|
*/ |
425
|
|
View Code Duplication |
public function createDraftFromCurrentVersion($contentId) |
426
|
|
|
{ |
427
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
428
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); |
429
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo( |
430
|
|
|
$contentInfo |
431
|
|
|
); |
432
|
|
|
|
433
|
|
|
if ($versionInfo->isDraft()) { |
434
|
|
|
throw new ForbiddenException('Current version is already in status DRAFT'); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
$contentDraft = $this->repository->getContentService()->createContentDraft($contentInfo); |
438
|
|
|
|
439
|
|
|
return new Values\CreatedVersion( |
440
|
|
|
array( |
441
|
|
|
'version' => new Values\Version( |
442
|
|
|
$contentDraft, |
443
|
|
|
$contentType, |
444
|
|
|
$this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) |
445
|
|
|
), |
446
|
|
|
) |
447
|
|
|
); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* A specific draft is updated. |
452
|
|
|
* |
453
|
|
|
* @param mixed $contentId |
454
|
|
|
* @param mixed $versionNumber |
455
|
|
|
* |
456
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\ForbiddenException |
457
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\BadRequestException |
458
|
|
|
* |
459
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\Version |
460
|
|
|
*/ |
461
|
|
|
public function updateVersion($contentId, $versionNumber, Request $request) |
462
|
|
|
{ |
463
|
|
|
$contentUpdateStruct = $this->inputDispatcher->parse( |
464
|
|
|
new Message( |
465
|
|
|
array( |
466
|
|
|
'Content-Type' => $request->headers->get('Content-Type'), |
467
|
|
|
'Url' => $this->router->generate( |
468
|
|
|
'ezpublish_rest_updateVersion', |
469
|
|
|
array( |
470
|
|
|
'contentId' => $contentId, |
471
|
|
|
'versionNumber' => $versionNumber, |
472
|
|
|
) |
473
|
|
|
), |
474
|
|
|
), |
475
|
|
|
$request->getContent() |
|
|
|
|
476
|
|
|
) |
477
|
|
|
); |
478
|
|
|
|
479
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo( |
480
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId), |
481
|
|
|
$versionNumber |
482
|
|
|
); |
483
|
|
|
|
484
|
|
|
if (!$versionInfo->isDraft()) { |
485
|
|
|
throw new ForbiddenException('Only version in status DRAFT can be updated'); |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
try { |
489
|
|
|
$this->repository->getContentService()->updateContent($versionInfo, $contentUpdateStruct); |
|
|
|
|
490
|
|
|
} catch (ContentValidationException $e) { |
491
|
|
|
throw new BadRequestException($e->getMessage()); |
492
|
|
|
} catch (ContentFieldValidationException $e) { |
493
|
|
|
throw new RESTContentFieldValidationException($e); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
$languages = null; |
497
|
|
|
if ($request->query->has('languages')) { |
498
|
|
|
$languages = explode(',', $request->query->get('languages')); |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
// Reload the content to handle languages GET parameter |
502
|
|
|
$content = $this->repository->getContentService()->loadContent( |
503
|
|
|
$contentId, |
504
|
|
|
$languages, |
505
|
|
|
$versionInfo->versionNo |
506
|
|
|
); |
507
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType( |
508
|
|
|
$content->getVersionInfo()->getContentInfo()->contentTypeId |
509
|
|
|
); |
510
|
|
|
|
511
|
|
|
return new Values\Version( |
512
|
|
|
$content, |
513
|
|
|
$contentType, |
514
|
|
|
$this->repository->getContentService()->loadRelations($content->getVersionInfo()), |
515
|
|
|
$request->getPathInfo() |
516
|
|
|
); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* The content version is published. |
521
|
|
|
* |
522
|
|
|
* @param mixed $contentId |
523
|
|
|
* @param mixed $versionNumber |
524
|
|
|
* |
525
|
|
|
* @throws ForbiddenException if version $versionNumber isn't a draft |
526
|
|
|
* |
527
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
528
|
|
|
*/ |
529
|
|
View Code Duplication |
public function publishVersion($contentId, $versionNumber) |
530
|
|
|
{ |
531
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo( |
532
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId), |
533
|
|
|
$versionNumber |
534
|
|
|
); |
535
|
|
|
|
536
|
|
|
if (!$versionInfo->isDraft()) { |
537
|
|
|
throw new ForbiddenException('Only version in status DRAFT can be published'); |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
$this->repository->getContentService()->publishVersion( |
541
|
|
|
$versionInfo |
542
|
|
|
); |
543
|
|
|
|
544
|
|
|
return new Values\NoContent(); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Redirects to the relations of the current version. |
549
|
|
|
* |
550
|
|
|
* @param mixed $contentId |
551
|
|
|
* |
552
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\TemporaryRedirect |
553
|
|
|
*/ |
554
|
|
View Code Duplication |
public function redirectCurrentVersionRelations($contentId) |
555
|
|
|
{ |
556
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
557
|
|
|
|
558
|
|
|
return new Values\TemporaryRedirect( |
559
|
|
|
$this->router->generate( |
560
|
|
|
'ezpublish_rest_redirectCurrentVersionRelations', |
561
|
|
|
array( |
562
|
|
|
'contentId' => $contentId, |
563
|
|
|
'versionNumber' => $contentInfo->currentVersionNo, |
564
|
|
|
) |
565
|
|
|
) |
566
|
|
|
); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* Loads the relations of the given version. |
571
|
|
|
* |
572
|
|
|
* @param mixed $contentId |
573
|
|
|
* @param mixed $versionNumber |
574
|
|
|
* |
575
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\RelationList |
576
|
|
|
*/ |
577
|
|
|
public function loadVersionRelations($contentId, $versionNumber, Request $request) |
578
|
|
|
{ |
579
|
|
|
$offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; |
580
|
|
|
$limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; |
581
|
|
|
|
582
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
583
|
|
|
$relationList = $this->repository->getContentService()->loadRelations( |
584
|
|
|
$this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) |
585
|
|
|
); |
586
|
|
|
|
587
|
|
|
$relationList = array_slice( |
588
|
|
|
$relationList, |
589
|
|
|
$offset >= 0 ? $offset : 0, |
590
|
|
|
$limit >= 0 ? $limit : null |
591
|
|
|
); |
592
|
|
|
|
593
|
|
|
$relationListValue = new Values\RelationList( |
594
|
|
|
$relationList, |
595
|
|
|
$contentId, |
596
|
|
|
$versionNumber, |
597
|
|
|
$request->getPathInfo() |
598
|
|
|
); |
599
|
|
|
|
600
|
|
|
if ($contentInfo->mainLocationId === null) { |
601
|
|
|
return $relationListValue; |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
return new Values\CachedValue( |
|
|
|
|
605
|
|
|
$relationListValue, |
606
|
|
|
array('locationId' => $contentInfo->mainLocationId) |
607
|
|
|
); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* Loads a relation for the given content object and version. |
612
|
|
|
* |
613
|
|
|
* @param mixed $contentId |
614
|
|
|
* @param int $versionNumber |
615
|
|
|
* @param mixed $relationId |
616
|
|
|
* |
617
|
|
|
* @throws \eZ\Publish\Core\REST\Common\Exceptions\NotFoundException |
618
|
|
|
* |
619
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\RestRelation |
620
|
|
|
*/ |
621
|
|
|
public function loadVersionRelation($contentId, $versionNumber, $relationId, Request $request) |
622
|
|
|
{ |
623
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
624
|
|
|
$relationList = $this->repository->getContentService()->loadRelations( |
625
|
|
|
$this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) |
626
|
|
|
); |
627
|
|
|
|
628
|
|
|
foreach ($relationList as $relation) { |
629
|
|
|
if ($relation->id == $relationId) { |
630
|
|
|
$relation = new Values\RestRelation($relation, $contentId, $versionNumber); |
631
|
|
|
|
632
|
|
|
if ($contentInfo->mainLocationId === null) { |
633
|
|
|
return $relation; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
return new Values\CachedValue( |
|
|
|
|
637
|
|
|
$relation, |
638
|
|
|
array('locationId' => $contentInfo->mainLocationId) |
639
|
|
|
); |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
throw new Exceptions\NotFoundException("Relation not found: '{$request->getPathInfo()}'."); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Deletes a relation of the given draft. |
648
|
|
|
* |
649
|
|
|
* @param mixed $contentId |
650
|
|
|
* @param int $versionNumber |
651
|
|
|
* @param mixed $relationId |
652
|
|
|
* |
653
|
|
|
* @throws \eZ\Publish\Core\REST\Server\Exceptions\ForbiddenException |
654
|
|
|
* @throws \eZ\Publish\Core\REST\Common\Exceptions\NotFoundException |
655
|
|
|
* |
656
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\NoContent |
657
|
|
|
*/ |
658
|
|
|
public function removeRelation($contentId, $versionNumber, $relationId, Request $request) |
659
|
|
|
{ |
660
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo( |
661
|
|
|
$this->repository->getContentService()->loadContentInfo($contentId), |
662
|
|
|
$versionNumber |
663
|
|
|
); |
664
|
|
|
|
665
|
|
|
$versionRelations = $this->repository->getContentService()->loadRelations($versionInfo); |
666
|
|
|
foreach ($versionRelations as $relation) { |
667
|
|
|
if ($relation->id == $relationId) { |
668
|
|
|
if ($relation->type !== Relation::COMMON) { |
669
|
|
|
throw new ForbiddenException('Relation is not of type COMMON'); |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
if (!$versionInfo->isDraft()) { |
673
|
|
|
throw new ForbiddenException('Relation of type COMMON can only be removed from drafts'); |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
$this->repository->getContentService()->deleteRelation($versionInfo, $relation->getDestinationContentInfo()); |
677
|
|
|
|
678
|
|
|
return new Values\NoContent(); |
679
|
|
|
} |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
throw new Exceptions\NotFoundException("Relation not found: '{$request->getPathInfo()}'."); |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
/** |
686
|
|
|
* Creates a new relation of type COMMON for the given draft. |
687
|
|
|
* |
688
|
|
|
* @param mixed $contentId |
689
|
|
|
* @param int $versionNumber |
690
|
|
|
* |
691
|
|
|
* @throws ForbiddenException if version $versionNumber isn't a draft |
692
|
|
|
* @throws ForbiddenException if a relation to the same content already exists |
693
|
|
|
* |
694
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\CreatedRelation |
695
|
|
|
*/ |
696
|
|
|
public function createRelation($contentId, $versionNumber, Request $request) |
697
|
|
|
{ |
698
|
|
|
$destinationContentId = $this->inputDispatcher->parse( |
699
|
|
|
new Message( |
700
|
|
|
array('Content-Type' => $request->headers->get('Content-Type')), |
701
|
|
|
$request->getContent() |
|
|
|
|
702
|
|
|
) |
703
|
|
|
); |
704
|
|
|
|
705
|
|
|
$contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); |
706
|
|
|
$versionInfo = $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber); |
707
|
|
|
if (!$versionInfo->isDraft()) { |
708
|
|
|
throw new ForbiddenException('Relation of type COMMON can only be added to drafts'); |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
try { |
712
|
|
|
$destinationContentInfo = $this->repository->getContentService()->loadContentInfo($destinationContentId); |
713
|
|
|
} catch (NotFoundException $e) { |
714
|
|
|
throw new ForbiddenException($e->getMessage()); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
$existingRelations = $this->repository->getContentService()->loadRelations($versionInfo); |
718
|
|
|
foreach ($existingRelations as $existingRelation) { |
719
|
|
|
if ($existingRelation->getDestinationContentInfo()->id == $destinationContentId) { |
720
|
|
|
throw new ForbiddenException('Relation of type COMMON to selected destination content ID already exists'); |
721
|
|
|
} |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
$relation = $this->repository->getContentService()->addRelation($versionInfo, $destinationContentInfo); |
725
|
|
|
|
726
|
|
|
return new Values\CreatedRelation( |
727
|
|
|
array( |
728
|
|
|
'relation' => new Values\RestRelation($relation, $contentId, $versionNumber), |
729
|
|
|
) |
730
|
|
|
); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
/** |
734
|
|
|
* Creates and executes a content view. |
735
|
|
|
* |
736
|
|
|
* @deprecated Since platform 1.0. Forwards the request to the new /views location, but returns a 301. |
737
|
|
|
* |
738
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\RestExecutedView |
739
|
|
|
*/ |
740
|
|
|
public function createView() |
741
|
|
|
{ |
742
|
|
|
$response = $this->forward('ezpublish_rest.controller.views:createView'); |
743
|
|
|
|
744
|
|
|
// Add 301 status code and location href |
745
|
|
|
$response->setStatusCode(301); |
746
|
|
|
$response->headers->set('Location', $this->router->generate('ezpublish_rest_views_create')); |
747
|
|
|
|
748
|
|
|
return $response; |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
/** |
752
|
|
|
* @param string $controller |
753
|
|
|
* |
754
|
|
|
* @return \Symfony\Component\HttpFoundation\Response |
755
|
|
|
*/ |
756
|
|
|
protected function forward($controller) |
757
|
|
|
{ |
758
|
|
|
$path['_controller'] = $controller; |
|
|
|
|
759
|
|
|
$subRequest = $this->container->get('request_stack')->getCurrentRequest()->duplicate(null, null, $path); |
760
|
|
|
|
761
|
|
|
return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); |
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
/** |
765
|
|
|
* @param \Symfony\Component\HttpFoundation\Request $request |
766
|
|
|
* |
767
|
|
|
* @return mixed |
768
|
|
|
*/ |
769
|
|
|
protected function parseCreateContentRequest(Request $request) |
770
|
|
|
{ |
771
|
|
|
return $this->inputDispatcher->parse( |
772
|
|
|
new Message( |
773
|
|
|
array('Content-Type' => $request->headers->get('Content-Type'), 'Url' => $request->getPathInfo()), |
774
|
|
|
$request->getContent() |
|
|
|
|
775
|
|
|
) |
776
|
|
|
); |
777
|
|
|
} |
778
|
|
|
|
779
|
|
|
/** |
780
|
|
|
* @param \Symfony\Component\HttpFoundation\Request $request |
781
|
|
|
* @param \eZ\Publish\Core\REST\Server\Values\RestContentCreateStruct $contentCreate |
782
|
|
|
* |
783
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
784
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException |
785
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
786
|
|
|
* |
787
|
|
|
* @return \eZ\Publish\Core\REST\Server\Values\CreatedContent |
788
|
|
|
*/ |
789
|
|
|
protected function doCreateContent(Request $request, RestContentCreateStruct $contentCreate) |
790
|
|
|
{ |
791
|
|
|
try { |
792
|
|
|
$content = $this->repository->getContentService()->createContent( |
793
|
|
|
$contentCreate->contentCreateStruct, |
794
|
|
|
array($contentCreate->locationCreateStruct) |
795
|
|
|
); |
796
|
|
|
} catch (ContentValidationException $e) { |
797
|
|
|
throw new BadRequestException($e->getMessage()); |
798
|
|
|
} catch (ContentFieldValidationException $e) { |
799
|
|
|
throw new RESTContentFieldValidationException($e); |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
$contentValue = null; |
803
|
|
|
$contentType = null; |
804
|
|
|
$relations = null; |
805
|
|
|
if ($this->getMediaType($request) === 'application/vnd.ez.api.content') { |
806
|
|
|
$contentValue = $content; |
807
|
|
|
$contentType = $this->repository->getContentTypeService()->loadContentType( |
808
|
|
|
$content->getVersionInfo()->getContentInfo()->contentTypeId |
809
|
|
|
); |
810
|
|
|
$relations = $this->repository->getContentService()->loadRelations($contentValue->getVersionInfo()); |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
return new Values\CreatedContent( |
814
|
|
|
array( |
815
|
|
|
'content' => new Values\RestContent( |
816
|
|
|
$content->contentInfo, |
817
|
|
|
null, |
818
|
|
|
$contentValue, |
819
|
|
|
$contentType, |
820
|
|
|
$relations |
821
|
|
|
), |
822
|
|
|
) |
823
|
|
|
); |
824
|
|
|
} |
825
|
|
|
} |
826
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.