<?php
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/
namespace Pimcore\Bundle\AdminBundle\Controller\Rest\Element;
use Pimcore\Bundle\AdminBundle\HttpFoundation\JsonResponse;
use Pimcore\Http\Exception\ResponseException;
use Pimcore\Model\DataObject;
use Pimcore\Model\Webservice\Data\DataObject\Concrete\In as WebserviceObjectIn;
use Pimcore\Model\Webservice\Data\DataObject\Concrete\Out as WebserviceObjectOut;
use Pimcore\Model\Webservice\Data\DataObject\Folder\In as WebserviceFolderIn;
use Pimcore\Model\Webservice\Data\DataObject\Folder\Out as WebserviceFolderOut;
use Pimcore\Tool;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* end point for object related data.
*
* - get object by id
* GET http://[YOUR-DOMAIN]/webservice/rest/object/id/1281?apikey=[API-KEY]
* returns json-encoded object data.
* - delete object by id
* DELETE http://[YOUR-DOMAIN]/webservice/rest/object/id/1281?apikey=[API-KEY]
* returns json encoded success value
* - create object
* PUT or POST http://[YOUR-DOMAIN]/webservice/rest/object?apikey=[API-KEY]
* body: json-encoded object data in the same format as returned by get object by id
* but with missing id field or id set to 0
* returns json encoded object id
* - update object
* PUT or POST http://[YOUR-DOMAIN]/webservice/rest/object/id/1281?apikey=[API-KEY]
* body: same as for create object. object id can be either in URI or as request payload
* returns json encoded success value
*/
class DataObjectController extends AbstractElementController
{
/**
* @Method("GET")
* @Route("/object/id/{id}", requirements={"id": "\d+"})
* @Route("/object")
*
* @api {get} /object Get object data
* @apiName Get object by id
* @apiGroup Object
* @apiSampleRequest off
* @apiParam {int} id an object id
* @apiParam {string} apikey your access token
* @apiParamExample {json} Request-Example:
* {
* "id": 1,
* "apikey": "21314njdsfn1342134"
* }
* @apiSuccess {json} success parameter of the returned data = true
* @apiError {json} success parameter of the returned data = false
* @apiErrorExample {json} Error-Response:
* {"success":false, "msg":"exception 'Exception' with message '....'"}
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true
* "data": {
* "path": "/crm/inquiries/",
* "creationDate": 1368630916,
* "modificationDate": 1388409137,
* "userModification": null,
* "childs": null,
* "elements": [
* {
* "type": "gender",
* "value": "female",
* "name": "gender",
* "language": null
* },
*
* ...
*
* }
*
* @param Request $request
* @param int|null $id
*
* @return JsonResponse
*
* @throws ResponseException
*/
public function getAction(Request $request, $id = null)
{
$id = $this->resolveId($request, $id);
$profile = $request->get('profiling');
$profileName = 'rest_object_get';
/** @var Stopwatch $stopwatch */
$stopwatch = null;
if ($profile) {
$stopwatch = $this->startProfiling();
$stopwatch->start('get', $profileName);
}
$object = $this->loadObject($id);
if ($profile) {
$stopwatch->stop('get');
$stopwatch->start('perm', $profileName);
}
$this->checkElementPermission($object, 'get');
if ($profile) {
$stopwatch->stop('perm');
$stopwatch->start('ws', $profileName);
}
/** @var WebserviceObjectOut|WebserviceFolderOut $out */
if ($object instanceof DataObject\Folder) {
$out = $this->service->getObjectFolderById($id);
} else {
$out = $this->service->getObjectConcreteById($id);
}
if ($profile) {
$stopwatch->stop('ws');
}
$data = $this->createSuccessData($out);
if ($profile) {
$data['profiling'] = $this->getProfilingData($profileName);
}
return $this->adminJson($data);
}
/**
* @Method({"POST", "PUT"})
* @Route("/object")
*
* @api {post} /object Create a new object
* @apiName Create a new object
* @apiGroup Object
* @apiSampleRequest off
* @apiDescription
* Request body: JSON-encoded object data in the same format as returned by get object by id for the data segment but with missing id field or id set to 0
*
* @apiParam {json} data a new object data
* @apiParam {string} apikey your access token
* @apiParamExample {json} Request-Example:
* {
* "apikey": "21314njdsfn1342134",
* "data": {
* "id": 61,
* "parentId": 48,
* "key": "test-product-key",
* "className": "product",
* "type": "object",
* "elements": [
* {
* "type": "input",
* "value": "some identyfier",
* "name": "identyfier",
* "language": null
* },
* {
* "type": "localizedfields",
* "value": [
* {
* "type": "input",
* "value": "Test",
* "name": "name1",
* "language": "en"
* },
* {
* "type": "input",
* "value": "1",
* "name": "name2",
* "language": "en"
* },
* {
* "type": "input",
* "value": null,
* "name": "name1",
* "language": "de"
* },
* {
* "type": "input",
* "value": "aaa",
* "name": "name2",
* "language": "de"
* }
* ],
* "name": "localizedfields",
* "language": null
* }
* ]
* }
* }
* @apiSuccess {json} success parameter of the returned data = true
* @apiError {json} success parameter of the returned data = false
* @apiErrorExample {json} Error-Response:
* {"success":false, "msg":"exception 'Exception' with message '....'"}
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true
* }
*
* @param Request $request
*
* @return JsonResponse
*/
public function createAction(Request $request)
{
$data = $this->getJsonData($request);
// get and normalize type
$type = $data['type'] = isset($data['type']) ? $data['type'] : 'object';
// add support for legacy behaviour, accepting the ID as payload parameter
if (isset($data['id'])) {
$id = $data['id'];
$object = $this->loadObject($id);
return $this->updateObject($object, $type, $data);
}
return $this->createObject($type, $data);
}
/**
* @Method({"POST", "PUT"})
* @Route("/object/id/{id}", requirements={"id": "\d+"})
*
* @api {put} /object/id/{id} Update an object
* @apiName Create a new object
* @apiGroup Object
* @apiSampleRequest off
* @apiDescription
* Request body: JSON-encoded object data in the same format as returned by get object by id for the data segment but with missing id field or id set to 0
*
* @apiParam {json} data a new object data
* @apiParam {string} apikey your access token
* @apiParamExample {json} Request-Example:
* {
* "apikey": "21314njdsfn1342134",
* "id": 66
* "data": {
* "parentId": 48,
* "key": "test-product-key",
* "className": "product",
* "type": "object",
* "elements": [
* {
* "type": "input",
* "value": "some identyfier",
* "name": "identyfier",
* "language": null
* },
* {
* "type": "localizedfields",
* "value": [
* {
* "type": "input",
* "value": "Test new",
* "name": "name1",
* "language": "en"
* },
* {
* "type": "input",
* "value": "1",
* "name": "name2",
* "language": "en"
* },
* {
* "type": "input",
* "value": null,
* "name": "name1",
* "language": "de"
* },
* {
* "type": "input",
* "value": "aaa",
* "name": "name2",
* "language": "de"
* }
* ],
* "name": "localizedfields",
* "language": null
* }
* ]
* }
* }
* @apiSuccess {json} success parameter of the returned data = true
* @apiError {json} success parameter of the returned data = false
* @apiErrorExample {json} Error-Response:
* {"success":false, "msg":"exception 'Exception' with message '....'"}
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* "id": 66
* }
*
* @param Request $request
* @param int|null $id
*
* @return JsonResponse
*/
public function updateAction(Request $request, $id)
{
$id = $this->resolveId($request, $id);
$data = $this->getJsonData($request);
// get and normalize type
$type = $data['type'] = isset($data['type']) ? $data['type'] : 'object';
$object = $this->loadObject($id);
return $this->updateObject($object, $type, $data);
}
/**
* @Method("DELETE")
* @Route("/object/id/{id}", requirements={"id": "\d+"})
* @Route("/object")
*
* @api {delete} /object/id/{id} Delete object
* @apiName Delete object
* @apiGroup Object
* @apiSampleRequest off
* @apiParam {int} id an object id
* @apiParam {string} apikey your access token
* @apiParamExample {json} Request-Example:
* {
* "id": 1,
* "apikey": "21314njdsfn1342134"
* }
* @apiSuccess {json} success parameter of the returned data = true
* @apiError {json} success parameter of the returned data = false
* @apiErrorExample {json} Error-Response:
* {"success":false, "msg":"exception 'Exception' with message '....'"}
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* }
*
* @param Request $request
* @param int|null $id
*
* @return JsonResponse
*
* @throws ResponseException
*/
public function deleteAction(Request $request, $id = null)
{
$id = $this->resolveId($request, $id);
$object = $this->loadObject($id);
$this->checkElementPermission($object, 'delete');
$success = $this->service->deleteObject($id);
if ($success) {
return $this->createSuccessResponse();
} else {
// TODO what to do on delete error? is bad request appropiate?
return $this->createErrorResponse();
}
}
/**
* @Method("GET")
* @Route("/object-list")
*
* Returns a list of object id/type pairs matching the given criteria.
* Example:
* GET http://[YOUR-DOMAIN]/webservice/rest/object-list?apikey=[API-KEY]&order=DESC&offset=3&orderKey=id&limit=2&condition=type%3D%27folder%27
*
* Parameters:
* - condition
* - sort order (if supplied then also the key must be provided)
* - sort order key
* - offset
* - limit
* - group by key
* - objectClass the name of the object class (without "Object_"). If the class does
* not exist the filter criteria will be ignored!
*
* @param Request $request
*
* @return JsonResponse
*/
public function listAction(Request $request)
{
$this->checkPermission('objects');
$condition = urldecode($request->get('condition'));
$order = $request->get('order');
$orderKey = $request->get('orderKey');
$offset = $request->get('offset');
$limit = $request->get('limit');
$groupBy = $request->get('groupBy');
$objectClass = $request->get('objectClass');
$result = $this->service->getObjectList($condition, $order, $orderKey, $offset, $limit, $groupBy, $objectClass);
return $this->createCollectionSuccessResponse($result);
}
/**
* @Method("GET")
* @Route("/object-meta/id/{id}", requirements={"id": "\d+"})
*
* end point for object metadata
* Example:
* GET http://[YOUR-DOMAIN]/webservice/rest/object-meta/id/1281?apikey=[API-KEY]
* returns the json-encoded class definition for the given object
*
* @param int $id
*
* @return JsonResponse
*
* @throws ResponseException
*/
public function objectMetaAction($id)
{
$this->checkPermission('classes');
$class = $this->service->getObjectMetadataById($id);
if (!$class) {
throw $this->createNotFoundResponseException();
}
return $this->createSuccessResponse($class);
}
/**
* @Method("GET")
* @Route("/object-count")
*
* Returns the total number of objects matching the given condition
* Example:
* GET http://[YOUR-DOMAIN]/webservice/rest/object-count?apikey=[API-KEY]&condition=type%3D%27folder%27
*
* Parameters:
* - condition
* - group by key
* - objectClass the name of the object class (without "Object_"). If the class does
* not exist the filter criteria will be ignored!
*
* @param Request $request
*
* @return JsonResponse
*/
public function countAction(Request $request)
{
$this->checkPermission('objects');
$condition = urldecode($request->get('condition'));
$groupBy = $request->get('groupBy');
$objectClass = $request->get('objectClass');
$params = [
'objectTypes' => [
DataObject\AbstractObject::OBJECT_TYPE_FOLDER,
DataObject\AbstractObject::OBJECT_TYPE_OBJECT,
DataObject\AbstractObject::OBJECT_TYPE_VARIANT
]
];
if (!empty($condition)) {
$params['condition'] = $condition;
}
if (!empty($groupBy)) {
$params['groupBy'] = $groupBy;
}
$listClassName = DataObject\AbstractObject::class;
if (!empty($objectClass)) {
$listClassName = '\\Pimcore\\Model\\DataObject\\' . ucfirst($objectClass);
if (!Tool::classExists($listClassName)) {
$listClassName = DataObject\AbstractObject::class;
}
}
$count = $listClassName::getTotalCount($params);
return $this->createSuccessResponse([
'totalCount' => $count
]);
}
/**
* @Method({"GET", "POST"})
* @Route("/object-inquire")
*
* Checks for existence of the given object IDs
*
* GET http://[YOUR-DOMAIN]/webservice/rest/object-inquire?apikey=[API-KEY]
*
* Parameters:
* - id single object ID
* - ids comma separated list of object IDs
* Returns:
* - List with true or false for each ID
*
* @param Request $request
*
* @return JsonResponse
*/
public function inquireAction(Request $request)
{
return $this->inquire($request, 'object');
}
/**
* @param int $id
*
* @return DataObject\AbstractObject
*
* @throws ResponseException
* if object was not found
*/
protected function loadObject($id)
{
$object = DataObject::getById((int)$id);
if ($object) {
return $object;
}
throw $this->createNotFoundResponseException([
'msg' => sprintf('Object %d does not exist', (int)$id),
'code' => static::ELEMENT_DOES_NOT_EXIST
]);
}
/**
* Create an object
*
* @param string $type
* @param array $data
*
* @return JsonResponse
*/
protected function createObject($type, array $data)
{
if ($type === 'folder') {
$class = WebserviceFolderIn::class;
$method = 'createObjectFolder';
} else {
$class = WebserviceObjectIn::class;
$method = 'createObjectConcrete';
}
$wsData = $this->fillWebserviceData($class, $data);
$object = new DataObject();
$object->setId($wsData->parentId);
$this->checkElementPermission($object, 'create');
$id = $this->service->$method($wsData);
if (null !== $id) {
return $this->createSuccessResponse([
'id' => $id
], false);
} else {
return $this->createErrorResponse();
}
}
/**
* Update an existing object
*
* @param DataObject\AbstractObject $object
* @param string $type
* @param array $data
*
* @return JsonResponse
*/
protected function updateObject(DataObject\AbstractObject $object, $type, array $data)
{
$this->checkElementPermission($object, 'update');
$data['id'] = $object->getId();
$success = false;
if ($type === 'folder') {
$wsData = $this->fillWebserviceData(WebserviceFolderIn::class, $data);
$success = $this->service->updateObjectFolder($wsData);
} else {
$wsData = $this->fillWebserviceData(WebserviceObjectIn::class, $data);
$success = $this->service->updateObjectConcrete($wsData);
}
if ($success) {
return $this->createSuccessResponse();
} else {
return $this->createErrorResponse();
}
}
}