diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c1f82d73a..4f4fda4e7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,6 +25,7 @@ jobs: - uses: ramsey/composer-install@v3 with: dependency-versions: highest + composer-options: "--ignore-platform-reqs --optimize-autoloader" - name: Run code style check run: composer run-script check-cs -- --format=checkstyle | cs2pr diff --git a/composer.json b/composer.json index 5724674da..0f33fde9c 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "ext-libxml": "*", "ext-simplexml": "*", "ext-xmlwriter": "*", + "api-platform/core": "dev-downgraded-deps", "hautelook/templated-uri-bundle": "^3.4", "ibexa/core": "~5.0.x-dev", "lexik/jwt-authentication-bundle": "^2.8", @@ -78,7 +79,15 @@ }, "extra": { "branch-alias": { - "dev-main": "5.0.x-dev" + "dev-main": "5.0.x-dev", + "dev-IBX-8335-full-api-platform": "5.0.x-dev" } - } + }, + "repositories": { + "api-platform/core": { + "type": "vcs", + "url": "https://github.com/tischsoic/core" + } + }, + "minimum-stability": "dev" } diff --git a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php new file mode 100644 index 000000000..e3123c632 --- /dev/null +++ b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php @@ -0,0 +1,36 @@ + + */ + private array $resources = []; + + public function create(): ResourceNameCollection + { + return new ResourceNameCollection($this->resources); + } + + /** + * @param array $newResources + */ + public function addResources(array $newResources): void + { + $this->resources = array_merge($this->resources, $newResources); + } +} diff --git a/src/bundle/ApiPlatform/Head.php b/src/bundle/ApiPlatform/Head.php new file mode 100644 index 000000000..119a9e744 --- /dev/null +++ b/src/bundle/ApiPlatform/Head.php @@ -0,0 +1,174 @@ + $context + */ + public function __invoke(array $context = []): OpenApi + { + $openApi = $this->decorated->__invoke($context); + $openApi = $this->addSchemas($openApi); + + $this->insertExampleFilesContent($openApi); + + return $openApi; + } + + private function addSchemas(OpenApi $openApi): OpenApi + { + $schemasCollection = $this->schemaCollectionFactory->create(); + $schemas = iterator_to_array($schemasCollection); + + $components = $openApi->getComponents(); + $components = $components->withSchemas(new \ArrayObject($schemas)); + + $openApi = $openApi->withComponents($components); + + return $openApi; + } + + private function insertExampleFilesContent(OpenApi $openApi): void + { + $paths = $openApi->getPaths(); + + /** @var \ApiPlatform\OpenApi\Model\PathItem $pathItem */ + foreach ($paths->getPaths() as $path => $pathItem) { + $newPathItem = $pathItem; + + /** @var array $methods */ + $methods = [ + 'GET' => $pathItem->getGet(), + 'PUT' => $pathItem->getPut(), + 'POST' => $pathItem->getPost(), + 'DELETE' => $pathItem->getDelete(), + 'OPTIONS' => $pathItem->getOptions(), + 'HEAD' => $pathItem->getHead(), + 'PATCH' => $pathItem->getPatch(), + 'TRACE' => $pathItem->getTrace(), + ]; + foreach ($methods as $method => $operation) { + if (empty($operation)) { + continue; + } + + $responses = $operation->getResponses(); + + if ($responses === null) { + continue; + } + + $newOperation = $operation; + + /** + * @var int $responseCode + * @var \ApiPlatform\OpenApi\Model\Response|array> $response + */ + foreach ($operation->getResponses() as $responseCode => $response) { + if (!is_array($response) || !array_key_exists('content', $response)) { + continue; + } + + $content = $response['content']; + $newContent = $content; + + foreach ($newContent as $mediaType => $responseContent) { + if (array_key_exists('x-ibexa-example-file', $responseContent)) { + $exampleFilePath = $this->kernel->locateResource($responseContent['x-ibexa-example-file']); + $exampleFileContent = file_get_contents($exampleFilePath); + $newContent[$mediaType]['example'] = $exampleFileContent; + unset($newContent[$mediaType]['x-ibexa-example-file']); + } + } + + if ($newContent !== $content) { + $newOperation = $newOperation->withResponse( + $responseCode, + new Response((string)$responseCode, new \ArrayObject($newContent)), + ); + } + } + + if ($newOperation !== $operation) { + switch ($method) { + case 'GET': + $newPathItem = $newPathItem->withGet($newOperation); + break; + case 'PUT': + $newPathItem = $newPathItem->withPut($newOperation); + break; + case 'POST': + $newPathItem = $newPathItem->withPost($newOperation); + break; + case 'DELETE': + $newPathItem = $newPathItem->withDelete($newOperation); + break; + case 'OPTIONS': + $newPathItem = $newPathItem->withOptions($newOperation); + break; + case 'HEAD': + $newPathItem = $newPathItem->withHead($newOperation); + break; + case 'PATCH': + $newPathItem = $newPathItem->withPatch($newOperation); + break; + case 'TRACE': + $newPathItem = $newPathItem->withTrace($newOperation); + break; + } + } + } + + if ($newPathItem !== $pathItem) { + $paths->addPath($path, $newPathItem); + } + } + } +} diff --git a/src/bundle/ApiPlatform/SchemasCollectionFactory.php b/src/bundle/ApiPlatform/SchemasCollectionFactory.php new file mode 100644 index 000000000..09f64d607 --- /dev/null +++ b/src/bundle/ApiPlatform/SchemasCollectionFactory.php @@ -0,0 +1,40 @@ + + */ + private array $providers = []; + + public function create(): SchemasCollection + { + $schemas = []; + + foreach ($this->providers as $provider) { + $schemas = array_merge($schemas, $provider->getSchemas()); + } + + return new SchemasCollection($schemas); + } + + public function addProvider(SchemasProviderInterface $provider): void + { + $this->providers[] = $provider; + } +} diff --git a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php new file mode 100644 index 000000000..c37caa4f2 --- /dev/null +++ b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php @@ -0,0 +1,35 @@ +hasDefinition(ClassNameResourceNameCollectionFactory::class)) { + return; + } + + $definition = $container->getDefinition(ClassNameResourceNameCollectionFactory::class); + + $taggedServiceIds = $container->findTaggedServiceIds(self::API_PLATFORM_RESOURCE_SERVICE_TAG); + foreach ($taggedServiceIds as $id => $attributes) { + $taggedServiceDefinition = $container->getDefinition($id); + $definition->addMethodCall( + 'addResources', + [[$taggedServiceDefinition->getClass()]] + ); + } + } +} diff --git a/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php b/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php new file mode 100644 index 000000000..f78cc6c68 --- /dev/null +++ b/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php @@ -0,0 +1,35 @@ +hasDefinition(SchemasCollectionFactory::class)) { + return; + } + + $definition = $container->getDefinition(SchemasCollectionFactory::class); + + $taggedServiceIds = $container->findTaggedServiceIds(self::API_PLATFORM_SCHEMA_PROVIDER_SERVICE_TAG); + foreach ($taggedServiceIds as $serviceId => $attributes) { + $definition->addMethodCall( + 'addProvider', + [new Reference($serviceId)] + ); + } + } +} diff --git a/src/bundle/DependencyInjection/IbexaRestExtension.php b/src/bundle/DependencyInjection/IbexaRestExtension.php index 42f8df050..dcd076ac3 100644 --- a/src/bundle/DependencyInjection/IbexaRestExtension.php +++ b/src/bundle/DependencyInjection/IbexaRestExtension.php @@ -8,6 +8,10 @@ namespace Ibexa\Bundle\Rest\DependencyInjection; use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; +use Ibexa\Bundle\Rest\DependencyInjection\Compiler\ClassNameResourceNamePass; +use Ibexa\Bundle\Rest\DependencyInjection\Compiler\SchemaProviderPass; +use Ibexa\Rest\ApiPlatform\SchemasProviderInterface; +use Ibexa\Rest\Server\Controller as RestController; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -35,11 +39,14 @@ public function getAlias(): string */ public function load(array $configs, ContainerBuilder $container) { + $this->configureApiPlatformAutotagging($container); + $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yml'); + $loader->load('api_platform.yml'); $loader->load('value_object_visitors.yml'); $loader->load('input_parsers.yml'); $loader->load('security.yml'); @@ -83,4 +90,13 @@ private function prependJMSTranslation(ContainerBuilder $container): void ], ]); } + + private function configureApiPlatformAutotagging(ContainerBuilder $container): void + { + $container->registerForAutoconfiguration(RestController::class) + ->addTag(ClassNameResourceNamePass::API_PLATFORM_RESOURCE_SERVICE_TAG); + + $container->registerForAutoconfiguration(SchemasProviderInterface::class) + ->addTag(SchemaProviderPass::API_PLATFORM_SCHEMA_PROVIDER_SERVICE_TAG); + } } diff --git a/src/bundle/IbexaRestBundle.php b/src/bundle/IbexaRestBundle.php index 0bdbb1d74..1c00c6338 100644 --- a/src/bundle/IbexaRestBundle.php +++ b/src/bundle/IbexaRestBundle.php @@ -23,6 +23,8 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new Compiler\InputParserPass()); $container->addCompilerPass(new Compiler\OutputVisitorPass()); $container->addCompilerPass(new Compiler\ValueObjectVisitorPass()); + $container->addCompilerPass(new Compiler\ClassNameResourceNamePass()); + $container->addCompilerPass(new Compiler\SchemaProviderPass()); if ($container->hasExtension('lexik_jwt_authentication')) { $container->addCompilerPass(new Compiler\LexikAuthorizationHeaderBridgePass()); diff --git a/src/bundle/Resources/api_platform/base_schemas.yml b/src/bundle/Resources/api_platform/base_schemas.yml new file mode 100644 index 000000000..67b6733f6 --- /dev/null +++ b/src/bundle/Resources/api_platform/base_schemas.yml @@ -0,0 +1,136 @@ +schemas: + BaseObject: + type: object + required: + - _media-type + properties: + _media-type: + xml: + attribute: true + name: media-type + type: string + _href: + xml: + attribute: true + name: href + type: string + Ref: + type: + $ref: "#/components/schemas/BaseObject" + UnixTimestamp: + type: integer + Href: + type: object + required: + - _href + properties: + _href: + xml: + attribute: true + name: href + type: string + Target: + description: Struct that stores extra target information for a SortClause object. + type: object + SortClause: + description: This class is the base for SortClause classes, used to set sorting of content queries. + type: object + required: + - direction + - target + - targetData + properties: + direction: + description: Sort direction. One of Query::SORT_ASC or Query::SORT_DESC. + type: string + target: + description: "Sort target, high level: section_identifier, attribute_value, etc." + type: string + targetData: + description: Extra target data, required by some sort clauses, field for instance. + type: + $ref: "#/components/schemas/Target" + ErrorMessage: + description: Represents an error response. Might contain additional properties depending on an error type. + type: object + required: + - errorCode + - errorMessage + - errorDescription + properties: + errorCode: + type: integer + errorMessage: + type: string + errorDescription: + type: string + Value: + description: Struct that stores extra value information for a Criterion object. + type: object + required: + - _languageCode + - "#text" + properties: + _languageCode: + description: Language code. + type: string + "#text": + description: Content type description. + type: [string, 'null'] + ValueObject: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + ValueArray: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + MultilingualValue: + allOf: + - $ref: "#/components/schemas/Value" + - description: Object that represents a multilingual (translated) value. + type: object + required: + - _languageCode + - "#text" + properties: + _languageCode: + description: Language code. + type: string + "#text": + description: Translation contents. + type: [string, 'null'] + KeyValue: + description: Key-value structure + type: object + required: + - _key + - "#text" + properties: + _key: + type: string + "#text": + type: [string, 'null'] + DateRange: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Representation of date range. + type: object + required: + - startDate + - endDate + properties: + startDate: + type: string + endDate: + type: string diff --git a/src/bundle/Resources/api_platform/bookmarks_schemas.yml b/src/bundle/Resources/api_platform/bookmarks_schemas.yml new file mode 100644 index 000000000..d70804aed --- /dev/null +++ b/src/bundle/Resources/api_platform/bookmarks_schemas.yml @@ -0,0 +1,80 @@ +schemas: + BookmarkList: + description: List of bookmarked Locations. + type: object + required: + - count + - items + properties: + count: + description: The total number of bookmarks. + type: integer + items: + description: List of bookmarked Locations. + type: array + items: + type: object + required: + - Location + - _media-type + - __href + properties: + Location: + type: + $ref: "#/components/schemas/Location" + _media-type: + type: string + __href: + type: string + BookmarkListWrapper: + type: object + required: + - BookmarkList + properties: + BookmarkList: + type: + $ref: "#/components/schemas/BookmarkList" + SummaryEntry: + type: object + required: + - identifier + - id + - names + - quantity + - Price + - PriceInclVat + - SubtotalPrice + - SubtotalPriceInclVat + - VatCategory + - Product + properties: + identifier: + type: string + id: + type: integer + names: + type: + $ref: "#/components/schemas/SummaryEntryNames" + quantity: + type: integer + Price: + type: + $ref: "#/components/schemas/RestPriceWrapper" + PriceInclVat: + type: + $ref: "#/components/schemas/RestPriceWrapper" + SubtotalPrice: + type: + $ref: "#/components/schemas/RestPriceWrapper" + SubtotalPriceInclVat: + type: + $ref: "#/components/schemas/RestPriceWrapper" + VatCategory: + type: + $ref: "#/components/schemas/VatCategory" + Product: + type: + $ref: "#/components/schemas/Product" + SummaryEntryNames: + type: + $ref: "#/components/schemas/ValueObject" diff --git a/src/bundle/Resources/api_platform/content_locations_schemas.yml b/src/bundle/Resources/api_platform/content_locations_schemas.yml new file mode 100644 index 000000000..813f4f613 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_locations_schemas.yml @@ -0,0 +1,189 @@ +schemas: + Location: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a Location in the Repository. + type: object + required: + - id + - priority + - hidden + - invisible + - remoteId + - ContentInfo + - ParentLocation + - pathString + - depth + - childCount + - sortField + - sortOrder + - Content + properties: + id: + description: The ID of the Location. + type: integer + priority: + description: Location priority. Position of the Location among its siblings when sorted using priority sort order. + type: integer + hidden: + description: Indicates that the Location entity has been explicitly marked as hidden. + type: boolean + invisible: + description: Indicates that the Location is implicitly marked as hidden by a parent Location. + type: boolean + remoteId: + description: Remote ID, universally unique identifier. + type: string + ContentInfo: + description: This class provides all version independent information of the content item. + type: + $ref: "#/components/schemas/ContentInfo" + ParentLocation: + description: Parent Location. + Children: + description: Children Location. + pathString: + description: The materialized path of the Location entry e.g. /1/2/. + type: string + depth: + description: Depth Location has in the Location tree. + type: integer + childCount: + description: Depth Location has in the Location tree. + type: integer + sortField: + description: "Specifies which property the child Locations should be sorted on. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: PATH." + enum: + - PATH + - PUBLISHED + - MODIFIED + - SECTION + - DEPTH + - CLASS_IDENTIFIER + - CLASS_NAME + - PRIORITY + - NAME + - MODIFIED_SUBNODE + - NODE_ID + - CONTENTOBJECT_ID + type: string + sortOrder: + description: "Specifies whether the sort order should be ascending or descending. Map for Location sort order to their respective Query SORT constants. One of the fallowing values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1." + enum: + - ASC + - DESC + type: string + Content: + description: Represents a content item in a specific version. + UrlAliases: + description: This class represents URL aliases. + LocationWrapper: + type: object + required: + - Location + properties: + Location: + $ref: "#/components/schemas/Location" + LocationCreate: + allOf: + - $ref: "#/components/schemas/BaseObject" + - type: object + required: + - ParentLocation + - priority + - hidden + - sortField + - sortOrder + properties: + ParentLocation: + type: object + required: + - _href + properties: + _href: + xml: + attribute: true + name: href + type: string + priority: + type: string + hidden: + type: [string, boolean] + sortField: + type: string + sortOrder: + type: string + LocationCreateWrapper: + type: object + required: + - LocationCreate + properties: + LocationCreate: + $ref: "#/components/schemas/LocationCreate" + LocationUpdateStruct: + description: This class is used for updating Location meta data. + type: object + required: + - priority + - remoteId + - hidden + - sortField + - sortOrder + properties: + priority: + description: If set the Location priority is changed to the new value. + type: string + remoteId: + description: If set the Location gets a new remoteId. Needs to be a unique Location->remoteId string value. + type: string + hidden: + type: boolean + sortField: + description: If set the sortField is changed. The sort field specifies which property the child Locations should be sorted on. Valid values are found at {@link Location::SORT_FIELD_*}. + enum: + - PATH + - PUBLISHED + - MODIFIED + - SECTION + - DEPTH + - CLASS_IDENTIFIER + - CLASS_NAME + - PRIORITY + - NAME + - MODIFIED_SUBNODE + - NODE_ID + - CONTENTOBJECT_ID + type: string + sortOrder: + description: If set the sortOrder is changed. The sort order specifies whether the sort order should be ascending or descending. Valid values are {@link Location::SORT_ORDER_*}. + enum: + - ASC + - DESC + type: string + LocationUpdateStructWrapper: + type: object + required: + - LocationUpdate + properties: + LocationUpdate: + $ref: "#/components/schemas/LocationUpdateStruct" + LocationList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a queried Location list holding a totalCount and a partial list of Locations (by offset/limit parameters and permission filters). + type: object + required: + - Location + properties: + Location: + description: The partial list of Locations controlled by offset/limit. + type: array + items: + $ref: "#/components/schemas/Ref" + LocationListWrapper: + type: object + required: + - LocationList + properties: + LocationList: + $ref: "#/components/schemas/LocationList" diff --git a/src/bundle/Resources/api_platform/content_objects_schemas.yml b/src/bundle/Resources/api_platform/content_objects_schemas.yml new file mode 100644 index 000000000..c83f50a66 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_objects_schemas.yml @@ -0,0 +1,506 @@ +schemas: + Content: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Content ID matcher class. + type: object + required: + - _remoteId + - _id + - ContentType + - Name + - Versions + - CurrentVersion + - Section + - Locations + - Owner + - mainLanguageCode + - currentVersionNo + - alwaysAvailable + - status + - ObjectStates + properties: + _remoteId: + description: Remote ID of the content type. + xml: + attribute: true + name: remoteId + type: string + _id: + description: Unique ID of the content type. + xml: + attribute: true + name: id + type: integer + ContentType: + description: Content type. + type: + $ref: "#/components/schemas/BaseObject" + Name: + description: Name of the domain object in a given language. + type: string + Versions: + description: Returns the VersionInfo for this version. + type: + $ref: "#/components/schemas/BaseObject" + CurrentVersion: + description: Current version. + type: + $ref: "#/components/schemas/BaseObject" + Section: + description: The Section to which the content item is assigned to. + type: + $ref: "#/components/schemas/BaseObject" + Locations: + description: Location of the content item. + type: + $ref: "#/components/schemas/BaseObject" + Owner: + description: The owner of the content item. + type: + $ref: "#/components/schemas/BaseObject" + lastModificationDate: + description: Content item modification date. + type: string + format: date-time + publishedDate: + description: Content item publication date. + type: string + format: date-time + mainLanguageCode: + description: The main language code of the content item. + type: string + currentVersionNo: + description: Current version number is the version number of the published version or the version number of a newly created draft (which is 1). + type: integer + alwaysAvailable: + description: Indicates if the content item is shown in the main language if it's not present in an other requested language. + type: boolean + status: + description: "Status of the content. Possible values: const STATUS_DRAFT = 0;const STATUS_PUBLISHED = 1; const STATUS_TRASHED = 2." + type: string + ObjectStates: + description: Object states. + type: + $ref: "#/components/schemas/BaseObject" + ContentWrapper: + type: object + required: + - Content + properties: + Content: + $ref: "#/components/schemas/Content" + ContentCreate: + description: This class is used for creating a new content item. + type: object + required: + - ContentType + - mainLanguageCode + - LocationCreate + - fields + properties: + ContentType: + description: The content type for which the new content item is created. + type: + oneOf: + - "#/components/schemas/Content" + - "#/components/schemas/Href" + Section: + description: The Section the content item is assigned to. If not set the Section of the parent is used or a default Section. + type: + $ref: "#/components/schemas/Href" + User: + description: The owner of the content. If not given the current authenticated User is set as owner. + type: integer + alwaysAvailable: + description: Indicates if the content item is shown in the main language if it's not present in an other requested language. + type: string + remoteId: + description: Remote identifier used as a custom identifier for the content item. Needs to be a unique Content->remoteId string value. + type: string + mainLanguageCode: + description: The main language code for the content. This language will also be used for as initial language for the first created version. It is also used as default language for added fields. + type: string + modificationDate: + description: Modification date. If not given, the current integer is used. + type: string + format: date-time + LocationCreate: + type: object + fields: + type: object + required: + - field + properties: + field: + type: array + items: + type: object + ContentCreateWrapper: + type: object + required: + - ContentCreate + properties: + ContentCreate: + $ref: "#/components/schemas/ContentCreate" + ContentUpdate: + description: This class is used to update a Content. + type: object + required: + - mainLanguageCode + - Section + - MainLocation + - Owner + - alwaysAvailable + - remoteId + properties: + mainLanguageCode: + type: string + Section: + type: + $ref: "#/components/schemas/Section" + MainLocation: + type: + $ref: "#/components/schemas/Location" + Owner: + type: + $ref: "#/components/schemas/User" + alwaysAvailable: + type: boolean + remoteId: + type: string + ContentInfo: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class provides all version independent information of the content item. + type: object + required: + - Content + properties: + Content: + description: Content ID matcher class. + type: + $ref: "#/components/schemas/Content" + ContentInfoWrapper: + type: object + required: + - ContentInfo + properties: + ContentInfo: + $ref: "#/components/schemas/ContentInfo" + ContentMetadataUpdate: + description: This class is used to update a Content metadata. + type: object + required: + - ownerId + - publishedDate + - modificationDate + - mainLanguageCode + - alwaysAvailable + - remoteId + - mainLocationId + - name + properties: + ownerId: {} + publishedDate: + type: string + format: date-time + modificationDate: + type: string + format: date-time + mainLanguageCode: + type: string + alwaysAvailable: + type: boolean + remoteId: + type: string + mainLocationId: {} + name: + type: string + ContentCreateContentType: + type: object + required: + - _href + - FieldDefinitions + properties: + _href: + xml: + attribute: true + name: href + type: string + FieldDefinitions: + allOf: + - $ref: "#/components/schemas/BaseObject" + - type: object + properties: + FieldDefinitions: + type: array + items: + $ref: "#/components/schemas/FieldDefinition" + ContentObjectStates: + description: Represents a list of object states. + type: object + required: + - ObjectState + properties: + ObjectState: + description: List of object state values. + type: array + items: + $ref: "#/components/schemas/Href" + ContentObjectStatesWrapper: + type: object + required: + - ContentObjectStates + properties: + ContentObjectStates: + $ref: "#/components/schemas/ContentObjectStates" + VersionInfo: + description: This class holds version information data. It also contains the corresponding {@link Content} to which the version belongs to. + type: object + required: + - id + - versionNo + - status + - modificationDate + - Creator + - creationDate + - initialLanguageCode + - languageCodes + - VersionTranslationInfo + - names + - Content + properties: + id: + description: Version ID. + type: integer + versionNo: + description: Version number. In contrast to {@link $id}, this is the version number, which only increments in scope of a single content item. + type: integer + status: + description: "One of: VersionInfo::STATUS_DRAFT=0, VersionInfo::STATUS_PUBLISHED=1, VersionInfo::STATUS_ARCHIVED=3." + enum: + - DRAFT + - PUBLISHED + - ARCHIVED + type: string + modificationDate: + description: The last modified date of this version. + type: string + format: date-time + Creator: + description: Creator of the version, in the search API this is referred to as the modifier of the published content. + type: + $ref: "#/components/schemas/BaseObject" + creationDate: + description: Content creation date. + type: string + format: date-time + initialLanguageCode: + description: The language code which is used for labeling a translation. + type: string + languageCodes: + description: List of languages in this version. Reflects which languages fields exists in for this version. + type: string + VersionTranslationInfo: + description: Translation information. + names: + description: Names. + type: + $ref: "#/components/schemas/ValueArray" + Content: + description: Represents a content item in a specific version. + type: + $ref: "#/components/schemas/BaseObject" + Version: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Returns the VersionInfo for this version. + type: object + required: + - VersionInfo + - Fields + - Relations + properties: + VersionInfo: + description: VersionInfo for this version. + Fields: + type: object + required: + - field + properties: + field: + description: Fields of a Company content item. + type: array + items: + $ref: "#/components/schemas/Field" + Relations: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Relations of the user. + type: object + required: + - Relation + properties: + Relation: + type: array + items: + $ref: "#/components/schemas/Relation" + VersionWrapper: + type: object + required: + - Version + properties: + Version: + $ref: "#/components/schemas/Version" + VersionUpdate: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is used to update a content version. + type: object + required: + - modificationDate + - initialLanguageCode + - fields + properties: + modificationDate: + type: string + format: date-time + initialLanguageCode: + type: string + fields: + type: array + items: + $ref: "#/components/schemas/Field" + VersionUpdateWrapper: + type: object + required: + - VersionUpdate + properties: + VersionUpdate: + $ref: "#/components/schemas/VersionUpdate" + VersionList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of all versions of the content. + type: object + required: + - VersionItem + properties: + VersionItem: + type: array + items: + $ref: "#/components/schemas/VersionItem" + VersionListWrapper: + type: object + required: + - VersionList + properties: + VersionList: + $ref: "#/components/schemas/VersionList" + VersionItem: + description: Version of content. + type: object + required: + - Version + - VersionInfo + properties: + Version: + description: Returns the VersionInfo for this version. + type: + $ref: "#/components/schemas/BaseObject" + VersionInfo: + type: + $ref: "#/components/schemas/VersionInfo" + VersionTranslationInfo: + description: Translation information. + type: object + required: + - _media-type + - Language + properties: + _media-type: + type: string + Language: + type: array + items: + $ref: "#/components/schemas/LanguageCode" + LanguageCode: + description: Language code. + type: object + required: + - languageCode + properties: + languageCode: + type: string + Relation: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Class representing a relation between content. + type: object + required: + - SourceContent + - DestinationContent + - RelationType + properties: + SourceContent: + description: The content of the source content of the relation. + type: + $ref: "#/components/schemas/Ref" + DestinationContent: + description: The content of the destination content of the relation. + type: + $ref: "#/components/schemas/Ref" + RelationType: + description: "The relation type bitmask. Relations: Relation::COMMON = 1, Relation::EMBED = 2, Relation::LINK = 4, Relation::FIELD = 8, Relation::ASSET = 16" + type: string + RelationWrapper: + type: object + required: + - Relation + properties: + Relation: + $ref: "#/components/schemas/Relation" + RelationCreate: + type: object + required: + - Destination + properties: + Destination: + type: + $ref: "#/components/schemas/Href" + RelationCreateWrapper: + type: object + required: + - RelationCreate + properties: + RelationCreate: + $ref: "#/components/schemas/RelationCreate" + RelationList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Class representing a list of relations between content. + type: object + required: + - Relation + properties: + Relation: + type: array + items: + $ref: "#/components/schemas/Relation" + RelationListWrapper: + type: object + required: + - Relations + properties: + Relations: + $ref: "#/components/schemas/RelationList" + Fields: + type: object + required: + - field + properties: + field: + type: array + items: + $ref: "#/components/schemas/Field" diff --git a/src/bundle/Resources/api_platform/content_sections_schemas.yml b/src/bundle/Resources/api_platform/content_sections_schemas.yml new file mode 100644 index 000000000..2b8a5ccff --- /dev/null +++ b/src/bundle/Resources/api_platform/content_sections_schemas.yml @@ -0,0 +1,63 @@ +schemas: + Section: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a Section. + type: object + required: + - sectionId + - identifier + - name + properties: + sectionId: + description: ID of the Section. + type: integer + identifier: + description: Unique identifier of the Section. + type: string + name: + description: Name of the Section. + type: string + SectionWrapper: + type: object + required: + - Section + properties: + Section: + $ref: "#/components/schemas/Section" + SectionInput: + type: object + required: + - identifier + - name + properties: + identifier: + type: string + name: + type: string + SectionInputWrapper: + type: object + required: + - SectionInput + properties: + SectionInput: + $ref: "#/components/schemas/SectionInput" + SectionList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a Section list. + type: object + required: + - Section + properties: + Section: + type: array + items: + $ref: "#/components/schemas/Section" + SectionListWrapper: + type: object + required: + - SectionList + properties: + SectionList: + $ref: "#/components/schemas/SectionList" diff --git a/src/bundle/Resources/api_platform/content_trash_schemas.yml b/src/bundle/Resources/api_platform/content_trash_schemas.yml new file mode 100644 index 000000000..c5aec9ffa --- /dev/null +++ b/src/bundle/Resources/api_platform/content_trash_schemas.yml @@ -0,0 +1,30 @@ +schemas: + Trash: + allOf: + - $ref: "#/components/schemas/BaseObject" + - type: object + required: + - TrashItem + properties: + TrashItem: + type: array + items: + $ref: "#/components/schemas/TrashItem" + TrashWrapper: + type: object + required: + - Trash + properties: + Trash: + $ref: "#/components/schemas/Trash" + TrashItem: + description: This class represents a trash item, which is actually a trashed Location. + type: + $ref: "#/components/schemas/Location" + TrashItemWrapper: + type: object + required: + - TrashItem + properties: + TrashItem: + $ref: "#/components/schemas/TrashItem" diff --git a/src/bundle/Resources/api_platform/content_type_groups_schemas.yml b/src/bundle/Resources/api_platform/content_type_groups_schemas.yml new file mode 100644 index 000000000..d32dbba68 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_type_groups_schemas.yml @@ -0,0 +1,104 @@ +schemas: + ContentTypeGroup: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a content type group value. + type: object + required: + - id + - identifier + - created + - modified + - Creator + - Modifier + - ContentTypes + properties: + id: + description: Primary key. + type: integer + identifier: + description: Readable string identifier of a group. + type: string + created: + description: Created date (integer). + type: string + format: date-time + modified: + description: Modified date (integer). + type: string + format: date-time + Creator: + description: Creator User ID. + type: + $ref: "#/components/schemas/BaseObject" + Modifier: + description: Modifier User ID. + type: + $ref: "#/components/schemas/BaseObject" + ContentTypes: + description: Content types. + type: + $ref: "#/components/schemas/BaseObject" + ContentTypeGroupWrapper: + type: object + required: + - ContentTypeGroup + properties: + ContentTypeGroup: + $ref: "#/components/schemas/ContentTypeGroup" + ContentTypeGroupInput: + type: object + ContentTypeGroupInputWrapper: + type: object + required: + - ContentTypeGroupInput + properties: + ContentTypeGroupInput: + $ref: "#/components/schemas/ContentTypeGroupInput" + ContentTypeGroupListWrapper: + type: object + required: + - ContentTypeGroupList + properties: + ContentTypeGroupList: + $ref: "#/components/schemas/ContentTypeGroupList" + ContentTypeGroupRef: + description: Content type group reference. + type: array + items: + allOf: + - $ref: "#/components/schemas/BaseObject" + type: object + properties: + unlink: + $ref: "#/components/schemas/Unlink" + ContentTypeGroupRefList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of content type groups references. + type: object + required: + - ContentTypeGroupRef + properties: + ContentTypeGroupRef: + type: + $ref: "#/components/schemas/ContentTypeGroupRef" + ContentTypeGroupList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of content type groups. + type: object + required: + - ContentTypeGroup + properties: + ContentTypeGroup: + type: array + items: + $ref: "#/components/schemas/ContentTypeGroup" + ContentTypeGroupRefListWrapper: + type: object + required: + - ContentTypeGroupRefList + properties: + ContentTypeGroupRefList: + $ref: "#/components/schemas/ContentTypeGroupRefList" diff --git a/src/bundle/Resources/api_platform/content_types_schemas.yml b/src/bundle/Resources/api_platform/content_types_schemas.yml new file mode 100644 index 000000000..b86770294 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_types_schemas.yml @@ -0,0 +1,561 @@ +schemas: + ContentType: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a content type. + type: object + required: + - id + - status + - identifier + - names + - descriptions + - creationDate + - modificationDate + - Creator + - Modifier + - Groups + - Draft + - remoteId + - urlAliasSchema + - nameSchema + - isContainer + - defaultAlwaysAvailable + - defaultSortField + - defaultSortOrder + - FieldDefinitions + properties: + id: + description: Content type ID. + type: integer + status: + description: "The status of the content type. Possible values: const STATUS_DEFINED = 0; Status constant for defined (aka published) Type, const STATUS_DRAFT = 1; Status constant for draft (aka temporary) Type; const STATUS_MODIFIED = 2; Status constant for modified (aka deferred for publishing) Type." + enum: + - DEFINED + - DRAFT + - MODIFIED + - PUBLISHED + type: string + identifier: + description: String identifier of a content type. + type: string + names: + description: Name of a content type. + descriptions: + description: Description of a content type. + creationDate: + description: Creation date of the content type. + type: string + format: date-time + modificationDate: + description: Modification date of the content type. + type: string + format: date-time + Creator: + description: Creator User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Modifier: + description: Modifier User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Groups: + description: Group User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Draft: + description: Draft of the content type. + type: + $ref: "#/components/schemas/BaseObject" + remoteId: + description: Unique remote ID of the content type. + type: string + urlAliasSchema: + description: URL alias schema. If nothing is provided, $nameSchema will be used instead. + type: [ string, 'null' ] + nameSchema: + description: Name schema. Can be composed of FieldDefinition identifier place holders. + type: string + isContainer: + description: A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected. + type: boolean + defaultAlwaysAvailable: + description: If an instance of a content type is created the always available flag is set by default to this value. + type: boolean + defaultSortField: + description: "Specifies which property the child Locations should be sorted on by default when created. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: const SORT_FIELD_PATH = 1; const SORT_FIELD_PUBLISHED = 2; const SORT_FIELD_MODIFIED = 3; const SORT_FIELD_SECTION = 4; const SORT_FIELD_DEPTH = 5; const SORT_FIELD_PRIORITY = 8; const SORT_FIELD_NAME = 9; const SORT_FIELD_NODE_ID = 11; const SORT_FIELD_CONTENTOBJECT_ID = 12." + enum: + - PATH + - PUBLISHED + - MODIFIED + - SECTION + - DEPTH + - PRIORITY + - NAME + - NODE_ID + - CONTENTOBJECT_ID + type: string + defaultSortOrder: + description: "Specifies whether the sort order should be ascending or descending by default when created. Map for Location sort order to their respective Query SORT constants. Possible values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1." + enum: + - ASC + - DESC + type: string + FieldDefinitions: + description: This method returns the content type Field definitions from this type. + ContentTypeWrapper: + type: object + required: + - ContentType + properties: + ContentType: + $ref: "#/components/schemas/ContentType" + ContentTypeCreate: + description: This class is used to create a content type. + type: object + required: + - identifier + - mainLanguageCode + - remoteId + - urlAliasSchema + - nameSchema + - isContainer + - defaultSortField + - defaultSortOrder + - defaultAlwaysAvailable + - names + - descriptions + properties: + identifier: + type: string + mainLanguageCode: + type: string + remoteId: + type: string + urlAliasSchema: + type: string + nameSchema: + type: string + isContainer: + type: boolean + defaultSortField: + description: Specifies which property the child Locations should be sorted on by default when created. + defaultSortOrder: + description: Specifies whether the sort order should be ascending or descending by default when created. + defaultAlwaysAvailable: + type: boolean + names: + type: + $ref: "#/components/schemas/ValueObject" + descriptions: + type: + $ref: "#/components/schemas/ValueObject" + FieldDefinition: + type: + $ref: "#/components/schemas/FieldDefinition" + creatorId: + description: If set, this value overrides the current user as creator. + creationDate: + type: string + format: date-time + ContentTypeCreateWrapper: + type: object + required: + - ContentTypeCreate + properties: + ContentTypeCreate: + $ref: "#/components/schemas/ContentTypeCreate" + ContentTypeUpdateStruct: + description: This class is used for updating a content type. + type: object + properties: + identifier: + description: If set the unique identifier of a type is changed to this value. + type: string + remoteId: + description: If set the remote ID is changed to this value. + type: string + urlAliasSchema: + description: If set the URL alias schema is changed to this value. + type: string + nameSchema: + description: f set the name schema is changed to this value. + type: string + isContainer: + description: If set the container flag is set to this value. + type: boolean + mainLanguageCode: + description: If set the main language is changed to this value. + type: string + defaultSortField: + description: If set the default sort field is changed to this value. + enum: + - PATH + - PUBLISHED + - MODIFIED + - SECTION + - DEPTH + - PRIORITY + - NAME + - NODE_ID + - CONTENTOBJECT_ID + type: string + defaultSortOrder: + description: If set the default sort order is set to this value. + enum: + - ASC + - DESC + type: string + defaultAlwaysAvailable: + description: If set the default always available flag is set to this value. + type: boolean + modifierId: + description: If set this value overrides the current User as creator. + type: integer + modificationDate: + description: If set this value overrides the current time for creation. + type: string + format: date-time + names: + description: If set this array of names with languageCode keys replace the complete name collection. + descriptions: + description: If set this array of descriptions with languageCode keys replace the complete description collection. + ContentTypeUpdate: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is used to update a content type. + type: object + properties: + identifier: + type: string + mainLanguageCode: + type: string + remoteId: + type: string + urlAliasSchema: + type: [ string, 'null' ] + nameSchema: + type: string + isContainer: + type: boolean + defaultSortField: + description: Specifies which property the child Locations should be sorted on by default when updated. + defaultSortOrder: + description: Specifies whether the sort order should be ascending or descending by default when updated. + defaultAlwaysAvailable: + type: [ string, boolean ] + names: + type: + $ref: "#/components/schemas/ValueObject" + descriptions: + type: + $ref: "#/components/schemas/ValueObject" + modifierId: + description: If set, this value overrides the current user as creator. + modificationDate: + type: string + format: date-time + ContentTypeUpdateWrapper: + type: object + required: + - ContentTypeUpdate + properties: + ContentTypeUpdate: + $ref: "#/components/schemas/ContentTypeUpdate" + ContentTypeList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of content types. + type: object + required: + - ContentType + properties: + ContentType: + type: array + items: + $ref: "#/components/schemas/ContentType" + ContentTypeListWrapper: + type: object + required: + - ContentTypeList + properties: + ContentTypeList: + $ref: "#/components/schemas/ContentTypeList" + ContentTypeInfo: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class stores content type information. + type: object + required: + - id + - status + - identifier + - names + - descriptions + - creationDate + - modificationDate + - Creator + - Modifier + - Groups + - Draft + - remoteId + - urlAliasSchema + - nameSchema + - isContainer + - mainLanguageCode + - defaultAlwaysAvailable + - defaultSortField + - defaultSortOrder + properties: + id: + description: Content type ID. + type: integer + status: + description: "The status of the content type. Possible values: const STATUS_DEFINED = 0; Status constant for defined (aka published) Type, const STATUS_DRAFT = 1; Status constant for draft (aka temporary) Type; const STATUS_MODIFIED = 2; Status constant for modified (aka deferred for publishing) Type." + enum: + - DEFINED + - DRAFT + - MODIFIED + - PUBLISHED + type: string + identifier: + description: String identifier of a content type. + type: string + names: + description: Name of a content type. + descriptions: + description: Description of a content type. + creationDate: + description: Creation date of the content type. + type: string + format: date-time + modificationDate: + description: Modification date of the content type. + type: string + format: date-time + Creator: + description: Creator User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Modifier: + description: Modifier User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Groups: + description: Group User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Draft: + description: Draft of the content type. + type: + $ref: "#/components/schemas/BaseObject" + remoteId: + description: Unique remote ID of the content type. + type: string + urlAliasSchema: + description: URL alias schema. If nothing is provided, $nameSchema will be used instead. + type: [ string, 'null' ] + nameSchema: + description: Name schema. Can be composed of FieldDefinition identifier place holders. + type: string + isContainer: + description: A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected. + type: boolean + mainLanguageCode: + description: Main language code. + type: string + defaultAlwaysAvailable: + description: If an instance of a content type is created the always available flag is set by default to this value. + type: boolean + defaultSortField: + description: "Specifies which property the child Locations should be sorted on by default when created. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: const SORT_FIELD_PATH = 1; const SORT_FIELD_PUBLISHED = 2; const SORT_FIELD_MODIFIED = 3; const SORT_FIELD_SECTION = 4; const SORT_FIELD_DEPTH = 5; const SORT_FIELD_PRIORITY = 8; const SORT_FIELD_NAME = 9; const SORT_FIELD_NODE_ID = 11; const SORT_FIELD_CONTENTOBJECT_ID = 12." + enum: + - PATH + - PUBLISHED + - MODIFIED + - SECTION + - DEPTH + - PRIORITY + - NAME + - NODE_ID + - CONTENTOBJECT_ID + type: string + defaultSortOrder: + description: "Specifies whether the sort order should be ascending or descending by default when created. Map for Location sort order to their respective Query SORT constants. Possible values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1." + enum: + - ASC + - DESC + type: string + ContentTypeInfoWrapper: + type: object + required: + - ContentTypeInfo + properties: + ContentTypeInfo: + $ref: "#/components/schemas/ContentTypeInfo" + ContentTypeInfoList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of content type information. + type: object + required: + - ContentType + properties: + ContentType: + description: This class stores content type information. + type: array + items: + $ref: "#/components/schemas/ContentTypeInfo" + ContentTypeInfoListWrapper: + type: object + required: + - ContentTypeList + properties: + ContentTypeList: + $ref: "#/components/schemas/ContentTypeInfoList" + Field: + description: This class represents a field of a content item. + type: object + required: + - fieldDefinitionIdentifier + - fieldValue + properties: + id: + description: The field ID. + type: integer + fieldDefinitionIdentifier: + description: The Field definition identifier. + type: string + languageCode: + description: The language code. + type: string + fieldTypeIdentifier: + description: Field Type identifier. + type: string + fieldValue: + description: A Field Type value or a value type which can be converted by the corresponding field type. + FieldDefinition: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a Field definition. + type: object + required: + - id + - identifier + - fieldType + - fieldGroup + - position + - isTranslatable + - isRequired + - isInfoCollector + - defaultValue + - isSearchable + - names + - descriptions + - fieldSettings + - validatorConfiguration + properties: + id: + description: The unique ID of this Field definition. + type: integer + identifier: + description: Readable string identifier of a Field definition. + type: string + fieldType: + description: String identifier of the field type. + type: string + fieldGroup: + description: Field group name. + type: string + position: + description: The position of the Field definition in the content type. + type: integer + isTranslatable: + description: If the field is translatable. + type: boolean + isRequired: + description: Is the field required. + type: boolean + isInfoCollector: + description: The flag if this field is used for information collection. + type: boolean + defaultValue: + description: Default value of the field. + isSearchable: + description: Indicates if th the content is searchable by this attribute. + type: boolean + names: + description: Names of content types. + descriptions: + description: Descriptions of content types. + fieldSettings: + description: Settings for the Field definition supported by the field type. + validatorConfiguration: + description: Validator configuration of this Field definition supported by the field type. + type: + oneOf: + - StringLengthValidatorWrapper + - array + FieldDefinitionWrapper: + type: object + required: + - FieldDefinition + properties: + FieldDefinition: + $ref: "#/components/schemas/FieldDefinition" + FieldDefinitionCreate: + type: + $ref: "#/components/schemas/BaseObject" + FieldDefinitionCreateWrapper: + type: object + required: + - FieldDefinitionCreate + properties: + FieldDefinitionCreate: + $ref: "#/components/schemas/FieldDefinitionCreate" + FieldDefinitionUpdate: + type: + $ref: "#/components/schemas/BaseObject" + FieldDefinitionUpdateWrapper: + type: object + required: + - FieldDefinitionUpdate + properties: + FieldDefinitionUpdate: + $ref: "#/components/schemas/FieldDefinitionUpdate" + FieldDefinitions: + type: object + required: + - FieldDefinition + properties: + FieldDefinition: + type: array + items: + $ref: "#/components/schemas/FieldDefinition" + FieldDefinitionsWrapper: + type: object + required: + - FieldDefinitions + properties: + FieldDefinitions: + $ref: "#/components/schemas/FieldDefinitions" + StringLengthValidator: + description: Validator for checking min. and max. length of strings. + type: object + required: + - maxStringLength + - minStringLength + properties: + maxStringLength: + description: Maximum length of strings. + type: [ integer, null ] + minStringLength: + description: Minimum length of strings. + type: [ integer, null ] + StringLengthValidatorWrapper: + type: object + required: + - StringLengthValidator + properties: + StringLengthValidator: + $ref: "#/components/schemas/StringLengthValidator" diff --git a/src/bundle/Resources/api_platform/content_url_aliases_schemas.yml b/src/bundle/Resources/api_platform/content_url_aliases_schemas.yml new file mode 100644 index 000000000..2d5432c40 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_url_aliases_schemas.yml @@ -0,0 +1,104 @@ +schemas: + UrlAlias: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a URL alias in the Repository. + type: object + required: + - _id + - _type + - resource + - path + - languageCodes + - alwaysAvailable + - isHistory + - forward + - custom + properties: + _id: + description: A unique identifier for the alias. + xml: + attribute: true + name: id + type: string + _type: + description: The type of the URL Alias i.e. one of URLAlias::LOCATION=0, URLAlias::RESOURCE=1, URLAlias::VIRTUAL=2. + enum: + - LOCATION + - RESOURCE + - VIRTUAL + xml: + attribute: true + name: type + type: string + resource: + description: If type = URLAlias::LOCATION it is a Location ID otherwise a string (e.g. /content/search). + path: + description: The full path of the alias. + type: string + languageCodes: + description: The languageCodes for which this path is valid. + type: string + alwaysAvailable: + description: Fallback indicator for other languages. + type: boolean + isHistory: + description: Indicates that this alias was autogenerated for an in the meanwhile archived version of the content. + type: boolean + forward: + description: Indicates if the URL should be redirected. + type: boolean + custom: + description: If false this alias was autogenerated otherwise manually created. + type: boolean + UrlAliasWrapper: + type: object + required: + - UrlAlias + properties: + UrlAlias: + $ref: "#/components/schemas/UrlAlias" + UrlAliasCreate: + type: object + required: + - _type + properties: + _type: + type: string + location: + type: + $ref: "#/components/schemas/Href" + resource: + type: string + languageCode: + type: string + alwaysAvailable: + type: [ string, boolean ] + forward: + type: [ string, boolean ] + UrlAliasCreateWrapper: + type: object + required: + - UrlAliasCreate + properties: + UrlAliasCreate: + $ref: "#/components/schemas/UrlAliasCreate" + UrlAliasRefList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of URL alias in the Repository. + type: object + required: + - UrlAlias + properties: + UrlAlias: + type: array + items: + $ref: "#/components/schemas/Ref" + UrlAliasRefListWrapper: + type: object + required: + - UrlAliasRefList + properties: + UrlAliasRefList: + $ref: "#/components/schemas/UrlAliasRefList" diff --git a/src/bundle/Resources/api_platform/content_url_wildcards_schemas.yml b/src/bundle/Resources/api_platform/content_url_wildcards_schemas.yml new file mode 100644 index 000000000..b360d37a6 --- /dev/null +++ b/src/bundle/Resources/api_platform/content_url_wildcards_schemas.yml @@ -0,0 +1,74 @@ +schemas: + UrlWildcard: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a URL alias in the Repository. + type: object + required: + - _id + - sourceUrl + - destinationUrl + - forward + properties: + _id: + description: The unique ID. + xml: + attribute: true + name: id + type: integer + sourceUrl: + description: The source URL. + type: string + destinationUrl: + description: The destination URL containing placeholders e.g. /destination/{1}. + type: string + forward: + description: Indicates if the URL is redirected or not. + type: boolean + UrlWildcardWrapper: + type: object + required: + - UrlWildcard + properties: + UrlWildcard: + $ref: "#/components/schemas/UrlWildcard" + UrlWildcardCreate: + description: Creates a new URL wildcard. + type: object + required: + - sourceUrl + - destinationUrl + - forward + properties: + sourceUrl: + type: string + destinationUrl: + type: string + forward: + type: [ string, boolean ] + UrlWildcardCreateWrapper: + type: object + required: + - URLWildcardCreate + properties: + URLWildcardCreate: + $ref: "#/components/schemas/UrlWildcardCreate" + UrlWildcardList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of URL alias in the Repository. + type: object + required: + - UrlWildcard + properties: + UrlWildcard: + type: array + items: + $ref: "#/components/schemas/UrlWildcard" + UrlWildcardListWrapper: + type: object + required: + - UrlWildcardList + properties: + UrlWildcardList: + $ref: "#/components/schemas/UrlWildcardList" diff --git a/src/bundle/Resources/api_platform/examples/GET/Root.json.example b/src/bundle/Resources/api_platform/examples/GET/Root.json.example new file mode 100644 index 000000000..e78d9b52b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/GET/Root.json.example @@ -0,0 +1,93 @@ +{ + "Root": { + "_media-type": "application/vnd.ibexa.api.Root+json", + "content": { + "_href": "/api/ibexa/v2/content/objects", + "_media-type": "" + }, + "contentByRemoteId": { + "_href": "/api/ibexa/v2/content/objects{?remoteId}", + "_media-type": "" + }, + "contentTypeByIdentifier": { + "_href": "/api/ibexa/v2/content/types{?identifier}", + "_media-type": "" + }, + "contentTypeGroupByIdentifier": { + "_href": "/api/ibexa/v2/content/typegroups{?identifier}", + "_media-type": "" + }, + "contentTypeGroups": { + "_href": "/api/ibexa/v2/content/typegroups", + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupList+json" + }, + "contentTypes": { + "_href": "/api/ibexa/v2/content/types", + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json" + }, + "createSession": { + "_href": "/api/ibexa/v2/user/sessions", + "_media-type": "application/vnd.ibexa.api.UserSession+json" + }, + "globalUrlAliases": { + "_href": "/api/ibexa/v2/content/urlaliases", + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json" + }, + "locationByPath": { + "_href": "/api/ibexa/v2/content/locations{?locationPath}", + "_media-type": "" + }, + "locationByRemoteId": { + "_href": "/api/ibexa/v2/content/locations{?remoteId}", + "_media-type": "" + }, + "objectStateGroups": { + "_href": "/api/ibexa/v2/content/objectstategroups", + "_media-type": "application/vnd.ibexa.api.ObjectStateGroupList+json" + }, + "objectStates": { + "_href": "/api/ibexa/v2/content/objectstategroups/{objectStateGroupId}/objectstates", + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json" + }, + "roles": { + "_href": "/api/ibexa/v2/user/roles", + "_media-type": "application/vnd.ibexa.api.RoleList+json" + }, + "rootLocation": { + "_href": "/api/ibexa/v2/content/locations/1/2", + "_media-type": "application/vnd.ibexa.api.Location+json" + }, + "rootMediaFolder": { + "_href": "/api/ibexa/v2/content/locations/1/43", + "_media-type": "application/vnd.ibexa.api.Location+json" + }, + "rootUserGroup": { + "_href": "/api/ibexa/v2/user/groups/1/5", + "_media-type": "application/vnd.ibexa.api.UserGroup+json" + }, + "sections": { + "_href": "/api/ibexa/v2/content/sections", + "_media-type": "application/vnd.ibexa.api.SectionList+json" + }, + "trash": { + "_href": "/api/ibexa/v2/content/trash", + "_media-type": "application/vnd.ibexa.api.Trash+json" + }, + "urlWildcards": { + "_href": "/api/ibexa/v2/content/urlwildcards", + "_media-type": "application/vnd.ibexa.api.UrlWildcardList+json" + }, + "users": { + "_href": "/api/ibexa/v2/user/users", + "_media-type": "application/vnd.ibexa.api.UserRefList+json" + }, + "views": { + "_href": "/api/ibexa/v2/views", + "_media-type": "application/vnd.ibexa.api.RefList+json" + }, + "refreshSession": { + "_media-type": "application\/vnd.ibexa.api.UserSession+json", + "_href": "\/api\/ezp\/v2\/user\/sessions\/{sessionId}\/refresh" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/GET/Root.xml.example b/src/bundle/Resources/api_platform/examples/GET/Root.xml.example new file mode 100644 index 000000000..8019452d5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/GET/Root.xml.example @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.json.example b/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.json.example new file mode 100644 index 000000000..ca99248ef --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.json.example @@ -0,0 +1,254 @@ +{ + "BookmarkList": { + "_media-type": "application/vnd.ibexa.api.BookmarkList+json", + "count": 3, + "items": [ + { + "_media-type": "application/vnd.ibexa.api.Bookmark+json", + "__href": "/api/ibexa/v2/bookmark/65", + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57/65", + "id": 65, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/65/", + "depth": 3, + "childCount": 0, + "remoteId": "aa538e305aea472eb221ce23d1cc4b50", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57/65/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/63" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57/65/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/63", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/63", + "_remoteId": "211e99934c8fef5900e4813b96ec5c87", + "_id": 63, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2" + }, + "Name": "Art3", + "TranslatedName": "Art3", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/63/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/63/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/63/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:34:17+00:00", + "publishedDate": "2021-06-28T11:34:17+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/63/objectstates" + } + } + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.Bookmark+json", + "__href": "/api/ibexa/v2/bookmark/68", + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/68", + "id": 68, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58" + }, + "pathString": "/1/2/58/68/", + "depth": 3, + "childCount": 0, + "remoteId": "b8cc4627dbc3ca693c85b6b06a8f7492", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/68/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/67" + }, + "sortField": "PUBLISHED", + "sortOrder": "DESC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/68/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/67", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/67", + "_remoteId": "180adb876af5755d65c1a362fcd619c8", + "_id": 67, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/45" + }, + "Name": "Tip1", + "TranslatedName": "Tip1", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/67/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/67/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/67/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:36:21+00:00", + "publishedDate": "2021-06-28T11:36:21+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/67/objectstates" + } + } + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.Bookmark+json", + "__href": "/api/ibexa/v2/bookmark/59", + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59", + "id": 59, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "pathString": "/1/2/59/", + "depth": 2, + "childCount": 3, + "remoteId": "fd949fc2eed1fff847d73021ff1a6ea9", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/58" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/58", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/58", + "_remoteId": "00b513aae54036a16e00fb3365c4b5f3", + "_id": 58, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "Dog Breed Catalog", + "TranslatedName": "Dog Breed Catalog", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/58/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/58/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/58/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:23:10+00:00", + "publishedDate": "2021-06-28T11:23:10+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/58/objectstates" + } + } + } + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.xml.example b/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.xml.example new file mode 100644 index 000000000..627155bff --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.xml.example @@ -0,0 +1,121 @@ + + + 3 + + + 65 + 0 + false + false + false + + /1/2/57/65/ + 3 + 0 + aa538e305aea472eb221ce23d1cc4b50 + + + PATH + ASC + + + + + Art3 + Art3 + + +
+ + + 2021-06-28T11:34:17+00:00 + 2021-06-28T11:34:17+00:00 + eng-GB + 1 + false + false + PUBLISHED + + + + + + + + 68 + 0 + false + false + false + + /1/2/58/68/ + 3 + 0 + b8cc4627dbc3ca693c85b6b06a8f7492 + + + PUBLISHED + DESC + + + + + Tip1 + Tip1 + + +
+ + + 2021-06-28T11:36:21+00:00 + 2021-06-28T11:36:21+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + + + + + 59 + 0 + false + false + false + + /1/2/59/ + 2 + 3 + fd949fc2eed1fff847d73021ff1a6ea9 + + + PATH + ASC + + + + + Dog Breed Catalog + Dog Breed Catalog + + +
+ + + 2021-06-28T11:23:10+00:00 + 2021-06-28T11:23:10+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example b/src/bundle/Resources/api_platform/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example new file mode 100644 index 000000000..e3dfbbc47 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example @@ -0,0 +1,15 @@ + + + https://images.unsplash.com/photo-1544568100-847a948585b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1MDk5MH0 + UtrE5DcgEyg + unsplash + + medium-coated brown dog during daytime + 5184 + 3888 + 2018-12-11T17:46:12-05:00 + 2020-10-23T23:08:44-04:00 + Jamie Street + https://unsplash.com/@jamie452?utm_source=Ibexa+Platform+DAM+Connector&utm_medium=referral&utm_campaign=api-credit + + diff --git a/src/bundle/Resources/api_platform/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example b/src/bundle/Resources/api_platform/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example new file mode 100644 index 000000000..b9455ea03 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example @@ -0,0 +1,8 @@ + + + http://127.0.0.1:8000/var/site/storage/images/_aliases/large/6/1/2/0/216-1-eng-GB/ez-logo-small.png + image/png + 20 + 20 + 1709 + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.json.example b/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.json.example new file mode 100644 index 000000000..387248e5b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58", + "id": 58, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "pathString": "/1/2/58/", + "depth": 2, + "childCount": 3, + "remoteId": "0cfe62f27753448d79aaa8acd8d01699", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/57" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/57", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/57", + "_remoteId": "782f5afa9d6587804daa911e88ff5bb9", + "_id": 57, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "All Tips", + "TranslatedName": "All Tips", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/57/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/57/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/57/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:22:52+00:00", + "publishedDate": "2021-06-28T11:22:52+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/57/objectstates" + } + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.xml.example new file mode 100644 index 000000000..d3401621c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/GET/LocationList.xml.example @@ -0,0 +1,38 @@ + + + 58 + 0 + false + false + false + + /1/2/58/ + 2 + 3 + 0cfe62f27753448d79aaa8acd8d01699 + + + PATH + ASC + + + + + All Tips + All Tips + + +
+ + + 2021-06-28T11:22:52+00:00 + 2021-06-28T11:22:52+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.json.example b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.json.example new file mode 100644 index 000000000..e7bff475d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59", + "id": 59, + "priority": 3, + "hidden": true, + "invisible": true, + "explicitlyHidden": true, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "pathString": "/1/2/59/", + "depth": 2, + "childCount": 5, + "remoteId": "remoteId-qwert999", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/58" + }, + "sortField": "NAME", + "sortOrder": "DESC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/58", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/58", + "_remoteId": "00b513aae54036a16e00fb3365c4b5f3", + "_id": 58, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "Dog Breed Catalog", + "TranslatedName": "Dog Breed Catalog", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/58/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/58/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/58/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:23:10+00:00", + "publishedDate": "2021-06-28T11:23:10+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/58/objectstates" + } + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.xml.example new file mode 100644 index 000000000..d3401621c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/Location.xml.example @@ -0,0 +1,38 @@ + + + 58 + 0 + false + false + false + + /1/2/58/ + 2 + 3 + 0cfe62f27753448d79aaa8acd8d01699 + + + PATH + ASC + + + + + All Tips + All Tips + + +
+ + + 2021-06-28T11:22:52+00:00 + 2021-06-28T11:22:52+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.json.example b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.json.example new file mode 100644 index 000000000..e7a29a87a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.json.example @@ -0,0 +1,9 @@ +{ + "LocationUpdate": { + "priority": "3", + "hidden": true, + "remoteId": "remoteId-qwert999", + "sortField": "NAME", + "sortOrder": "DESC" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example new file mode 100644 index 000000000..c4ffb31f0 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example @@ -0,0 +1,8 @@ + + + 3 + true + remoteId-qwert999 + CLASS + DESC + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.json.example b/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.json.example new file mode 100644 index 000000000..387248e5b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58", + "id": 58, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "pathString": "/1/2/58/", + "depth": 2, + "childCount": 3, + "remoteId": "0cfe62f27753448d79aaa8acd8d01699", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/57" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/58/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/57", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/57", + "_remoteId": "782f5afa9d6587804daa911e88ff5bb9", + "_id": 57, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "All Tips", + "TranslatedName": "All Tips", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/57/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/57/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/57/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:22:52+00:00", + "publishedDate": "2021-06-28T11:22:52+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/57/objectstates" + } + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.xml.example new file mode 100644 index 000000000..f45e159a5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/path/GET/Location.xml.example @@ -0,0 +1,34 @@ + + + 61 + 0 + false + false + + /1/2/61/ + 2 + 0 + 2cfa66027e3806b113d994c9c26d3a66 + + + NAME + ASC + + + + + 666 + + +
+ + + 2018-11-09T14:49:52+01:00 + 2018-11-09T14:49:52+01:00 + eng-GB + 1 + false + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/path/children/GET/LocationList.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/path/children/GET/LocationList.xml.example new file mode 100644 index 000000000..82e59109a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/path/children/GET/LocationList.xml.example @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example b/src/bundle/Resources/api_platform/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example new file mode 100644 index 000000000..26a02c360 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example @@ -0,0 +1,5 @@ + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.json.example b/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.json.example new file mode 100644 index 000000000..5e8bd68f3 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.json.example @@ -0,0 +1,153 @@ +{ + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/109", + "_remoteId": "8716f007ba15f6f35139acb55e39b811", + "_id": 109, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2" + }, + "Name": "draft article", + "TranslatedName": "draft article", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/109/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/109/currentversion", + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/109/versions/1", + "VersionInfo": { + "id": 587, + "versionNo": 1, + "status": "DRAFT", + "modificationDate": "2023-05-24T06:27:54+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2023-05-24T06:27:54+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "draft article" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/109" + } + }, + "Fields": { + "field": [ + { + "id": 488, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "draft article" + }, + { + "id": 489, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 490, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [] + }, + { + "id": 491, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
draft draft
\n", + "xhtml5edit": "\n

draft draft

\n" + } + }, + { + "id": 492, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
draft draft draft
\n", + "xhtml5edit": "\n

draft draft draft

\n" + } + }, + { + "id": 493, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 494, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezobjectrelation", + "fieldValue": { + "destinationContentId": null + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/109/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/placeholder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/109/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "DRAFT", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/109/objectstates" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.xml.example new file mode 100644 index 000000000..35f5cbb16 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/POST/Content.xml.example @@ -0,0 +1,113 @@ + + + + draft article + draft article + + + + + 586 + 1 + DRAFT + 2023-05-24T06:24:46+00:00 + + 2023-05-24T06:24:46+00:00 + eng-GB + eng-GB + + + eng-GB + + + + draft article + + + + + + 481 + title + eng-GB + ezstring + draft article + + + 482 + short_title + eng-GB + ezstring + + + + 483 + author + eng-GB + ezauthor + + + + 484 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml" xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom" version="5.0-variant ezpublish-1.0"><para>draft draft</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"><p>draft draft</p></section> + + + + + 485 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml" xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom" version="5.0-variant ezpublish-1.0"><para>draft draft draft</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"><p>draft draft draft</p></section> + + + + + 486 + enable_comments + eng-GB + ezboolean + false + + + 487 + image + eng-GB + ezobjectrelation + + + + + + + + /placeholder + + + image/svg+xml + + + +
+ + + eng-GB + 1 + true + false + DRAFT + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.json.example b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.json.example new file mode 100644 index 000000000..97f903c01 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.json.example @@ -0,0 +1,49 @@ +{ + "ContentCreate": { + "ContentType": { + "_href": "/api/ibexa/v2/content/types/2" + }, + "mainLanguageCode": "eng-GB", + "LocationCreate": { + "_media-type": "application/vnd.ibexa.api.LocationCreate", + "ParentLocation": { + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "priority": "0", + "hidden": "false", + "sortField": "PATH", + "sortOrder": "ASC" + }, + "Section": { + "_href": "/api/ibexa/v2/content/sections/1" + }, + "alwaysAvailable": "true", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "draft article" + }, + { + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": + { + "xml": "

draft draft

" + } + }, + { + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "

draft draft draft

" + } + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.xml.example new file mode 100644 index 000000000..08c966645 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.xml.example @@ -0,0 +1,36 @@ + + + + eng-GB + + + 0 + false + PATH + ASC + +
+ true + + + + title + eng-GB + draft article + + + intro + eng-GB + +

draft draft

]]> + + + + body + eng-GB + +

draft draft draft

]]> + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentInfo.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentInfo.xml.example new file mode 100644 index 000000000..88ae84b8c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/POST/ContentInfo.xml.example @@ -0,0 +1,17 @@ + + + + draft article + draft article + + +
+ + + eng-GB + 1 + true + false + DRAFT + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.json.example new file mode 100644 index 000000000..0384cebbc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.json.example @@ -0,0 +1,136 @@ +{ + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/54", + "_remoteId": "9e863fbb0fb835ce050032b4f00de61d", + "_id": 54, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "Forms", + "TranslatedName": "Forms", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/54/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/54/currentversion", + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/54/versions/1", + "VersionInfo": { + "id": 514, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2018-09-17T06:48:13+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2018-09-17T06:48:13+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Forms" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/54" + } + }, + "Fields": { + "field": [ + { + "id": 249, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Forms" + }, + { + "id": 250, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 251, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + }, + { + "id": 252, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/54/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#folder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/6" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/55" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/54/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2018-09-17T06:48:13+00:00", + "publishedDate": "2018-09-17T06:48:13+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/54/objectstates" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.xml.example new file mode 100644 index 000000000..bf3dcdccb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.xml.example @@ -0,0 +1,93 @@ + + + + Forms + Forms + + + + + 514 + 1 + PUBLISHED + 2018-09-17T06:48:13+00:00 + + 2018-09-17T06:48:13+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Forms + + + + + + 249 + name + eng-GB + ezstring + Forms + + + 250 + short_name + eng-GB + ezstring + + + + 251 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 252 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /bundles/ezplatformadminui/img/ez-icons.svg#folder + + + image/svg+xml + + + +
+ + + + 2018-09-17T06:48:13+00:00 + 2018-09-17T06:48:13+00:00 + eng-GB + 1 + false + false + PUBLISHED + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/ContentInfo.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/GET/ContentInfo.xml.example new file mode 100644 index 000000000..e69de29bb diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example new file mode 100644 index 000000000..202e2149d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example @@ -0,0 +1,17 @@ + + + + article1 + + +
+ + + + 2019-02-19T15:00:34+01:00 + 2019-02-19T15:00:34+01:00 + eng-GB + 1 + false + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example new file mode 100644 index 000000000..1388ba5ab --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example @@ -0,0 +1,9 @@ + + + eng-GB +
+ + + false + 69848aeb86164c35e5c98202eebe9e05 + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.json.example new file mode 100644 index 000000000..434f069a2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.json.example @@ -0,0 +1,89 @@ +{ + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/5", + "VersionInfo": { + "id": 581, + "versionNo": 5, + "status": "DRAFT", + "modificationDate": "2023-05-24T06:09:49+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2023-05-24T06:09:49+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Features" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/107" + } + }, + "Fields": { + "field": [ + { + "id": 477, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Features" + }, + { + "id": 478, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 479, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + }, + { + "id": 480, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/5/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/placeholder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.xml.example new file mode 100644 index 000000000..d6cb4c486 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/COPY/Version.xml.example @@ -0,0 +1,73 @@ + + + + 580 + 4 + DRAFT + 2023-05-24T06:08:47+00:00 + + 2023-05-24T06:08:47+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Features + + + + + + 477 + name + eng-GB + ezstring + Features + + + 478 + short_name + eng-GB + ezstring + + + + 479 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 480 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /placeholder + + + image/svg+xml + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.json.example new file mode 100644 index 000000000..a37da599d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.json.example @@ -0,0 +1,89 @@ +{ + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/2", + "VersionInfo": { + "id": 578, + "versionNo": 2, + "status": "PUBLISHED", + "modificationDate": "2023-05-24T05:59:09+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2023-05-24T05:59:02+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Features" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/107" + } + }, + "Fields": { + "field": [ + { + "id": 477, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Features" + }, + { + "id": 478, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 479, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + }, + { + "id": 480, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/2/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/placeholder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.xml.example new file mode 100644 index 000000000..585f91f91 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/currentversion/GET/Version.xml.example @@ -0,0 +1,73 @@ + + + + 578 + 2 + PUBLISHED + 2023-05-24T05:59:09+00:00 + + 2023-05-24T05:59:02+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Features + + + + + + 477 + name + eng-GB + ezstring + Features + + + 478 + short_name + eng-GB + ezstring + + + + 479 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 480 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /placeholder + + + image/svg+xml + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.json.example new file mode 100644 index 000000000..20280fba5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.json.example @@ -0,0 +1,12 @@ +{ + "LocationList": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/63/locations", + "Location": [ + { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57/65" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.xml.example new file mode 100644 index 000000000..77e1c81c4 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.xml.example @@ -0,0 +1,4 @@ + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example new file mode 100644 index 000000000..06e2720ae --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/114", + "id": 114, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59" + }, + "pathString": "/1/2/59/114/", + "depth": 3, + "childCount": 0, + "remoteId": "47a1e4ee082fb64e93a822dcfe3a5614", + "Children": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/114/children" + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/59" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/locations/1/2/59/114/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/59", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/59", + "_remoteId": "0fe1b1543f886b0becd4d9b2c7c517d0", + "_id": 59, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2" + }, + "Name": "Art1", + "TranslatedName": "Art1", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/59/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/59/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/59/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:33:01+00:00", + "publishedDate": "2021-06-28T11:33:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/59/objectstates" + } + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example new file mode 100644 index 000000000..99fa70128 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example @@ -0,0 +1,38 @@ + + + 96 + 0 + false + true + false + + /1/2/42/96/ + 3 + 0 + 58133c8c75230e5debe362a28b92d27a + + + PATH + ASC + + + + + Art3 + Art3 + + +
+ + + 2021-06-28T11:34:17+00:00 + 2021-06-28T11:34:17+00:00 + eng-GB + 1 + false + false + PUBLISHED + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.json.example new file mode 100644 index 000000000..44d6271ad --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.json.example @@ -0,0 +1,12 @@ +{ + "LocationCreate": { + "_media-type": "application/vnd.ibexa.api.LocationCreate", + "ParentLocation": { + "_href": "/api/ibexa/v2/content/locations/1/59" + }, + "priority": "0", + "hidden": false, + "sortField": "PATH", + "sortOrder": "ASC" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example new file mode 100644 index 000000000..9686bfc0d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example @@ -0,0 +1,8 @@ + + + + 0 + false + PATH + ASC + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example new file mode 100644 index 000000000..4e8035e60 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example @@ -0,0 +1,23 @@ +{ + "ContentObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "ObjectState": [ + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates/1" + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/3/objectstates/3" + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7/objectstates/7" + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/8/objectstates/8" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example new file mode 100644 index 000000000..f6533987f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.json.example new file mode 100644 index 000000000..6c847b4d9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.json.example @@ -0,0 +1,9 @@ +{ + "ContentObjectStates": { + "ObjectState": [ + { + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates/2" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.xml.example new file mode 100644 index 000000000..086a32de7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.request.xml.example @@ -0,0 +1,4 @@ + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.json.example new file mode 100644 index 000000000..87c9b1709 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.json.example @@ -0,0 +1,11 @@ +{ + "ContentObjectStates": { + "_media-type": "application\/vnd.ibexa.api.ContentObjectStates+json", + "ObjectState": [ + { + "_media-type": "application\/vnd.ibexa.api.ObjectState+json", + "_href": "\/api\/ibexa\/v2\/content\/objectstategroups\/2\/objectstates\/2" + } + ] + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example new file mode 100644 index 000000000..2cdc2305a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example @@ -0,0 +1,4 @@ + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.json.example new file mode 100644 index 000000000..3c042e954 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.json.example @@ -0,0 +1,125 @@ +{ + "VersionList": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/61/versions", + "VersionItem": [ + { + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/61/versions/1" + }, + "VersionInfo": { + "id": 521, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-28T11:33:49+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-06-28T11:33:31+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/61" + } + } + }, + { + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/61/versions/2" + }, + "VersionInfo": { + "id": 580, + "versionNo": 2, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:10:02+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:10:02+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/61" + } + } + }, + { + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/61/versions/3" + }, + "VersionInfo": { + "id": 581, + "versionNo": 3, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:16:50+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:16:50+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/61" + } + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.xml.example new file mode 100644 index 000000000..ca94eaf9e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.xml.example @@ -0,0 +1,69 @@ + + + + + + 521 + 1 + PUBLISHED + 2021-06-28T11:33:49+00:00 + + 2021-06-28T11:33:31+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + + + 580 + 2 + DRAFT + 2021-07-26T08:10:02+00:00 + + 2021-07-26T08:10:02+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + + + 581 + 3 + DRAFT + 2021-07-26T08:16:50+00:00 + + 2021-07-26T08:16:50+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example new file mode 100644 index 000000000..580dc2315 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example @@ -0,0 +1,89 @@ +{ + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/8", + "VersionInfo": { + "id": 584, + "versionNo": 8, + "status": "DRAFT", + "modificationDate": "2023-05-24T06:14:07+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2023-05-24T06:14:07+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Features" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/107" + } + }, + "Fields": { + "field": [ + { + "id": 477, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Features" + }, + { + "id": 478, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 479, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + }, + { + "id": 480, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/8/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/placeholder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example new file mode 100644 index 000000000..b04721156 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example @@ -0,0 +1,73 @@ + + + + 583 + 7 + DRAFT + 2023-05-24T06:13:50+00:00 + + 2023-05-24T06:13:50+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Features + + + + + + 477 + name + eng-GB + ezstring + Features + + + 478 + short_name + eng-GB + ezstring + + + + 479 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 480 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /placeholder + + + image/svg+xml + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example new file mode 100644 index 000000000..c6510ea95 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example @@ -0,0 +1,89 @@ +{ + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/5", + "VersionInfo": { + "id": 581, + "versionNo": 5, + "status": "ARCHIVED", + "modificationDate": "2023-05-24T06:11:50+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2023-05-24T06:09:49+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Features" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/107" + } + }, + "Fields": { + "field": [ + { + "id": 477, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Features" + }, + { + "id": 478, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 479, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + }, + { + "id": 480, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/5/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/placeholder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example new file mode 100644 index 000000000..9286adb0c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example @@ -0,0 +1,73 @@ + + + + 581 + 5 + ARCHIVED + 2023-05-24T06:11:50+00:00 + + 2023-05-24T06:09:49+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Features + + + + + + 477 + name + eng-GB + ezstring + Features + + + 478 + short_name + eng-GB + ezstring + + + + 479 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 480 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /placeholder + + + image/svg+xml + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example new file mode 100644 index 000000000..3bb4e3b18 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example @@ -0,0 +1,48 @@ + + + + 45 + 4 + DRAFT + 2012-02-20T12:00:00 + + 22012-02-20T12:00:00 + ger-DE + + Neuer Titel + + + + + + 1234 + title + ger-DE + Neuer Titel + + + 1235 + summary + ger-DE + Dies ist eine neuse Zusammenfassungy + + + authors + ger-DE + + + + + + + + + + + + COMMON + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example new file mode 100644 index 000000000..2fbff5a67 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example @@ -0,0 +1,21 @@ + + + 2012-02-20T12:00:00 + ger-DE + + + 1234 + title + ger-DE + Neuer Titel + + + 1235 + summary + ger-DE + Dies ist eine neuse Zusammenfassungy + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example new file mode 100644 index 000000000..7ccd94776 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example @@ -0,0 +1,21 @@ +{ + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/9/relations", + "Relation": [ + { + "_media-type": "application/vnd.ibexa.api.Relation+json", + "_href": "/api/ibexa/v2/content/objects/107/versions/9/relations/7", + "SourceContent": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/107" + }, + "DestinationContent": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/52" + }, + "RelationType": "LINK" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example new file mode 100644 index 000000000..07934ebd3 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example @@ -0,0 +1,8 @@ + + + + + + LINK + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example new file mode 100644 index 000000000..dd5981dc1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example @@ -0,0 +1,15 @@ +{ + "Relation": { + "_media-type": "application/vnd.ibexa.api.Relation+json", + "_href": "/api/ibexa/v2/content/objects/59/versions/2/relations/38", + "SourceContent": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/59" + }, + "DestinationContent": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/59" + }, + "RelationType": "COMMON" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example new file mode 100644 index 000000000..da36f0d4a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example @@ -0,0 +1,6 @@ + + + + + COMMON + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example new file mode 100644 index 000000000..84e14562a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example @@ -0,0 +1,7 @@ +{ + "RelationCreate": { + "Destination": { + "_href": "/api/ibexa/v2/content/objects/59" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example new file mode 100644 index 000000000..0eed6ae84 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example @@ -0,0 +1,4 @@ + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example new file mode 100644 index 000000000..4f7e851d1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example @@ -0,0 +1,6 @@ + + + + + COMMON + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example new file mode 100644 index 000000000..a180f21cc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example @@ -0,0 +1,92 @@ +{ + "ObjectStateGroupList": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroupList+json", + "_href": "/api/ibexa/v2/content/objectstategroups", + "ObjectStateGroup": [ + { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2", + "id": 2, + "identifier": "ez_lock", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Lock" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/3", + "id": 3, + "identifier": "accessibility", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/3/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Accessibility" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/6", + "id": 6, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/6/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example new file mode 100644 index 000000000..6d30c9c74 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example @@ -0,0 +1,42 @@ + + + + 2 + ez_lock + eng-GB + eng-GB + + + Lock + + + + + + + 3 + accessibility + eng-GB + eng-GB + + + Accessibility + + + + + + + 6 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.json.example new file mode 100644 index 000000000..f18e09708 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example new file mode 100644 index 000000000..a03639afc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 5 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example new file mode 100644 index 000000000..3e3051b20 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example @@ -0,0 +1,22 @@ +{ + "ObjectStateGroupCreate": { + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example new file mode 100644 index 000000000..e508acf72 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example @@ -0,0 +1,11 @@ + + + custom + eng-GB + + Custom State + + + Custom Object state + + \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example new file mode 100644 index 000000000..f18e09708 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example new file mode 100644 index 000000000..c139dad0d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 7 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example new file mode 100644 index 000000000..3c57a7018 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Custom State name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example new file mode 100644 index 000000000..9d0860d03 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 7 + custom-states + eng-GB + eng-GB + + + New Custom State name + + + Custom Object state + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example new file mode 100644 index 000000000..8510b2684 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example @@ -0,0 +1,12 @@ +{ + "ObjectStateGroupUpdate": { + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Custom State name" + } + ] + } + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example new file mode 100644 index 000000000..c0d7b5c7e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example @@ -0,0 +1,6 @@ + + + + New Custom State name + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example new file mode 100644 index 000000000..f2dc764ec --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example @@ -0,0 +1,66 @@ +{ + "ObjectStateList": { + "_media-type": "application/vnd.ibexa.api.ObjectStateList+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates", + "ObjectState": [ + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates/1", + "id": 1, + "identifier": "not_locked", + "priority": 0, + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Not locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates/2", + "id": 2, + "identifier": "locked", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example new file mode 100644 index 000000000..0f13a16a0 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example @@ -0,0 +1,31 @@ + + + + 1 + not_locked + 0 + + eng-GB + eng-GB + + Not locked + + + + + + + 2 + locked + 1 + + eng-GB + eng-GB + + Locked + + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example new file mode 100644 index 000000000..723b8c07a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7/objectstates/7", + "id": 7, + "identifier": "new-state", + "priority": 0, + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/7" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example new file mode 100644 index 000000000..5fc3fb7de --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example @@ -0,0 +1,15 @@ + + + 5 + new-state + 0 + + eng-GB + eng-GB + + New State + + + New Object State + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example new file mode 100644 index 000000000..840517259 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example @@ -0,0 +1,24 @@ +{ + "ObjectStateCreate": { + "identifier": "new-state", + "priority": "1", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State" + } + ] + } + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example new file mode 100644 index 000000000..b021c96d1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example @@ -0,0 +1,12 @@ + + + new-state + 1 + eng-GB + + New State + + + New Object State + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example new file mode 100644 index 000000000..992d43639 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2/objectstates/2", + "id": 2, + "identifier": "locked", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example new file mode 100644 index 000000000..63924f793 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example @@ -0,0 +1,15 @@ + + +2 +locked +1 + +eng-GB +eng-GB + + Locked + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example new file mode 100644 index 000000000..f7e10a139 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ibexa.api.ObjectState+json", + "_href": "/api/ibexa/v2/content/objectstategroups/6/objectstates/9", + "id": 9, + "identifier": "closed", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ibexa.api.ObjectStateGroup+json", + "_href": "/api/ibexa/v2/content/objectstategroups/6" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example new file mode 100644 index 000000000..20f9c542e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example @@ -0,0 +1,15 @@ + + + 2 + locked + 1 + + eng-GB + eng-GB + + New Object State name + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example new file mode 100644 index 000000000..519d608f2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example @@ -0,0 +1,12 @@ +{ + "ObjectStateUpdate": { + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State name" + } + ] + } + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example new file mode 100644 index 000000000..8b7271316 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example @@ -0,0 +1,8 @@ + + + 3 + eng-GB + + New Object State name + + diff --git a/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.json.example b/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.json.example new file mode 100644 index 000000000..4747a2a9c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.json.example @@ -0,0 +1,50 @@ +{ + "SectionList": { + "_media-type": "application/vnd.ibexa.api.SectionList+json", + "_href": "/api/ibexa/v2/content/sections", + "Section": [ + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1", + "sectionId": 1, + "identifier": "standard", + "name": "Standard" + }, + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2", + "sectionId": 2, + "identifier": "users", + "name": "Users" + }, + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/3", + "sectionId": 3, + "identifier": "media", + "name": "Media" + }, + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/6", + "sectionId": 6, + "identifier": "form", + "name": "Form" + }, + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/7", + "sectionId": 7, + "identifier": "template", + "name": "Template" + }, + { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/8", + "sectionId": 8, + "identifier": "restricted", + "name": "Restricted" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.xml.example new file mode 100644 index 000000000..13de53b1d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/GET/SectionList.xml.example @@ -0,0 +1,33 @@ + + +
+ 1 + standard + Standard +
+
+ 2 + users + Users +
+
+ 3 + media + Media +
+
+ 6 + form + Form +
+
+ 7 + template + Template +
+
+ 8 + restricted + Restricted +
+
diff --git a/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.json.example b/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.json.example new file mode 100644 index 000000000..46ac7c049 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/13", + "sectionId": 13, + "identifier": "restricted", + "name": "Restricted" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.xml.example new file mode 100644 index 000000000..5dd9f2635 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/POST/Section.xml.example @@ -0,0 +1,6 @@ + +
+ 7 + restricted + Restricted +
diff --git a/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example b/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example new file mode 100644 index 000000000..f4b5bcfa1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example @@ -0,0 +1,6 @@ +{ + "SectionInput": { + "identifier": "restricted", + "name": "Restricted" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.xml.example new file mode 100644 index 000000000..d09d79015 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/POST/SectionInput.xml.example @@ -0,0 +1,5 @@ + + + restricted + Restricted + \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.json.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.json.example new file mode 100644 index 000000000..4a14d9f7c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/10", + "sectionId": 10, + "identifier": "design", + "name": "Design" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.xml.example new file mode 100644 index 000000000..2f7d484de --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/GET/Section.xml.example @@ -0,0 +1,6 @@ + +
+ 10 + design + Design +
diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example new file mode 100644 index 000000000..84a448514 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/7", + "sectionId": 7, + "identifier": "template", + "name": "Template" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example new file mode 100644 index 000000000..89ba20fd7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example @@ -0,0 +1,6 @@ + +
+ 7 + template + Template +
diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.json.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.json.example new file mode 100644 index 000000000..6e93f4201 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.json.example @@ -0,0 +1,6 @@ +{ + "SectionInput": { + "identifier": "template", + "name": "Template" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.xml.example b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.xml.example new file mode 100644 index 000000000..1160bc416 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/sections/section_id/PATCH/SectionInput.xml.example @@ -0,0 +1,5 @@ + + + template + Template + diff --git a/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.json.example b/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.json.example new file mode 100644 index 000000000..5dd83e3ce --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.json.example @@ -0,0 +1,146 @@ +{ + "Trash": { + "_media-type": "application/vnd.ibexa.api.Trash+json", + "_href": "/api/ibexa/v2/content/trash", + "TrashItem": [ + { + "_media-type": "application/vnd.ibexa.api.TrashItem+json", + "_href": "/api/ibexa/v2/content/trash/87", + "id": 87, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/87/", + "depth": 3, + "childCount": 0, + "remoteId": "7cc6565354858f39a794bf64aa2c2761", + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/91" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/91", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/91", + "_remoteId": "de906bef76f08700662bfaf1579871f0", + "_id": 91, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2" + }, + "Name": "test_article", + "TranslatedName": "test_article", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/91/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/91/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/91/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/11" + }, + "lastModificationDate": "2021-07-19T08:31:01+00:00", + "publishedDate": "2021-07-19T08:31:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/91/objectstates" + } + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.TrashItem+json", + "_href": "/api/ibexa/v2/content/trash/89", + "id": 89, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2" + }, + "pathString": "/1/2/89/", + "depth": 2, + "childCount": 0, + "remoteId": "256c2e7d71e927bab32a901878827312", + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/96" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/96", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/96", + "_remoteId": "d5bad9eb55cfe8572adf04452a2b206e", + "_id": 96, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1" + }, + "Name": "All Articles test", + "TranslatedName": "All Articles test", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/96/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/96/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/96/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "lastModificationDate": "2021-07-19T12:43:02+00:00", + "publishedDate": "2021-07-19T12:42:47+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 2, + "alwaysAvailable": true, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/96/objectstates" + } + } + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.xml.example b/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.xml.example new file mode 100644 index 000000000..feab5489e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/trash/GET/Trash.xml.example @@ -0,0 +1,34 @@ + + + + 58 + 0 + false + false + + /1/2/56/58/ + 3 + 0 + 59800915ad2eb8514de0bebe84f6ccba + + PATH + ASC + + + + Folder 1 + + +
+ + + 2019-02-06T09:03:09+01:00 + 2019-02-06T09:03:09+01:00 + eng-GB + 1 + true + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.json.example b/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.json.example new file mode 100644 index 000000000..86fd0f2eb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.json.example @@ -0,0 +1,71 @@ +{ + "TrashItem": { + "_media-type": "application/vnd.ibexa.api.TrashItem+json", + "_href": "/api/ibexa/v2/content/trash/87", + "id": 87, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/87/", + "depth": 3, + "childCount": 0, + "remoteId": "7cc6565354858f39a794bf64aa2c2761", + "Content": { + "_media-type": "application/vnd.ibexa.api.Content+json", + "_href": "/api/ibexa/v2/content/objects/91" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/91", + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/91", + "_remoteId": "de906bef76f08700662bfaf1579871f0", + "_id": 91, + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2" + }, + "Name": "test_article", + "TranslatedName": "test_article", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/91/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/91/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/91/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/11" + }, + "lastModificationDate": "2021-07-19T08:31:01+00:00", + "publishedDate": "2021-07-19T08:31:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ibexa.api.ContentObjectStates+json", + "_href": "/api/ibexa/v2/content/objects/91/objectstates" + } + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.xml.example b/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.xml.example new file mode 100644 index 000000000..1c886e140 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.xml.example @@ -0,0 +1,33 @@ + + + 81 + 0 + false + false + + /1/2/42/81/ + 3 + 0 + 135e8a84b61848a67be36e9552d2724d + + PATH + ASC + + + + Folder 1 + + +
+ + + 2019-04-25T12:45:58+02:00 + 2019-04-25T12:45:58+02:00 + eng-GB + 1 + true + TRASHED + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.json.example new file mode 100644 index 000000000..45838cbc7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.json.example @@ -0,0 +1,68 @@ +{ + "ContentTypeGroupList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupList+json", + "_href": "/api/ibexa/v2/content/typegroups", + "ContentTypeGroup": [ + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/1", + "id": 1, + "identifier": "Content", + "created": "2002-09-05T09:08:48+00:00", + "modified": "2002-10-06T16:35:06+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json", + "_href": "/api/ibexa/v2/content/typegroups/1/types" + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/2", + "id": 2, + "identifier": "Users", + "created": "2002-09-05T09:09:01+00:00", + "modified": "2002-10-06T16:35:13+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json", + "_href": "/api/ibexa/v2/content/typegroups/2/types" + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/3", + "id": 3, + "identifier": "Media", + "created": "2002-09-14T13:22:23+00:00", + "modified": "2002-10-06T16:35:20+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json", + "_href": "/api/ibexa/v2/content/typegroups/3/types" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.xml.example new file mode 100644 index 000000000..7794b023a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.xml.example @@ -0,0 +1,30 @@ + + + + 1 + Content + 2002-09-05T11:08:48+02:00 + 2002-10-06T18:35:06+02:00 + + + + + + 2 + Users + 2002-09-05T11:09:01+02:00 + 2002-10-06T18:35:13+02:00 + + + + + + 3 + Media + 2002-09-14T15:22:23+02:00 + 2002-10-06T18:35:20+02:00 + + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.json.example new file mode 100644 index 000000000..5cd4cdbcc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.json.example @@ -0,0 +1,22 @@ +{ + "ContentTypeGroup": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/6", + "id": 6, + "identifier": "newContentTypeGroup", + "created": "2021-08-11T09:44:18+00:00", + "modified": "2021-08-11T09:44:18+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json", + "_href": "/api/ibexa/v2/content/typegroups/6/types" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.xml.example new file mode 100644 index 000000000..8c63d3a72 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.xml.example @@ -0,0 +1,10 @@ + + + 7 + newContentTypeGroup + 2012-02-31T12:45:00 + 2012-02-31T12:45:00 + + + + diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.json.example new file mode 100644 index 000000000..164105095 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.json.example @@ -0,0 +1,5 @@ +{ + "ContentTypeGroupInput": { + "identifier": "newContentTypeGroup" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example new file mode 100644 index 000000000..f88b35a97 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example @@ -0,0 +1,4 @@ + + + newContentTypeGroup + diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example new file mode 100644 index 000000000..cfcff3bb4 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example @@ -0,0 +1,1595 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeList+json", + "_href": "/api/ibexa/v2/content/typegroups/1/types", + "ContentType": [ + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/50", + "id": 50, + "status": "DEFINED", + "identifier": "copy_of_article_50", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-14T10:30:00+00:00", + "modificationDate": "2021-07-14T10:30:00+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/50/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/50/draft" + }, + "remoteId": "fc9434492469d136ffe13377bfdcbb28", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/197", + "id": 197, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/198", + "id": 198, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/199", + "id": 199, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/200", + "id": 200, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/201", + "id": 201, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/202", + "id": 202, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/203", + "id": 203, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/44", + "id": 44, + "status": "DEFINED", + "identifier": "dog_breed", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Dog Breed" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:28:00+00:00", + "modificationDate": "2021-06-28T11:29:02+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/44/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/44/draft" + }, + "remoteId": "b348c95f7c573d4d502d6b59f80e618a", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/190", + "id": 190, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/191", + "id": 191, + "identifier": "photo", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Photo" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/192", + "id": 192, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Full Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/55", + "id": 55, + "status": "DEFINED", + "identifier": "folder_55", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-28T07:57:22+00:00", + "modificationDate": "2021-07-28T07:57:34+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/55/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/55/draft" + }, + "remoteId": "4eb7bc76f6d0bd1d5ded2d8936cc6afb", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/55/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/55/fieldDefinitions/210", + "id": 210, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/55/fieldDefinitions/211", + "id": 211, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/55/fieldDefinitions/212", + "id": 212, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/55/fieldDefinitions/213", + "id": 213, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
\n", + "xhtml5edit": "\n
\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/45", + "id": 45, + "status": "DEFINED", + "identifier": "tip", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Tip" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:29:19+00:00", + "modificationDate": "2021-07-26T09:33:06+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/45/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/45/draft" + }, + "remoteId": "e6490e8a785edacd48629f2022be3125", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/193", + "id": 193, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/194", + "id": 194, + "identifier": "body", + "fieldType": "eztext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/204", + "id": 204, + "identifier": "richtext", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "richtext" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example new file mode 100644 index 000000000..e8def2575 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example @@ -0,0 +1,1034 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ibexa.api.ContentTypeList+xml" href="/api/ibexa/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2021-06-28T11:31:22+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/50"> + <id>50</id> + <status>DEFINED</status> + <identifier>copy_of_article_50</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-07-14T10:30:00+00:00</creationDate> + <modificationDate>2021-07-14T10:30:00+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/50/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/50/draft"/> + <remoteId>fc9434492469d136ffe13377bfdcbb28</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/197"> + <id>197</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/198"> + <id>198</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/199"> + <id>199</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/200"> + <id>200</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/201"> + <id>201</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/202"> + <id>202</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/50/fieldDefinitions/203"> + <id>203</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/44"> + <id>44</id> + <status>DEFINED</status> + <identifier>dog_breed</identifier> + <names> + <value languageCode="eng-GB">Dog Breed</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-06-28T11:28:00+00:00</creationDate> + <modificationDate>2021-06-28T11:29:02+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/44/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/44/draft"/> + <remoteId>b348c95f7c573d4d502d6b59f80e618a</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/44/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/44/fieldDefinitions/190"> + <id>190</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/44/fieldDefinitions/191"> + <id>191</id> + <identifier>photo</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Photo</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/44/fieldDefinitions/192"> + <id>192</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup>content</fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Full Description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2015-11-29T21:14:32+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/4"> + <id>4</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/155"> + <id>155</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/119"> + <id>119</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/156"> + <id>156</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/55"> + <id>55</id> + <status>DEFINED</status> + <identifier>folder_55</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-07-28T07:57:22+00:00</creationDate> + <modificationDate>2021-07-28T07:57:34+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/55/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/55/draft"/> + <remoteId>4eb7bc76f6d0bd1d5ded2d8936cc6afb</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/55/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/55/fieldDefinitions/210"> + <id>210</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/55/fieldDefinitions/211"> + <id>211</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/55/fieldDefinitions/212"> + <id>212</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/55/fieldDefinitions/213"> + <id>213</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/43"> + <id>43</id> + <status>DEFINED</status> + <identifier>form</identifier> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <creationDate>2018-09-17T06:46:13+00:00</creationDate> + <modificationDate>2018-09-17T06:47:14+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/43/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/43/draft"/> + <remoteId>6f7f21df775a33c1e4bbc76b48c38476</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions/189"> + <id>189</id> + <identifier>form</identifier> + <fieldType>ezform</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="fields"/> + <value key="content_id"/> + <value key="content_field_id"/> + <value key="language_code"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/45"> + <id>45</id> + <status>DEFINED</status> + <identifier>tip</identifier> + <names> + <value languageCode="eng-GB">Tip</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-06-28T11:29:19+00:00</creationDate> + <modificationDate>2021-07-26T09:33:06+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/45/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/45/draft"/> + <remoteId>e6490e8a785edacd48629f2022be3125</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/45/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/45/fieldDefinitions/193"> + <id>193</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/45/fieldDefinitions/194"> + <id>194</id> + <identifier>body</identifier> + <fieldType>eztext</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="textRows">10</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/45/fieldDefinitions/204"> + <id>204</id> + <identifier>richtext</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup>content</fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">richtext</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/42"> + <id>42</id> + <status>DEFINED</status> + <identifier>landing_page</identifier> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2015-07-03T12:00:26+00:00</creationDate> + <modificationDate>2015-07-03T12:00:26+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/42/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/42/draft"/> + <remoteId>60c03e9758465eb69d56b3afb6adf18e</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/185"> + <id>185</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">Title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/186"> + <id>186</id> + <identifier>description</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>20</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page description</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/187"> + <id>187</id> + <identifier>page</identifier> + <fieldType>ezlandingpage</fieldType> + <fieldGroup>content</fieldGroup> + <position>30</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="layout">default</value> + <value key="zones"> + <value> + <value key="id">default_id</value> + <value key="name">default</value> + <value key="blocks"/> + </value> + </value> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page</value> + </descriptions> + <fieldSettings> + <value key="availableBlocks"/> + <value key="availableLayouts"/> + <value key="editorMode">page_view_mode</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example new file mode 100644 index 000000000..5235bc58c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example @@ -0,0 +1,22 @@ +{ + "ContentTypeGroup": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/4", + "id": 4, + "identifier": "updatedIdentifer", + "created": "2021-08-11T09:44:18+00:00", + "modified": "2021-08-11T10:10:04+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfoList+json", + "_href": "/api/ibexa/v2/content/typegroups/6/types" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example new file mode 100644 index 000000000..2357f9257 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroup media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/1"> + <id>4</id> + <identifier>updatedIdentifer</identifier> + <created>2002-09-05T11:08:48+02:00</created> + <modified>2019-02-22T14:42:55+01:00</modified> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <ContentTypes media-type="application/vnd.ibexa.api.ContentTypeInfoList+xml" href="/api/ibexa/v2/content/typegroups/1/types"/> +</ContentTypeGroup> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example new file mode 100644 index 000000000..bf3fa9ebc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example @@ -0,0 +1,5 @@ +{ + "ContentTypeGroupInput": { + "identifier": "updatedIdentifer" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example new file mode 100644 index 000000000..2a79ff0f6 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupInput> + <identifier>updatedIdentifer</identifier> +</ContentTypeGroupInput> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example new file mode 100644 index 000000000..c7c2076fb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example @@ -0,0 +1,2011 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeList+json", + "_href": "/api/ibexa/v2/content/types", + "ContentType": [ + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/50", + "id": 50, + "status": "DEFINED", + "identifier": "copy_of_article_50", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-14T10:30:00+00:00", + "modificationDate": "2021-07-14T10:30:00+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/50/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/50/draft" + }, + "remoteId": "fc9434492469d136ffe13377bfdcbb28", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/197", + "id": 197, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/198", + "id": 198, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/199", + "id": 199, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/200", + "id": 200, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/201", + "id": 201, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/202", + "id": 202, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/50/fieldDefinitions/203", + "id": 203, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/44", + "id": 44, + "status": "DEFINED", + "identifier": "dog_breed", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Dog Breed" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:28:00+00:00", + "modificationDate": "2021-06-28T11:29:02+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/44/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/44/draft" + }, + "remoteId": "b348c95f7c573d4d502d6b59f80e618a", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/190", + "id": 190, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/191", + "id": 191, + "identifier": "photo", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Photo" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/44/fieldDefinitions/192", + "id": 192, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Full Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/45", + "id": 45, + "status": "DEFINED", + "identifier": "tip", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Tip" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:29:19+00:00", + "modificationDate": "2021-07-26T09:33:06+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/45/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/45/draft" + }, + "remoteId": "e6490e8a785edacd48629f2022be3125", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/193", + "id": 193, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/194", + "id": 194, + "identifier": "body", + "fieldType": "eztext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/45/fieldDefinitions/204", + "id": 204, + "identifier": "richtext", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "richtext" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4", + "id": 4, + "status": "DEFINED", + "identifier": "user", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2004-04-15T08:39:24+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/4/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4/draft" + }, + "remoteId": "40faa822edc579b02c25f6bb7beec3ad", + "urlAliasSchema": null, + "nameSchema": "<first_name> <last_name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions/8", + "id": 8, + "identifier": "first_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "First name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions/9", + "id": 9, + "identifier": "last_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Last name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions/12", + "id": 12, + "identifier": "user_account", + "fieldType": "ezuser", + "fieldGroup": "", + "position": 3, + "isTranslatable": false, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User account" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "PasswordTTL": 0, + "PasswordTTLWarning": 0, + "RequireUniqueEmail": false, + "UsernamePattern": "^[^@]+$" + }, + "validatorConfiguration": { + "PasswordValueValidator": { + "requireAtLeastOneUpperCaseCharacter": true, + "requireAtLeastOneLowerCaseCharacter": true, + "requireAtLeastOneNumericCharacter": true, + "requireAtLeastOneNonAlphanumericCharacter": false, + "requireNewPassword": false, + "minLength": 10 + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions/179", + "id": 179, + "identifier": "signature", + "fieldType": "eztext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Signature" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/4/fieldDefinitions/180", + "id": 180, + "identifier": "image", + "fieldType": "ezimage", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": 10 + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/3", + "id": 3, + "status": "DEFINED", + "identifier": "user_group", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User group" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2003-03-24T08:32:23+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/3/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/3/draft" + }, + "remoteId": "25b4268cdcd01921b808a0d854b877ef", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/3/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/3/fieldDefinitions/6", + "id": 6, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/3/fieldDefinitions/7", + "id": 7, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/12", + "id": 12, + "status": "DEFINED", + "identifier": "file", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "File" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2003-05-08T09:17:52+00:00", + "modificationDate": "2003-05-08T09:21:09+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/12/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/12/draft" + }, + "remoteId": "637d58bfddf164627bdfd265733280a0", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/12/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/12/fieldDefinitions/146", + "id": 146, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New file", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/12/fieldDefinitions/147", + "id": 147, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/12/fieldDefinitions/148", + "id": 148, + "identifier": "file", + "fieldType": "ezbinaryfile", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "File" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": null + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/5", + "id": 5, + "status": "DEFINED", + "identifier": "image", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-09-08T11:36:32+00:00", + "modificationDate": "2003-03-24T08:33:04+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/5/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/5/draft" + }, + "remoteId": "f6df12aa74e36230eb675f364fccd25a", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/5/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/5/fieldDefinitions/116", + "id": 116, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 150, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/5/fieldDefinitions/117", + "id": 117, + "identifier": "caption", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Caption" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/5/fieldDefinitions/118", + "id": 118, + "identifier": "image", + "fieldType": "ezimage", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": 10 + } + } + } + ] + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example new file mode 100644 index 000000000..097f475b1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfoList media-type="application/vnd.ibexa.api.ContentTypeInfoList+xml" href="/api/ibexa/v2/content/typegroups/1/types"> + <ContentTypeInfo media-type="application/vnd.ibexa.api.ContentTypeInfo+xml" href="/api/ibexa/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + </ContentTypeInfo> + <ContentTypeInfo media-type="application/vnd.ibexa.api.ContentTypeInfo+xml" href="/api/ibexa/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2015-11-29T22:14:32+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + </ContentTypeInfo> +</ContentTypeInfoList> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example new file mode 100644 index 000000000..b33671c66 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ibexa.api.ContentTypeList+xml" href="/api/ibexa/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example new file mode 100644 index 000000000..beac49c71 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/20/draft"> + <id>20</id> + <status>DRAFT</status> + <identifier>newContentType</identifier> + <names> + <value languageCode="eng-GB">New content type</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is a description</value> + </descriptions> + <creationDate>2019-02-26T09:39:58+01:00</creationDate> + <modificationDate>2019-02-26T09:39:58+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/20/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/20/draft"/> + <remoteId>remoteId-qwert548</remoteId> + <urlAliasSchema><title></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/20/draft/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/20/draft/fieldDefinitions/223"> + <id>223</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New Title</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is the title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="minStringLength">0</value> + <value key="maxStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example new file mode 100644 index 000000000..175bf7100 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example @@ -0,0 +1,86 @@ +{ + "ContentTypeCreate": { + "identifier": "new_content_type", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New content type" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is a description" + } + ] + }, + "remoteId": "remoteId-qwerty548", + "urlAliasSchema": "<title>", + "nameSchema": "<title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "FieldDefinition": [ + { + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New Title", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is the title" + } + ] + } + }, + { + "identifier": "summary", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Summary" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is the summary" + } + ] + } + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example new file mode 100644 index 000000000..261e7a475 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeCreate> + <identifier>newContentType</identifier> + <names> + <value languageCode="eng-US">New content type</value> + </names> + <descriptions> + <value languageCode="eng-US">This is a description</value> + </descriptions> + <remoteId>remoteId-qwert548</remoteId> + <urlAliasSchema><title></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-US</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions> + <FieldDefinition> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New Title</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-US">Title</value> + </names> + <descriptions> + <value languageCode="eng-US">This is the title</value> + </descriptions> + </FieldDefinition> + <FieldDefinition> + <identifier>summary</identifier> + <fieldType>ezxmltext</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="utf-8"?><section/></value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-US">Summary</value> + </names> + <descriptions> + <value languageCode="eng-US">This is the summary</value> + </descriptions> + </FieldDefinition> + </FieldDefinitions> +</ContentTypeCreate> diff --git a/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example b/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example new file mode 100644 index 000000000..31c1f62d1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example @@ -0,0 +1,733 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeList+json", + "_href": "/api/ibexa/v2/content/typegroups/1/types", + "ContentType": [ + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2004-04-20T09:56:29+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": null, + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/154", + "id": 154, + "identifier": "image", + "fieldType": "ezobjectrelation", + "fieldGroup": "", + "position": 7, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "selectionMethod": "SELECTION_BROWSE", + "selectionRoot": "", + "selectionContentTypes": [] + }, + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example b/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example new file mode 100644 index 000000000..918783beb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example @@ -0,0 +1,480 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ibexa.api.ContentTypeList+xml" href="/api/ibexa/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2004-04-20T09:56:29+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2015-11-29T21:14:32+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/4"> + <id>4</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/155"> + <id>155</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/119"> + <id>119</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/1/fieldDefinitions/156"> + <id>156</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/43"> + <id>43</id> + <status>DEFINED</status> + <identifier>form</identifier> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <creationDate>2018-09-17T06:46:13+00:00</creationDate> + <modificationDate>2018-09-17T06:47:14+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/43/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/43/draft"/> + <remoteId>6f7f21df775a33c1e4bbc76b48c38476</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/43/fieldDefinitions/189"> + <id>189</id> + <identifier>form</identifier> + <fieldType>ezform</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="fields"/> + <value key="content_id"/> + <value key="content_field_id"/> + <value key="language_code"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/42"> + <id>42</id> + <status>DEFINED</status> + <identifier>landing_page</identifier> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2015-07-03T12:00:26+00:00</creationDate> + <modificationDate>2015-07-03T12:00:26+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/42/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/42/draft"/> + <remoteId>60c03e9758465eb69d56b3afb6adf18e</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/185"> + <id>185</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">Title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/186"> + <id>186</id> + <identifier>description</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>20</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page description</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/42/fieldDefinitions/187"> + <id>187</id> + <identifier>page</identifier> + <fieldType>ezlandingpage</fieldType> + <fieldGroup>content</fieldGroup> + <position>30</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="layout">default</value> + <value key="zones"> + <value> + <value key="id">default_id</value> + <value key="name">default</value> + <value key="blocks"/></value> + </value> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page</value> + </descriptions> + <fieldSettings> + <value key="availableBlocks"/> + <value key="availableLayouts"/> + <value key="editorMode">page_view_mode</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.json.example new file mode 100644 index 000000000..67593935b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.json.example @@ -0,0 +1,303 @@ +{ + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ibexa.api.FieldDefinitionList+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ibexa.co/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.xml.example new file mode 100644 index 000000000..59ea81432 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.xml.example @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ibexa.co/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example new file mode 100644 index 000000000..6abf1d5bd --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example @@ -0,0 +1,46 @@ +{ + "ContentTypeInfo": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfo+json", + "_href": "/api/ibexa/v2/content/types/1/draft", + "id": 1, + "status": "DRAFT", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-08-11T11:17:58+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example new file mode 100644 index 000000000..ce2a73a08 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfo media-type="application/vnd.ibexa.api.ContentTypeInfo+xml" href="/api/ibexa/v2/content/types/3/draft"> + <id>3</id> + <status>DRAFT</status> + <identifier>user_group</identifier> + <names> + <value languageCode="eng-GB">User group</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2019-02-25T14:41:53+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/3/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/3/draft"/> + <remoteId>25b4268cdcd01921b808a0d854b877ef</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> +</ContentTypeInfo> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example new file mode 100644 index 000000000..bd7dd507e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example @@ -0,0 +1,6 @@ +{ + "ContentTypeUpdate": { + "_media-type": "application/vnd.ibexa.api.ContentTypeUpdate", + "defaultAlwaysAvailable": "true" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example new file mode 100644 index 000000000..9e9d31ba7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeUpdate> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> +</ContentTypeUpdate> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example new file mode 100644 index 000000000..6b12d0c7b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example @@ -0,0 +1,51 @@ +{ + "ContentTypeInfo": { + "_media-type": "application/vnd.ibexa.api.ContentTypeInfo+json", + "_href": "/api/ibexa/v2/content/types/2/draft", + "id": 2, + "status": "DRAFT", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Updated content type name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is an updated content type description" + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-08-11T11:58:24+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": null, + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC" + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example new file mode 100644 index 000000000..a96fbd1d8 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfo media-type="application/vnd.ibexa.api.ContentTypeInfo+xml" href="/api/ibexa/v2/content/types/14/draft"> + <id>14</id> + <status>DRAFT</status> + <identifier>new_content_type</identifier> + <names> + <value languageCode="eng-GB">Updated content type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated content type description</value> + </descriptions> + <creationDate>2019-02-06T10:56:36+01:00</creationDate> + <modificationDate>2019-02-25T12:15:51+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/14/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/14/draft"/> + <remoteId>d7ae816f22fe929e67a359a2ef6e7cde</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> +</ContentTypeInfo> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example new file mode 100644 index 000000000..8e9377267 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example @@ -0,0 +1,21 @@ +{ + "ContentTypeUpdate": { + "_media-type": "application/vnd.ibexa.api.ContentTypeUpdate", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Updated content type name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is an updated content type description" + } + ] + } + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example new file mode 100644 index 000000000..675fdf951 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeUpdate> + <names> + <value languageCode="eng-GB">Updated content type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated content type description</value> + </descriptions> +</ContentTypeUpdate> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example new file mode 100644 index 000000000..2edf26ace --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/14"> + <id>14</id> + <status>DEFINED</status> + <identifier>copy_of_article_14</identifier> + <names> + <value languageCode="eng-GB">Updated content type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated content type description</value> + </descriptions> + <creationDate>2019-02-06T10:56:36+01:00</creationDate> + <modificationDate>2019-02-25T12:15:51+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Modifier media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <Groups media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/types/14/groups"/> + <Draft media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/14/draft"/> + <remoteId>d7ae816f22fe929e67a359a2ef6e7cde</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ibexa.api.FieldDefinitionList+xml" href="/api/ibexa/v2/content/types/14/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/14/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example new file mode 100644 index 000000000..e994eb0f5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/14/draft/fieldDefinitions/221"> + <id>221</id> + <identifier>name2</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>0</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names/> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">32</value> + <value key="minStringLength">8</value> + </value> + </validatorConfiguration> +</FieldDefinition> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example new file mode 100644 index 000000000..3282099a8 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinitionCreate> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <isRequired>true</isRequired> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">32</value> + <value key="minStringLength">8</value> + </value> +</validatorConfiguration> +</FieldDefinitionCreate> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example new file mode 100644 index 000000000..df7975cd9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example @@ -0,0 +1,38 @@ +{ + "FieldDefinition": { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example new file mode 100644 index 000000000..f61269499 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> +</FieldDefinition> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example new file mode 100644 index 000000000..a941520fe --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/15/draft/fieldDefinitions/197"> + <id>197</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup>new_field_group</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> +</FieldDefinition> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example new file mode 100644 index 000000000..c1f333a86 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinitionUpdate> + <fieldGroup>new_field_group</fieldGroup> + <position>10</position> +</FieldDefinitionUpdate> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example new file mode 100644 index 000000000..df7975cd9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example @@ -0,0 +1,38 @@ +{ + "FieldDefinition": { + "_media-type": "application/vnd.ibexa.api.FieldDefinition+json", + "_href": "/api/ibexa/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example new file mode 100644 index 000000000..f61269499 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ibexa.api.FieldDefinition+xml" href="/api/ibexa/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> +</FieldDefinition> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example new file mode 100644 index 000000000..44c021757 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "ContentTypeGroupRefList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/typegroups/2/types", + "ContentTypeGroupRef": [ + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/1" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example new file mode 100644 index 000000000..5f6bef747 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/typegroups/2/types"> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/1"/> +</ContentTypeGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example new file mode 100644 index 000000000..b14ad38b7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/typegroups/15/types"> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/1"> + <unlink href="/api/ibexa/v2/content/types/15/groups/1" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/3"> + <unlink href="/api/ibexa/v2/content/types/15/groups/3" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/2"> + <unlink href="/api/ibexa/v2/content/types/15/groups/2" method="DELETE"/> + </ContentTypeGroupRef> +</ContentTypeGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example new file mode 100644 index 000000000..74fbb967f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example @@ -0,0 +1,24 @@ +{ + "ContentTypeGroupRefList": { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroupRefList+json", + "_href": "/api/ibexa/v2/content/typegroups/2/types", + "ContentTypeGroupRef": [ + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/1", + "unlink": { + "_href": "/api/ibexa/v2/content/types/2/groups/1", + "_method": "DELETE" + } + }, + { + "_media-type": "application/vnd.ibexa.api.ContentTypeGroup+json", + "_href": "/api/ibexa/v2/content/typegroups/2", + "unlink": { + "_href": "/api/ibexa/v2/content/types/2/groups/2", + "_method": "DELETE" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example new file mode 100644 index 000000000..cd216a5ed --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ibexa.api.ContentTypeGroupRefList+xml" href="/api/ibexa/v2/content/typegroups/15/types"> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/1"> + <unlink href="/api/ibexa/v2/content/types/15/groups/1" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ibexa.api.ContentTypeGroup+xml" href="/api/ibexa/v2/content/typegroups/2"> + <unlink href="/api/ibexa/v2/content/types/15/groups/2" method="DELETE"/> + </ContentTypeGroupRef> +</ContentTypeGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.json.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.json.example new file mode 100644 index 000000000..40008fd0f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.json.example @@ -0,0 +1,12 @@ +{ + "UrlAliasRefList": { + "_media-type": "application/vnd.ibexa.api.UrlAliasRefList+json", + "_href": "/api/ibexa/v2/content/urlaliases", + "UrlAlias": [ + { + "_media-type": "application/vnd.ibexa.api.UrlAlias+json", + "_href": "/api/ibexa/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.xml.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.xml.example new file mode 100644 index 000000000..2c7e90a83 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAliasRefList media-type="application/vnd.ibexa.api.UrlAliasRefList+xml" href="/api/ibexa/v2/content/urlaliases"> + <UrlAlias media-type="application/vnd.ibexa.api.UrlAlias+xml" href="/api/ibexa/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" /> +</UrlAliasRefList> diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.json.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.json.example new file mode 100644 index 000000000..078382871 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.json.example @@ -0,0 +1,15 @@ +{ + "UrlAlias": { + "_media-type": "application/vnd.ibexa.api.UrlAlias+json", + "_href": "/api/ibexa/v2/content/urlaliases/0-f530173ad554787c1fe30dc929d98360", + "_id": "0-f530173ad554787c1fe30dc929d98360", + "_type": "RESOURCE", + "resource": "content/view/full", + "path": "/example-global", + "languageCodes": "eng-GB", + "alwaysAvailable": true, + "isHistory": false, + "forward": true, + "custom": true + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.xml.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.xml.example new file mode 100644 index 000000000..2eca610bc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAlias.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAlias media-type="application/vnd.ibexa.api.UrlAlias+xml" href="/api/ibexa/v2/content/urlaliases/0-deca3dadca45c3dff7861274b8e67ed7" id="0-deca3dadca45c3dff7861274b8e67ed7" type="LOCATION"> + <location media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/59"/> + <path>/example-urlalias</path> + <languageCodes>eng-GB</languageCodes> + <alwaysAvailable>true</alwaysAvailable> + <isHistory>false</isHistory> + <forward>true</forward> + <custom>true</custom> +</UrlAlias> diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.json.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.json.example new file mode 100644 index 000000000..0eb520514 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.json.example @@ -0,0 +1,10 @@ +{ + "UrlAliasCreate": { + "_type": "GLOBAL", + "resource": "module:content/view/full", + "path": "example-global", + "languageCode": "eng-GB", + "alwaysAvailable": "true", + "forward": "true" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.xml.example new file mode 100644 index 000000000..218ab60ec --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.xml.example @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAliasCreate type="LOCATION"> + <location href='/api/ibexa/v2/content/locations/1/2/59' /> + <path>example-global</path> + <languageCode>eng-GB</languageCode> + <alwaysAvailable>true</alwaysAvailable> + <forward>true</forward> +</UrlAliasCreate> diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example new file mode 100644 index 000000000..9fe37c238 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example @@ -0,0 +1,15 @@ +{ + "UrlAlias": { + "_media-type": "application/vnd.ibexa.api.UrlAlias+json", + "_href": "/api/ibexa/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36", + "_id": "0-2cecdff5f90b8595d76b68c417b09f36", + "_type": "RESOURCE", + "resource": "content/view/full", + "path": "/example-global", + "languageCodes": "eng-GB", + "alwaysAvailable": true, + "isHistory": false, + "forward": true, + "custom": true + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example b/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example new file mode 100644 index 000000000..565b070ef --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAlias media-type="application/vnd.ibexa.api.UrlAlias+xml" href="/api/ibexa/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" id="0-2cecdff5f90b8595d76b68c417b09f36" type="RESOURCE"> + <resource>content/view/full</resource> + <path>/example-global</path> + <languageCodes>eng-GB</languageCodes> + <alwaysAvailable>true</alwaysAvailable> + <isHistory>false</isHistory> + <forward>true</forward> + <custom>true</custom> +</UrlAlias> diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.json.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.json.example new file mode 100644 index 000000000..c8e1bbefc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.json.example @@ -0,0 +1,16 @@ +{ + "UrlWildcardList": { + "_media-type": "application/vnd.ibexa.api.UrlWildcardList+json", + "_href": "/api/ibexa/v2/content/urlwildcards", + "UrlWildcard": [ + { + "_media-type": "application/vnd.ibexa.api.UrlWildcard+json", + "_href": "/api/ibexa/v2/content/urlwildcards/1", + "_id": 1, + "sourceUrl": "/api/ibexa/v2/content/location/2", + "destinationUrl": "/api/ibexa/v2/content/location/59", + "forward": true + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.xml.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.xml.example new file mode 100644 index 000000000..6ca3442d9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.xml.example @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcardList media-type="application/vnd.ibexa.api.UrlWildcardList+xml" href="/api/ibexa/v2/content/urlwildcards"> + <UrlWildcard media-type="application/vnd.ibexa.api.UrlWildcard+xml" href="/api/ibexa/v2/content/urlwildcards/1" id="1"> + <sourceUrl>/api/ibexa/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ibexa/v2/content/location/59</destinationUrl> + <forward>true</forward> + </UrlWildcard> +</UrlWildcardList> diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.json.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.json.example new file mode 100644 index 000000000..1bef2daff --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.json.example @@ -0,0 +1,10 @@ +{ + "UrlWildcard": { + "_media-type": "application/vnd.ibexa.api.UrlWildcard+json", + "_href": "/api/ibexa/v2/content/urlwildcards/6", + "_id": 6, + "sourceUrl": "/api/ibexa/v2/content/location/2", + "destinationUrl": "/api/ibexa/v2/content/location/59", + "forward": true + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.xml.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.xml.example new file mode 100644 index 000000000..34e7bee41 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcard.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcard media-type="application/vnd.ibexa.api.UrlWildcard+xml" href="/api/ibexa/v2/content/urlwildcards/1" id="1"> + <sourceUrl>/api/ibexa/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ibexa/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcard> diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example new file mode 100644 index 000000000..0bc4c24da --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example @@ -0,0 +1,7 @@ +{ + "URLWildcardCreate": { + "sourceUrl": "/api/ibexa/v2/content/location/2", + "destinationUrl": "/api/ibexa/v2/content/location/59", + "forward": true + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example new file mode 100644 index 000000000..a77f9f13f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <UrlWildcardCreate> + <sourceUrl>/api/ibexa/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ibexa/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcardCreate> diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example new file mode 100644 index 000000000..f51ac49b1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example @@ -0,0 +1,10 @@ +{ + "UrlWildcard": { + "_media-type": "application/vnd.ibexa.api.UrlWildcard+json", + "_href": "/api/ibexa/v2/content/urlwildcards/4", + "_id": 4, + "sourceUrl": "/api/ibexa/v2/content/location/2", + "destinationUrl": "/api/ibexa/v2/content/location/59", + "forward": true + } +} diff --git a/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example b/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example new file mode 100644 index 000000000..b4830abe8 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcard media-type="application/vnd.ibexa.api.UrlWildcard+xml" href="/api/ibexa/v2/content/urlwildcards/4" id="4"> + <sourceUrl>/api/ibexa/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ibexa/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcard> diff --git a/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example new file mode 100644 index 000000000..e55d78560 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example @@ -0,0 +1,21 @@ +{ + "LanguageList": { + "_media-type": "application/vnd.ibexa.api.LanguageList+json", + "_href": "/api/ibexa/v2/languages", + "Language": [ + { + "_media-type": "application/vnd.ibexa.api.Language+json", + "_href": "/api/ibexa/v2/languages/eng-GB", + "languageId": 2, + "languageCode": "eng-GB", + "name": "English (United Kingdom)" + }, { + "_href": "/api/ibexa/v2/languages/pol-PL", + "_media-type": "application/vnd.ibexa.api.Language+json", + "languageCode": "pol-PL", + "languageId": 4, + "name": "Polish (polski)" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example new file mode 100644 index 000000000..f61cfa725 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<LanguageList media-type="application/vnd.ibexa.api.LanguageList+xml" href="/api/ibexa/v2/languages"> + <Language media-type="application/vnd.ibexa.api.Language+xml" href="/api/ibexa/v2/languages/eng-GB"> + <languageId>2</languageId> + <languageCode>eng-GB</languageCode> + <name>English (United Kingdom)</name> + </Language> + <Language href="/api/ibexa/v2/languages/pol-PL" media-type="application/vnd.ibexa.api.Language+xml"> + <languageId>4</languageId> + <languageCode>pol-PL</languageCode> + <name>Polish (polski)</name> + </Language> +</LanguageList> diff --git a/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example new file mode 100644 index 000000000..6c00456f2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example @@ -0,0 +1,9 @@ +{ + "Language": { + "_media-type": "application/vnd.ibexa.api.Language+json", + "_href": "/api/ibexa/v2/languages/eng-GB", + "languageId": 2, + "languageCode": "eng-GB", + "name": "English (United Kingdom)" + } +} diff --git a/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example new file mode 100644 index 000000000..43519f1cd --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Language media-type="application/vnd.ibexa.api.Language+xml" href="/api/ibexa/v2/languages/eng-GB"> + <languageId>2</languageId> + <languageCode>eng-GB</languageCode> + <name>English (United Kingdom)</name> +</Language> diff --git a/src/bundle/Resources/api_platform/examples/services/countries/GET/CountriesList.xml.example b/src/bundle/Resources/api_platform/examples/services/countries/GET/CountriesList.xml.example new file mode 100644 index 000000000..374c6aa36 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/services/countries/GET/CountriesList.xml.example @@ -0,0 +1,1479 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CountryList media-type="application/vnd.ibexa.api.CountryList+xml"> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AF"> + <name>Afghanistan</name> + <Alpha2>AF</Alpha2> + <Alpha3>AFG</Alpha3> + <IDC>93</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AX"> + <name>Åland</name> + <Alpha2>AX</Alpha2> + <Alpha3>ALA</Alpha3> + <IDC>358</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AL"> + <name>Albania</name> + <Alpha2>AL</Alpha2> + <Alpha3>ALB</Alpha3> + <IDC>355</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DZ"> + <name>Algeria</name> + <Alpha2>DZ</Alpha2> + <Alpha3>DZA</Alpha3> + <IDC>213</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AS"> + <name>American Samoa</name> + <Alpha2>AS</Alpha2> + <Alpha3>ASM</Alpha3> + <IDC>1684</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AD"> + <name>Andorra</name> + <Alpha2>AD</Alpha2> + <Alpha3>AND</Alpha3> + <IDC>376</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AO"> + <name>Angola</name> + <Alpha2>AO</Alpha2> + <Alpha3>AGO</Alpha3> + <IDC>244</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AI"> + <name>Anguilla</name> + <Alpha2>AI</Alpha2> + <Alpha3>AIA</Alpha3> + <IDC>1264</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AQ"> + <name>Antarctica</name> + <Alpha2>AQ</Alpha2> + <Alpha3>ATA</Alpha3> + <IDC>672</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AG"> + <name>Antigua and Barbuda</name> + <Alpha2>AG</Alpha2> + <Alpha3>ATG</Alpha3> + <IDC>1268</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AR"> + <name>Argentina</name> + <Alpha2>AR</Alpha2> + <Alpha3>ARG</Alpha3> + <IDC>54</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AM"> + <name>Armenia</name> + <Alpha2>AM</Alpha2> + <Alpha3>ARM</Alpha3> + <IDC>374</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AW"> + <name>Aruba</name> + <Alpha2>AW</Alpha2> + <Alpha3>ABW</Alpha3> + <IDC>297</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AU"> + <name>Australia</name> + <Alpha2>AU</Alpha2> + <Alpha3>AUS</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AT"> + <name>Austria</name> + <Alpha2>AT</Alpha2> + <Alpha3>AUT</Alpha3> + <IDC>43</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AZ"> + <name>Azerbaijan</name> + <Alpha2>AZ</Alpha2> + <Alpha3>AZE</Alpha3> + <IDC>994</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BS"> + <name>Bahamas</name> + <Alpha2>BS</Alpha2> + <Alpha3>BHS</Alpha3> + <IDC>1242</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BH"> + <name>Bahrain</name> + <Alpha2>BH</Alpha2> + <Alpha3>BHR</Alpha3> + <IDC>973</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BD"> + <name>Bangladesh</name> + <Alpha2>BD</Alpha2> + <Alpha3>BGD</Alpha3> + <IDC>880</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BB"> + <name>Barbados</name> + <Alpha2>BB</Alpha2> + <Alpha3>BRB</Alpha3> + <IDC>1246</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BY"> + <name>Belarus</name> + <Alpha2>BY</Alpha2> + <Alpha3>BLR</Alpha3> + <IDC>375</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BE"> + <name>Belgium</name> + <Alpha2>BE</Alpha2> + <Alpha3>BEL</Alpha3> + <IDC>32</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BZ"> + <name>Belize</name> + <Alpha2>BZ</Alpha2> + <Alpha3>BLZ</Alpha3> + <IDC>501</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BJ"> + <name>Benin</name> + <Alpha2>BJ</Alpha2> + <Alpha3>BEN</Alpha3> + <IDC>229</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BM"> + <name>Bermuda</name> + <Alpha2>BM</Alpha2> + <Alpha3>BMU</Alpha3> + <IDC>1441</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BT"> + <name>Bhutan</name> + <Alpha2>BT</Alpha2> + <Alpha3>BTN</Alpha3> + <IDC>975</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BO"> + <name>Bolivia</name> + <Alpha2>BO</Alpha2> + <Alpha3>BOL</Alpha3> + <IDC>591</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BA"> + <name>Bosnia and Herzegovina</name> + <Alpha2>BA</Alpha2> + <Alpha3>BIH</Alpha3> + <IDC>387</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BW"> + <name>Botswana</name> + <Alpha2>BW</Alpha2> + <Alpha3>BWA</Alpha3> + <IDC>267</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BV"> + <name>Bouvet Island</name> + <Alpha2>BV</Alpha2> + <Alpha3>BVT</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BR"> + <name>Brazil</name> + <Alpha2>BR</Alpha2> + <Alpha3>BRA</Alpha3> + <IDC>55</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IO"> + <name>British Indian Ocean Territory</name> + <Alpha2>IO</Alpha2> + <Alpha3>IOT</Alpha3> + <IDC>246</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BN"> + <name>Brunei Darussalam</name> + <Alpha2>BN</Alpha2> + <Alpha3>BRN</Alpha3> + <IDC>673</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BG"> + <name>Bulgaria</name> + <Alpha2>BG</Alpha2> + <Alpha3>BGR</Alpha3> + <IDC>359</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BF"> + <name>Burkina Faso</name> + <Alpha2>BF</Alpha2> + <Alpha3>BFA</Alpha3> + <IDC>226</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BI"> + <name>Burundi</name> + <Alpha2>BI</Alpha2> + <Alpha3>BDI</Alpha3> + <IDC>257</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KH"> + <name>Cambodia</name> + <Alpha2>KH</Alpha2> + <Alpha3>KHM</Alpha3> + <IDC>855</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CM"> + <name>Cameroon</name> + <Alpha2>CM</Alpha2> + <Alpha3>CMR</Alpha3> + <IDC>237</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CA"> + <name>Canada</name> + <Alpha2>CA</Alpha2> + <Alpha3>CAN</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CV"> + <name>Cape Verde</name> + <Alpha2>CV</Alpha2> + <Alpha3>CPV</Alpha3> + <IDC>238</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KY"> + <name>Cayman Islands</name> + <Alpha2>KY</Alpha2> + <Alpha3>CYM</Alpha3> + <IDC>1345</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CF"> + <name>Central African Republic</name> + <Alpha2>CF</Alpha2> + <Alpha3>CAF</Alpha3> + <IDC>236</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TD"> + <name>Chad</name> + <Alpha2>TD</Alpha2> + <Alpha3>TCD</Alpha3> + <IDC>235</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CL"> + <name>Chile</name> + <Alpha2>CL</Alpha2> + <Alpha3>CHL</Alpha3> + <IDC>56</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CN"> + <name>China</name> + <Alpha2>CN</Alpha2> + <Alpha3>CHN</Alpha3> + <IDC>86</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CX"> + <name>Christmas Island</name> + <Alpha2>CX</Alpha2> + <Alpha3>CXR</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CC"> + <name>Cocos (Keeling) Islands</name> + <Alpha2>CC</Alpha2> + <Alpha3>CCK</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CO"> + <name>Colombia</name> + <Alpha2>CO</Alpha2> + <Alpha3>COL</Alpha3> + <IDC>57</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KM"> + <name>Comoros</name> + <Alpha2>KM</Alpha2> + <Alpha3>COM</Alpha3> + <IDC>269</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CG"> + <name>Congo</name> + <Alpha2>CG</Alpha2> + <Alpha3>COG</Alpha3> + <IDC>242</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CD"> + <name>Congo, The Democratic Republic Of The</name> + <Alpha2>CD</Alpha2> + <Alpha3>COD</Alpha3> + <IDC>243</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CK"> + <name>Cook Islands</name> + <Alpha2>CK</Alpha2> + <Alpha3>COK</Alpha3> + <IDC>682</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CR"> + <name>Costa Rica</name> + <Alpha2>CR</Alpha2> + <Alpha3>CRI</Alpha3> + <IDC>506</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CI"> + <name>Côte d'Ivoire</name> + <Alpha2>CI</Alpha2> + <Alpha3>CIV</Alpha3> + <IDC>225</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HR"> + <name>Croatia</name> + <Alpha2>HR</Alpha2> + <Alpha3>HRV</Alpha3> + <IDC>385</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CU"> + <name>Cuba</name> + <Alpha2>CU</Alpha2> + <Alpha3>CUB</Alpha3> + <IDC>53</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CY"> + <name>Cyprus</name> + <Alpha2>CY</Alpha2> + <Alpha3>CYP</Alpha3> + <IDC>357</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CZ"> + <name>Czech Republic</name> + <Alpha2>CZ</Alpha2> + <Alpha3>CZE</Alpha3> + <IDC>420</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DK"> + <name>Denmark</name> + <Alpha2>DK</Alpha2> + <Alpha3>DNK</Alpha3> + <IDC>45</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DJ"> + <name>Djibouti</name> + <Alpha2>DJ</Alpha2> + <Alpha3>DJI</Alpha3> + <IDC>253</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DM"> + <name>Dominica</name> + <Alpha2>DM</Alpha2> + <Alpha3>DMA</Alpha3> + <IDC>1767</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DO"> + <name>Dominican Republic</name> + <Alpha2>DO</Alpha2> + <Alpha3>DOM</Alpha3> + <IDC>1809</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="EC"> + <name>Ecuador</name> + <Alpha2>EC</Alpha2> + <Alpha3>ECU</Alpha3> + <IDC>593</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="EG"> + <name>Egypt</name> + <Alpha2>EG</Alpha2> + <Alpha3>EGY</Alpha3> + <IDC>20</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SV"> + <name>El Salvador</name> + <Alpha2>SV</Alpha2> + <Alpha3>SLV</Alpha3> + <IDC>503</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GQ"> + <name>Equatorial Guinea</name> + <Alpha2>GQ</Alpha2> + <Alpha3>GNQ</Alpha3> + <IDC>240</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ER"> + <name>Eritrea</name> + <Alpha2>ER</Alpha2> + <Alpha3>ERI</Alpha3> + <IDC>291</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="EE"> + <name>Estonia</name> + <Alpha2>EE</Alpha2> + <Alpha3>EST</Alpha3> + <IDC>372</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ET"> + <name>Ethiopia</name> + <Alpha2>ET</Alpha2> + <Alpha3>ETH</Alpha3> + <IDC>251</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FK"> + <name>Falkland Islands (Malvinas)</name> + <Alpha2>FK</Alpha2> + <Alpha3>FLK</Alpha3> + <IDC>500</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FO"> + <name>Faroe Islands</name> + <Alpha2>FO</Alpha2> + <Alpha3>FRO</Alpha3> + <IDC>298</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FJ"> + <name>Fiji</name> + <Alpha2>FJ</Alpha2> + <Alpha3>FJI</Alpha3> + <IDC>679</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FI"> + <name>Finland</name> + <Alpha2>FI</Alpha2> + <Alpha3>FIN</Alpha3> + <IDC>358</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FR"> + <name>France</name> + <Alpha2>FR</Alpha2> + <Alpha3>FRA</Alpha3> + <IDC>33</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GF"> + <name>French Guiana</name> + <Alpha2>GF</Alpha2> + <Alpha3>GUF</Alpha3> + <IDC>594</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PF"> + <name>French Polynesia</name> + <Alpha2>PF</Alpha2> + <Alpha3>PYF</Alpha3> + <IDC>689</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TF"> + <name>French Southern Territories</name> + <Alpha2>TF</Alpha2> + <Alpha3>ATF</Alpha3> + <IDC>0</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GA"> + <name>Gabon</name> + <Alpha2>GA</Alpha2> + <Alpha3>GAB</Alpha3> + <IDC>241</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GM"> + <name>Gambia</name> + <Alpha2>GM</Alpha2> + <Alpha3>GMB</Alpha3> + <IDC>220</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GE"> + <name>Georgia</name> + <Alpha2>GE</Alpha2> + <Alpha3>GEO</Alpha3> + <IDC>995</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="DE"> + <name>Germany</name> + <Alpha2>DE</Alpha2> + <Alpha3>DEU</Alpha3> + <IDC>49</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GH"> + <name>Ghana</name> + <Alpha2>GH</Alpha2> + <Alpha3>GHA</Alpha3> + <IDC>233</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GI"> + <name>Gibraltar</name> + <Alpha2>GI</Alpha2> + <Alpha3>GIB</Alpha3> + <IDC>350</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GR"> + <name>Greece</name> + <Alpha2>GR</Alpha2> + <Alpha3>GRC</Alpha3> + <IDC>30</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GL"> + <name>Greenland</name> + <Alpha2>GL</Alpha2> + <Alpha3>GRL</Alpha3> + <IDC>299</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GD"> + <name>Grenada</name> + <Alpha2>GD</Alpha2> + <Alpha3>GRD</Alpha3> + <IDC>1473</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GP"> + <name>Guadeloupe</name> + <Alpha2>GP</Alpha2> + <Alpha3>GLP</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GU"> + <name>Guam</name> + <Alpha2>GU</Alpha2> + <Alpha3>GUM</Alpha3> + <IDC>1671</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GT"> + <name>Guatemala</name> + <Alpha2>GT</Alpha2> + <Alpha3>GTM</Alpha3> + <IDC>502</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GG"> + <name>Guernsey</name> + <Alpha2>GG</Alpha2> + <Alpha3>GGY</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GN"> + <name>Guinea</name> + <Alpha2>GN</Alpha2> + <Alpha3>GIN</Alpha3> + <IDC>224</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GW"> + <name>Guinea-Bissau</name> + <Alpha2>GW</Alpha2> + <Alpha3>GNB</Alpha3> + <IDC>245</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GY"> + <name>Guyana</name> + <Alpha2>GY</Alpha2> + <Alpha3>GUY</Alpha3> + <IDC>592</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HT"> + <name>Haiti</name> + <Alpha2>HT</Alpha2> + <Alpha3>HTI</Alpha3> + <IDC>509</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HM"> + <name>Heard Island and McDonald Islands</name> + <Alpha2>HM</Alpha2> + <Alpha3>HMD</Alpha3> + <IDC>672</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HN"> + <name>Honduras</name> + <Alpha2>HN</Alpha2> + <Alpha3>HND</Alpha3> + <IDC>504</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HK"> + <name>Hong Kong</name> + <Alpha2>HK</Alpha2> + <Alpha3>HKG</Alpha3> + <IDC>852</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="HU"> + <name>Hungary</name> + <Alpha2>HU</Alpha2> + <Alpha3>HUN</Alpha3> + <IDC>36</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IS"> + <name>Iceland</name> + <Alpha2>IS</Alpha2> + <Alpha3>ISL</Alpha3> + <IDC>354</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IN"> + <name>India</name> + <Alpha2>IN</Alpha2> + <Alpha3>IND</Alpha3> + <IDC>91</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ID"> + <name>Indonesia</name> + <Alpha2>ID</Alpha2> + <Alpha3>IDN</Alpha3> + <IDC>62</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IR"> + <name>Iran, Islamic Republic of</name> + <Alpha2>IR</Alpha2> + <Alpha3>IRN</Alpha3> + <IDC>98</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IQ"> + <name>Iraq</name> + <Alpha2>IQ</Alpha2> + <Alpha3>IRQ</Alpha3> + <IDC>964</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IE"> + <name>Ireland</name> + <Alpha2>IE</Alpha2> + <Alpha3>IRL</Alpha3> + <IDC>353</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IM"> + <name>Isle of Man</name> + <Alpha2>IM</Alpha2> + <Alpha3>IMN</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IL"> + <name>Israel</name> + <Alpha2>IL</Alpha2> + <Alpha3>ISR</Alpha3> + <IDC>972</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="IT"> + <name>Italy</name> + <Alpha2>IT</Alpha2> + <Alpha3>ITA</Alpha3> + <IDC>39</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="JM"> + <name>Jamaica</name> + <Alpha2>JM</Alpha2> + <Alpha3>JAM</Alpha3> + <IDC>1876</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="JP"> + <name>Japan</name> + <Alpha2>JP</Alpha2> + <Alpha3>JPN</Alpha3> + <IDC>81</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="JE"> + <name>Jersey</name> + <Alpha2>JE</Alpha2> + <Alpha3>JEY</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="JO"> + <name>Jordan</name> + <Alpha2>JO</Alpha2> + <Alpha3>JOR</Alpha3> + <IDC>962</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KZ"> + <name>Kazakhstan</name> + <Alpha2>KZ</Alpha2> + <Alpha3>KAZ</Alpha3> + <IDC>7</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KE"> + <name>Kenya</name> + <Alpha2>KE</Alpha2> + <Alpha3>KEN</Alpha3> + <IDC>254</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KI"> + <name>Kiribati</name> + <Alpha2>KI</Alpha2> + <Alpha3>KIR</Alpha3> + <IDC>686</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KP"> + <name>Korea, Democratic People's Republic of</name> + <Alpha2>KP</Alpha2> + <Alpha3>PRK</Alpha3> + <IDC>850</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KR"> + <name>Korea, Republic of</name> + <Alpha2>KR</Alpha2> + <Alpha3>KOR</Alpha3> + <IDC>82</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KW"> + <name>Kuwait</name> + <Alpha2>KW</Alpha2> + <Alpha3>KWT</Alpha3> + <IDC>965</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KG"> + <name>Kyrgyzstan</name> + <Alpha2>KG</Alpha2> + <Alpha3>KGZ</Alpha3> + <IDC>996</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LA"> + <name>Lao People's Democratic Republic</name> + <Alpha2>LA</Alpha2> + <Alpha3>LAO</Alpha3> + <IDC>856</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LV"> + <name>Latvia</name> + <Alpha2>LV</Alpha2> + <Alpha3>LVA</Alpha3> + <IDC>371</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LB"> + <name>Lebanon</name> + <Alpha2>LB</Alpha2> + <Alpha3>LBN</Alpha3> + <IDC>961</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LS"> + <name>Lesotho</name> + <Alpha2>LS</Alpha2> + <Alpha3>LSO</Alpha3> + <IDC>266</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LR"> + <name>Liberia</name> + <Alpha2>LR</Alpha2> + <Alpha3>LBR</Alpha3> + <IDC>231</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LY"> + <name>Libyan Arab Jamahiriya</name> + <Alpha2>LY</Alpha2> + <Alpha3>LBY</Alpha3> + <IDC>218</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LI"> + <name>Liechtenstein</name> + <Alpha2>LI</Alpha2> + <Alpha3>LIE</Alpha3> + <IDC>423</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LT"> + <name>Lithuania</name> + <Alpha2>LT</Alpha2> + <Alpha3>LTU</Alpha3> + <IDC>370</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LU"> + <name>Luxembourg</name> + <Alpha2>LU</Alpha2> + <Alpha3>LUX</Alpha3> + <IDC>352</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MO"> + <name>Macau</name> + <Alpha2>MO</Alpha2> + <Alpha3>MAC</Alpha3> + <IDC>853</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MK"> + <name>Macedonia, The Former Yugoslav Republic of</name> + <Alpha2>MK</Alpha2> + <Alpha3>MKD</Alpha3> + <IDC>389</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MG"> + <name>Madagascar</name> + <Alpha2>MG</Alpha2> + <Alpha3>MDG</Alpha3> + <IDC>261</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MW"> + <name>Malawi</name> + <Alpha2>MW</Alpha2> + <Alpha3>MWI</Alpha3> + <IDC>265</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MY"> + <name>Malaysia</name> + <Alpha2>MY</Alpha2> + <Alpha3>MYS</Alpha3> + <IDC>60</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MV"> + <name>Maldives</name> + <Alpha2>MV</Alpha2> + <Alpha3>MDV</Alpha3> + <IDC>960</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ML"> + <name>Mali</name> + <Alpha2>ML</Alpha2> + <Alpha3>MLI</Alpha3> + <IDC>223</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MT"> + <name>Malta</name> + <Alpha2>MT</Alpha2> + <Alpha3>MLT</Alpha3> + <IDC>356</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MH"> + <name>Marshall Islands</name> + <Alpha2>MH</Alpha2> + <Alpha3>MHL</Alpha3> + <IDC>692</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MQ"> + <name>Martinique</name> + <Alpha2>MQ</Alpha2> + <Alpha3>MTQ</Alpha3> + <IDC>596</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MR"> + <name>Mauritania</name> + <Alpha2>MR</Alpha2> + <Alpha3>MRT</Alpha3> + <IDC>222</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MU"> + <name>Mauritius</name> + <Alpha2>MU</Alpha2> + <Alpha3>MUS</Alpha3> + <IDC>230</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="YT"> + <name>Mayotte</name> + <Alpha2>YT</Alpha2> + <Alpha3>MYT</Alpha3> + <IDC>262</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MX"> + <name>Mexico</name> + <Alpha2>MX</Alpha2> + <Alpha3>MEX</Alpha3> + <IDC>52</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="FM"> + <name>Micronesia, Federated States of</name> + <Alpha2>FM</Alpha2> + <Alpha3>FSM</Alpha3> + <IDC>691</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MD"> + <name>Moldova, Republic of</name> + <Alpha2>MD</Alpha2> + <Alpha3>MDA</Alpha3> + <IDC>373</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MC"> + <name>Monaco</name> + <Alpha2>MC</Alpha2> + <Alpha3>MCO</Alpha3> + <IDC>377</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MN"> + <name>Mongolia</name> + <Alpha2>MN</Alpha2> + <Alpha3>MNG</Alpha3> + <IDC>976</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ME"> + <name>Montenegro</name> + <Alpha2>ME</Alpha2> + <Alpha3>MNE</Alpha3> + <IDC>382</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MS"> + <name>Montserrat</name> + <Alpha2>MS</Alpha2> + <Alpha3>MSR</Alpha3> + <IDC>1664</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MA"> + <name>Morocco</name> + <Alpha2>MA</Alpha2> + <Alpha3>MAR</Alpha3> + <IDC>212</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MZ"> + <name>Mozambique</name> + <Alpha2>MZ</Alpha2> + <Alpha3>MOZ</Alpha3> + <IDC>258</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MM"> + <name>Myanmar</name> + <Alpha2>MM</Alpha2> + <Alpha3>MMR</Alpha3> + <IDC>95</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NA"> + <name>Namibia</name> + <Alpha2>NA</Alpha2> + <Alpha3>NAM</Alpha3> + <IDC>264</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NR"> + <name>Nauru</name> + <Alpha2>NR</Alpha2> + <Alpha3>NRU</Alpha3> + <IDC>674</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NP"> + <name>Nepal</name> + <Alpha2>NP</Alpha2> + <Alpha3>NPL</Alpha3> + <IDC>977</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NL"> + <name>Netherlands</name> + <Alpha2>NL</Alpha2> + <Alpha3>NLD</Alpha3> + <IDC>31</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AN"> + <name>Netherlands Antilles</name> + <Alpha2>AN</Alpha2> + <Alpha3>ANT</Alpha3> + <IDC>599</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NC"> + <name>New Caledonia</name> + <Alpha2>NC</Alpha2> + <Alpha3>NCL</Alpha3> + <IDC>687</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NZ"> + <name>New Zealand</name> + <Alpha2>NZ</Alpha2> + <Alpha3>NZL</Alpha3> + <IDC>64</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NI"> + <name>Nicaragua</name> + <Alpha2>NI</Alpha2> + <Alpha3>NIC</Alpha3> + <IDC>505</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NE"> + <name>Niger</name> + <Alpha2>NE</Alpha2> + <Alpha3>NER</Alpha3> + <IDC>227</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NG"> + <name>Nigeria</name> + <Alpha2>NG</Alpha2> + <Alpha3>NGA</Alpha3> + <IDC>234</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NU"> + <name>Niue</name> + <Alpha2>NU</Alpha2> + <Alpha3>NIU</Alpha3> + <IDC>683</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NF"> + <name>Norfolk Island</name> + <Alpha2>NF</Alpha2> + <Alpha3>NFK</Alpha3> + <IDC>6723</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MP"> + <name>Northern Mariana Islands</name> + <Alpha2>MP</Alpha2> + <Alpha3>MNP</Alpha3> + <IDC>1670</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="NO"> + <name>Norway</name> + <Alpha2>NO</Alpha2> + <Alpha3>NOR</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="OM"> + <name>Oman</name> + <Alpha2>OM</Alpha2> + <Alpha3>OMN</Alpha3> + <IDC>968</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PK"> + <name>Pakistan</name> + <Alpha2>PK</Alpha2> + <Alpha3>PAK</Alpha3> + <IDC>92</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PW"> + <name>Palau</name> + <Alpha2>PW</Alpha2> + <Alpha3>PLW</Alpha3> + <IDC>680</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PS"> + <name>Palestinian Territory, Occupied</name> + <Alpha2>PS</Alpha2> + <Alpha3>PSE</Alpha3> + <IDC>970</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PA"> + <name>Panama</name> + <Alpha2>PA</Alpha2> + <Alpha3>PAN</Alpha3> + <IDC>507</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PG"> + <name>Papua New Guinea</name> + <Alpha2>PG</Alpha2> + <Alpha3>PNG</Alpha3> + <IDC>675</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PY"> + <name>Paraguay</name> + <Alpha2>PY</Alpha2> + <Alpha3>PRY</Alpha3> + <IDC>595</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PE"> + <name>Peru</name> + <Alpha2>PE</Alpha2> + <Alpha3>PER</Alpha3> + <IDC>51</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PH"> + <name>Philippines</name> + <Alpha2>PH</Alpha2> + <Alpha3>PHL</Alpha3> + <IDC>63</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PN"> + <name>Pitcairn</name> + <Alpha2>PN</Alpha2> + <Alpha3>PCN</Alpha3> + <IDC>64</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PL"> + <name>Poland</name> + <Alpha2>PL</Alpha2> + <Alpha3>POL</Alpha3> + <IDC>48</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PT"> + <name>Portugal</name> + <Alpha2>PT</Alpha2> + <Alpha3>PRT</Alpha3> + <IDC>351</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PR"> + <name>Puerto Rico</name> + <Alpha2>PR</Alpha2> + <Alpha3>PRI</Alpha3> + <IDC>1787</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="QA"> + <name>Qatar</name> + <Alpha2>QA</Alpha2> + <Alpha3>QAT</Alpha3> + <IDC>974</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="RE"> + <name>Reunion</name> + <Alpha2>RE</Alpha2> + <Alpha3>REU</Alpha3> + <IDC>262</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="RO"> + <name>Romania</name> + <Alpha2>RO</Alpha2> + <Alpha3>ROU</Alpha3> + <IDC>40</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="RU"> + <name>Russian Federation</name> + <Alpha2>RU</Alpha2> + <Alpha3>RUS</Alpha3> + <IDC>7</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="RW"> + <name>Rwanda</name> + <Alpha2>RW</Alpha2> + <Alpha3>RWA</Alpha3> + <IDC>250</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="BL"> + <name>Saint Barthélemy</name> + <Alpha2>BL</Alpha2> + <Alpha3>BLM</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SH"> + <name>Saint Helena</name> + <Alpha2>SH</Alpha2> + <Alpha3>SHN</Alpha3> + <IDC>290</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="KN"> + <name>Saint Kitts and Nevis</name> + <Alpha2>KN</Alpha2> + <Alpha3>KNA</Alpha3> + <IDC>1869</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LC"> + <name>Saint Lucia</name> + <Alpha2>LC</Alpha2> + <Alpha3>LCA</Alpha3> + <IDC>1758</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="MF"> + <name>Saint Martin</name> + <Alpha2>MF</Alpha2> + <Alpha3>MAF</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="PM"> + <name>Saint Pierre and Miquelon</name> + <Alpha2>PM</Alpha2> + <Alpha3>SPM</Alpha3> + <IDC>508</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VC"> + <name>Saint Vincent and The Grenadines</name> + <Alpha2>VC</Alpha2> + <Alpha3>VCT</Alpha3> + <IDC>1784</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="WS"> + <name>Samoa</name> + <Alpha2>WS</Alpha2> + <Alpha3>WSM</Alpha3> + <IDC>685</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SM"> + <name>San Marino</name> + <Alpha2>SM</Alpha2> + <Alpha3>SMR</Alpha3> + <IDC>378</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ST"> + <name>Sao Tome and Principe</name> + <Alpha2>ST</Alpha2> + <Alpha3>STP</Alpha3> + <IDC>239</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SA"> + <name>Saudi Arabia</name> + <Alpha2>SA</Alpha2> + <Alpha3>SAU</Alpha3> + <IDC>966</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SN"> + <name>Senegal</name> + <Alpha2>SN</Alpha2> + <Alpha3>SEN</Alpha3> + <IDC>221</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="RS"> + <name>Serbia</name> + <Alpha2>RS</Alpha2> + <Alpha3>SRB</Alpha3> + <IDC>381</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SC"> + <name>Seychelles</name> + <Alpha2>SC</Alpha2> + <Alpha3>SYC</Alpha3> + <IDC>248</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SL"> + <name>Sierra Leone</name> + <Alpha2>SL</Alpha2> + <Alpha3>SLE</Alpha3> + <IDC>232</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SG"> + <name>Singapore</name> + <Alpha2>SG</Alpha2> + <Alpha3>SGP</Alpha3> + <IDC>65</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SK"> + <name>Slovakia</name> + <Alpha2>SK</Alpha2> + <Alpha3>SVK</Alpha3> + <IDC>421</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SI"> + <name>Slovenia</name> + <Alpha2>SI</Alpha2> + <Alpha3>SVN</Alpha3> + <IDC>386</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SB"> + <name>Solomon Islands</name> + <Alpha2>SB</Alpha2> + <Alpha3>SLB</Alpha3> + <IDC>677</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SO"> + <name>Somalia</name> + <Alpha2>SO</Alpha2> + <Alpha3>SOM</Alpha3> + <IDC>252</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ZA"> + <name>South Africa</name> + <Alpha2>ZA</Alpha2> + <Alpha3>ZAF</Alpha3> + <IDC>27</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GS"> + <name>South Georgia and The South Sandwich Islands</name> + <Alpha2>GS</Alpha2> + <Alpha3>SGS</Alpha3> + <IDC>500</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ES"> + <name>Spain</name> + <Alpha2>ES</Alpha2> + <Alpha3>ESP</Alpha3> + <IDC>34</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="LK"> + <name>Sri Lanka</name> + <Alpha2>LK</Alpha2> + <Alpha3>LKA</Alpha3> + <IDC>94</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SD"> + <name>Sudan</name> + <Alpha2>SD</Alpha2> + <Alpha3>SDN</Alpha3> + <IDC>249</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SR"> + <name>Suriname</name> + <Alpha2>SR</Alpha2> + <Alpha3>SUR</Alpha3> + <IDC>597</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SJ"> + <name>Svalbard and Jan Mayen</name> + <Alpha2>SJ</Alpha2> + <Alpha3>SJM</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SZ"> + <name>Swaziland</name> + <Alpha2>SZ</Alpha2> + <Alpha3>SWZ</Alpha3> + <IDC>268</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SE"> + <name>Sweden</name> + <Alpha2>SE</Alpha2> + <Alpha3>SWE</Alpha3> + <IDC>46</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="CH"> + <name>Switzerland</name> + <Alpha2>CH</Alpha2> + <Alpha3>CHE</Alpha3> + <IDC>41</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="SY"> + <name>Syrian Arab Republic</name> + <Alpha2>SY</Alpha2> + <Alpha3>SYR</Alpha3> + <IDC>963</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TW"> + <name>Taiwan</name> + <Alpha2>TW</Alpha2> + <Alpha3>TWN</Alpha3> + <IDC>886</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TJ"> + <name>Tajikistan</name> + <Alpha2>TJ</Alpha2> + <Alpha3>TJK</Alpha3> + <IDC>992</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TZ"> + <name>Tanzania, United Republic of</name> + <Alpha2>TZ</Alpha2> + <Alpha3>TZA</Alpha3> + <IDC>255</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TH"> + <name>Thailand</name> + <Alpha2>TH</Alpha2> + <Alpha3>THA</Alpha3> + <IDC>66</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TL"> + <name>Timor-Leste</name> + <Alpha2>TL</Alpha2> + <Alpha3>TLS</Alpha3> + <IDC>670</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TG"> + <name>Togo</name> + <Alpha2>TG</Alpha2> + <Alpha3>TGO</Alpha3> + <IDC>228</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TK"> + <name>Tokelau</name> + <Alpha2>TK</Alpha2> + <Alpha3>TKL</Alpha3> + <IDC>690</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TO"> + <name>Tonga</name> + <Alpha2>TO</Alpha2> + <Alpha3>TON</Alpha3> + <IDC>676</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TT"> + <name>Trinidad and Tobago</name> + <Alpha2>TT</Alpha2> + <Alpha3>TTO</Alpha3> + <IDC>1868</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TN"> + <name>Tunisia</name> + <Alpha2>TN</Alpha2> + <Alpha3>TUN</Alpha3> + <IDC>216</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TR"> + <name>Turkey</name> + <Alpha2>TR</Alpha2> + <Alpha3>TUR</Alpha3> + <IDC>90</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TM"> + <name>Turkmenistan</name> + <Alpha2>TM</Alpha2> + <Alpha3>TKM</Alpha3> + <IDC>993</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TC"> + <name>Turks and Caicos Islands</name> + <Alpha2>TC</Alpha2> + <Alpha3>TCA</Alpha3> + <IDC>1649</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="TV"> + <name>Tuvalu</name> + <Alpha2>TV</Alpha2> + <Alpha3>TUV</Alpha3> + <IDC>688</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="UG"> + <name>Uganda</name> + <Alpha2>UG</Alpha2> + <Alpha3>UGA</Alpha3> + <IDC>256</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="UA"> + <name>Ukraine</name> + <Alpha2>UA</Alpha2> + <Alpha3>UKR</Alpha3> + <IDC>380</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="AE"> + <name>United Arab Emirates</name> + <Alpha2>AE</Alpha2> + <Alpha3>ARE</Alpha3> + <IDC>971</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="GB"> + <name>United Kingdom</name> + <Alpha2>GB</Alpha2> + <Alpha3>GBR</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="UM"> + <name>United States Minor Outlying Islands</name> + <Alpha2>UM</Alpha2> + <Alpha3>UMI</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="US"> + <name>United States of America</name> + <Alpha2>US</Alpha2> + <Alpha3>USA</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="UY"> + <name>Uruguay</name> + <Alpha2>UY</Alpha2> + <Alpha3>URY</Alpha3> + <IDC>598</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="UZ"> + <name>Uzbekistan</name> + <Alpha2>UZ</Alpha2> + <Alpha3>UZB</Alpha3> + <IDC>998</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VU"> + <name>Vanuatu</name> + <Alpha2>VU</Alpha2> + <Alpha3>VUT</Alpha3> + <IDC>678</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VA"> + <name>Holy See (Vatican City State)</name> + <Alpha2>VA</Alpha2> + <Alpha3>VAT</Alpha3> + <IDC>3906</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VE"> + <name>Venezuela</name> + <Alpha2>VE</Alpha2> + <Alpha3>VEN</Alpha3> + <IDC>58</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VN"> + <name>Viet Nam</name> + <Alpha2>VN</Alpha2> + <Alpha3>VNM</Alpha3> + <IDC>84</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VG"> + <name>Virgin Islands, British</name> + <Alpha2>VG</Alpha2> + <Alpha3>VGB</Alpha3> + <IDC>1284</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="VI"> + <name>Virgin Islands, U.S.</name> + <Alpha2>VI</Alpha2> + <Alpha3>VIR</Alpha3> + <IDC>1340</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="WF"> + <name>Wallis and Futuna</name> + <Alpha2>WF</Alpha2> + <Alpha3>WLF</Alpha3> + <IDC>681</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="EH"> + <name>Western Sahara</name> + <Alpha2>EH</Alpha2> + <Alpha3>ESH</Alpha3> + <IDC>212</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="YE"> + <name>Yemen</name> + <Alpha2>YE</Alpha2> + <Alpha3>YEM</Alpha3> + <IDC>967</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ZM"> + <name>Zambia</name> + <Alpha2>ZM</Alpha2> + <Alpha3>ZMB</Alpha3> + <IDC>260</IDC> + </Country> + <Country media-type="application/vnd.ibexa.api.Country+xml" id="ZW"> + <name>Zimbabwe</name> + <Alpha2>ZW</Alpha2> + <Alpha3>ZWE</Alpha3> + <IDC>263</IDC> + </Country> +</CountryList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.json.example new file mode 100644 index 000000000..afe640263 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.json.example @@ -0,0 +1,156 @@ +{ + "UserGroupList": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/15", + "_id": 14, + "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4" + }, + "name": "Administrator User", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/14/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/13/15" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/14/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:13:50+00:00", + "lastModificationDate": "2011-03-25T14:07:04+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/14/versions/3", + "VersionInfo": { + "id": 499, + "versionNo": 3, + "status": "PUBLISHED", + "modificationDate": "2011-03-25T14:07:04+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2011-03-25T14:03:03+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator User" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/14" + } + }, + "Fields": { + "field": [ + { + "id": 28, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator" + }, + { + "id": 29, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "User" + }, + { + "id": 30, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 14, + "login": "admin", + "email": "admin@link.invalid", + "passwordUpdatedAt": null, + "enabled": true, + "maxLogin": 10, + "plainPassword": null + } + }, + { + "id": 178, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 180, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/14/versions/3/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13" + }, + "Subgroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/15/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/15/users" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/15/roles" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.xml.example new file mode 100644 index 000000000..a6ea1d93f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.xml.example @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupList media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/groups"> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13/15" id="14" remoteId="1bb4fe25487f05527efa8bfd394cecc7"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/4"/> + <name>Administrator User</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/14/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/13/15"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/14/locations"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <publishDate>2002-10-06T16:13:50+00:00</publishDate> + <lastModificationDate>2011-03-25T14:07:04+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/14/versions/3"> + <VersionInfo> + <id>499</id> + <versionNo>3</versionNo> + <status>PUBLISHED</status> + <modificationDate>2011-03-25T14:07:04+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2011-03-25T14:03:03+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator User</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/14"/> + </VersionInfo> + <Fields> + <field> + <id>28</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator</fieldValue> + </field> + <field> + <id>29</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>User</fieldValue> + </field> + <field> + <id>30</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">14</value> + <value key="login">admin</value> + <value key="email">admin@link.invalid</value> + <value key="passwordUpdatedAt"/> + <value key="enabled">true</value> + <value key="maxLogin">10</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>178</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>180</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/14/versions/3/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13"/> + <Subgroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/groups/1/5/13/15/subgroups"/> + <Users media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/groups/1/5/13/15/users"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/13/15/roles"/> + </UserGroup> +</UserGroupList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example new file mode 100644 index 000000000..56b569809 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/groups/13/subgroups", + "UserGroup": [ + { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/112" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example new file mode 100644 index 000000000..2ec89ff11 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/groups/13/subgroups"> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13/112"/> +</UserGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.json.example new file mode 100644 index 000000000..1df6338d7 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.json.example @@ -0,0 +1,16 @@ +{ + "UserRefList": { + "_media-type": "application/vnd.ibexa.api.UserRefList+json", + "_href": "/api/ibexa/v2/user/groups/13/users", + "User": [ + { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/113" + }, + { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.xml.example new file mode 100644 index 000000000..633e04a03 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserRefList media-type="application/vnd.ibexa.api.UserRefList+xml" href="/api/ibexa/v2/user/groups/13/users"> + <User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/113"/> + <User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> +</UserRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.json.example new file mode 100644 index 000000000..56f81323a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13", + "_id": 12, + "_remoteId": "9b47a45624b023b1a76c73b74d704acf", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/3" + }, + "name": "Administrator users", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/12/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/13" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/12/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:12:55+00:00", + "lastModificationDate": "2002-10-06T16:12:55+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/12/versions/1", + "VersionInfo": { + "id": 440, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2002-10-06T16:12:55+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2002-10-06T16:12:40+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator users" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/12" + } + }, + "Fields": { + "field": [ + { + "id": 24, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator users" + }, + { + "id": 25, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/12/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/users" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/roles" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.xml.example new file mode 100644 index 000000000..2145db50a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/GET/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/3"/> + <name>Administrator users</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/12/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/13"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/12/locations"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <publishDate>2002-10-06T16:12:55+00:00</publishDate> + <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/12/versions/1"> + <VersionInfo> + <id>440</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2002-10-06T16:12:55+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2002-10-06T16:12:40+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator users</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/12"/> + </VersionInfo> + <Fields> + <field> + <id>24</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator users</fieldValue> + </field> + <field> + <id>25</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/12/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/groups/1/5/13/subgroups"/> + <Users media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/groups/1/5/13/users"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/13/roles"/> +</UserGroup> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.json.example new file mode 100644 index 000000000..56f81323a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13", + "_id": 12, + "_remoteId": "9b47a45624b023b1a76c73b74d704acf", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/3" + }, + "name": "Administrator users", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/12/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/13" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/12/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:12:55+00:00", + "lastModificationDate": "2002-10-06T16:12:55+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/12/versions/1", + "VersionInfo": { + "id": 440, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2002-10-06T16:12:55+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2002-10-06T16:12:40+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator users" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/12" + } + }, + "Fields": { + "field": [ + { + "id": 24, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator users" + }, + { + "id": 25, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/12/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/users" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13/roles" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.xml.example new file mode 100644 index 000000000..2145db50a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/3"/> + <name>Administrator users</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/12/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/13"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/12/locations"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <publishDate>2002-10-06T16:12:55+00:00</publishDate> + <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/12/versions/1"> + <VersionInfo> + <id>440</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2002-10-06T16:12:55+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2002-10-06T16:12:40+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator users</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/12"/> + </VersionInfo> + <Fields> + <field> + <id>24</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator users</fieldValue> + </field> + <field> + <id>25</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/12/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/groups/1/5/13/subgroups"/> + <Users media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/groups/1/5/13/users"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/13/roles"/> +</UserGroup> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.json.example new file mode 100644 index 000000000..270526545 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.json.example @@ -0,0 +1,7 @@ +{ + "UserGroupUpdate":{ + "Section": { + "_href": "/api/ibexa/v2/content/sections/2" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example new file mode 100644 index 000000000..1cda91de1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupUpdate> + <Section href="/api/ibexa/v2/content/sections/2" /> +</UserGroupUpdate> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example new file mode 100644 index 000000000..2a429ff57 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example @@ -0,0 +1,10 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/groups/1/42/44/roles/1", + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/1" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example new file mode 100644 index 000000000..4441ea6dd --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/42/44/roles/1"> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/1"/> +</RoleAssignment> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.json.example new file mode 100644 index 000000000..027f2d71b --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.json.example @@ -0,0 +1,23 @@ +{ + "RoleAssignInput": { + "Role": { + "_href": "/api/ibexa/v2/user/roles/2", + "_media-type": "application/vnd.ibexa.api.RoleAssignInput+json" + }, + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_href": "/api/ibexa/v2/content/sections/1", + "_media-type": "application/vnd.ibexa.api.Section+json" + }, + { + "_href": "/api/ibexa/v2/content/sections/4", + "_media-type": "application/vnd.ibexa.api.Section+json" + } + ] + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example new file mode 100644 index 000000000..ddaefdad1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignInput> + <Role href="/api/ibexa/v2/user/roles/10" media-type="application/vnd.ibexa.api.RoleAssignInput+xml"/> + <limitation identifier="Section"> + <values> + <ref href="/api/ibexa/v2/content/sections/1" media-type="application/vnd.ibexa.api.Section+xml" /> + <ref href="/api/ibexa/v2/content/sections/4" media-type="application/vnd.ibexa.api.Section+xml" /> + </values> + </limitation> +</RoleAssignInput> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example new file mode 100644 index 000000000..4be381050 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example @@ -0,0 +1,16 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/12/2/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/groups/1/12/2/roles/2", + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example new file mode 100644 index 000000000..28f7279ec --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles"> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/3"/> +</RoleAssignment> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles/10"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/10"/> +</RoleAssignment> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles/10"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="4"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/10"/> +</RoleAssignment> +</RoleAssignmentList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example new file mode 100644 index 000000000..98d139c12 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example @@ -0,0 +1,27 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/57/58/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/groups/1/57/58/roles/3", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/3" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example new file mode 100644 index 000000000..5c7da74a6 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles"> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/5/65/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/3"/> +</RoleAssignment> +</RoleAssignmentList> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example new file mode 100644 index 000000000..3db7d95a4 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example @@ -0,0 +1,10 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/groups/1/11/12/roles/1", + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/1" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example new file mode 100644 index 000000000..367622000 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/groups/1/11/12/roles/1"> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/1"/> +</RoleAssignment> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example new file mode 100644 index 000000000..ffa7a38f4 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/81", + "_id": 87, + "_remoteId": "remoteId-qwert098", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/3" + }, + "name": "UserGroup2", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/87/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/81" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/87/locations" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2021-08-09T09:06:49+00:00", + "lastModificationDate": "2021-08-09T09:06:49+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/87/versions/1", + "VersionInfo": { + "id": 578, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-08-09T09:06:49+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-08-09T09:06:49+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "UserGroup2" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/87" + } + }, + "Fields": { + "field": [ + { + "id": 384, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "UserGroup2" + }, + { + "id": 385, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "This is the description of the user group" + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/87/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/81/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/81/users" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/groups/1/5/81/roles" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example new file mode 100644 index 000000000..7f182f8de --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/79" id="85" remoteId="remoteId-qwert098"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/3"/> + <name>UserGroup2</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/85/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/79"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/85/locations"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <publishDate>2021-08-09T09:03:35+00:00</publishDate> + <lastModificationDate>2021-08-09T09:03:35+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/85/versions/1"> + <VersionInfo> + <id>576</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-08-09T09:03:35+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2021-08-09T09:03:35+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">UserGroup2</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/85"/> + </VersionInfo> + <Fields> + <field> + <id>380</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>UserGroup2</fieldValue> + </field> + <field> + <id>381</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>This is the description of the user group</fieldValue> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/85/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/groups/1/5/79/subgroups"/> + <Users media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/groups/1/5/79/users"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/groups/1/5/79/roles"/> +</UserGroup> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example new file mode 100644 index 000000000..876e65bdf --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example @@ -0,0 +1,20 @@ +{ + "UserGroupCreate": { + "mainLanguageCode": "eng-GB", + "remoteId": "remoteId-qwert098", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldValue": "UserGroup2" + }, + { + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldValue": "This is the description of the user group" + } + ] + } + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example new file mode 100644 index 000000000..2ae0b4aaa --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupCreate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <remoteId>remoteId-qwert098</remoteId> + <fields> + <field> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>UserGroup</fieldValue> + </field> + <field> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>This is the description of the user group</fieldValue> + </field> + </fields> +</UserGroupCreate> \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.json.example new file mode 100644 index 000000000..c0866de65 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.json.example @@ -0,0 +1,149 @@ +{ + "User": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/57", + "_id": 57, + "_remoteId": "remoteId-qwert426", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4" + }, + "name": "Yura Rajzer", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/57/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/13/58" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/57/locations" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/57/groups" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2021-08-11T13:52:13+00:00", + "lastModificationDate": "2021-08-11T13:52:13+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/57/versions/1", + "VersionInfo": { + "id": 517, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-08-11T13:52:13+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-08-11T13:52:13+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Yura Rajzer" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/57" + } + }, + "Fields": { + "field": [ + { + "id": 262, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Yura" + }, + { + "id": 263, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Rajzer" + }, + { + "id": 264, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 57, + "login": "yura", + "email": "yurarajzer@example.net", + "passwordUpdatedAt": 1628689933, + "enabled": true, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 265, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 266, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/57/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "yura", + "email": "yurarajzer@example.net", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/users/57/groups" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/57/roles" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.xml.example new file mode 100644 index 000000000..bbe48cbe9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/User.xml.example @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/59" id="59" remoteId="remoteId-qwert426"> +<ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/4"/> +<name>Yura Rajzer</name> +<Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/59/versions"/> +<Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> +<MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/65/67"/> +<Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/59/locations"/> +<Groups media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/59/groups"/> +<Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> +<publishDate>2019-02-27T11:23:42+01:00</publishDate> +<lastModificationDate>2019-02-27T11:23:42+01:00</lastModificationDate> +<mainLanguageCode>eng-GB</mainLanguageCode> +<alwaysAvailable>true</alwaysAvailable> +<Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/59/versions/1"> + <VersionInfo> + <id>515</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2019-02-27T11:23:42+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2019-02-27T11:23:42+01:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Yura Rajzer</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/59"/> + </VersionInfo> + <Fields> + <field> + <id>207</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Yura</fieldValue> + </field> + <field> + <id>208</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Rajzer</fieldValue> + </field> + <field> + <id>209</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">59</value> + <value key="login">yura</value> + <value key="email">yurarajzer@example.net</value> + <value key="enabled">true</value> + <value key="maxLogin">0</value> + </fieldValue> + </field> + <field> + <id>210</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>211</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/59/versions/1/relations"/> +</Version> +<login>yura</login> +<email>yurarajzer@example.net</email> +<enabled>true</enabled> +<UserGroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/users/59/groups"/> +<Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/59/roles"/> +</User> diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.json.example b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.json.example new file mode 100644 index 000000000..d403e5324 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.json.example @@ -0,0 +1,35 @@ +{ + "UserCreate": { + "mainLanguageCode": "eng-GB", + "remoteId": "remoteId-23456789", + "login": "johnsmith2", + "email": "example@example.com", + "password": "Secrepassword5!", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldValue": "John" + }, + { + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldValue": "Smith" + }, + { + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 14, + "login": "johnsmith", + "email": "example@example.com", + "enabled": true, + "maxLogin": 10 + } + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.xml.example b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.xml.example new file mode 100644 index 000000000..1c281381e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.xml.example @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserCreate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <remoteId>remoteId-23456789</remoteId> + <login>johnsmith</login> + <email>example@example.com</email> + <password>Secrepassword5!</password> + <fields> + <field> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>John</fieldValue> + </field> + <field> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>Smith</fieldValue> + </field> + <field> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">14</value> + <value key="login">johnsmith</value> + <value key="email">example@example.com</value> + <value key="enabled">true</value> + <value key="maxLogin">10</value> + </fieldValue> + </field> + </fields> +</UserCreate> diff --git a/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.json.example b/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.json.example new file mode 100644 index 000000000..05f6b541d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.json.example @@ -0,0 +1,104 @@ +{ + "PolicyList": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/policies", + "Policy": [ + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/349", + "id": 349, + "module": "content", + "function": "read", + "limitations": { + "limitation": [ + { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "3" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "6" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/350", + "id": 350, + "module": "user", + "function": "login", + "limitations": { + "limitation": [ + { + "_identifier": "SiteAccess", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "2282622326" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1766001124" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/351", + "id": 351, + "module": "content", + "function": "view_embed", + "limitations": { + "limitation": [ + { + "_identifier": "Class", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "5" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "12" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/353", + "id": 353, + "module": "user", + "function": "password" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.xml.example b/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.xml.example new file mode 100644 index 000000000..6f546c316 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/policies/GET/PolicyList.xml.example @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyList media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/policies"> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/349"> + <id>349</id> + <module>content</module> + <function>read</function> + <limitations> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="3"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="6"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/350"> + <id>350</id> + <module>user</module> + <function>login</function> + <limitations> + <limitation identifier="SiteAccess"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="2282622326"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1766001124"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/351"> + <id>351</id> + <module>content</module> + <function>view_embed</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="5"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="12"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/352"> + <id>352</id> + <module>user</module> + <function>register</function> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/353"> + <id>353</id> + <module>user</module> + <function>password</function> + </Policy> +</PolicyList> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.json.example b/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.json.example new file mode 100644 index 000000000..6cdbc21f2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.json.example @@ -0,0 +1,71 @@ +{ + "RoleList": { + "_media-type": "application/vnd.ibexa.api.RoleList+json", + "_href": "/api/ibexa/v2/user/roles", + "Role": [ + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/1", + "identifier": "Anonymous", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/1/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2", + "identifier": "Administrator", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/2/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/3", + "identifier": "Editor", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/3/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/4", + "identifier": "Member", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/4/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/10", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/10/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/14", + "identifier": "NewRole5", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/14/policies" + } + }, + { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/15", + "identifier": "NewRole7", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/15/policies" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.xml.example new file mode 100644 index 000000000..a408cb426 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/GET/RoleList.xml.example @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleList media-type="application/vnd.ibexa.api.RoleList+xml" href="/api/ibexa/v2/user/roles"> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/1"> + <identifier>Anonymous</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/1/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/2"> + <identifier>Administrator</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/2/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/3"> + <identifier>Editor</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/3/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/4"> + <identifier>Member</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/4/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/10"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/10/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/14"> + <identifier>NewRole5</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/14/policies"/> + </Role> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/15"> + <identifier>NewRole7</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/15/policies"/> + </Role> +</RoleList> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.json.example b/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.json.example new file mode 100644 index 000000000..f26242811 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/21", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/21/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.xml.example new file mode 100644 index 000000000..0d7808c86 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/POST/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/19"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/19/policies"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example b/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example new file mode 100644 index 000000000..c2cfef57e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "NewRole" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example new file mode 100644 index 000000000..febdc5635 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>NewRole</identifier> +</RoleInput> \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.json.example new file mode 100644 index 000000000..f26242811 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/21", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/21/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.xml.example new file mode 100644 index 000000000..7909c201a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/GET/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/21"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/21/policies"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.json.example new file mode 100644 index 000000000..e7295d979 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/21", + "identifier": "NewIdentifier", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/21/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.xml.example new file mode 100644 index 000000000..bf7ef5438 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/5"> + <identifier>NewIdentifier</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/5/policies"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.json.example new file mode 100644 index 000000000..d31874469 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "NewIdentifier" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.xml.example new file mode 100644 index 000000000..ffe9e53bb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/PATCH/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>NewIdentifier</identifier> +</RoleInput> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.json.example new file mode 100644 index 000000000..8bdccb5a3 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/6", + "identifier": "Editor", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/6/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.xml.example new file mode 100644 index 000000000..ba485b805 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role href="/user/roles/11" media-type="application/vnd.ibexa.api.RoleDraft+xml"> + <identifier>MyRole</identifier> + <Policies href="/user/roles/11/policies" media-type="application/vnd.ibexa.api.PolicyList+xml"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.json.example new file mode 100644 index 000000000..9d3c937ee --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/27", + "identifier": "Anonymous", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/27/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.xml.example new file mode 100644 index 000000000..79aa775ec --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/GET/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/27"> + <identifier>Anonymous</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/27/policies"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example new file mode 100644 index 000000000..cfd1b3d0c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/6", + "identifier": "UpdatedIdentifier", + "Policies": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/6/policies" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example new file mode 100644 index 000000000..66e1a057a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/9"> + <identifier>UpdatedIdentifier</identifier> + <Policies media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/9/policies"/> +</Role> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.json.example new file mode 100644 index 000000000..23bed0ebf --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "UpdatedIdentifier" + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.xml.example new file mode 100644 index 000000000..1796642ea --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>UpdatedIdentifier</identifier> +</RoleInput> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.json.example new file mode 100644 index 000000000..9b48841e3 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.json.example @@ -0,0 +1,104 @@ +{ + "PolicyList": { + "_media-type": "application/vnd.ibexa.api.PolicyList+json", + "_href": "/api/ibexa/v2/user/roles/1/policies", + "Policy": [ + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/349", + "id": 349, + "module": "content", + "function": "read", + "limitations": { + "limitation": [ + { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "3" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "6" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/350", + "id": 350, + "module": "user", + "function": "login", + "limitations": { + "limitation": [ + { + "_identifier": "SiteAccess", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "2282622326" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1766001124" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/351", + "id": 351, + "module": "content", + "function": "view_embed", + "limitations": { + "limitation": [ + { + "_identifier": "Class", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "5" + }, + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "12" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + }, + { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/353", + "id": 353, + "module": "user", + "function": "password" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.xml.example new file mode 100644 index 000000000..8a8b328e5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.xml.example @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyList media-type="application/vnd.ibexa.api.PolicyList+xml" href="/api/ibexa/v2/user/roles/1/policies"> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/349"> + <id>349</id> + <module>content</module> + <function>read</function> + <limitations> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="3"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="6"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/350"> + <id>350</id> + <module>user</module> + <function>login</function> + <limitations> + <limitation identifier="SiteAccess"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="2282622326"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1766001124"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/351"> + <id>351</id> + <module>content</module> + <function>view_embed</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="5"/> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="12"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/352"> + <id>352</id> + <module>user</module> + <function>register</function> + </Policy> + <Policy media-type="application/vnd.ibexa.api.Policy+xml" href="/api/ibexa/v2/user/roles/1/policies/353"> + <id>353</id> + <module>user</module> + <function>password</function> + </Policy> +</PolicyList> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/Policy.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/Policy.xml.example new file mode 100644 index 000000000..d3db9de1a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/Policy.xml.example @@ -0,0 +1,17 @@ +<Policy href="/user/roles/11/policies/55" media-type="application/vnd.ibexa.api.Policy+xml"> + <id>55</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/13"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/12"/> + </values> + </limitation> + </limitations> + </Policy> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/PolicyCreate.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/PolicyCreate.xml.example new file mode 100644 index 000000000..1bb010014 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/POST/PolicyCreate.xml.example @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyCreate> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="2"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="1"/> + </values> + </limitation> + </limitations> +</PolicyCreate> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example new file mode 100644 index 000000000..dd22b0a2d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example @@ -0,0 +1,9 @@ +{ + "Policy": { + "_media-type": "application/vnd.ibexa.api.Policy+json", + "_href": "/api/ibexa/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.xml.example new file mode 100644 index 000000000..8240b8750 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.xml.example @@ -0,0 +1,19 @@ +<Policy href="/user/roles/11/policies/45" media-type="application/vnd.ibexa.api.Policy+xml"> + <id>45</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/10" media-type="application/vnd.ibexa.api.ContentType+xml" /> + <ref href="/content/types/11" media-type="application/vnd.ibexa.api.ContentType+xml" /> + <ref href="/content/types/12" media-type="application/vnd.ibexa.api.ContentType+xml" /> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/4" media-type="application/vnd.ibexa.api.ContentType+xml" /> + </values> + </limitation> + </limitations> +</Policy> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example new file mode 100644 index 000000000..86cfd7746 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example @@ -0,0 +1,17 @@ +<Policy href="/user/roles/11/policies/55" media-type="application/vnd.ibexa.api.Policy+xml"> + <id>55</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/14"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/10"/> + </values> + </limitation> + </limitations> + </Policy> diff --git a/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example new file mode 100644 index 000000000..47ff30ff3 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyUpdate> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="2"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="1"/> + </values> + </limitation> + </limitations> +</PolicyUpdate> diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.json.example b/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.json.example new file mode 100644 index 000000000..879723680 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.json.example @@ -0,0 +1,12 @@ +{ + "Session": { + "_media-type": "application/vnd.ibexa.api.Session", + "name": "eZSSID", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lkneri34ijajedfw39orj3j93", + "User": { + "_href": "/user/users/14", + "_media-type": "application/vnd.ibexa.api.User+json" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example b/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example new file mode 100644 index 000000000..ad2418606 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Session href="/user/sessions/sessionID" media-type="application/vnd.ibexa.api.Session+xml"> + <name>eZSSID</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ibexa.api.User+xml"/> +</Session> diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.json.example b/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.json.example new file mode 100644 index 000000000..9b45a75ce --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.json.example @@ -0,0 +1,7 @@ +{ + "SessionInput": { + "_media-type": "application/vnd.ibexa.api.SessionInput", + "login": "admin", + "password": "secret" + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.xml.example b/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.xml.example new file mode 100644 index 000000000..ae0f88db0 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SessionInput> + <login>admin</login> + <password>secret</password> +</SessionInput> diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example b/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example new file mode 100644 index 000000000..879723680 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example @@ -0,0 +1,12 @@ +{ + "Session": { + "_media-type": "application/vnd.ibexa.api.Session", + "name": "eZSSID", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lkneri34ijajedfw39orj3j93", + "User": { + "_href": "/user/users/14", + "_media-type": "application/vnd.ibexa.api.User+json" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.xml.example b/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.xml.example new file mode 100644 index 000000000..039aa875c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.xml.example @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Session href="user/sessions/go327ij2cirpo59pb6rrv2a4el2/refresh" media-type="application/vnd.ibexa.api.Session+xml"> + <name>eZSSID</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ibexa.api.User+xml"/> +</Session> diff --git a/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.json.example b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.json.example new file mode 100644 index 000000000..3de11a92c --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.json.example @@ -0,0 +1,7 @@ +{ + "JWT": { + "_media-type": "application/vnd.ibexa.api.JWT+json", + "_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3ODAsImV4cCI6MTYwMjg0NDM4MCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.0LHa799HwSwwfDBZd2V0q2xHwGt86PpyZamKnXHQyYI", + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3ODAsImV4cCI6MTYwMjg0NDM4MCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.0LHa799HwSwwfDBZd2V0q2xHwGt86PpyZamKnXHQyYI" + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.xml.example b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.xml.example new file mode 100644 index 000000000..7d59afac2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<JWT media-type="application/vnd.ibexa.api.JWT+xml" token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3NjEsImV4cCI6MTYwMjg0NDM2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.LsmdVjad7wMwVQUo4vSftT0zHbJyArOMd23b417E2jI"> + <token>eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3NjEsImV4cCI6MTYwMjg0NDM2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.LsmdVjad7wMwVQUo4vSftT0zHbJyArOMd23b417E2jI</token> +</JWT> diff --git a/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.json.example b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.json.example new file mode 100644 index 000000000..cc2dc54d6 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.json.example @@ -0,0 +1,7 @@ +{ + "JWTInput": { + "_media-type": "application/vnd.ibexa.api.JWTInput", + "username": "admin", + "password": "publish" + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.xml.example b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.xml.example new file mode 100644 index 000000000..b7a053478 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.xml.example @@ -0,0 +1,4 @@ +<JWTInput> + <password>publish</password> + <username>admin</username> +</JWTInput> diff --git a/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.json.example b/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.json.example new file mode 100644 index 000000000..bd915b1cc --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.json.example @@ -0,0 +1,155 @@ +{ + "UserList": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/users", + "User": [ + { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14", + "_id": 14, + "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4" + }, + "name": "Administrator User", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/14/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/13/15" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/14/locations" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/14/groups" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:13:50+00:00", + "lastModificationDate": "2011-03-25T14:07:04+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/14/versions/3", + "VersionInfo": { + "id": 499, + "versionNo": 3, + "status": "PUBLISHED", + "modificationDate": "2011-03-25T14:07:04+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2011-03-25T14:03:03+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator User" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/14" + } + }, + "Fields": { + "field": [ + { + "id": 28, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator" + }, + { + "id": 29, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "User" + }, + { + "id": 30, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 14, + "login": "admin", + "email": "admin@link.invalid", + "passwordUpdatedAt": null, + "enabled": true, + "maxLogin": 10, + "plainPassword": null + } + }, + { + "id": 178, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 180, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/14/versions/3/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "admin", + "email": "admin@link.invalid", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/users/14/groups" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/14/roles" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.xml.example new file mode 100644 index 000000000..6dbef3cdf --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/GET/UserList.xml.example @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserList media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/users"> + <User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14" id="14" remoteId="1bb4fe25487f05527efa8bfd394cecc7"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/4"/> + <name>Administrator User</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/14/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/13/15"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/14/locations"/> + <Groups media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/14/groups"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <publishDate>2002-10-06T18:13:50+02:00</publishDate> + <lastModificationDate>2011-03-25T15:07:04+01:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/14/versions/3"> + <VersionInfo> + <id>499</id> + <versionNo>3</versionNo> + <status>PUBLISHED</status> + <modificationDate>2011-03-25T15:07:04+01:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2011-03-25T15:03:03+01:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator User</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/14"/> + </VersionInfo> + <Fields> + <field> + <id>28</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator</fieldValue> + </field> + <field> + <id>29</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>User</fieldValue> + </field> + <field> + <id>30</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">14</value> + <value key="login">admin</value> + <value key="email">nospam@ibexa.co</value> + <value key="enabled">true</value> + <value key="maxLogin">10</value> + </fieldValue> + </field> + <field> + <id>178</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>180</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/14/versions/3/relations"/> + </Version> + <login>admin</login> + <email>nospam@ibexa.co</email> + <enabled>true</enabled> + <UserGroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/users/14/groups"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/14/roles"/> + </User> +</UserList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/GET/UserRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/GET/UserRefList.xml.example new file mode 100644 index 000000000..c8cf9a66d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/GET/UserRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserRefList media-type="application/vnd.ibexa.api.UserRefList+xml" href="/api/ibexa/v2/user/users"> + <User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> +</UserRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example new file mode 100644 index 000000000..e0318f6dd --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example @@ -0,0 +1,155 @@ +{ + "UserList": { + "_media-type": "application/vnd.ibexa.api.UserList+json", + "_href": "/api/ibexa/v2/user/users", + "User": [ + { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/79", + "_id": 79, + "_remoteId": "bcf0764b417f05af21852a1f03fb1f13", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4" + }, + "name": "Jose Vargas", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/79/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/12/79" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/79/locations" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/79/groups" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/10" + }, + "publishDate": "2021-06-29T08:23:42+00:00", + "lastModificationDate": "2021-06-29T08:23:42+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/79/versions/1", + "VersionInfo": { + "id": 547, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-29T08:23:42+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/10" + }, + "creationDate": "2021-06-29T08:23:42+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Jose Vargas" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/79" + } + }, + "Fields": { + "field": [ + { + "id": 342, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Jose" + }, + { + "id": 343, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Vargas" + }, + { + "id": 344, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 79, + "login": "josevargas", + "email": "aa@bb.cc", + "passwordUpdatedAt": 1624955022, + "enabled": true, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 345, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 346, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/79/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "josevargas", + "email": "aa@bb.cc", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/users/79/groups" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/79/roles" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example new file mode 100644 index 000000000..fab328704 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserList media-type="application/vnd.ibexa.api.UserList+xml" href="/api/ibexa/v2/user/users"> + <User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/4"/> + <name>Jose Vargas</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/79/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/12/79"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/79/locations"/> + <Groups media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/79/groups"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/10"/> + <publishDate>2021-06-29T08:23:42+00:00</publishDate> + <lastModificationDate>2021-06-29T08:23:42+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/79/versions/1"> + <VersionInfo> + <id>547</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-06-29T08:23:42+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/10"/> + <creationDate>2021-06-29T08:23:42+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Jose Vargas</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/79"/> + </VersionInfo> + <Fields> + <field> + <id>342</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Jose</fieldValue> + </field> + <field> + <id>343</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Vargas</fieldValue> + </field> + <field> + <id>344</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">79</value> + <value key="login">josevargas</value> + <value key="email">aa@bb.cc</value> + <value key="passwordUpdatedAt">1624955022</value> + <value key="enabled">true</value> + <value key="maxLogin">0</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>345</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>346</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/79/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <login>josevargas</login> + <email>aa@bb.cc</email> + <enabled>true</enabled> + <UserGroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/users/79/groups"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/79/roles"/> + </User> +</UserList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.json.example new file mode 100644 index 000000000..8f1a529db --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.json.example @@ -0,0 +1,149 @@ +{ + "User": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/79", + "_id": 79, + "_remoteId": "bcf0764b417f05af21852a1f03fb1f13", + "ContentType": { + "_media-type": "application/vnd.ibexa.api.ContentType+json", + "_href": "/api/ibexa/v2/content/types/4" + }, + "name": "Jose Vargas", + "Versions": { + "_media-type": "application/vnd.ibexa.api.VersionList+json", + "_href": "/api/ibexa/v2/content/objects/79/versions" + }, + "Section": { + "_media-type": "application/vnd.ibexa.api.Section+json", + "_href": "/api/ibexa/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ibexa.api.Location+json", + "_href": "/api/ibexa/v2/content/locations/1/5/12/79" + }, + "Locations": { + "_media-type": "application/vnd.ibexa.api.LocationList+json", + "_href": "/api/ibexa/v2/content/objects/79/locations" + }, + "Groups": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/79/groups" + }, + "Owner": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/10" + }, + "publishDate": "2021-06-29T08:23:42+00:00", + "lastModificationDate": "2021-07-29T12:00:24+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ibexa.api.Version+json", + "_href": "/api/ibexa/v2/content/objects/79/versions/11", + "VersionInfo": { + "id": 614, + "versionNo": 11, + "status": "PUBLISHED", + "modificationDate": "2021-07-29T12:00:24+00:00", + "Creator": { + "_media-type": "application/vnd.ibexa.api.User+json", + "_href": "/api/ibexa/v2/user/users/14" + }, + "creationDate": "2021-07-29T12:00:24+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ibexa.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Jose Vargas" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ibexa.api.ContentInfo+json", + "_href": "/api/ibexa/v2/content/objects/79" + } + }, + "Fields": { + "field": [ + { + "id": 342, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Jose" + }, + { + "id": 343, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Vargas" + }, + { + "id": 344, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 79, + "login": "josevargas", + "email": "aa@bb.cc", + "passwordUpdatedAt": null, + "enabled": false, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 345, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 346, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ibexa.api.RelationList+json", + "_href": "/api/ibexa/v2/content/objects/79/versions/11/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ibexa.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "josevargas", + "email": "aa@bb.cc", + "enabled": false, + "UserGroups": { + "_media-type": "application/vnd.ibexa.api.UserGroupList+json", + "_href": "/api/ibexa/v2/user/users/79/groups" + }, + "Roles": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/79/roles" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.xml.example new file mode 100644 index 000000000..caf3abbaf --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.xml.example @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<User media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13"> + <ContentType media-type="application/vnd.ibexa.api.ContentType+xml" href="/api/ibexa/v2/content/types/4"/> + <name>Jose Vargas</name> + <Versions media-type="application/vnd.ibexa.api.VersionList+xml" href="/api/ibexa/v2/content/objects/79/versions"/> + <Section media-type="application/vnd.ibexa.api.Section+xml" href="/api/ibexa/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/5/12/79"/> + <Locations media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/objects/79/locations"/> + <Groups media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/79/groups"/> + <Owner media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/10"/> + <publishDate>2021-06-29T08:23:42+00:00</publishDate> + <lastModificationDate>2021-07-29T11:45:07+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ibexa.api.Version+xml" href="/api/ibexa/v2/content/objects/79/versions/6"> + <VersionInfo> + <id>609</id> + <versionNo>6</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-07-29T11:45:07+00:00</modificationDate> + <Creator media-type="application/vnd.ibexa.api.User+xml" href="/api/ibexa/v2/user/users/14"/> + <creationDate>2021-07-29T11:45:07+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ibexa.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Jose Vargas</value> + </names> + <Content media-type="application/vnd.ibexa.api.ContentInfo+xml" href="/api/ibexa/v2/content/objects/79"/> + </VersionInfo> + <Fields> + <field> + <id>342</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Jose</fieldValue> + </field> + <field> + <id>343</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Vargas</fieldValue> + </field> + <field> + <id>344</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">79</value> + <value key="login">josevargas</value> + <value key="email">aa@bb.cc</value> + <value key="passwordUpdatedAt"/> + <value key="enabled">false</value> + <value key="maxLogin">0</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>345</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>346</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ibexa.api.RelationList+xml" href="/api/ibexa/v2/content/objects/79/versions/6/relations"/> + <Thumbnail media-type="application/vnd.ibexa.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <login>josevargas</login> + <email>aa@bb.cc</email> + <enabled>false</enabled> + <UserGroups media-type="application/vnd.ibexa.api.UserGroupList+xml" href="/api/ibexa/v2/user/users/79/groups"/> + <Roles media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/79/roles"/> +</User> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.json.example new file mode 100644 index 000000000..fd43e1d7f --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.json.example @@ -0,0 +1,6 @@ +{ + "UserUpdate": { + "login": "josevargas", + "enabled": false + } +} \ No newline at end of file diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.xml.example new file mode 100644 index 000000000..fe09ab444 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserUpdate> + <login>josevargas</login> + <enabled>false</enabled> +</UserUpdate> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example new file mode 100644 index 000000000..dc45cf331 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/55/groups"> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/12"> + <unassign href="/api/ibexa/v2/user/users/55/groups/12" method="DELETE"/> + </UserGroup> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/14"> + <unassign href="/api/ibexa/v2/user/users/55/groups/14" method="DELETE"/> + </UserGroup> +</UserGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example new file mode 100644 index 000000000..8e7c8f7cb --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example @@ -0,0 +1,24 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/115/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/12", + "unassign": { + "_href": "/api/ibexa/v2/user/users/115/groups/12", + "_method": "DELETE" + } + }, + { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13", + "unassign": { + "_href": "/api/ibexa/v2/user/users/115/groups/13", + "_method": "DELETE" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example new file mode 100644 index 000000000..ed2ceff01 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ibexa.api.UserGroupRefList+xml" href="/api/ibexa/v2/user/users/115/groups"> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/12"> + <unassign href="/api/ibexa/v2/user/users/79/groups/12" method="DELETE"/> + </UserGroup> + <UserGroup media-type="application/vnd.ibexa.api.UserGroup+xml" href="/api/ibexa/v2/user/groups/1/5/13"> + <unassign href="/api/ibexa/v2/user/users/115/groups/13" method="DELETE"/> + </UserGroup> +</UserGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example new file mode 100644 index 000000000..f0e5f585e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList href="/user/users/45/groups" + media-type="application/vnd.ibexa.api.UserGroupRefList"> + <UserGroup href="/user/groups/1/5/34" media-type="application/vnd.ibexa.api.UserGroup"> + <unassign href="/user/users/45/groups/34" method="DELETE" /> + </UserGroup> + <UserGroup href="/user/groups/1/5/88" media-type="application/vnd.ibexa.api.UserGroup"> + <unassign href="/user/users/45/groups/88" method="DELETE" /> + </UserGroup> +</UserGroupRefList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example new file mode 100644 index 000000000..1d5808dc1 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ibexa.api.UserGroupRefList+json", + "_href": "/api/ibexa/v2/user/users/57/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ibexa.api.UserGroup+json", + "_href": "/api/ibexa/v2/user/groups/1/5/13" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example new file mode 100644 index 000000000..b265f016e --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example @@ -0,0 +1,27 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/115/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example new file mode 100644 index 000000000..1fb30959a --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/115/roles"> + <RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/2"/> + </RoleAssignment> +</RoleAssignmentList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example new file mode 100644 index 000000000..ea756134d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example @@ -0,0 +1,23 @@ +{ + "RoleAssignInput": { + "Role": { + "_href": "/api/ibexa/v2/user/roles/2", + "_media-type": "application/vnd.ibexa.api.RoleAssignInput+xml" + }, + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_href": "/api/ibexa/v2/content/sections/1", + "_media-type": "application/vnd.ibexa.api.Section+xml" + }, + { + "_href": "/api/ibexa/v2/content/sections/2", + "_media-type": "application/vnd.ibexa.api.Section+xml" + } + ] + } + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example new file mode 100644 index 000000000..243f3ff7d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignInput> + <Role href="/api/ibexa/v2/user/roles/2" media-type="application/vnd.ibexa.api.RoleAssignInput+xml"/> + <limitation identifier="Section"> + <values> + <ref href="/api/ibexa/v2/content/sections/1" media-type="application/vnd.ibexa.api.Section+xml" /> + <ref href="/api/ibexa/v2/content/sections/2" media-type="application/vnd.ibexa.api.Section+xml" /> + </values> + </limitation> +</RoleAssignInput> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example new file mode 100644 index 000000000..22b919336 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example @@ -0,0 +1,46 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/115/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "2" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2" + } + }, + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example new file mode 100644 index 000000000..4800db41d --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ibexa.api.RoleAssignmentList+xml" href="/api/ibexa/v2/user/users/115/roles"> + <RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/2"/> + </RoleAssignment> + <RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="2"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/2"/> + </RoleAssignment> +</RoleAssignmentList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example new file mode 100644 index 000000000..16dc02334 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example @@ -0,0 +1,24 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ibexa.api.RoleAssignmentList+json", + "_href": "/api/ibexa/v2/user/users/57/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/57/roles/1", + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/1" + } + }, + { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/57/roles/2", + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/2" + } + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example new file mode 100644 index 000000000..2c74de453 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList href="/user/groups/1/5/65/roles" media-type="application/vnd.ibexa.api.RoleAssignmentList+xml"> + <RoleAssignment href="/user/groups/1/5/65/roles/5" media-type="application/vnd.ibexa.api.RoleAssignment+xml"> + <Role href="/user/roles/5" media-type="application/vnd.ibexa.api.Role+xml"/> + </RoleAssignment> + <RoleAssignment href="/user/groups/1/5/65/roles/11" media-type="application/vnd.ibexa.api.RoleAssignment+xml"> + <limitation identifier="Section"> + <values> + <ref href="/content/sections/1" media-type="application/vnd.ibexa.api.Section+xml" /> + <ref href="/content/sections/4" media-type="application/vnd.ibexa.api.Section+xml" /> + </values> + </limitation> + <Role href="/user/roles/11" media-type="application/vnd.ibexa.api.Role+xml"/> + </RoleAssignment> +</RoleAssignmentList> diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example new file mode 100644 index 000000000..527244157 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example @@ -0,0 +1,21 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ibexa.api.RoleAssignment+json", + "_href": "/api/ibexa/v2/user/users/113/roles/3", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ibexa.api.ref+json", + "_href": "6" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ibexa.api.Role+json", + "_href": "/api/ibexa/v2/user/roles/3" + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example new file mode 100644 index 000000000..0511e11c5 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ibexa.api.RoleAssignment+xml" href="/api/ibexa/v2/user/users/113/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ibexa.api.ref+xml" href="6"/> + </values> + </limitation> + <Role media-type="application/vnd.ibexa.api.Role+xml" href="/api/ibexa/v2/user/roles/3"/> +</RoleAssignment> diff --git a/src/bundle/Resources/api_platform/examples/views/POST/View.xml.v11.example b/src/bundle/Resources/api_platform/examples/views/POST/View.xml.v11.example new file mode 100644 index 000000000..d476a11c8 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/views/POST/View.xml.v11.example @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<View href="/views/TitleView" media-type="application/vnd.ibexa.api.View+xml; version=1.1"> + <identifier>TitleView</identifier> + <User href="/user/users/14" media-type="vnd.ibexa.api.User+xml"/> + <public>false</public> + <LocationQuery> + <Filter> + <ParentLocationIdCriterion>2</ParentLocationIdCriterion> + </Filter> + <limit>10</limit> + <offset>0</offset> + <SortClauses> + <ContentName>ascending</ContentName> + </SortClauses> + <FacetBuilders> + <contentTypeFacetBuilder/> + </FacetBuilders> + </LocationQuery> + <Result href="/content/views/view1234/results" + media-type="application/vnd.ibexa.api.ViewResult+xml" count="34" time="31" maxScore="1.0"> + <searchHits> + <searchHit score="1.0" index="installid1234567890"> + <hightlight/> + <value> + <Location media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1/2"> + <id>2</id> + <priority>0</priority> + <hidden>false</hidden> + <invisible>false</invisible> + <ParentLocation media-type="application/vnd.ibexa.api.Location+xml" href="/api/ibexa/v2/content/locations/1"/> + <pathString>/1/2/</pathString> + <depth>1</depth> + <childCount>8</childCount> + <remoteId>f3e90596361e31d496d4026eb624c983</remoteId> + <Children media-type="application/vnd.ibexa.api.LocationList+xml" href="/api/ibexa/v2/content/locations/1/2/children"/> + <Content media-type="application/vnd.ibexa.api.Content+xml" href="/api/ibexa/v2/content/objects/57"/> + <sortField>PRIORITY</sortField> + <sortOrder>ASC</sortOrder> + <UrlAliases media-type="application/vnd.ibexa.api.UrlAliasRefList+xml" href="/api/ibexa/v2/content/locations/1/2/urlaliases"/> + </Location> + + </value> + </searchHit> + <!-- ... --> + </searchHits> + <facets> + <contentTypeFacet> + <contentTypeFacetEntry> + <contentType href="/content/types/1" media-type="application/vnd.ibexa.api.ContentType+xml"/> + <count>3</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/7" media-type="application/vnd.ibexa.api.ContentType+xml"/> + <count>9</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/11" media-type="application/vnd.ibexa.api.ContentType+xml"/> + <count>1</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/15" media-type="application/vnd.ibexa.api.ContentType+xml"/> + <count>8</count> + </contentTypeFacetEntry> + </contentTypeFacet> + </facets> + </Result> +</View> diff --git a/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.json.example b/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.json.example new file mode 100644 index 000000000..eb4514cc9 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.json.example @@ -0,0 +1,28 @@ +{ + "ViewInput": { + "identifier": "TitleView", + "Query": { + "Filter": { + "ContentTypeIdentifierCriterion": "image", + "SectionIdentifierCriterion": "media", + "DateMetadataCriterion": { + "Target": "modified", + "Value": 1675681020, + "Operator": "gte" + } + }, + "limit": 10, + "offset": 0, + "SortClauses": { + "ContentName": "ascending" + }, + "Aggregations": [ + { + "ContentTypeTermAggregation": { + "name": "some name" + } + } + ] + } + } +} diff --git a/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.xml.example b/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.xml.example new file mode 100644 index 000000000..45cc1f489 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/views/POST/ViewInput.xml.example @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ViewInput> + <identifier>TitleView</identifier> + <Query> + <Filter> + <ContentTypeIdentifierCriterion>image</ContentTypeIdentifierCriterion> + <SectionIdentifierCriterion>media</SectionIdentifierCriterion> + <DateMetadataCriterion> + <Target>modified</Target> + <Value>1675681020</Value> + <Operator>gte</Operator> + </DateMetadataCriterion> + </Filter> + <limit>10</limit> + <offset>0</offset> + <SortClauses> + <ContentName>ascending</ContentName> + </SortClauses> + <Aggregations> + <Aggregation> + <ContentTypeTermAggregation> + <name>some name</name> + </ContentTypeTermAggregation> + </Aggregation> + </Aggregations> + </Query> +</ViewInput> diff --git a/src/bundle/Resources/api_platform/language_schemas.yml b/src/bundle/Resources/api_platform/language_schemas.yml new file mode 100644 index 000000000..5750c417e --- /dev/null +++ b/src/bundle/Resources/api_platform/language_schemas.yml @@ -0,0 +1,46 @@ +schemas: + Language: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a language in the Repository. + type: object + required: + - languageId + - languageCode + - name + properties: + languageId: + description: The language ID (auto generated). + type: [string, 'null'] + languageCode: + description: The languageCode code. + type: string + name: + description: Human readable name of the language. + type: string + LanguageWrapper: + type: object + required: + - Language + properties: + Language: + $ref: "#/components/schemas/Language" + LanguageList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of languages. + type: object + required: + - Language + properties: + Language: + type: array + items: + $ref: "#/components/schemas/Language" + LanguageListWrapper: + type: object + required: + - LanguageList + properties: + LanguageList: + $ref: "#/components/schemas/LanguageList" diff --git a/src/bundle/Resources/api_platform/object_state_groups_schemas.yml b/src/bundle/Resources/api_platform/object_state_groups_schemas.yml new file mode 100644 index 000000000..5209ab18f --- /dev/null +++ b/src/bundle/Resources/api_platform/object_state_groups_schemas.yml @@ -0,0 +1,240 @@ +schemas: + ObjectState: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a Object state value. + type: object + required: + - id + - identifier + - priority + - ObjectStateGroup + - languageCodes + - names + - descriptions + properties: + id: + description: Primary key. + type: integer + identifier: + description: Readable string identifier of the Object state. + type: string + priority: + description: Priority for ordering. + type: integer + ObjectStateGroup: + description: The Object state group this Object state belongs to. + type: + $ref: "#/components/schemas/BaseObject" + languageCodes: + description: The available language codes for names an descriptions. + type: string + names: + description: Names. + descriptions: + description: Descriptions. + ObjectStateWrapper: + type: object + required: + - ObjectState + properties: + ObjectState: + $ref: "#/components/schemas/ObjectState" + ObjectStateCreate: + description: This class represents a value for creating Object states. + type: object + required: + - identifier + - priority + - defaultLanguageCode + - languageCodes + - names + - descriptions + properties: + identifier: + description: Readable unique string identifier of a group. + type: string + priority: + description: Priority for ordering. If not set the Object state is created as the last one. + type: string + defaultLanguageCode: + description: The default language code. + type: string + languageCodes: + description: Language codes. + type: string + names: + description: An array of names with languageCode keys. At least one name in the main language is required. + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + descriptions: + description: An array of descriptions with languageCode keys. + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + ObjectStateCreateWrapper: + type: object + required: + - ObjectStateCreate + properties: + ObjectStateCreate: + $ref: "#/components/schemas/ObjectStateCreate" + ObjectStateUpdate: + description: This class represents a value for updating Object states. + type: object + properties: + identifier: + description: Readable unique string identifier of a group. + type: string + defaultLanguageCode: + description: The default language code. + type: string + names: + description: An array of names with languageCode keys. + descriptions: + description: An array of descriptions with languageCode keys. + ObjectStateUpdateWrapper: + type: object + required: + - ObjectStateUpdate + properties: + ObjectStateUpdate: + $ref: "#/components/schemas/ObjectStateUpdate" + ObjectStateList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of Object states. + type: object + required: + - ObjectState + properties: + ObjectState: + type: array + items: + $ref: "#/components/schemas/ObjectState" + ObjectStateListWrapper: + type: object + required: + - ObjectStateList + properties: + ObjectStateList: + $ref: "#/components/schemas/ObjectStateList" + ObjectStateGroup: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents an Object state group value. + type: object + required: + - id + - identifier + - defaultLanguageCode + - languageCodes + - ObjectStates + - names + - descriptions + properties: + id: + description: Primary key. + type: integer + identifier: + description: Readable string identifier of a group. + type: string + defaultLanguageCode: + description: The default language code. + type: string + languageCodes: + description: The available language codes for names an descriptions. + type: string + ObjectStates: + description: Object States. + type: + $ref: "#/components/schemas/BaseObject" + names: + description: List of names. + descriptions: + description: List of descriptions. + ObjectStateGroupWrapper: + type: object + required: + - ObjectStateGroup + properties: + ObjectStateGroup: + $ref: "#/components/schemas/ObjectStateGroup" + ObjectStateGroupCreate: + description: This class represents a value for creating Object state groups. + type: object + required: + - identifier + - defaultLanguageCode + - names + - descriptions + properties: + identifier: + description: Readable unique string identifier of a group. + type: string + defaultLanguageCode: + description: The default language code. + type: string + names: + description: An array of names with languageCode keys. At least one name in the main language is required. + descriptions: + description: An array of descriptions with languageCode keys. + ObjectStateGroupCreateWrapper: + type: object + required: + - ObjectStateGroupCreate + properties: + ObjectStateGroupCreate: + $ref: "#/components/schemas/ObjectStateGroupCreate" + ObjectStateGroupUpdate: + description: This class represents a value for updating Object state groups. + type: object + properties: + identifier: + description: Readable unique string identifier of a group. + type: string + defaultLanguageCode: + description: The default language code. + type: string + names: + description: An array of names with languageCode keys. + descriptions: + description: An array of descriptions with languageCode keys. + ObjectStateGroupUpdateWrapper: + type: object + required: + - ObjectStateGroupUpdate + properties: + ObjectStateGroupUpdate: + $ref: "#/components/schemas/ObjectStateGroupUpdate" + ObjectStateGroupList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of Object state groups. + type: object + required: + - ObjectStateGroup + properties: + ObjectStateGroup: + description: This class represents an Object state group value. + type: array + items: + $ref: "#/components/schemas/ObjectStateGroup" + ObjectStateGroupListWrapper: + type: object + required: + - ObjectStateGroupList + properties: + ObjectStateGroupList: + $ref: "#/components/schemas/ObjectStateGroupList" diff --git a/src/bundle/Resources/api_platform/root_schemas.yml b/src/bundle/Resources/api_platform/root_schemas.yml new file mode 100644 index 000000000..b01950808 --- /dev/null +++ b/src/bundle/Resources/api_platform/root_schemas.yml @@ -0,0 +1,141 @@ +schemas: + Root: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a root. + type: object + required: + - content + - contentByRemoteId + - contentTypes + - contentTypeByIdentifier + - contentTypeGroups + - contentTypeGroupByIdentifier + - users + - roles + - rootLocation + - rootUserGroup + - rootMediaFolder + - locationByRemoteId + - locationByPath + - trash + - sections + - views + - objectStateGroups + - objectStates + - globalUrlAliases + - urlWildcards + - createSession + - $refreshSession + properties: + content: + description: Content. + type: + $ref: "#/components/schemas/BaseObject" + contentByRemoteId: + description: Content by the given remote ID. + type: + $ref: "#/components/schemas/BaseObject" + contentTypes: + description: Content types. + type: + $ref: "#/components/schemas/BaseObject" + contentTypeByIdentifier: + description: Content type by the given identifier. + type: + $ref: "#/components/schemas/BaseObject" + contentTypeGroups: + description: Content type Groups. + type: + $ref: "#/components/schemas/BaseObject" + contentTypeGroupByIdentifier: + description: Content type Groups by the given identifier. + type: + $ref: "#/components/schemas/BaseObject" + users: + description: Users. + type: + $ref: "#/components/schemas/BaseObject" + usersByRoleId: + description: Users by Role ID. + type: + $ref: "#/components/schemas/BaseObject" + usersByRemoteId: + description: Users by remote ID. + type: + $ref: "#/components/schemas/BaseObject" + usersByEmail: + description: Users by e-mail. + type: + $ref: "#/components/schemas/BaseObject" + usersByLogin: + description: Users by login. + type: + $ref: "#/components/schemas/BaseObject" + roles: + description: Roles. + type: + $ref: "#/components/schemas/BaseObject" + rootLocation: + description: Root Location. + type: + $ref: "#/components/schemas/BaseObject" + rootUserGroup: + description: Root User Group. + type: + $ref: "#/components/schemas/BaseObject" + rootMediaFolder: + description: Root media folder. + type: + $ref: "#/components/schemas/BaseObject" + locationByRemoteId: + description: Location by remote ID. + type: + $ref: "#/components/schemas/BaseObject" + locationByPath: + description: Location by path. + type: + $ref: "#/components/schemas/BaseObject" + trash: + description: Trash. + type: + $ref: "#/components/schemas/BaseObject" + sections: + description: Sections. + type: + $ref: "#/components/schemas/BaseObject" + views: + description: Views. + type: + $ref: "#/components/schemas/BaseObject" + objectStateGroups: + description: Object state groups. + type: + $ref: "#/components/schemas/BaseObject" + objectStates: + description: Object states. + type: + $ref: "#/components/schemas/BaseObject" + globalUrlAliases: + description: Global URL aliases. + type: + $ref: "#/components/schemas/BaseObject" + urlWildcards: + description: URL Wildcards. + type: + $ref: "#/components/schemas/BaseObject" + createSession: + description: Creates a new session based on the credentials provided as POST parameters. + type: + $ref: "#/components/schemas/BaseObject" + refreshSession: + description: Refresh given session. + type: + $ref: "#/components/schemas/BaseObject" + RootWrapper: + type: object + required: + - Root + properties: + Root: + $ref: "#/components/schemas/Root" diff --git a/src/bundle/Resources/api_platform/services_schemas.yml b/src/bundle/Resources/api_platform/services_schemas.yml new file mode 100644 index 000000000..375ccec10 --- /dev/null +++ b/src/bundle/Resources/api_platform/services_schemas.yml @@ -0,0 +1,50 @@ +schemas: + CountryList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is representing an ISO-3166 formatted list of world countries. + type: object + required: + - Country + properties: + Country: + type: array + items: + $ref: "#/components/schemas/Country" + Country: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is representing a country. + type: object + required: + - _id + - name + - Alpha2 + - Alpha3 + - IDC + properties: + _id: + description: ID that represents a country name. + xml: + attribute: true + name: id + type: string + name: + description: Name of the country. + type: string + Alpha2: + description: Two-letter code that represents a country name. + type: string + Alpha3: + description: Three-letter code that represents a country name. + type: string + IDC: + description: IDC + type: integer + CountryListWrapper: + type: object + required: + - CountryList + properties: + CountryList: + $ref: "#/components/schemas/CountryList" diff --git a/src/bundle/Resources/api_platform/user/roles_schemas.yml b/src/bundle/Resources/api_platform/user/roles_schemas.yml new file mode 100644 index 000000000..88672bb0c --- /dev/null +++ b/src/bundle/Resources/api_platform/user/roles_schemas.yml @@ -0,0 +1,273 @@ +schemas: + Role: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a role. + type: object + required: + - identifier + - Policies + properties: + identifier: + description: Readable string identifier of a role. + type: string + Policies: + description: Returns the list of policies of this role. + type: + $ref: "#/components/schemas/BaseObject" + RoleWrapper: + type: object + required: + - Role + properties: + Role: + $ref: "#/components/schemas/Role" + RoleDraft: + description: This class represents a draft of a role, extends Role. + type: + $ref: "#/components/schemas/Role" + RoleDraftWrapper: + type: object + required: + - Role + properties: + Role: + $ref: "#/components/schemas/RoleDraft" + RoleInput: + description: This class represents a Role input. + type: object + required: + - identifier + properties: + identifier: + type: string + RoleInputWrapper: + type: object + required: + - RoleInput + properties: + RoleInput: + $ref: "#/components/schemas/RoleInput" + RoleList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a list roles. + type: object + required: + - Role + properties: + Role: + type: array + items: + $ref: "#/components/schemas/Role" + RoleListWrapper: + type: object + required: + - RoleList + properties: + RoleList: + $ref: "#/components/schemas/RoleList" + Policy: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a policy value. + type: object + required: + - id + - module + - function + properties: + id: + description: ID of the policy. + type: integer + module: + description: Name of module, associated with the Policy e.g. content. + type: string + function: + description: Name of the module function Or all functions with '*' e.g. read. + type: string + limitations: + description: Limitations. + type: object + required: + - limitation + properties: + limitation: + type: array + items: + $ref: "#/components/schemas/Limitation" + PolicyWrapper: + type: object + required: + - Policy + properties: + Policy: + $ref: "#/components/schemas/Policy" + Limitation: + description: This class represents a Limitation applied to a policy. + type: object + required: + - _identifier + - values + properties: + _identifier: + description: "Returns the limitation identifier (one of the defined constants) or a custom limitation. Constants: CONTENTTYPE = Class; LANGUAGE = Language; LOCATION = Node; OWNER = Owner; PARENTOWNER = ParentOwner; PARENTCONTENTTYPE = ParentClass; PARENTDEPTH = ParentDepth; SECTION = Section; NEWSECTION = NewSection; SITEACCESS = SiteAccess; STATE = State; NEWSTATE = NewState; SUBTREE = Subtree; USERGROUP = Group; PARENTUSERGROUP = ParentGroup; STATUS = Status." + enum: + - ContentType + - Language + - Location + - Owner + - Parentowner + - ParentContentType + - ParentDepth + - Section + - NewSection + - SiteAccess + - State + - NewState + - Subtree + - UserGroup + - ParentUserGroup + - Status + - Class + xml: + attribute: true + name: identifier + type: string + values: + description: A read-only list of IDs or identifiers for which the limitation should be applied. The value of this property must conform to a hash, which means that it may only consist of array and scalar values, but must not contain objects or resources. + type: object + required: + - $ref + properties: + ref: + type: array + items: + $ref: "#/components/schemas/Ref" + PolicyList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: List of policies. + type: object + required: + - Policy + properties: + Policy: + type: array + items: + $ref: "#/components/schemas/Policy" + PolicyListWrapper: + type: object + required: + - PolicyList + properties: + PolicyList: + $ref: "#/components/schemas/PolicyList" + PolicyCreate: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is used to create a Policy. + type: object + required: + - module + - function + - Limitations + properties: + module: + description: Name of module associated with the Policy. For example, content. + type: string + function: + description: Name of the module function, or all functions with ''*''. For example, read. + type: string + Limitations: + type: array + items: + $ref: "#/components/schemas/Limitation" + PolicyCreateWrapper: + type: object + required: + - PolicyCreate + properties: + PolicyCreate: + $ref: "#/components/schemas/PolicyCreate" + PolicyUpdate: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class is used to update a Policy. + type: object + required: + - Limitations + properties: + Limitations: + type: array + items: + $ref: "#/components/schemas/Limitation" + PolicyUpdateWrapper: + type: object + required: + - PolicyUpdate + properties: + PolicyUpdate: + $ref: "#/components/schemas/PolicyUpdate" + RoleAssignment: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This value object represents an assignment of a User or User group to a role including a limitation. + type: object + required: + - Role + properties: + limitation: + description: Returns the limitation of the role assignment. + Role: + description: Returns the role to which the User or User group is assigned to. + type: + $ref: "#/components/schemas/Ref" + RoleAssignmentWrapper: + type: object + required: + - RoleAssignment + properties: + RoleAssignment: + $ref: "#/components/schemas/RoleAssignment" + RoleAssignInput: + description: This class represents a Role assign input. + type: object + required: + - Role + - limitation + properties: + Role: + description: Returns the Role to which the user or user group is assigned to. + type: + $ref: "#/components/schemas/Ref" + limitation: + description: Returns the Limitation of the Role assignment. + type: + $ref: "#/components/schemas/Limitation" + RoleAssignInputWrapper: + type: object + required: + - RoleAssignInput + properties: + RoleAssignInput: + $ref: "#/components/schemas/RoleAssignInput" + RoleAssignmentList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This value object represents a list of assignments of a User or User group to a role including a limitation. + type: object + required: + - RoleAssignment + properties: + RoleAssignment: + type: array + items: + $ref: "#/components/schemas/RoleAssignment" + RoleAssignmentListWrapper: + type: object + required: + - RoleAssignmentList + properties: + RoleAssignmentList: + $ref: "#/components/schemas/RoleAssignmentList" diff --git a/src/bundle/Resources/api_platform/user/sessions_schemas.yml b/src/bundle/Resources/api_platform/user/sessions_schemas.yml new file mode 100644 index 000000000..045877d8f --- /dev/null +++ b/src/bundle/Resources/api_platform/user/sessions_schemas.yml @@ -0,0 +1,52 @@ +schemas: + Session: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Value for session. + type: object + required: + - name + - identifier + - csrfToken + - User + properties: + name: + description: Name. + type: string + identifier: + description: Identifier. + type: string + csrfToken: + description: csrfToken. + type: string + User: + description: User. + type: + $ref: "#/components/schemas/BaseObject" + SessionWrapper: + type: object + required: + - Session + properties: + Session: + $ref: "#/components/schemas/Session" + SessionInput: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a session input. + type: object + required: + - login + - password + properties: + login: + type: string + password: + type: string + SessionInputWrapper: + type: object + required: + - SessionInput + properties: + SessionInput: + $ref: "#/components/schemas/SessionInput" diff --git a/src/bundle/Resources/api_platform/user/token_schemas.yml b/src/bundle/Resources/api_platform/user/token_schemas.yml new file mode 100644 index 000000000..44d95555b --- /dev/null +++ b/src/bundle/Resources/api_platform/user/token_schemas.yml @@ -0,0 +1,39 @@ +schemas: + JWT: + description: This class represents the JWT authentication token + type: object + required: + - token + properties: + token: + description: JWT authentication token + type: string + JWTWrapper: + type: object + required: + - JWT + properties: + JWT: + $ref: "#/components/schemas/JWT" + JWTInput: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents the input for a JWT authentication token + type: object + required: + - username + - password + properties: + username: + description: User name + type: string + password: + description: User password + type: string + JWTInputWrapper: + type: object + required: + - JWTInput + properties: + JWTInput: + $ref: "#/components/schemas/JWTInput" diff --git a/src/bundle/Resources/api_platform/user/user_groups_schemas.yml b/src/bundle/Resources/api_platform/user/user_groups_schemas.yml new file mode 100644 index 000000000..7f3d5163a --- /dev/null +++ b/src/bundle/Resources/api_platform/user/user_groups_schemas.yml @@ -0,0 +1,192 @@ +schemas: + UserGroup: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Content ID matcher class. + type: object + required: + - _remoteId + - _id + - ContentType + - name + - Versions + - Section + - MainLocation + - Locations + - Owner + - lastModificationDate + - mainLanguageCode + - alwaysAvailable + - Version + - ParentUserGroup + - Subgroups + - Users + - Roles + properties: + _remoteId: + description: Remote ID of the content type. + xml: + attribute: true + name: remoteId + type: string + _id: + description: Unique ID of the content type. + xml: + attribute: true + name: id + type: integer + ContentType: + description: Content type. + type: + $ref: "#/components/schemas/Ref" + name: + type: string + Versions: + description: Returns the VersionInfo for this version. + type: + $ref: "#/components/schemas/Ref" + Section: + description: The Section to which the content item is assigned to. + type: + $ref: "#/components/schemas/Ref" + MainLocation: + type: + $ref: "#/components/schemas/Ref" + Locations: + description: Location of the content item. + type: + $ref: "#/components/schemas/Ref" + Owner: + description: The owner of the content item. + type: + $ref: "#/components/schemas/Ref" + lastModificationDate: + description: Content item modification date. + type: string + format: date-time + mainLanguageCode: + description: The main language code of the content item. + type: string + alwaysAvailable: + type: boolean + Version: + $ref: "#/components/schemas/Version" + ParentUserGroup: + type: + $ref: "#/components/schemas/Ref" + Subgroups: + type: + $ref: "#/components/schemas/Ref" + Users: + type: + $ref: "#/components/schemas/Ref" + Roles: + type: + $ref: "#/components/schemas/Ref" + UserGroupWrapper: + type: object + required: + - UserGroup + properties: + UserGroup: + $ref: "#/components/schemas/UserGroup" + UserGroupList: + description: This class represents a User Group list. + type: + $ref: "#/components/schemas/BaseObject" + UserGroupListWrapper: + type: object + required: + - UserGroupList + properties: + UserGroupList: + $ref: "#/components/schemas/UserGroupList" + UserGroupRefList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Returns a list of the sub groups. + type: object + required: + - UserGroup + properties: + UserGroup: + description: This class represents a User group. + type: array + items: + $ref: "#/components/schemas/Ref" + UserGroupRefListWrapper: + type: object + required: + - UserGroupRefList + properties: + UserGroupRefList: + $ref: "#/components/schemas/UserGroupRefList" + UserGroupCreate: + description: This class is used to create a User Group. + type: object + required: + - mainLanguageCode + - remoteId + - fields + properties: + mainLanguageCode: + type: string + remoteId: + type: string + fields: + type: + $ref: "#/components/schemas/Fields" + UserGroupCreateWrapper: + type: object + required: + - UserGroupCreate + properties: + UserGroupCreate: + $ref: "#/components/schemas/UserGroupCreate" + UserGroupUpdate: + description: This class is used to update a User group in the Repository. + type: object + required: + - Section + properties: + Section: + type: object + required: + - _href + properties: + _href: + xml: + attribute: true + name: href + type: string + UserGroupUpdateWrapper: + type: object + required: + - UserGroupUpdate + properties: + UserGroupUpdate: + $ref: "#/components/schemas/UserGroupUpdate" + UserGroupUnassign: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a User group. + type: object + required: + - Unassign + properties: + Unassign: + type: + $ref: "#/components/schemas/Unlink" + Unlink: + description: Unlink a content type group from a content type. + type: object + required: + - _href + - _method + properties: + _href: + type: string + _method: + enum: + - DELETE + type: string diff --git a/src/bundle/Resources/api_platform/user/users_schemas.yml b/src/bundle/Resources/api_platform/user/users_schemas.yml new file mode 100644 index 000000000..9d9902fa9 --- /dev/null +++ b/src/bundle/Resources/api_platform/user/users_schemas.yml @@ -0,0 +1,206 @@ +schemas: + User: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a User value. + type: object + required: + - _id + - _remoteId + - ContentType + - name + - Versions + - Section + - MainLocation + - Locations + - Groups + - Owner + - publishDate + - lastModificationDate + - mainLanguageCode + - alwaysAvailable + - Version + - login + - email + - enabled + - UserGroups + - Roles + properties: + _id: + description: Unique ID of the content type. + xml: + attribute: true + name: id + type: integer + _remoteId: + description: Remote ID of the content type. + xml: + attribute: true + name: remoteId + type: string + ContentType: + description: This class represents a content type. + type: + $ref: "#/components/schemas/BaseObject" + name: + description: Name of the domain object in a given language. + type: string + Versions: + description: Returns the VersionInfo for this version. + type: + $ref: "#/components/schemas/BaseObject" + Section: + description: The Section to which the content item is assigned. + type: + $ref: "#/components/schemas/BaseObject" + MainLocation: + description: Main Location of the object. + type: + $ref: "#/components/schemas/BaseObject" + Locations: + description: Locations of the object. + type: + $ref: "#/components/schemas/BaseObject" + Groups: + description: Group User of the content type. + type: + $ref: "#/components/schemas/BaseObject" + Owner: + description: The owner of the content item. + type: + $ref: "#/components/schemas/BaseObject" + publishDate: + description: Content publication date. + type: string + format: date-time + lastModificationDate: + description: Content modification date. + type: string + format: date-time + mainLanguageCode: + description: The main language code of the content item. + type: string + alwaysAvailable: + description: Indicates if the content item is shown in the main language if it's not present in an other requested language. + type: boolean + Version: + description: Returns the VersionInfo for this version. + login: + description: User login. + type: string + email: + description: User email address. + type: string + enabled: + description: Flag to Signal if User is enabled or not. User can not login if false. + type: boolean + UserGroups: + description: User groups. + type: + $ref: "#/components/schemas/BaseObject" + Roles: + description: Roles. + type: + $ref: "#/components/schemas/BaseObject" + UserWrapper: + type: object + required: + - User + properties: + User: + $ref: "#/components/schemas/User" + UserList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: This class represents a list of users. + type: object + required: + - User + properties: + User: + type: array + items: + $ref: "#/components/schemas/User" + UserListWrapper: + type: object + required: + - UserList + properties: + UserList: + $ref: "#/components/schemas/UserList" + UserRefList: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Returns a list of the users. + type: object + required: + - User + properties: + User: + description: This class represents a User. + type: array + items: + $ref: "#/components/schemas/BaseObject" + UserRefListWrapper: + type: object + required: + - UserRefList + properties: + UserRefList: + $ref: "#/components/schemas/UserRefList" + UserUpdate: + description: This class is used to update a User. + type: object + required: + - login + properties: + login: + type: string + email: + type: string + password: + type: string + enabled: + type: boolean + maxLogin: + type: integer + ContentUpdate: + description: The update structure for the profile content. + ContentMetadataUpdate: + description: The update structure for the profile metadata. + UserUpdateWrapper: + type: object + required: + - UserUpdate + properties: + UserUpdate: + $ref: "#/components/schemas/UserUpdate" + UserCreate: + description: This class is used to create a User. + type: object + required: + - mainLanguageCode + - remoteId + - login + - email + - password + properties: + mainLanguageCode: + type: string + remoteId: + type: string + login: + type: string + email: + type: string + password: + type: string + enabled: + type: boolean + UserCreateWrapper: + type: object + required: + - UserCreate + properties: + UserCreate: + $ref: "#/components/schemas/UserCreate" diff --git a/src/bundle/Resources/api_platform/views_schemas.yml b/src/bundle/Resources/api_platform/views_schemas.yml new file mode 100644 index 000000000..62c0b0467 --- /dev/null +++ b/src/bundle/Resources/api_platform/views_schemas.yml @@ -0,0 +1,151 @@ +schemas: + View: + description: View. + type: object + required: + - identifier + - User + - public + - LocationQuery + - Result + properties: + identifier: + description: Content identifier. + type: string + User: + type: + $ref: "#/components/schemas/User" + public: + type: boolean + LocationQuery: + type: + $ref: "#/components/schemas/LocationQuery" + Result: + type: + $ref: "#/components/schemas/BaseObject" + ViewInput: + description: This class represents a View input. + type: object + required: + - identifier + - Query + properties: + identifier: + description: Content identifier. + type: string + languageCode: + type: string + useAlwaysAvailable: + type: string + Query: + type: + $ref: "#/components/schemas/Query" + ViewInputWrapper: + type: object + required: + - ViewInput + properties: + ViewInput: + $ref: "#/components/schemas/ViewInput" + Query: + description: This class is used to perform a Content query. + type: object + properties: + Filter: + description: The Query filter. Can contain multiple criterion, as items of a logical one (by default AND). + type: + $ref: "#/components/schemas/Criterion" + Query: + description: The Query query. Can contain multiple criterion, as items of a logical one (by default AND). + type: + $ref: "#/components/schemas/Criterion" + sortClauses: + description: Query sorting clauses. + type: array + items: + $ref: "#/components/schemas/SortClause" + facetBuilders: + description: An array of facet builders. Search engines may ignore any, or given facet builders they don't support and will just return search result facets supported by the engine. API consumer should dynamically iterate over returned facets for further use. + type: array + items: + $ref: "#/components/schemas/FacetBuilder" + offset: + description: Query offset. Sets the offset for search hits, used for paging the results. + type: integer + limit: + description: Query limit. Limit for number of search hits to return. If value is `0`, search query will not return any search hits, useful for doing a count. + type: integer + spellcheck: + description: If true spellcheck suggestions are returned. + type: boolean + performCount: + description: If true, search engine should perform count even if that means extra lookup. + type: boolean + Criterion: + description: Criterion implementations. + type: object + properties: + operator: + description: The operator used by the Criterion. + type: string + value: + description: The value(s) matched by the Criteria. + type: array + items: { } + target: + description: The target used by the Criteria (field, metadata...). + type: string + valueData: + description: Additional value data, required by some Criteria, MapLocationDistance for instance. + Specifications: + description: Criterion description function. Returns the combination of the Criterion's supported operator/value, as an array of objects. + type: array + items: + $ref: "#/components/schemas/Specifications" + FacetBuilder: + description: This class is the base class for facet builders. + type: object + required: + - name + - global + - filter + - limit + - minCount + properties: + name: + description: The name of the facet. + type: string + global: + description: If true the facet runs in a global mode not restricted by the query. + type: boolean + filter: + description: An additional facet filter that will further filter the documents the facet will be executed on. + type: + $ref: "#/components/schemas/Criterion" + limit: + description: Number of facets (terms) returned. + type: integer + minCount: + description: Specifies the minimum count. Only facet groups with more or equal results are returned. + type: integer + Specifications: + description: This class is used by Criteria to describe which operators they support. Instances of this class are returned in an array by the {@see Criterion::getSpecifications()} method. + type: object + required: + - operator + - valueFormat + - valueTypes + - valueCount + properties: + operator: + description: Specified operator, as one of the Operator::* constants. + type: string + valueFormat: + description: Format supported for the Criterion value, either {@see self::FORMAT_SINGLE} for single or {@see self::FORMAT_ARRAY} for multiple. + type: string + valueTypes: + description: "Accepted values types, specifying what type of variables are accepted as a value. Criterion input value type description constants: const TYPE_INTEGER = 1; const TYPE_STRING = 2; const TYPE_BOOLEAN = 4." + type: integer + valueCount: + description: Limitation on the number of items as the value. Only usable if {@see $valueFormat} is {@see self::FORMAT_ARRAY}. + type: integer diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml new file mode 100644 index 000000000..cccb71de2 --- /dev/null +++ b/src/bundle/Resources/config/api_platform.yml @@ -0,0 +1,70 @@ +services: + ibexa.api_platform.action.entrypoint: + parent: api_platform.action.entrypoint + arguments: + index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' + index_1: '@ibexa.api_platform.state_provider.documentation.content_negotiation' + + ibexa.api_platform.state_provider.documentation.content_negotiation: + parent: api_platform.state_provider.documentation.content_negotiation + arguments: + index_0: '@ibexa.api_platform.swagger_ui.documentation.provider' + + ibexa.api_platform.swagger_ui.documentation.provider: + parent: api_platform.swagger_ui.documentation.provider + arguments: + index_1: '@ibexa.api_platform.ibexa_openapi.factory' + + ibexa.api_platform.ibexa_openapi.factory: + class: 'Ibexa\Bundle\Rest\ApiPlatform\OpenApiFactory' + decorates: ibexa.api_platform.openapi.factory + arguments: + - '@.inner' + - '@Ibexa\Bundle\Rest\ApiPlatform\SchemasCollectionFactory' + - '@Symfony\Component\HttpKernel\KernelInterface' + + ibexa.api_platform.openapi.factory: + parent: api_platform.openapi.factory + arguments: + index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' + + Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory: ~ + + # OpenAPI Command + + ibexa.api_platform.openapi.command: + parent: api_platform.openapi.command + arguments: + index_0: '@ibexa.api_platform.ibexa_openapi.factory' + tags: + - { name: 'console.command', command: 'ibexa:openapi' } + + # Collecting schemas + + Ibexa\Bundle\Rest\ApiPlatform\SchemasCollectionFactory: ~ + + ibexa.api_platform.schemas_provider.rest: + class: Ibexa\Rest\ApiPlatform\SchemasProvider + autowire: true + autoconfigure: true + arguments: + $files: + - '@@IbexaRestBundle/Resources/api_platform/base_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/bookmarks_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_locations_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_objects_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_sections_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_trash_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_type_groups_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_types_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_url_aliases_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/content_url_wildcards_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/language_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/object_state_groups_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/services_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/views_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/user/roles_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/user/sessions_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/user/token_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/user/user_groups_schemas.yml' + - '@@IbexaRestBundle/Resources/api_platform/user/users_schemas.yml' diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index 69e858b8d..6696d376e 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -8,25 +8,38 @@ ibexa.rest.load_root_resource: methods: [GET] + +# OpenAPI documentation + + +ibexa.api_platform.documentation: + path: /doc/{index}.{_format} + controller: ibexa.api_platform.action.entrypoint + defaults: + index: index + _format: ~ + + + # Sections ibexa.rest.list_sections: path: /content/sections defaults: - _controller: Ibexa\Rest\Server\Controller\Section:listSections + _controller: Ibexa\Rest\Server\Controller\Section\SectionListController:listSections methods: [GET] ibexa.rest.create_section: path: /content/sections defaults: - _controller: Ibexa\Rest\Server\Controller\Section:createSection + _controller: Ibexa\Rest\Server\Controller\Section\SectionCreateController:createSection methods: [POST] ibexa.rest.load_section: path: /content/sections/{sectionId} defaults: - _controller: Ibexa\Rest\Server\Controller\Section:loadSection + _controller: Ibexa\Rest\Server\Controller\Section\SectionLoadByIdController:loadSection methods: [GET] requirements: sectionId: \d+ @@ -34,7 +47,7 @@ ibexa.rest.load_section: ibexa.rest.update_section: path: /content/sections/{sectionId} defaults: - _controller: Ibexa\Rest\Server\Controller\Section:updateSection + _controller: Ibexa\Rest\Server\Controller\Section\SectionUpdateController:updateSection methods: [PATCH] requirements: sectionId: \d+ @@ -42,7 +55,7 @@ ibexa.rest.update_section: ibexa.rest.delete_section: path: /content/sections/{sectionId} defaults: - _controller: Ibexa\Rest\Server\Controller\Section:deleteSection + _controller: Ibexa\Rest\Server\Controller\Section\SectionDeleteController:deleteSection methods: [DELETE] requirements: sectionId: \d+ @@ -50,7 +63,7 @@ ibexa.rest.delete_section: ibexa.rest.refresh_session: path: /user/sessions/{sessionId}/refresh defaults: - _controller: Ibexa\Rest\Server\Controller\SessionController:refreshSessionAction + _controller: Ibexa\Rest\Server\Controller\Session\SessionRefreshController:refreshSessionAction csrf_protection: false methods: [POST] @@ -59,7 +72,7 @@ ibexa.rest.refresh_session: ibexa.rest.content.copy: path: /content/objects/{contentId} - controller: Ibexa\Rest\Server\Controller\Content::copy + controller: Ibexa\Rest\Server\Controller\Content\ContentCopyController::copy condition: 'ibexa_get_media_type(request) === "CopyContentInput"' methods: [POST] options: @@ -69,7 +82,7 @@ ibexa.rest.content.copy: ibexa.rest.content.create_draft_from_version: path: /content/objects/{contentId}/versions/{versionNumber} - controller: Ibexa\Rest\Server\Controller\Content::createDraftFromVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromVersionController::createDraftFromVersion condition: 'ibexa_get_media_type(request) === "CreateDraftFromVersionInput"' methods: [POST] options: @@ -77,7 +90,7 @@ ibexa.rest.content.create_draft_from_version: ibexa.rest.content.create_draft_from_current_version: path: /content/objects/{contentId}/currentversion - controller: Ibexa\Rest\Server\Controller\Content::createDraftFromCurrentVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromCurrentVersionController::createDraftFromCurrentVersion condition: 'ibexa_get_media_type(request) === "CreateDraftFromCurrentVersionInput"' methods: [POST] options: @@ -87,7 +100,7 @@ ibexa.rest.content.create_draft_from_current_version: ibexa.rest.content.publish_version: path: /content/objects/{contentId}/versions/{versionNumber} - controller: Ibexa\Rest\Server\Controller\Content::publishVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionPublishController::publishVersion condition: 'ibexa_get_media_type(request) === "PublishContentVersionInput"' methods: [POST] options: @@ -99,19 +112,19 @@ ibexa.rest.content.publish_version: ibexa.rest.redirect_content: path: /content/objects defaults: - _controller: Ibexa\Rest\Server\Controller\Content:redirectContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentRedirectController:redirectContent methods: [GET] ibexa.rest.create_content: path: /content/objects defaults: - _controller: Ibexa\Rest\Server\Controller\Content:createContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentCreateController:createContent methods: [POST] ibexa.rest.update_content_metadata: path: /content/objects/{contentId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:updateContentMetadata + _controller: Ibexa\Rest\Server\Controller\Content\ContentMetadataUpdateController:updateContentMetadata methods: [PATCH] requirements: contentId: \d+ @@ -119,7 +132,7 @@ ibexa.rest.update_content_metadata: ibexa.rest.load_content: path: /content/objects/{contentId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:loadContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentLoadByIdController:loadContent methods: [GET] requirements: contentId: \d+ @@ -127,7 +140,7 @@ ibexa.rest.load_content: ibexa.rest.delete_content: path: /content/objects/{contentId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:deleteContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentDeleteController:deleteContent methods: [DELETE] requirements: contentId: \d+ @@ -135,7 +148,7 @@ ibexa.rest.delete_content: ibexa.rest.copy_content: path: /content/objects/{contentId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:copyContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentCopyController:copyContent methods: [COPY] requirements: contentId: \d+ @@ -143,13 +156,13 @@ ibexa.rest.copy_content: ibexa.rest.delete_content_translation: path: /content/objects/{contentId}/translations/{languageCode} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:deleteContentTranslation + _controller: Ibexa\Rest\Server\Controller\Content\ContentTranslationDeleteController:deleteContentTranslation methods: [DELETE] ibexa.rest.redirect_current_version_relations: path: /content/objects/{contentId}/relations defaults: - _controller: Ibexa\Rest\Server\Controller\Content:redirectCurrentVersionRelations + _controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRelationsRedirectController:redirectCurrentVersionRelations methods: [GET] requirements: contentId: \d+ @@ -157,7 +170,7 @@ ibexa.rest.redirect_current_version_relations: ibexa.rest.load_content_versions: path: /content/objects/{contentId}/versions defaults: - _controller: Ibexa\Rest\Server\Controller\Content:loadContentVersions + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsListController:loadContentVersions methods: [GET] requirements: contentId: \d+ @@ -165,7 +178,7 @@ ibexa.rest.load_content_versions: ibexa.rest.load_version_relations: path: /content/objects/{contentId}/versions/{versionNumber}/relations defaults: - _controller: Ibexa\Rest\Server\Controller\Content:loadVersionRelations + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsRelationsListController:loadVersionRelations methods: [GET] requirements: contentId: \d+ @@ -174,7 +187,7 @@ ibexa.rest.load_version_relations: ibexa.rest.create_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations defaults: - _controller: Ibexa\Rest\Server\Controller\Content:createRelation + _controller: Ibexa\Rest\Server\Controller\Content\ContentRelationCreateController:createRelation methods: [POST] requirements: contentId: \d+ @@ -183,7 +196,7 @@ ibexa.rest.create_relation: ibexa.rest.load_version_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations/{relationId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:loadVersionRelation + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationLoadByIdController:loadVersionRelation methods: [GET] requirements: contentId: \d+ @@ -193,7 +206,7 @@ ibexa.rest.load_version_relation: ibexa.rest.remove_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations/{relationId} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:removeRelation + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationDeleteController:removeRelation methods: [DELETE] requirements: contentId: \d+ @@ -203,7 +216,7 @@ ibexa.rest.remove_relation: ibexa.rest.load_content_in_version: path: /content/objects/{contentId}/versions/{versionNumber} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:loadContentInVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentInVersionLoadController:loadContentInVersion methods: [GET] requirements: contentId: \d+ @@ -212,7 +225,7 @@ ibexa.rest.load_content_in_version: ibexa.rest.update_version: path: /content/objects/{contentId}/versions/{versionNumber} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:updateVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionUpdateController:updateVersion methods: [PATCH] requirements: contentId: \d+ @@ -221,7 +234,7 @@ ibexa.rest.update_version: ibexa.rest.delete_content_version: path: /content/objects/{contentId}/versions/{versionNumber} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:deleteContentVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionDeleteController:deleteContentVersion methods: [DELETE] requirements: contentId: \d+ @@ -230,7 +243,7 @@ ibexa.rest.delete_content_version: ibexa.rest.delete_translation_from_draft: path: /content/objects/{contentId}/versions/{versionNumber}/translations/{languageCode} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:deleteTranslationFromDraft + _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftTranslationDeleteController:deleteTranslationFromDraft methods: [DELETE] requirements: contentId: \d+ @@ -239,7 +252,7 @@ ibexa.rest.delete_translation_from_draft: ibexa.rest.create_draft_from_version: path: /content/objects/{contentId}/versions/{versionNumber} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:createDraftFromVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromVersionController:createDraftFromVersion methods: [COPY] requirements: contentId: \d+ @@ -248,7 +261,7 @@ ibexa.rest.create_draft_from_version: ibexa.rest.publish_version: path: /content/objects/{contentId}/versions/{versionNumber} defaults: - _controller: Ibexa\Rest\Server\Controller\Content:publishVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionPublishController:publishVersion methods: [PUBLISH] requirements: contentId: \d+ @@ -257,7 +270,7 @@ ibexa.rest.publish_version: ibexa.rest.redirect_current_version: path: /content/objects/{contentId}/currentversion defaults: - _controller: Ibexa\Rest\Server\Controller\Content:redirectCurrentVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRedirectController:redirectCurrentVersion methods: [GET] requirements: contentId: \d+ @@ -265,7 +278,7 @@ ibexa.rest.redirect_current_version: ibexa.rest.create_draft_from_current_version: path: /content/objects/{contentId}/currentversion defaults: - _controller: Ibexa\Rest\Server\Controller\Content:createDraftFromCurrentVersion + _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromCurrentVersionController:createDraftFromCurrentVersion methods: [COPY] requirements: contentId: \d+ @@ -273,7 +286,7 @@ ibexa.rest.create_draft_from_current_version: ibexa.rest.hide_content: path: /content/objects/{contentId}/hide defaults: - _controller: Ibexa\Rest\Server\Controller\Content:hideContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentHideController:hideContent methods: [POST] requirements: contentId: \d+ @@ -281,7 +294,7 @@ ibexa.rest.hide_content: ibexa.rest.reveal_content: path: /content/objects/{contentId}/reveal defaults: - _controller: Ibexa\Rest\Server\Controller\Content:revealContent + _controller: Ibexa\Rest\Server\Controller\Content\ContentRevealController:revealContent methods: [POST] requirements: contentId: \d+ @@ -302,7 +315,7 @@ ibexa.rest.binary_content.get_image_variation: ibexa.rest.create_content_view: path: /content/views defaults: - _controller: Ibexa\Rest\Server\Controller\Content:createView + _controller: Ibexa\Rest\Server\Controller\Content\Content:createView methods: [POST] # Views, Platform 1.0 @@ -335,19 +348,19 @@ ibexa.rest.views.load.results: ibexa.rest.load_object_state_groups: path: /content/objectstategroups defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:loadObjectStateGroups + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupListController:loadObjectStateGroups methods: [GET] ibexa.rest.create_object_state_group: path: /content/objectstategroups defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:createObjectStateGroup + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupCreateController:createObjectStateGroup methods: [POST] ibexa.rest.load_object_state_group: path: /content/objectstategroups/{objectStateGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:loadObjectStateGroup + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupLoadByIdController:loadObjectStateGroup methods: [GET] requirements: objectStateGroupId: \d+ @@ -355,7 +368,7 @@ ibexa.rest.load_object_state_group: ibexa.rest.update_object_state_group: path: /content/objectstategroups/{objectStateGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:updateObjectStateGroup + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupUpdateController:updateObjectStateGroup methods: [PATCH] requirements: objectStateGroupId: \d+ @@ -363,7 +376,7 @@ ibexa.rest.update_object_state_group: ibexa.rest.delete_object_state_group: path: /content/objectstategroups/{objectStateGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:deleteObjectStateGroup + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController:deleteObjectStateGroup methods: [DELETE] requirements: objectStateGroupId: \d+ @@ -371,7 +384,7 @@ ibexa.rest.delete_object_state_group: ibexa.rest.load_object_states: path: /content/objectstategroups/{objectStateGroupId}/objectstates defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:loadObjectStates + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateListController:loadObjectStates methods: [GET] requirements: objectStateGroupId: \d+ @@ -379,7 +392,7 @@ ibexa.rest.load_object_states: ibexa.rest.create_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:createObjectState + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateCreateController:createObjectState methods: [POST] requirements: objectStateGroupId: \d+ @@ -387,7 +400,7 @@ ibexa.rest.create_object_state: ibexa.rest.load_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:loadObjectState + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateLoadByIdController:loadObjectState methods: [GET] requirements: objectStateGroupId: \d+ @@ -396,7 +409,7 @@ ibexa.rest.load_object_state: ibexa.rest.update_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:updateObjectState + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateUpdateController:updateObjectState methods: [PATCH] requirements: objectStateGroupId: \d+ @@ -405,7 +418,7 @@ ibexa.rest.update_object_state: ibexa.rest.delete_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:deleteObjectState + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController:deleteObjectState methods: [DELETE] requirements: objectStateGroupId: \d+ @@ -414,7 +427,7 @@ ibexa.rest.delete_object_state: ibexa.rest.get_object_states_for_content: path: /content/objects/{contentId}/objectstates defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:getObjectStatesForContent + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentListController:getObjectStatesForContent methods: [GET] requirements: contentId: \d+ @@ -422,7 +435,7 @@ ibexa.rest.get_object_states_for_content: ibexa.rest.set_object_states_for_content: path: /content/objects/{contentId}/objectstates defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState:setObjectStatesForContent + _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentUpdateController:setObjectStatesForContent methods: [PATCH] requirements: contentId: \d+ @@ -433,18 +446,18 @@ ibexa.rest.set_object_states_for_content: ibexa.rest.languages.list: path: /languages methods: [GET] - controller: Ibexa\Rest\Server\Controller\Language::listLanguages + controller: Ibexa\Rest\Server\Controller\Language\LanguageListController::listLanguages ibexa.rest.languages.view: path: /languages/{languageCode} methods: [GET] - controller: Ibexa\Rest\Server\Controller\Language::loadLanguage + controller: Ibexa\Rest\Server\Controller\Language\LanguageLoadByIdController::loadLanguage # Locations ibexa.rest.location.copy: path: /content/locations/{locationPath} - controller: Ibexa\Rest\Server\Controller\Location::copy + controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeCopyController::copy condition: 'ibexa_get_media_type(request) === "CopyLocationInput"' methods: [POST] options: @@ -454,7 +467,7 @@ ibexa.rest.location.copy: ibexa.rest.trash_location: path: /content/locations/{locationPath} - controller: Ibexa\Rest\Server\Controller\Trash::trashLocation + controller: Ibexa\Rest\Server\Controller\Trash\LocationTrashController::trashLocation condition: 'ibexa_get_media_type(request) === "TrashLocationInput"' methods: [POST, TRASH] options: @@ -464,7 +477,7 @@ ibexa.rest.trash_location: ibexa.rest.location.swap: path: /content/locations/{locationPath} - controller: Ibexa\Rest\Server\Controller\Location::swap + controller: Ibexa\Rest\Server\Controller\Location\LocationSwapController::swap condition: 'ibexa_get_media_type(request) === "SwapLocationInput"' methods: [POST] options: @@ -474,7 +487,7 @@ ibexa.rest.location.swap: ibexa.rest.location.move: path: /content/locations/{locationPath} - controller: Ibexa\Rest\Server\Controller\Location::moveLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeMoveController::moveLocation condition: 'ibexa_get_media_type(request) === "MoveLocationInput"' methods: [POST] options: @@ -485,13 +498,13 @@ ibexa.rest.location.move: ibexa.rest.redirect_location: path: /content/locations defaults: - _controller: Ibexa\Rest\Server\Controller\Location:redirectLocation + _controller: Ibexa\Rest\Server\Controller\Location\LocationRedirectController:redirectLocation methods: [GET] ibexa.rest.load_location: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:loadLocation + _controller: Ibexa\Rest\Server\Controller\Location\LocationLoadByPathController:loadLocation methods: [GET] requirements: locationPath: "[0-9/]+" @@ -499,7 +512,7 @@ ibexa.rest.load_location: ibexa.rest.update_location: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:updateLocation + _controller: Ibexa\Rest\Server\Controller\Location\LocationUpdateController:updateLocation methods: [PATCH] requirements: locationPath: "[0-9/]+" @@ -507,7 +520,7 @@ ibexa.rest.update_location: ibexa.rest.delete_subtree: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:deleteSubtree + _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeDeleteController:deleteSubtree methods: [DELETE] requirements: locationPath: "[0-9/]+" @@ -515,7 +528,7 @@ ibexa.rest.delete_subtree: ibexa.rest.copy_subtree: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:copySubtree + _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeCopyController:copySubtree methods: [COPY] requirements: locationPath: "[0-9/]+" @@ -523,7 +536,7 @@ ibexa.rest.copy_subtree: ibexa.rest.move_subtree: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:moveSubtree + _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeMoveController:moveSubtree methods: [MOVE] requirements: locationPath: "[0-9/]+" @@ -531,7 +544,7 @@ ibexa.rest.move_subtree: ibexa.rest.swap_location: path: /content/locations/{locationPath} defaults: - _controller: Ibexa\Rest\Server\Controller\Location:swapLocation + _controller: Ibexa\Rest\Server\Controller\Location\LocationSwapController:swapLocation methods: [SWAP] requirements: locationPath: "[0-9/]+" @@ -539,7 +552,7 @@ ibexa.rest.swap_location: ibexa.rest.load_location_children: path: /content/locations/{locationPath}/children defaults: - _controller: Ibexa\Rest\Server\Controller\Location:loadLocationChildren + _controller: Ibexa\Rest\Server\Controller\Location\LocationChildrenListController:loadLocationChildren methods: [GET] requirements: locationPath: "[0-9/]+" @@ -547,7 +560,7 @@ ibexa.rest.load_location_children: ibexa.rest.load_locations_for_content: path: /content/objects/{contentId}/locations defaults: - _controller: Ibexa\Rest\Server\Controller\Location:loadLocationsForContent + _controller: Ibexa\Rest\Server\Controller\Location\LocationForContentListController:loadLocationsForContent methods: [GET] requirements: contentId: \d+ @@ -555,7 +568,7 @@ ibexa.rest.load_locations_for_content: ibexa.rest.create_location: path: /content/objects/{contentId}/locations defaults: - _controller: Ibexa\Rest\Server\Controller\Location:createLocation + _controller: Ibexa\Rest\Server\Controller\Location\LocationCreateController:createLocation methods: [POST] requirements: contentId: \d+ @@ -566,7 +579,7 @@ ibexa.rest.create_location: ibexa.rest.content_type.publish_draft: path: /content/types/{contentTypeId}/draft - controller: Ibexa\Rest\Server\Controller\ContentType::publishContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftPublishController::publishContentTypeDraft condition: 'ibexa_get_media_type(request) === "PublishContentTypeInput"' methods: [POST] options: @@ -576,7 +589,7 @@ ibexa.rest.content_type.publish_draft: ibexa.rest.content_type.copy: path: /content/types/{contentTypeId} - controller: Ibexa\Rest\Server\Controller\ContentType::copyContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCopyController::copyContentType condition: 'ibexa_get_media_type(request) === "CopyContentTypeInput"' methods: [POST] options: @@ -587,19 +600,19 @@ ibexa.rest.content_type.copy: ibexa.rest.load_content_type_group_list: path: /content/typegroups defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeGroupList + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupsListController:loadContentTypeGroupList methods: [GET] ibexa.rest.create_content_type_group: path: /content/typegroups defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:createContentTypeGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupCreateController:createContentTypeGroup methods: [POST] ibexa.rest.load_content_type_group: path: /content/typegroups/{contentTypeGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupLoadByIdController:loadContentTypeGroup methods: [GET] requirements: contentTypeGroupId: \d+ @@ -607,7 +620,7 @@ ibexa.rest.load_content_type_group: ibexa.rest.update_content_type_group: path: /content/typegroups/{contentTypeGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:updateContentTypeGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupUpdateController:updateContentTypeGroup methods: [PATCH] requirements: contentTypeGroupId: \d+ @@ -615,7 +628,7 @@ ibexa.rest.update_content_type_group: ibexa.rest.delete_content_type_group: path: /content/typegroups/{contentTypeGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:deleteContentTypeGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupDeleteController:deleteContentTypeGroup methods: [DELETE] requirements: contentTypeGroupId: \d+ @@ -623,7 +636,7 @@ ibexa.rest.delete_content_type_group: ibexa.rest.list_content_types_for_group: path: /content/typegroups/{contentTypeGroupId}/types defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:listContentTypesForGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListForGroupController:listContentTypesForGroup methods: [GET] requirements: contentTypeGroupId: \d+ @@ -631,7 +644,7 @@ ibexa.rest.list_content_types_for_group: ibexa.rest.create_content_type: path: /content/typegroups/{contentTypeGroupId}/types defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:createContentType + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCreateController:createContentType methods: [POST] requirements: contentTypeGroupId: \d+ @@ -640,13 +653,13 @@ ibexa.rest.list_content_types: # @todo: Handle all GET parameters path: /content/types defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:listContentTypes + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListController:listContentTypes methods: [GET] ibexa.rest.copy_content_type: path: /content/types/{contentTypeId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:copyContentType + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCopyController:copyContentType methods: [COPY] requirements: contentTypeId: \d+ @@ -654,7 +667,7 @@ ibexa.rest.copy_content_type: ibexa.rest.load_content_type: path: /content/types/{contentTypeId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentType + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLoadByIdController:loadContentType methods: [GET] requirements: contentTypeId: \d+ @@ -662,7 +675,7 @@ ibexa.rest.load_content_type: ibexa.rest.create_content_type_draft: path: /content/types/{contentTypeId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:createContentTypeDraft + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftCreateController:createContentTypeDraft methods: [POST] requirements: contentTypeId: \d+ @@ -670,7 +683,7 @@ ibexa.rest.create_content_type_draft: ibexa.rest.delete_content_type: path: /content/types/{contentTypeId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:deleteContentType + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDeleteController:deleteContentType methods: [DELETE] requirements: contentTypeId: \d+ @@ -678,7 +691,7 @@ ibexa.rest.delete_content_type: ibexa.rest.delete_content_type_draft: path: /content/types/{contentTypeId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:deleteContentTypeDraft + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftDeleteController:deleteContentTypeDraft methods: [DELETE] requirements: contentTypeId: \d+ @@ -686,7 +699,7 @@ ibexa.rest.delete_content_type_draft: ibexa.rest.load_content_type_field_definition_list: path: /content/types/{contentTypeId}/fieldDefinitions defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeFieldDefinitionList + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionListController:loadContentTypeFieldDefinitionList methods: [GET] requirements: contentTypeId: \d+ @@ -694,7 +707,7 @@ ibexa.rest.load_content_type_field_definition_list: ibexa.rest.load_content_type_field_definition: path: /content/types/{contentTypeId}/fieldDefinitions/{fieldDefinitionId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeFieldDefinition + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionLoadByIdController:loadContentTypeFieldDefinition methods: [GET] requirements: contentTypeId: \d+ @@ -702,7 +715,7 @@ ibexa.rest.load_content_type_field_definition: ibexa.rest.load_content_type_field_definition_by_identifier: path: /content/types/{contentTypeId}/fieldDefinition/{fieldDefinitionIdentifier} - controller: Ibexa\Rest\Server\Controller\ContentType::loadContentTypeFieldDefinitionByIdentifier + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionLoadByIdentifierController::loadContentTypeFieldDefinitionByIdentifier methods: [GET] requirements: contentTypeId: \d+ @@ -711,7 +724,7 @@ ibexa.rest.load_content_type_field_definition_by_identifier: ibexa.rest.load_content_type_draft: path: /content/types/{contentTypeId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeDraft + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftLoadController:loadContentTypeDraft methods: [GET] requirements: contentTypeId: \d+ @@ -719,7 +732,7 @@ ibexa.rest.load_content_type_draft: ibexa.rest.update_content_type_draft: path: /content/types/{contentTypeId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:updateContentTypeDraft + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftUpdateController:updateContentTypeDraft methods: [PATCH] requirements: contentTypeId: \d+ @@ -727,7 +740,7 @@ ibexa.rest.update_content_type_draft: ibexa.rest.publish_content_type_draft: path: /content/types/{contentTypeId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:publishContentTypeDraft + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftPublishController:publishContentTypeDraft methods: [PUBLISH] requirements: contentTypeId: \d+ @@ -735,7 +748,7 @@ ibexa.rest.publish_content_type_draft: ibexa.rest.load_content_type_draft_field_definition_list: path: /content/types/{contentTypeId}/draft/fieldDefinitions defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeDraftFieldDefinitionList + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionListController:loadContentTypeDraftFieldDefinitionList methods: [GET] requirements: contentTypeId: \d+ @@ -743,7 +756,7 @@ ibexa.rest.load_content_type_draft_field_definition_list: ibexa.rest.add_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:addContentTypeDraftFieldDefinition + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFeildDefinitionAddController:addContentTypeDraftFieldDefinition methods: [POST] requirements: contentTypeId: \d+ @@ -751,7 +764,7 @@ ibexa.rest.add_content_type_draft_field_definition: ibexa.rest.load_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadContentTypeDraftFieldDefinition + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionLoadByIdController:loadContentTypeDraftFieldDefinition methods: [GET] requirements: contentTypeId: \d+ @@ -760,7 +773,7 @@ ibexa.rest.load_content_type_draft_field_definition: ibexa.rest.update_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:updateContentTypeDraftFieldDefinition + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionUpdateController:updateContentTypeDraftFieldDefinition methods: [PATCH] requirements: contentTypeId: \d+ @@ -769,7 +782,7 @@ ibexa.rest.update_content_type_draft_field_definition: ibexa.rest.remove_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:removeContentTypeDraftFieldDefinition + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionDeleteController:removeContentTypeDraftFieldDefinition methods: [DELETE] requirements: contentTypeId: \d+ @@ -778,7 +791,7 @@ ibexa.rest.remove_content_type_draft_field_definition: ibexa.rest.load_groups_of_content_type: path: /content/types/{contentTypeId}/groups defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:loadGroupsOfContentType + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupListController:loadGroupsOfContentType methods: [GET] requirements: contentTypeId: \d+ @@ -787,7 +800,7 @@ ibexa.rest.link_content_type_to_group: # Handle GET parameter group in controller. Most likely already done path: /content/types/{contentTypeId}/groups defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:linkContentTypeToGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLinkToGroupController:linkContentTypeToGroup methods: [POST] requirements: contentTypeId: \d+ @@ -795,7 +808,7 @@ ibexa.rest.link_content_type_to_group: ibexa.rest.unlink_content_type_from_group: path: /content/types/{contentTypeId}/groups/{contentTypeGroupId} defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType:unlinkContentTypeFromGroup + _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeUnlinkFromGroupController:unlinkContentTypeFromGroup methods: [DELETE] requirements: contentTypeId: \d+ @@ -807,7 +820,7 @@ ibexa.rest.unlink_content_type_from_group: ibexa.rest.trash.restore_trash_item: path: /content/trash/{trashItemId} - controller: Ibexa\Rest\Server\Controller\Trash::restoreItem + controller: Ibexa\Rest\Server\Controller\Trash\TrashItemRestoreController::restoreItem condition: 'ibexa_get_media_type(request) === "RestoreTrashItemInput"' methods: [POST] options: @@ -818,19 +831,19 @@ ibexa.rest.trash.restore_trash_item: ibexa.rest.load_trash_items: path: /content/trash defaults: - _controller: Ibexa\Rest\Server\Controller\Trash:loadTrashItems + _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemListController:loadTrashItems methods: [GET] ibexa.rest.empty_trash: path: /content/trash defaults: - _controller: Ibexa\Rest\Server\Controller\Trash:emptyTrash + _controller: Ibexa\Rest\Server\Controller\Trash\TrashEmptyController:emptyTrash methods: [DELETE] ibexa.rest.load_trash_item: path: /content/trash/{trashItemId} defaults: - _controller: Ibexa\Rest\Server\Controller\Trash:loadTrashItem + _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemLoadByIdController:loadTrashItem methods: [GET] requirements: trashItemId: \d+ @@ -838,7 +851,7 @@ ibexa.rest.load_trash_item: ibexa.rest.delete_trash_item: path: /content/trash/{trashItemId} defaults: - _controller: Ibexa\Rest\Server\Controller\Trash:deleteTrashItem + _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemDeleteController:deleteTrashItem methods: [DELETE] requirements: trashItemId: \d+ @@ -846,7 +859,7 @@ ibexa.rest.delete_trash_item: ibexa.rest.restore_trash_item: path: /content/trash/{trashItemId} defaults: - _controller: Ibexa\Rest\Server\Controller\Trash:restoreTrashItem + _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemRestoreController:restoreTrashItem methods: [MOVE] requirements: trashItemId: \d+ @@ -858,19 +871,19 @@ ibexa.rest.restore_trash_item: ibexa.rest.list_url_wildcards: path: /content/urlwildcards defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard:listURLWildcards + _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardListController:listURLWildcards methods: [GET] ibexa.rest.create_url_wildcard: path: /content/urlwildcards defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard:createURLWildcard + _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardCreateController:createURLWildcard methods: [POST] ibexa.rest.load_url_wildcard: path: /content/urlwildcards/{urlWildcardId} defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard:loadURLWildcard + _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardLoadByIdController:loadURLWildcard methods: [GET] requirements: urlWildcardId: \d+ @@ -878,7 +891,7 @@ ibexa.rest.load_url_wildcard: ibexa.rest.delete_url_wildcard: path: /content/urlwildcards/{urlWildcardId} defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard:deleteURLWildcard + _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardDeleteController:deleteURLWildcard methods: [DELETE] requirements: urlWildcardId: \d+ @@ -890,7 +903,7 @@ ibexa.rest.delete_url_wildcard: ibexa.rest.list_policies_for_user: path: /user/policies defaults: - _controller: Ibexa\Rest\Server\Controller\Role:listPoliciesForUser + _controller: Ibexa\Rest\Server\Controller\Role\Role:listPoliciesForUser methods: [GET] @@ -899,7 +912,7 @@ ibexa.rest.list_policies_for_user: ibexa.rest.role.publish: path: /user/roles/{roleId}/draft - controller: Ibexa\Rest\Server\Controller\Role::publishRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftPublishController::publishRoleDraft condition: 'ibexa_get_media_type(request) === "PublishRoleInput"' methods: [POST] options: @@ -910,20 +923,20 @@ ibexa.rest.role.publish: ibexa.rest.list_roles: path: /user/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:listRoles + _controller: Ibexa\Rest\Server\Controller\Role\RoleListController:listRoles methods: [GET] ibexa.rest.create_role: path: /user/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:createRole + _controller: Ibexa\Rest\Server\Controller\Role\RoleCreateController:createRole methods: [POST] requirements: ibexa.rest.create_role_draft: path: /user/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:createRoleDraft + _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftCreateController:createRoleDraft methods: [POST] requirements: roleId: \d+ @@ -931,7 +944,7 @@ ibexa.rest.create_role_draft: ibexa.rest.load_role: path: /user/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRole + _controller: Ibexa\Rest\Server\Controller\Role\RoleLoadByIdController:loadRole methods: [GET] requirements: roleId: \d+ @@ -939,7 +952,7 @@ ibexa.rest.load_role: ibexa.rest.load_role_draft: path: /user/roles/{roleId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRoleDraft + _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftLoadByRoleIdController:loadRoleDraft methods: [GET] requirements: roleId: \d+ @@ -947,7 +960,7 @@ ibexa.rest.load_role_draft: ibexa.rest.update_role: path: /user/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:updateRole + _controller: Ibexa\Rest\Server\Controller\Role\RoleUpdateController:updateRole methods: [PATCH] requirements: roleId: \d+ @@ -955,7 +968,7 @@ ibexa.rest.update_role: ibexa.rest.update_role_draft: path: /user/roles/{roleId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\Role:updateRoleDraft + _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftUpdateController:updateRoleDraft methods: [PATCH] requirements: roleId: \d+ @@ -963,7 +976,7 @@ ibexa.rest.update_role_draft: ibexa.rest.publish_role_draft: path: /user/roles/{roleId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\Role:publishRoleDraft + _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftPublishController:publishRoleDraft methods: [PUBLISH] requirements: roleId: \d+ @@ -971,7 +984,7 @@ ibexa.rest.publish_role_draft: ibexa.rest.delete_role: path: /user/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:deleteRole + _controller: Ibexa\Rest\Server\Controller\Role\RoleDeleteController:deleteRole methods: [DELETE] requirements: roleId: \d+ @@ -979,7 +992,7 @@ ibexa.rest.delete_role: ibexa.rest.delete_role_draft: path: /user/roles/{roleId}/draft defaults: - _controller: Ibexa\Rest\Server\Controller\Role:deleteRoleDraft + _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftDeleteController:deleteRoleDraft methods: [DELETE] requirements: roleId: \d+ @@ -987,7 +1000,7 @@ ibexa.rest.delete_role_draft: ibexa.rest.load_policies: path: /user/roles/{roleId}/policies defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadPolicies + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyListController:loadPolicies methods: [GET] requirements: roleId: \d+ @@ -995,7 +1008,7 @@ ibexa.rest.load_policies: ibexa.rest.add_policy: path: /user/roles/{roleId}/policies defaults: - _controller: Ibexa\Rest\Server\Controller\Role:addPolicy + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyCreateController:addPolicy methods: [POST] requirements: roleId: \d+ @@ -1003,7 +1016,7 @@ ibexa.rest.add_policy: ibexa.rest.delete_policies: path: /user/roles/{roleId}/policies defaults: - _controller: Ibexa\Rest\Server\Controller\Role:deletePolicies + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteAllFromRoleController:deletePolicies methods: [DELETE] requirements: roleId: \d+ @@ -1011,7 +1024,7 @@ ibexa.rest.delete_policies: ibexa.rest.load_policy: path: /user/roles/{roleId}/policies/{policyId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadPolicy + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyLoadByIdController:loadPolicy methods: [GET] requirements: roleId: \d+ @@ -1020,7 +1033,7 @@ ibexa.rest.load_policy: ibexa.rest.update_policy: path: /user/roles/{roleId}/policies/{policyId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:updatePolicy + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyUpdateController:updatePolicy methods: [PATCH] requirements: roleId: \d+ @@ -1029,7 +1042,7 @@ ibexa.rest.update_policy: ibexa.rest.delete_policy: path: /user/roles/{roleId}/policies/{policyId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:deletePolicy + _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteController:deletePolicy methods: [DELETE] requirements: roleId: \d+ @@ -1040,7 +1053,7 @@ ibexa.rest.delete_policy: ibexa.rest.user_group.move: path: /user/groups/{groupPath} - controller: Ibexa\Rest\Server\Controller\User::moveGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupMoveController::moveGroup condition: 'ibexa_get_media_type(request) === "MoveUserGroupInput"' methods: [POST] options: @@ -1051,32 +1064,32 @@ ibexa.rest.user_group.move: ibexa.rest.verify_users: path: /user/users defaults: - _controller: Ibexa\Rest\Server\Controller\User:verifyUsers + _controller: Ibexa\Rest\Server\Controller\User\UserVerifyController:verifyUsers methods: [HEAD] ibexa.rest.load_users: path: /user/users defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUsers + _controller: Ibexa\Rest\Server\Controller\User\UserListController:loadUsers methods: [GET] ibexa.rest.load_user: path: /user/users/{userId} defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUser + _controller: Ibexa\Rest\Server\Controller\User\UserLoadByIdController:loadUser methods: [GET] requirements: userId: \d+ ibexa.rest.current_user: path: /user/current - controller: Ibexa\Rest\Server\Controller\User::redirectToCurrentUser + controller: Ibexa\Rest\Server\Controller\User\UserRedirectToCurrentUserController::redirectToCurrentUser methods: [GET] ibexa.rest.update_user: path: /user/users/{userId} defaults: - _controller: Ibexa\Rest\Server\Controller\User:updateUser + _controller: Ibexa\Rest\Server\Controller\User\UserUpdateController:updateUser methods: [PATCH] requirements: userId: \d+ @@ -1084,7 +1097,7 @@ ibexa.rest.update_user: ibexa.rest.delete_user: path: /user/users/{userId} defaults: - _controller: Ibexa\Rest\Server\Controller\User:deleteUser + _controller: Ibexa\Rest\Server\Controller\User\UserDeleteController:deleteUser methods: [DELETE] requirements: userId: \d+ @@ -1092,7 +1105,7 @@ ibexa.rest.delete_user: ibexa.rest.load_user_groups_of_user: path: /user/users/{userId}/groups defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUserGroupsOfUser + _controller: Ibexa\Rest\Server\Controller\User\UserGroupsOfUserListController:loadUserGroupsOfUser methods: [GET] requirements: userId: \d+ @@ -1100,7 +1113,7 @@ ibexa.rest.load_user_groups_of_user: ibexa.rest.assign_user_to_user_group: path: /user/users/{userId}/groups defaults: - _controller: Ibexa\Rest\Server\Controller\User:assignUserToUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserAssignToUserGroupController:assignUserToUserGroup methods: [POST] requirements: userId: \d+ @@ -1108,7 +1121,7 @@ ibexa.rest.assign_user_to_user_group: ibexa.rest.unassign_user_from_user_group: path: /user/users/{userId}/groups/{groupPath} defaults: - _controller: Ibexa\Rest\Server\Controller\User:unassignUserFromUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserUnassignFromUserGroupController:unassignUserFromUserGroup methods: [DELETE] requirements: userId: \d+ @@ -1117,7 +1130,7 @@ ibexa.rest.unassign_user_from_user_group: ibexa.rest.load_user_drafts: path: /user/users/{userId}/drafts defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUserDrafts + _controller: Ibexa\Rest\Server\Controller\User\UserDraftListController:loadUserDrafts methods: [GET] requirements: userId: \d+ @@ -1125,7 +1138,7 @@ ibexa.rest.load_user_drafts: ibexa.rest.load_role_assignments_for_user: path: /user/users/{userId}/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRoleAssignmentsForUser + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserListController:loadRoleAssignmentsForUser methods: [GET] requirements: userId: \d+ @@ -1133,7 +1146,7 @@ ibexa.rest.load_role_assignments_for_user: ibexa.rest.assign_role_to_user: path: /user/users/{userId}/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:assignRoleToUser + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserController:assignRoleToUser methods: [POST] requirements: userId: \d+ @@ -1141,7 +1154,7 @@ ibexa.rest.assign_role_to_user: ibexa.rest.load_role_assignment_for_user: path: /user/users/{userId}/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRoleAssignmentForUser + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserLoadByIdController:loadRoleAssignmentForUser methods: [GET] requirements: userId: \d+ @@ -1150,7 +1163,7 @@ ibexa.rest.load_role_assignment_for_user: ibexa.rest.unassign_role_from_user: path: /user/users/{userId}/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:unassignRoleFromUser + _controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserController:unassignRoleFromUser methods: [DELETE] requirements: userId: \d+ @@ -1159,25 +1172,25 @@ ibexa.rest.unassign_role_from_user: ibexa.rest.load_user_groups: path: /user/groups defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUserGroups + _controller: Ibexa\Rest\Server\Controller\User\UserGroupListController:loadUserGroups methods: [GET] ibexa.rest.load_root_user_group: path: /user/groups/root defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadRootUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupOfRootLoadController:loadRootUserGroup methods: [GET] ibexa.rest.create_root_user_group: path: /user/groups/subgroups defaults: - _controller: Ibexa\Rest\Server\Controller\User:createUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController:createUserGroup methods: [POST] ibexa.rest.load_user_group: path: /user/groups/{groupPath} defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupLoadByPathController:loadUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1185,7 +1198,7 @@ ibexa.rest.load_user_group: ibexa.rest.update_user_group: path: /user/groups/{groupPath} defaults: - _controller: Ibexa\Rest\Server\Controller\User:updateUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupUpdateController:updateUserGroup methods: [PATCH] requirements: groupPath: "[0-9/]+" @@ -1193,7 +1206,7 @@ ibexa.rest.update_user_group: ibexa.rest.delete_user_group: path: /user/groups/{groupPath} defaults: - _controller: Ibexa\Rest\Server\Controller\User:deleteUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupDeleteController:deleteUserGroup methods: [DELETE] requirements: groupPath: "[0-9/]+" @@ -1201,7 +1214,7 @@ ibexa.rest.delete_user_group: ibexa.rest.move_user_group: path: /user/groups/{groupPath} defaults: - _controller: Ibexa\Rest\Server\Controller\User:moveUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupMoveController:moveUserGroup methods: [MOVE] requirements: groupPath: "[0-9/]+" @@ -1209,7 +1222,7 @@ ibexa.rest.move_user_group: ibexa.rest.load_sub_user_groups: path: /user/groups/{groupPath}/subgroups defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadSubUserGroups + _controller: Ibexa\Rest\Server\Controller\User\UserSubGroupListController:loadSubUserGroups methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1217,7 +1230,7 @@ ibexa.rest.load_sub_user_groups: ibexa.rest.create_user_group: path: /user/groups/{groupPath}/subgroups defaults: - _controller: Ibexa\Rest\Server\Controller\User:createUserGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController:createUserGroup methods: [POST] requirements: groupPath: "[0-9/]+" @@ -1225,7 +1238,7 @@ ibexa.rest.create_user_group: ibexa.rest.load_users_from_group: path: /user/groups/{groupPath}/users defaults: - _controller: Ibexa\Rest\Server\Controller\User:loadUsersFromGroup + _controller: Ibexa\Rest\Server\Controller\User\UserGroupUsersListController:loadUsersFromGroup methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1233,7 +1246,7 @@ ibexa.rest.load_users_from_group: ibexa.rest.create_user: path: /user/groups/{groupPath}/users defaults: - _controller: Ibexa\Rest\Server\Controller\User:createUser + _controller: Ibexa\Rest\Server\Controller\User\UserCreateController:createUser methods: [POST] requirements: groupPath: "[0-9/]+" @@ -1241,7 +1254,7 @@ ibexa.rest.create_user: ibexa.rest.load_role_assignments_for_user_group: path: /user/groups/{groupPath}/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRoleAssignmentsForUserGroup + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupListController:loadRoleAssignmentsForUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1249,7 +1262,7 @@ ibexa.rest.load_role_assignments_for_user_group: ibexa.rest.assign_role_to_user_group: path: /user/groups/{groupPath}/roles defaults: - _controller: Ibexa\Rest\Server\Controller\Role:assignRoleToUserGroup + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserGroupController:assignRoleToUserGroup methods: [POST] requirements: groupPath: "[0-9/]+" @@ -1257,7 +1270,7 @@ ibexa.rest.assign_role_to_user_group: ibexa.rest.load_role_assignment_for_user_group: path: /user/groups/{groupPath}/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:loadRoleAssignmentForUserGroup + _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupLoadByIdController:loadRoleAssignmentForUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1266,7 +1279,7 @@ ibexa.rest.load_role_assignment_for_user_group: ibexa.rest.unassign_role_from_user_group: path: /user/groups/{groupPath}/roles/{roleId} defaults: - _controller: Ibexa\Rest\Server\Controller\Role:unassignRoleFromUserGroup + _controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserGroupController:unassignRoleFromUserGroup methods: [DELETE] requirements: groupPath: "[0-9/]+" @@ -1275,13 +1288,13 @@ ibexa.rest.unassign_role_from_user_group: ibexa.rest.create_session: path: /user/sessions defaults: - _controller: Ibexa\Rest\Server\Controller\SessionController:createSessionAction + _controller: Ibexa\Rest\Server\Controller\Session\SessionCreateController:createSessionAction csrf_protection: false methods: [POST] ibexa.rest.check_session: path: /user/sessions/current - controller: Ibexa\Rest\Server\Controller\SessionController::checkSessionAction + controller: Ibexa\Rest\Server\Controller\Session\SessionCheckController::checkSessionAction methods: [GET] defaults: csrf_protection: false @@ -1289,7 +1302,7 @@ ibexa.rest.check_session: ibexa.rest.delete_session: path: /user/sessions/{sessionId} defaults: - _controller: Ibexa\Rest\Server\Controller\SessionController:deleteSessionAction + _controller: Ibexa\Rest\Server\Controller\Session\SessionDeleteController:deleteSessionAction csrf_protection: false methods: [DELETE] @@ -1299,13 +1312,13 @@ ibexa.rest.delete_session: ibexa.rest.list_global_url_aliases: path: /content/urlaliases defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias:listGlobalURLAliases + _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListGlobalController:listGlobalURLAliases methods: [GET] ibexa.rest.list_location_url_aliases: path: /content/locations/{locationPath}/urlaliases defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias:listLocationURLAliases + _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListLocationController:listLocationURLAliases methods: [GET] requirements: locationPath: "[0-9/]+" @@ -1313,19 +1326,19 @@ ibexa.rest.list_location_url_aliases: ibexa.rest.create_url_alias: path: /content/urlaliases defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias:createURLAlias + _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasCreateController:createURLAlias methods: [POST] ibexa.rest.load_url_alias: path: /content/urlaliases/{urlAliasId} defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias:loadURLAlias + _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasLoadByIdController:loadURLAlias methods: [GET] ibexa.rest.delete_url_alias: path: /content/urlaliases/{urlAliasId} defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias:deleteURLAlias + _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasDeleteController:deleteURLAlias methods: [DELETE] @@ -1343,7 +1356,7 @@ ibexa.rest.load_country_list: ibexa.rest.create_bookmark: path: /bookmark/{locationId} defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark:createBookmark + _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkCreateController:createBookmark methods: [POST] requirements: locationId: "[0-9]+" @@ -1351,7 +1364,7 @@ ibexa.rest.create_bookmark: ibexa.rest.delete_bookmark: path: /bookmark/{locationId} defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark:deleteBookmark + _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkDeleteController:deleteBookmark methods: [DELETE] requirements: locationId: "[0-9]+" @@ -1359,7 +1372,7 @@ ibexa.rest.delete_bookmark: ibexa.rest.is_bookmarked: path: /bookmark/{locationId} defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark:isBookmarked + _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkIsBookmarkedController:isBookmarked methods: [GET, HEAD] requirements: locationId: "[0-9]+" @@ -1367,7 +1380,7 @@ ibexa.rest.is_bookmarked: ibexa.rest.load_bookmarks: path: /bookmark defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark:loadBookmarks + _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkListController:loadBookmarks methods: [GET] # JWT diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index ca2f412e6..aa7f47c2c 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -85,11 +85,12 @@ services: arguments: ['@Ibexa\Rest\Server\Service\ExpressionRouterRootResourceBuilder'] tags: [controller.service_arguments] - Ibexa\Rest\Server\Controller\Section: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.section' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Section\: + resource: '../../../lib/Server/Controller/Section' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments Ibexa\Rest\Server\Controller\BinaryContent: parent: Ibexa\Rest\Server\Controller @@ -98,76 +99,75 @@ services: - '@ibexa.config.resolver' tags: [controller.service_arguments] - Ibexa\Rest\Server\Controller\Content: - parent: Ibexa\Rest\Server\Controller - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Content\: + resource: '../../../lib/Server/Controller/Content' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\ContentType: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.content_type' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\ContentType\: + resource: '../../../lib/Server/Controller/ContentType' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\Role: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.role' - - '@ibexa.api.service.user' - - '@ibexa.api.service.location' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Role\: + resource: '../../../lib/Server/Controller/Role' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\Language: + Ibexa\Rest\Server\Controller\Language\: + resource: '../../../lib/Server/Controller/Language' autowire: true - tags: [controller.service_arguments] + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\Location: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.location' - - '@ibexa.api.service.content' - - '@ibexa.api.service.trash' - - '@ibexa.api.service.url_alias' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Location\: + resource: '../../../lib/Server/Controller/Location' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\ObjectState: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.object_state' - - '@ibexa.api.service.content' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\ObjectState\: + resource: '../../../lib/Server/Controller/ObjectState' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\Trash: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.trash' - - '@ibexa.api.service.location' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Trash\: + resource: '../../../lib/Server/Controller/Trash' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\User: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.user' - - '@ibexa.api.service.role' - - '@ibexa.api.service.content' - - '@ibexa.api.service.content_type' - - '@ibexa.api.service.location' - - '@ibexa.api.service.section' - - '@ibexa.api.repository' - - '@Ibexa\Contracts\Core\Repository\PermissionResolver' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\User\: + resource: '../../../lib/Server/Controller/User' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\URLWildcard: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.url_wildcard' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\URLWildcard\: + resource: '../../../lib/Server/Controller/URLWildcard' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments - Ibexa\Rest\Server\Controller\URLAlias: - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.url_alias' - - '@ibexa.api.service.location' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\URLAlias\: + resource: '../../../lib/Server/Controller/URLAlias' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments Ibexa\Rest\Server\Controller\Views: parent: Ibexa\Rest\Server\Controller @@ -175,9 +175,10 @@ services: - '@ibexa.api.service.search' tags: [controller.service_arguments] - Ibexa\Rest\Server\Controller\SessionController: - class: Ibexa\Rest\Server\Controller\SessionController - parent: Ibexa\Rest\Server\Controller + Ibexa\Rest\Server\Controller\Session\: + resource: '../../../lib/Server/Controller/Session' + autowire: true + autoconfigure: true arguments: $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' $userService: '@Ibexa\Contracts\Core\Repository\UserService' @@ -187,13 +188,12 @@ services: $configResolver: '@Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface' tags: [controller.service_arguments] - Ibexa\Rest\Server\Controller\Bookmark: - class: Ibexa\Rest\Server\Controller\Bookmark - parent: Ibexa\Rest\Server\Controller - arguments: - - '@ibexa.api.service.bookmark' - - '@ibexa.api.service.location' - tags: [controller.service_arguments] + Ibexa\Rest\Server\Controller\Bookmark\: + resource: '../../../lib/Server/Controller/Bookmark' + autowire: true + autoconfigure: true + tags: + - controller.service_arguments Ibexa\Rest\Server\Controller\JWT: autowire: true diff --git a/src/lib/ApiPlatform/SchemasCollection.php b/src/lib/ApiPlatform/SchemasCollection.php new file mode 100644 index 000000000..9eba9677c --- /dev/null +++ b/src/lib/ApiPlatform/SchemasCollection.php @@ -0,0 +1,32 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\ApiPlatform; + +final class SchemasCollection implements \IteratorAggregate, \Countable +{ + /** + * @param array<string, mixed> $schemas + */ + public function __construct(private readonly array $schemas = []) + { + } + + /** + * @return \Traversable<string> + */ + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->schemas); + } + + public function count(): int + { + return \count($this->schemas); + } +} diff --git a/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php b/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php new file mode 100644 index 000000000..0acc62a1a --- /dev/null +++ b/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php @@ -0,0 +1,14 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\ApiPlatform; + +interface SchemasCollectionFactoryInterface +{ + public function create(): mixed; +} diff --git a/src/lib/ApiPlatform/SchemasProvider.php b/src/lib/ApiPlatform/SchemasProvider.php new file mode 100644 index 000000000..c17e023c7 --- /dev/null +++ b/src/lib/ApiPlatform/SchemasProvider.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\ApiPlatform; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Yaml\Yaml; + +final class SchemasProvider implements SchemasProviderInterface +{ + /** + * @param array<string> $files + */ + public function __construct( + private readonly KernelInterface $kernel, + private readonly array $files, + ) { + } + + public function getSchemas(): array + { + $allSchemas = []; + + foreach ($this->files as $fileName) { + $filePath = $this->kernel->locateResource($fileName); + $schemas = Yaml::parseFile($filePath); + + if (isset($schemas['schemas'])) { + $allSchemas = array_merge($allSchemas, $schemas['schemas']); + } + } + + return $allSchemas; + } +} diff --git a/src/lib/ApiPlatform/SchemasProviderInterface.php b/src/lib/ApiPlatform/SchemasProviderInterface.php new file mode 100644 index 000000000..b58bb8aeb --- /dev/null +++ b/src/lib/ApiPlatform/SchemasProviderInterface.php @@ -0,0 +1,17 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\ApiPlatform; + +interface SchemasProviderInterface +{ + /** + * @return array<string, mixed> + */ + public function getSchemas(): array; +} diff --git a/src/lib/Server/Controller/Bookmark.php b/src/lib/Server/Controller/Bookmark.php deleted file mode 100644 index 377067a3e..000000000 --- a/src/lib/Server/Controller/Bookmark.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\BookmarkService; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Contracts\Rest\Exceptions; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Value as RestValue; -use Symfony\Component\HttpFoundation\Request; - -class Bookmark extends RestController -{ - /** - * @var \Ibexa\Contracts\Core\Repository\BookmarkService - */ - protected $bookmarkService; - - /** - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Bookmark constructor. - * - * @param \Ibexa\Contracts\Core\Repository\BookmarkService $bookmarkService - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - */ - public function __construct(BookmarkService $bookmarkService, LocationService $locationService) - { - $this->bookmarkService = $bookmarkService; - $this->locationService = $locationService; - } - - /** - * Add given location to bookmarks. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @param int $locationId - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * - * @return \Ibexa\Rest\Value - */ - public function createBookmark(Request $request, int $locationId): RestValue - { - $location = $this->locationService->loadLocation($locationId); - - try { - $this->bookmarkService->createBookmark($location); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.is_bookmarked', - [ - 'locationId' => $locationId, - ] - ) - ); - } catch (InvalidArgumentException $e) { - return new Values\Conflict(); - } - } - - /** - * Deletes a given bookmark. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @param int $locationId - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * - * @return \Ibexa\Rest\Value - */ - public function deleteBookmark(Request $request, int $locationId): RestValue - { - $location = $this->locationService->loadLocation($locationId); - - try { - $this->bookmarkService->deleteBookmark($location); - - return new Values\NoContent(); - } catch (InvalidArgumentException $e) { - throw new Exceptions\NotFoundException("Location {$locationId} is not bookmarked"); - } - } - - /** - * Checks if given location is bookmarked. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @param int $locationId - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * - * @return \Ibexa\Rest\Server\Values\OK - */ - public function isBookmarked(Request $request, int $locationId): Values\OK - { - $location = $this->locationService->loadLocation($locationId); - - if (!$this->bookmarkService->isBookmarked($location)) { - throw new Exceptions\NotFoundException("Location {$locationId} is not bookmarked"); - } - - return new Values\OK(); - } - - /** - * List bookmarked locations. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - * - * @return \Ibexa\Rest\Value - */ - public function loadBookmarks(Request $request): RestValue - { - $offset = $request->query->getInt('offset', 0); - $limit = $request->query->getInt('limit', 25); - - $restLocations = []; - $bookmarks = $this->bookmarkService->loadBookmarks($offset, $limit); - foreach ($bookmarks as $bookmark) { - $restLocations[] = new Values\RestLocation( - $bookmark, - $this->locationService->getLocationChildCount($bookmark) - ); - } - - return new Values\BookmarkList($bookmarks->totalCount, $restLocations); - } - - /** - * Extracts and returns an item id from a path, e.g. /1/2/58 => 58. - * - * @param string $path - * - * @return mixed - */ - private function extractLocationIdFromPath(string $path) - { - $pathParts = explode('/', $path); - - return array_pop($pathParts); - } -} diff --git a/src/lib/Server/Controller/Bookmark/BookmarkCreateController.php b/src/lib/Server/Controller/Bookmark/BookmarkCreateController.php new file mode 100644 index 000000000..ff31d560f --- /dev/null +++ b/src/lib/Server/Controller/Bookmark/BookmarkCreateController.php @@ -0,0 +1,113 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Bookmark; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\BookmarkService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/bookmark/{locationId}', + name: 'Create bookmark', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Add given Location to bookmarks of the current user.', + tags: [ + 'Bookmark', + ], + parameters: [ + new Model\Parameter( + name: 'locationId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Created.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to given Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the given Location does not exist.', + ], + Response::HTTP_CONFLICT => [ + 'description' => 'Error - Location is already bookmarked.', + ], + ], + ), +)] +class BookmarkCreateController extends RestController +{ + /** + * @var \Ibexa\Contracts\Core\Repository\BookmarkService + */ + protected $bookmarkService; + + /** + * @var \Ibexa\Contracts\Core\Repository\LocationService + */ + protected $locationService; + + /** + * Bookmark constructor. + * + * @param \Ibexa\Contracts\Core\Repository\BookmarkService $bookmarkService + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + */ + public function __construct(BookmarkService $bookmarkService, LocationService $locationService) + { + $this->bookmarkService = $bookmarkService; + $this->locationService = $locationService; + } + + /** + * Add given location to bookmarks. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param int $locationId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return \Ibexa\Rest\Value + */ + public function createBookmark(Request $request, int $locationId): RestValue + { + $location = $this->locationService->loadLocation($locationId); + + try { + $this->bookmarkService->createBookmark($location); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.is_bookmarked', + [ + 'locationId' => $locationId, + ] + ) + ); + } catch (InvalidArgumentException $e) { + return new Values\Conflict(); + } + } +} diff --git a/src/lib/Server/Controller/Bookmark/BookmarkDeleteController.php b/src/lib/Server/Controller/Bookmark/BookmarkDeleteController.php new file mode 100644 index 000000000..527c8a0d6 --- /dev/null +++ b/src/lib/Server/Controller/Bookmark/BookmarkDeleteController.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Bookmark; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\BookmarkService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/bookmark/{locationId}', + name: 'Delete bookmark', + openapi: new Model\Operation( + summary: 'Deletes the given Location from bookmarks of the current user.', + tags: [ + 'Bookmark', + ], + parameters: [ + new Model\Parameter( + name: 'locationId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'Deleted - no content.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized for the given Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the given Location does not exist / is not bookmarked.', + ], + ], + ), +)] +class BookmarkDeleteController extends RestController +{ + /** + * @var \Ibexa\Contracts\Core\Repository\BookmarkService + */ + protected $bookmarkService; + + /** + * @var \Ibexa\Contracts\Core\Repository\LocationService + */ + protected $locationService; + + /** + * Bookmark constructor. + * + * @param \Ibexa\Contracts\Core\Repository\BookmarkService $bookmarkService + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + */ + public function __construct(BookmarkService $bookmarkService, LocationService $locationService) + { + $this->bookmarkService = $bookmarkService; + $this->locationService = $locationService; + } + + /** + * Deletes a given bookmark. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param int $locationId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return \Ibexa\Rest\Value + */ + public function deleteBookmark(Request $request, int $locationId): RestValue + { + $location = $this->locationService->loadLocation($locationId); + + try { + $this->bookmarkService->deleteBookmark($location); + + return new Values\NoContent(); + } catch (InvalidArgumentException $e) { + throw new Exceptions\NotFoundException("Location {$locationId} is not bookmarked"); + } + } +} diff --git a/src/lib/Server/Controller/Bookmark/BookmarkIsBookmarkedController.php b/src/lib/Server/Controller/Bookmark/BookmarkIsBookmarkedController.php new file mode 100644 index 000000000..8414073ec --- /dev/null +++ b/src/lib/Server/Controller/Bookmark/BookmarkIsBookmarkedController.php @@ -0,0 +1,97 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Bookmark; + +use ApiPlatform\OpenApi\Model; +use Ibexa\Bundle\Rest\ApiPlatform\Head; +use Ibexa\Contracts\Core\Repository\BookmarkService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Head( + uriTemplate: '/bookmark/{locationId}', + name: 'Check if Location is bookmarked', + openapi: new Model\Operation( + summary: 'Checks if the given Location is bookmarked by the current user.', + tags: [ + 'Bookmark', + ], + parameters: [ + new Model\Parameter( + name: 'locationId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - bookmarked.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized for the given Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the given Location does not exist / is not bookmarked.', + ], + ], + ), +)] +class BookmarkIsBookmarkedController extends RestController +{ + /** + * @var \Ibexa\Contracts\Core\Repository\BookmarkService + */ + protected $bookmarkService; + + /** + * @var \Ibexa\Contracts\Core\Repository\LocationService + */ + protected $locationService; + + /** + * Bookmark constructor. + * + * @param \Ibexa\Contracts\Core\Repository\BookmarkService $bookmarkService + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + */ + public function __construct(BookmarkService $bookmarkService, LocationService $locationService) + { + $this->bookmarkService = $bookmarkService; + $this->locationService = $locationService; + } + + /** + * Checks if given location is bookmarked. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param int $locationId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return \Ibexa\Rest\Server\Values\OK + */ + public function isBookmarked(Request $request, int $locationId): Values\OK + { + $location = $this->locationService->loadLocation($locationId); + + if (!$this->bookmarkService->isBookmarked($location)) { + throw new Exceptions\NotFoundException("Location {$locationId} is not bookmarked"); + } + + return new Values\OK(); + } +} diff --git a/src/lib/Server/Controller/Bookmark/BookmarkListController.php b/src/lib/Server/Controller/Bookmark/BookmarkListController.php new file mode 100644 index 000000000..5f916ffc0 --- /dev/null +++ b/src/lib/Server/Controller/Bookmark/BookmarkListController.php @@ -0,0 +1,112 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Bookmark; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\BookmarkService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/bookmark', + name: 'List of bookmarks', + openapi: new Model\Operation( + summary: 'Lists bookmarked Locations for the current user.', + tags: [ + 'Bookmark', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.BookmarkList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/BookmarkList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.xml.example', + ], + 'application/vnd.ibexa.api.BookmarkList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/BookmarkListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/bookmark/GET/BookmarkList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to list bookmarks.', + ], + ], + ), +)] +class BookmarkListController extends RestController +{ + /** + * @var \Ibexa\Contracts\Core\Repository\BookmarkService + */ + protected $bookmarkService; + + /** + * @var \Ibexa\Contracts\Core\Repository\LocationService + */ + protected $locationService; + + /** + * Bookmark constructor. + * + * @param \Ibexa\Contracts\Core\Repository\BookmarkService $bookmarkService + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + */ + public function __construct(BookmarkService $bookmarkService, LocationService $locationService) + { + $this->bookmarkService = $bookmarkService; + $this->locationService = $locationService; + } + + /** + * List bookmarked locations. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return \Ibexa\Rest\Value + */ + public function loadBookmarks(Request $request): RestValue + { + $offset = $request->query->getInt('offset', 0); + $limit = $request->query->getInt('limit', 25); + + $restLocations = []; + $bookmarks = $this->bookmarkService->loadBookmarks($offset, $limit); + foreach ($bookmarks as $bookmark) { + $restLocations[] = new Values\RestLocation( + $bookmark, + $this->locationService->getLocationChildCount($bookmark) + ); + } + + return new Values\BookmarkList($bookmarks->totalCount, $restLocations); + } +} diff --git a/src/lib/Server/Controller/Content.php b/src/lib/Server/Controller/Content.php deleted file mode 100644 index 2b7e680b7..000000000 --- a/src/lib/Server/Controller/Content.php +++ /dev/null @@ -1,888 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; -use Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException; -use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; -use Ibexa\Contracts\Core\Repository\Values\Content\Language; -use Ibexa\Contracts\Core\Repository\Values\Content\Relation; -use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; -use Ibexa\Contracts\Rest\Exceptions; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\BadRequestException; -use Ibexa\Rest\Server\Exceptions\ContentFieldValidationException as RESTContentFieldValidationException; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Server\Values\RestContentCreateStruct; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\HttpKernelInterface; - -/** - * Content controller. - */ -class Content extends RestController -{ - /** - * Loads a content info by remote ID. - * - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - * - * @return \Ibexa\Rest\Server\Values\TemporaryRedirect - */ - public function redirectContent(Request $request) - { - if (!$request->query->has('remoteId')) { - throw new BadRequestException("'remoteId' parameter is required."); - } - - $contentInfo = $this->repository->getContentService()->loadContentInfoByRemoteId( - $request->query->get('remoteId') - ); - - return new Values\TemporaryRedirect( - $this->router->generate( - 'ibexa.rest.load_content', - [ - 'contentId' => $contentInfo->id, - ] - ) - ); - } - - /** - * Loads a content info, potentially with the current version embedded. - * - * @param mixed $contentId - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return \Ibexa\Rest\Server\Values\RestContent - */ - public function loadContent($contentId, Request $request) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - $mainLocation = null; - if (!empty($contentInfo->mainLocationId)) { - $mainLocation = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - - $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); - - $contentVersion = null; - $relations = null; - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.content') { - $languages = Language::ALL; - if ($request->query->has('languages')) { - $languages = explode(',', $request->query->get('languages')); - } - - $contentVersion = $this->repository->getContentService()->loadContent($contentId, $languages); - $relations = $this->repository->getContentService()->loadRelations($contentVersion->getVersionInfo()); - } - - $restContent = new Values\RestContent( - $contentInfo, - $mainLocation, - $contentVersion, - $contentType, - $relations, - $request->getPathInfo() - ); - - if ($contentInfo->mainLocationId === null) { - return $restContent; - } - - return new Values\CachedValue( - $restContent, - ['locationId' => $contentInfo->mainLocationId] - ); - } - - /** - * Updates a content's metadata. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\RestContent - */ - public function updateContentMetadata($contentId, Request $request) - { - $updateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - // update section - if ($updateStruct->sectionId !== null) { - $section = $this->repository->getSectionService()->loadSection($updateStruct->sectionId); - $this->repository->getSectionService()->assignSection($contentInfo, $section); - $updateStruct->sectionId = null; - } - - // @todo Consider refactoring! ContentService::updateContentMetadata throws the same exception - // in case the updateStruct is empty and if remoteId already exists. Since REST version of update struct - // includes section ID in addition to other fields, we cannot throw exception if only sectionId property - // is set, so we must skip updating content in that case instead of allowing propagation of the exception. - foreach ($updateStruct as $propertyName => $propertyValue) { - if ($propertyName !== 'sectionId' && $propertyValue !== null) { - // update content - $this->repository->getContentService()->updateContentMetadata($contentInfo, $updateStruct); - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - break; - } - } - - try { - $locationInfo = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } catch (NotFoundException $e) { - $locationInfo = null; - } - - return new Values\RestContent( - $contentInfo, - $locationInfo - ); - } - - /** - * Loads a specific version of a given content object. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\TemporaryRedirect - */ - public function redirectCurrentVersion($contentId) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - return new Values\TemporaryRedirect( - $this->router->generate( - 'ibexa.rest.load_content_in_version', - [ - 'contentId' => $contentId, - 'versionNumber' => $contentInfo->currentVersionNo, - ] - ) - ); - } - - /** - * Loads a specific version of a given content object. - * - * @param mixed $contentId - * @param int $versionNumber - * - * @return \Ibexa\Rest\Server\Values\Version - */ - public function loadContentInVersion($contentId, $versionNumber, Request $request) - { - $languages = Language::ALL; - if ($request->query->has('languages')) { - $languages = explode(',', $request->query->get('languages')); - } - - $content = $this->repository->getContentService()->loadContent( - $contentId, - $languages, - $versionNumber - ); - $contentType = $this->repository->getContentTypeService()->loadContentType( - $content->getVersionInfo()->getContentInfo()->contentTypeId - ); - - $versionValue = new Values\Version( - $content, - $contentType, - $this->repository->getContentService()->loadRelations($content->getVersionInfo()), - $request->getPathInfo() - ); - - if ($content->contentInfo->mainLocationId === null || $content->versionInfo->status === VersionInfo::STATUS_DRAFT) { - return $versionValue; - } - - return new Values\CachedValue( - $versionValue, - ['locationId' => $content->contentInfo->mainLocationId] - ); - } - - /** - * Creates a new content draft assigned to the authenticated user. - * If a different userId is given in the input it is assigned to the - * given user but this required special rights for the authenticated - * user (this is useful for content staging where the transfer process - * does not have to authenticate with the user which created the content - * object in the source server). The user has to publish the content if - * it should be visible. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return \Ibexa\Rest\Server\Values\CreatedContent - */ - public function createContent(Request $request) - { - $contentCreate = $this->parseContentRequest($request); - - return $this->doCreateContent($request, $contentCreate); - } - - /** - * The content is deleted. If the content has locations (which is required in 4.x) - * on delete all locations assigned the content object are deleted via delete subtree. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteContent($contentId) - { - $this->repository->getContentService()->deleteContent( - $this->repository->getContentService()->loadContentInfo($contentId) - ); - - return new Values\NoContent(); - } - - /** - * Creates a new content object as copy under the given parent location given in the destination header. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\ResourceCreated - */ - public function copyContent($contentId, Request $request) - { - $destination = $request->headers->get('Destination'); - - $parentLocationParts = explode('/', $destination); - $copiedContent = $this->repository->getContentService()->copyContent( - $this->repository->getContentService()->loadContentInfo($contentId), - $this->repository->getLocationService()->newLocationCreateStruct(array_pop($parentLocationParts)) - ); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_content', - ['contentId' => $copiedContent->id] - ) - ); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function copy(int $contentId, Request $request): Values\ResourceCreated - { - $contentService = $this->repository->getContentService(); - $locationService = $this->repository->getLocationService(); - - $contentInfo = $contentService->loadContentInfo($contentId); - - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $destinationLocation */ - $destinationLocation = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent(), - ), - ); - - $copiedContent = $contentService->copyContent( - $contentInfo, - $locationService->newLocationCreateStruct($destinationLocation->getId()), - ); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_content', - ['contentId' => $copiedContent->id], - ) - ); - } - - /** - * Deletes a translation from all the Versions of the given Content Object. - * - * If any non-published Version contains only the Translation to be deleted, that entire Version will be deleted - * - * @param int $contentId - * @param string $languageCode - * - * @return \Ibexa\Rest\Server\Values\NoContent - * - * @throws \Exception - */ - public function deleteContentTranslation($contentId, $languageCode) - { - $contentService = $this->repository->getContentService(); - - $this->repository->beginTransaction(); - try { - $contentInfo = $contentService->loadContentInfo($contentId); - $contentService->deleteTranslation( - $contentInfo, - $languageCode - ); - - $this->repository->commit(); - - return new Values\NoContent(); - } catch (\Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Returns a list of all versions of the content. This method does not - * include fields and relations in the Version elements of the response. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\VersionList - */ - public function loadContentVersions($contentId, Request $request) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - return new Values\VersionList( - $this->repository->getContentService()->loadVersions($contentInfo), - $request->getPathInfo() - ); - } - - /** - * The version is deleted. - * - * @param mixed $contentId - * @param mixed $versionNumber - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteContentVersion($contentId, $versionNumber) - { - $versionInfo = $this->repository->getContentService()->loadVersionInfo( - $this->repository->getContentService()->loadContentInfo($contentId), - $versionNumber - ); - - if ($versionInfo->isPublished()) { - throw new ForbiddenException('Versions with PUBLISHED status cannot be deleted'); - } - - $this->repository->getContentService()->deleteVersion( - $versionInfo - ); - - return new Values\NoContent(); - } - - /** - * Remove the given Translation from the given Version Draft. - * - * @param int $contentId - * @param int $versionNumber - * @param string $languageCode - * - * @return \Ibexa\Rest\Server\Values\NoContent - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - */ - public function deleteTranslationFromDraft($contentId, $versionNumber, $languageCode) - { - $contentService = $this->repository->getContentService(); - $versionInfo = $contentService->loadVersionInfoById($contentId, $versionNumber); - - if (!$versionInfo->isDraft()) { - throw new ForbiddenException('Translation can be deleted from a DRAFT version only'); - } - - $contentService->deleteTranslationFromDraft($versionInfo, $languageCode); - - return new Values\NoContent(); - } - - /** - * The system creates a new draft version as a copy from the given version. - * - * @param mixed $contentId - * @param mixed $versionNumber - * - * @return \Ibexa\Rest\Server\Values\CreatedVersion - */ - public function createDraftFromVersion($contentId, $versionNumber) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); - $contentDraft = $this->repository->getContentService()->createContentDraft( - $contentInfo, - $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) - ); - - return new Values\CreatedVersion( - [ - 'version' => new Values\Version( - $contentDraft, - $contentType, - $this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) - ), - ] - ); - } - - /** - * The system creates a new draft version as a copy from the current version. - * - * @param mixed $contentId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if the current version is already a draft - * - * @return \Ibexa\Rest\Server\Values\CreatedVersion - */ - public function createDraftFromCurrentVersion($contentId) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); - $versionInfo = $this->repository->getContentService()->loadVersionInfo( - $contentInfo - ); - - if ($versionInfo->isDraft()) { - throw new ForbiddenException('Current version already has DRAFT status'); - } - - $contentDraft = $this->repository->getContentService()->createContentDraft($contentInfo); - - return new Values\CreatedVersion( - [ - 'version' => new Values\Version( - $contentDraft, - $contentType, - $this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) - ), - ] - ); - } - - /** - * A specific draft is updated. - * - * @param mixed $contentId - * @param mixed $versionNumber - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - * - * @return \Ibexa\Rest\Server\Values\Version - */ - public function updateVersion($contentId, $versionNumber, Request $request) - { - $contentUpdateStruct = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - 'Url' => $this->router->generate( - 'ibexa.rest.update_version', - [ - 'contentId' => $contentId, - 'versionNumber' => $versionNumber, - ] - ), - ], - $request->getContent() - ) - ); - - $versionInfo = $this->repository->getContentService()->loadVersionInfo( - $this->repository->getContentService()->loadContentInfo($contentId), - $versionNumber - ); - - if (!$versionInfo->isDraft()) { - throw new ForbiddenException('Only versions with DRAFT status can be updated'); - } - - try { - $this->repository->getContentService()->updateContent($versionInfo, $contentUpdateStruct); - } catch (ContentValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (ContentFieldValidationException $e) { - throw new RESTContentFieldValidationException($e); - } - - $languages = null; - if ($request->query->has('languages')) { - $languages = explode(',', $request->query->get('languages')); - } - - // Reload the content to handle languages GET parameter - $content = $this->repository->getContentService()->loadContent( - $contentId, - $languages, - $versionInfo->versionNo - ); - $contentType = $this->repository->getContentTypeService()->loadContentType( - $content->getVersionInfo()->getContentInfo()->contentTypeId - ); - - return new Values\Version( - $content, - $contentType, - $this->repository->getContentService()->loadRelations($content->getVersionInfo()), - $request->getPathInfo() - ); - } - - /** - * The content version is published. - * - * @param mixed $contentId - * @param mixed $versionNumber - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if version $versionNumber isn't a draft - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function publishVersion($contentId, $versionNumber) - { - $versionInfo = $this->repository->getContentService()->loadVersionInfo( - $this->repository->getContentService()->loadContentInfo($contentId), - $versionNumber - ); - - if (!$versionInfo->isDraft()) { - throw new ForbiddenException('Only versions with DRAFT status can be published'); - } - - $this->repository->getContentService()->publishVersion( - $versionInfo - ); - - return new Values\NoContent(); - } - - /** - * Redirects to the relations of the current version. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\TemporaryRedirect - */ - public function redirectCurrentVersionRelations($contentId) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - return new Values\TemporaryRedirect( - $this->router->generate( - 'ibexa.rest.redirect_current_version_relations', - [ - 'contentId' => $contentId, - 'versionNumber' => $contentInfo->currentVersionNo, - ] - ) - ); - } - - /** - * Loads the relations of the given version. - * - * @param mixed $contentId - * @param mixed $versionNumber - * - * @return \Ibexa\Rest\Server\Values\RelationList - */ - public function loadVersionRelations($contentId, $versionNumber, Request $request) - { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; - - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - $relationList = $this->repository->getContentService()->loadRelations( - $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) - ); - - $relationList = array_slice( - $relationList, - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : null - ); - - $relationListValue = new Values\RelationList( - $relationList, - $contentId, - $versionNumber, - $request->getPathInfo() - ); - - if ($contentInfo->mainLocationId === null) { - return $relationListValue; - } - - return new Values\CachedValue( - $relationListValue, - ['locationId' => $contentInfo->mainLocationId] - ); - } - - /** - * Loads a relation for the given content object and version. - * - * @param mixed $contentId - * @param int $versionNumber - * @param mixed $relationId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\RestRelation - */ - public function loadVersionRelation($contentId, $versionNumber, $relationId, Request $request) - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - $relationList = $this->repository->getContentService()->loadRelations( - $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) - ); - - foreach ($relationList as $relation) { - if ($relation->id == $relationId) { - $relation = new Values\RestRelation($relation, $contentId, $versionNumber); - - if ($contentInfo->mainLocationId === null) { - return $relation; - } - - return new Values\CachedValue( - $relation, - ['locationId' => $contentInfo->mainLocationId] - ); - } - } - - throw new Exceptions\NotFoundException("Relation not found: '{$request->getPathInfo()}'."); - } - - /** - * Deletes a relation of the given draft. - * - * @param mixed $contentId - * @param int $versionNumber - * @param mixed $relationId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function removeRelation($contentId, $versionNumber, $relationId, Request $request) - { - $versionInfo = $this->repository->getContentService()->loadVersionInfo( - $this->repository->getContentService()->loadContentInfo($contentId), - $versionNumber - ); - - $versionRelations = $this->repository->getContentService()->loadRelations($versionInfo); - foreach ($versionRelations as $relation) { - if ($relation->id == $relationId) { - if ($relation->type !== Relation::COMMON) { - throw new ForbiddenException('Relation is not of type COMMON'); - } - - if (!$versionInfo->isDraft()) { - throw new ForbiddenException('Relation of type COMMON can only be removed from drafts'); - } - - $this->repository->getContentService()->deleteRelation($versionInfo, $relation->getDestinationContentInfo()); - - return new Values\NoContent(); - } - } - - throw new Exceptions\NotFoundException("Could not find Relation '{$request->getPathInfo()}'."); - } - - /** - * Creates a new relation of type COMMON for the given draft. - * - * @param mixed $contentId - * @param int $versionNumber - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if version $versionNumber isn't a draft - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if a relation to the same content already exists - * - * @return \Ibexa\Rest\Server\Values\CreatedRelation - */ - public function createRelation($contentId, $versionNumber, Request $request) - { - $destinationContentId = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - $versionInfo = $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber); - if (!$versionInfo->isDraft()) { - throw new ForbiddenException('Relation of type COMMON can only be added to drafts'); - } - - try { - $destinationContentInfo = $this->repository->getContentService()->loadContentInfo($destinationContentId); - } catch (NotFoundException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - $existingRelations = $this->repository->getContentService()->loadRelations($versionInfo); - foreach ($existingRelations as $existingRelation) { - if ($existingRelation->getDestinationContentInfo()->id == $destinationContentId) { - throw new ForbiddenException('Relation of type COMMON to the selected destination content ID already exists'); - } - } - - $relation = $this->repository->getContentService()->addRelation($versionInfo, $destinationContentInfo); - - return new Values\CreatedRelation( - [ - 'relation' => new Values\RestRelation($relation, $contentId, $versionNumber), - ] - ); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function hideContent(int $contentId): Values\NoContent - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - $this->repository->getContentService()->hideContent($contentInfo); - - return new Values\NoContent(); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function revealContent(int $contentId): Values\NoContent - { - $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); - - $this->repository->getContentService()->revealContent($contentInfo); - - return new Values\NoContent(); - } - - /** - * Creates and executes a content view. - * - * @deprecated Since platform 1.0. Forwards the request to the new /views location, but returns a 301. - * - * @return \Ibexa\Rest\Server\Values\RestExecutedView - */ - public function createView() - { - $response = $this->forward('ezpublish_rest.controller.views:createView'); - - // Add 301 status code and location href - $response->setStatusCode(301); - $response->headers->set('Location', $this->router->generate('ibexa.rest.views.create')); - - return $response; - } - - /** - * @param string $controller - * - * @return \Symfony\Component\HttpFoundation\Response - */ - protected function forward($controller) - { - $path['_controller'] = $controller; - $subRequest = $this->container->get('request_stack')->getCurrentRequest()->duplicate(null, null, $path); - - return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - } - - /** - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return mixed - */ - protected function parseContentRequest(Request $request) - { - return $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type'), 'Url' => $request->getPathInfo()], - $request->getContent() - ) - ); - } - - /** - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Ibexa\Rest\Server\Values\RestContentCreateStruct $contentCreate - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * - * @return \Ibexa\Rest\Server\Values\CreatedContent - */ - protected function doCreateContent(Request $request, RestContentCreateStruct $contentCreate) - { - try { - $contentCreateStruct = $contentCreate->contentCreateStruct; - $contentCreate->locationCreateStruct->sortField = $contentCreateStruct->contentType->defaultSortField; - $contentCreate->locationCreateStruct->sortOrder = $contentCreateStruct->contentType->defaultSortOrder; - - $content = $this->repository->getContentService()->createContent( - $contentCreateStruct, - [$contentCreate->locationCreateStruct] - ); - } catch (ContentValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (ContentFieldValidationException $e) { - throw new RESTContentFieldValidationException($e); - } - - $contentValue = null; - $contentType = null; - $relations = null; - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.content') { - $contentValue = $content; - $contentType = $this->repository->getContentTypeService()->loadContentType( - $content->getVersionInfo()->getContentInfo()->contentTypeId - ); - $relations = $this->repository->getContentService()->loadRelations($contentValue->getVersionInfo()); - } - - return new Values\CreatedContent( - [ - 'content' => new Values\RestContent( - $content->contentInfo, - null, - $contentValue, - $contentType, - $relations - ), - ] - ); - } -} diff --git a/src/lib/Server/Controller/Content/ContentCopyController.php b/src/lib/Server/Controller/Content/ContentCopyController.php new file mode 100644 index 000000000..3b49eeed6 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentCopyController.php @@ -0,0 +1,73 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class ContentCopyController extends RestController +{ + /** + * Creates a new content object as copy under the given parent location given in the destination header. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\ResourceCreated + */ + public function copyContent($contentId, Request $request) + { + $destination = $request->headers->get('Destination'); + + $parentLocationParts = explode('/', $destination); + $copiedContent = $this->repository->getContentService()->copyContent( + $this->repository->getContentService()->loadContentInfo($contentId), + $this->repository->getLocationService()->newLocationCreateStruct(array_pop($parentLocationParts)) + ); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_content', + ['contentId' => $copiedContent->id] + ) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function copy(int $contentId, Request $request): Values\ResourceCreated + { + $contentService = $this->repository->getContentService(); + $locationService = $this->repository->getLocationService(); + + $contentInfo = $contentService->loadContentInfo($contentId); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $destinationLocation */ + $destinationLocation = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent(), + ), + ); + + $copiedContent = $contentService->copyContent( + $contentInfo, + $locationService->newLocationCreateStruct($destinationLocation->getId()), + ); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_content', + ['contentId' => $copiedContent->id], + ) + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentCreateController.php b/src/lib/Server/Controller/Content/ContentCreateController.php new file mode 100644 index 000000000..53a1fc6de --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentCreateController.php @@ -0,0 +1,191 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Exceptions\ContentFieldValidationException as RESTContentFieldValidationException; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Server\Values\RestContentCreateStruct; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects', + name: 'Create content item', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a draft assigned to the authenticated user. If a different user ID is given in the input, the draft is assigned to the given user but this action requires special permissions for the authenticated user (this is useful for content staging where the transfer process does not have to authenticate with the user who created the content item in the source server). The user needs to publish the content item if it should be visible.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'Content - If set, all information for the content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the content item (excluding the current version) is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The ContentCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.xml.example', + ], + 'application/vnd.ibexa.api.ContentCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/POST/ContentCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Content+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Content', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.xml.example', + ], + 'application/vnd.ibexa.api.Content+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.json.example', + ], + 'application/vnd.ibexa.api.ContentInfo+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition or the validation on a field fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this Object in this Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the parent Location specified in the request body does not exist.', + ], + ], + ), +)] +class ContentCreateController extends RestController +{ + /** + * Creates a new content draft assigned to the authenticated user. + * If a different userId is given in the input it is assigned to the + * given user but this required special rights for the authenticated + * user (this is useful for content staging where the transfer process + * does not have to authenticate with the user which created the content + * object in the source server). The user has to publish the content if + * it should be visible. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return \Ibexa\Rest\Server\Values\CreatedContent + */ + public function createContent(Request $request) + { + $contentCreate = $this->parseContentRequest($request); + + return $this->doCreateContent($request, $contentCreate); + } + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return mixed + */ + protected function parseContentRequest(Request $request) + { + return $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type'), 'Url' => $request->getPathInfo()], + $request->getContent() + ) + ); + } + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \Ibexa\Rest\Server\Values\RestContentCreateStruct $contentCreate + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return \Ibexa\Rest\Server\Values\CreatedContent + */ + protected function doCreateContent(Request $request, RestContentCreateStruct $contentCreate) + { + try { + $contentCreateStruct = $contentCreate->contentCreateStruct; + $contentCreate->locationCreateStruct->sortField = $contentCreateStruct->contentType->defaultSortField; + $contentCreate->locationCreateStruct->sortOrder = $contentCreateStruct->contentType->defaultSortOrder; + + $content = $this->repository->getContentService()->createContent( + $contentCreateStruct, + [$contentCreate->locationCreateStruct] + ); + } catch (ContentValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (ContentFieldValidationException $e) { + throw new RESTContentFieldValidationException($e); + } + + $contentValue = null; + $contentType = null; + $relations = null; + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.content') { + $contentValue = $content; + $contentType = $this->repository->getContentTypeService()->loadContentType( + $content->getVersionInfo()->getContentInfo()->contentTypeId + ); + $relations = $this->repository->getContentService()->loadRelations($contentValue->getVersionInfo()); + } + + return new Values\CreatedContent( + [ + 'content' => new Values\RestContent( + $content->contentInfo, + null, + $contentValue, + $contentType, + $relations + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentCurrentVersionRedirectController.php b/src/lib/Server/Controller/Content/ContentCurrentVersionRedirectController.php new file mode 100644 index 000000000..eb41ddb1e --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentCurrentVersionRedirectController.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/currentversion', + name: 'Get current version', + openapi: new Model\Operation( + summary: 'Redirects to the current version of the content item.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Version+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Version', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example', + ], + 'application/vnd.ibexa.api.Version+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example', + ], + ], + ], + Response::HTTP_TEMPORARY_REDIRECT => [ + 'description' => 'Temporary redirect.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the resource does not exist.', + ], + ], + ), +)] +class ContentCurrentVersionRedirectController extends RestController +{ + /** + * Loads a specific version of a given content object. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\TemporaryRedirect + */ + public function redirectCurrentVersion($contentId) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + return new Values\TemporaryRedirect( + $this->router->generate( + 'ibexa.rest.load_content_in_version', + [ + 'contentId' => $contentId, + 'versionNumber' => $contentInfo->currentVersionNo, + ] + ) + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentCurrentVersionRelationsRedirectController.php b/src/lib/Server/Controller/Content/ContentCurrentVersionRelationsRedirectController.php new file mode 100644 index 000000000..ef04ecd5f --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentCurrentVersionRelationsRedirectController.php @@ -0,0 +1,70 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/relations', + name: 'Load Relations of content item', + openapi: new Model\Operation( + summary: 'Redirects to the Relations of the current version.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_TEMPORARY_REDIRECT => [ + 'description' => 'Temporary redirect.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item was not found.', + ], + ], + ), +)] +class ContentCurrentVersionRelationsRedirectController extends RestController +{ + /** + * Redirects to the relations of the current version. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\TemporaryRedirect + */ + public function redirectCurrentVersionRelations($contentId) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + return new Values\TemporaryRedirect( + $this->router->generate( + 'ibexa.rest.redirect_current_version_relations', + [ + 'contentId' => $contentId, + 'versionNumber' => $contentInfo->currentVersionNo, + ] + ) + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentDeleteController.php b/src/lib/Server/Controller/Content/ContentDeleteController.php new file mode 100644 index 000000000..fe2293284 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentDeleteController.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objects/{contentId}', + name: 'Delete Content', + openapi: new Model\Operation( + summary: 'Deletes content item. If content item has multiple Locations, all of them will be deleted via delete a subtree.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'The content item is deleted.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - content item was not found.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this content item.', + ], + ], + ), +)] +class ContentDeleteController extends RestController +{ + /** + * The content is deleted. If the content has locations (which is required in 4.x) + * on delete all locations assigned the content object are deleted via delete subtree. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteContent($contentId) + { + $this->repository->getContentService()->deleteContent( + $this->repository->getContentService()->loadContentInfo($contentId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentDraftCreateFromCurrentVersionController.php b/src/lib/Server/Controller/Content/ContentDraftCreateFromCurrentVersionController.php new file mode 100644 index 000000000..a3ba24cc8 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentDraftCreateFromCurrentVersionController.php @@ -0,0 +1,112 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/currentversion', + name: 'Create a draft from current version', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'The system creates a new draft as a copy of the current version. COPY or POST with header X-HTTP-Method-Override COPY.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated version is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Created', + 'content' => [ + 'application/vnd.ibexa.api.Version+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Version', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example', + ], + 'application/vnd.ibexa.api.Version+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update this content item.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the current version is already a draft.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item was not found.', + ], + ], + ), +)] +class ContentDraftCreateFromCurrentVersionController extends RestController +{ + /** + * The system creates a new draft version as a copy from the current version. + * + * @param mixed $contentId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if the current version is already a draft + * + * @return \Ibexa\Rest\Server\Values\CreatedVersion + */ + public function createDraftFromCurrentVersion($contentId) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); + $versionInfo = $this->repository->getContentService()->loadVersionInfo( + $contentInfo + ); + + if ($versionInfo->isDraft()) { + throw new ForbiddenException('Current version already has DRAFT status'); + } + + $contentDraft = $this->repository->getContentService()->createContentDraft($contentInfo); + + return new Values\CreatedVersion( + [ + 'version' => new Values\Version( + $contentDraft, + $contentType, + $this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentDraftCreateFromVersionController.php b/src/lib/Server/Controller/Content/ContentDraftCreateFromVersionController.php new file mode 100644 index 000000000..e6690170f --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentDraftCreateFromVersionController.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}', + name: 'Create a draft from a version', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'The system creates a new draft as a copy of the given version. COPY or POST with header X-HTTP-Method-Override COPY.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated version is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Created.', + 'content' => [ + 'application/vnd.ibexa.api.Version+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Version', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example', + ], + 'application/vnd.ibexa.api.Version+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item was not found.', + ], + ], + ), +)] +class ContentDraftCreateFromVersionController extends RestController +{ + /** + * The system creates a new draft version as a copy from the given version. + * + * @param mixed $contentId + * @param mixed $versionNumber + * + * @return \Ibexa\Rest\Server\Values\CreatedVersion + */ + public function createDraftFromVersion($contentId, $versionNumber) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); + $contentDraft = $this->repository->getContentService()->createContentDraft( + $contentInfo, + $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) + ); + + return new Values\CreatedVersion( + [ + 'version' => new Values\Version( + $contentDraft, + $contentType, + $this->repository->getContentService()->loadRelations($contentDraft->getVersionInfo()) + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentDraftTranslationDeleteController.php b/src/lib/Server/Controller/Content/ContentDraftTranslationDeleteController.php new file mode 100644 index 000000000..9d2cc6134 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentDraftTranslationDeleteController.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}/translations/{languageCode}', + name: 'Delete translation from version draft', + openapi: new Model\Operation( + summary: 'Removes a translation from a version draft.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'languageCode', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - removes a translation from a version draft.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this translation.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the version is not in draft state.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item or version number were not found.', + ], + Response::HTTP_NOT_ACCEPTABLE => [ + 'description' => 'Error - the given translation does not exist for the version.', + ], + Response::HTTP_CONFLICT => [ + 'description' => 'Error - the specified translation is the only one the version has or is the main translation.', + ], + ], + ), +)] +class ContentDraftTranslationDeleteController extends RestController +{ + /** + * Remove the given Translation from the given Version Draft. + * + * @param int $contentId + * @param int $versionNumber + * @param string $languageCode + * + * @return \Ibexa\Rest\Server\Values\NoContent + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + */ + public function deleteTranslationFromDraft($contentId, $versionNumber, $languageCode) + { + $contentService = $this->repository->getContentService(); + $versionInfo = $contentService->loadVersionInfoById($contentId, $versionNumber); + + if (!$versionInfo->isDraft()) { + throw new ForbiddenException('Translation can be deleted from a DRAFT version only'); + } + + $contentService->deleteTranslationFromDraft($versionInfo, $languageCode); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentHideController.php b/src/lib/Server/Controller/Content/ContentHideController.php new file mode 100644 index 000000000..98df55240 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentHideController.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/hide', + name: 'Hide content item', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Makes or keep the content item invisible', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'OK - Object item is hidden.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to change Object item visibility.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content item was not found.', + ], + ], + ), +)] +class ContentHideController extends RestController +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function hideContent(int $contentId): Values\NoContent + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + $this->repository->getContentService()->hideContent($contentInfo); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentInVersionLoadController.php b/src/lib/Server/Controller/Content/ContentInVersionLoadController.php new file mode 100644 index 000000000..e0620917e --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentInVersionLoadController.php @@ -0,0 +1,134 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}', + name: 'Load version', + openapi: new Model\Operation( + summary: 'Loads a specific version of a content item. This method returns Fields and relations.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'Only return the version if the given ETag is the not current one, otherwise a 304 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the version list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Version+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Version', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example', + ], + 'application/vnd.ibexa.api.Version+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example', + ], + ], + ], + Response::HTTP_NOT_MODIFIED => [ + 'description' => 'Error - the ETag does not match the current one.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the ID or version is not found.', + ], + ], + ), +)] +class ContentInVersionLoadController extends RestController +{ + /** + * Loads a specific version of a given content object. + * + * @param mixed $contentId + * @param int $versionNumber + * + * @return \Ibexa\Rest\Server\Values\Version + */ + public function loadContentInVersion($contentId, $versionNumber, Request $request) + { + $languages = Language::ALL; + if ($request->query->has('languages')) { + $languages = explode(',', $request->query->get('languages')); + } + + $content = $this->repository->getContentService()->loadContent( + $contentId, + $languages, + $versionNumber + ); + $contentType = $this->repository->getContentTypeService()->loadContentType( + $content->getVersionInfo()->getContentInfo()->contentTypeId + ); + + $versionValue = new Values\Version( + $content, + $contentType, + $this->repository->getContentService()->loadRelations($content->getVersionInfo()), + $request->getPathInfo() + ); + + if ($content->contentInfo->mainLocationId === null || $content->versionInfo->status === VersionInfo::STATUS_DRAFT) { + return $versionValue; + } + + return new Values\CachedValue( + $versionValue, + ['locationId' => $content->contentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentLoadByIdController.php b/src/lib/Server/Controller/Content/ContentLoadByIdController.php new file mode 100644 index 000000000..6148492c5 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentLoadByIdController.php @@ -0,0 +1,142 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}', + name: 'Load content', + openapi: new Model\Operation( + summary: 'Loads the content item for the given ID. Depending on the Accept header the current version is embedded (i.e. the current published version or if it does not exist, the draft of the authenticated user).', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'Content - If set, all information for the content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the content item (excluding the current version) is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'If the provided ETag matches the current ETag then a "304 Not Modified" is returned. The ETag changes if the meta data has changed, this happens also if there is a new published version.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Content+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Content', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.xml.example', + ], + 'application/vnd.ibexa.api.Content+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/GET/Content.json.example', + ], + 'application/vnd.ibexa.api.ContentInfo+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfo', + ], + ], + 'application/vnd.ibexa.api.ContentInfo+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this object. This could also happen if there is no published version yet and another user owns a draft of this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the ID is not found.', + ], + ], + ), +)] +class ContentLoadByIdController extends RestController +{ + /** + * Loads a content info, potentially with the current version embedded. + * + * @param mixed $contentId + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return \Ibexa\Rest\Server\Values\RestContent + */ + public function loadContent($contentId, Request $request) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + $mainLocation = null; + if (!empty($contentInfo->mainLocationId)) { + $mainLocation = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + + $contentType = $this->repository->getContentTypeService()->loadContentType($contentInfo->contentTypeId); + + $contentVersion = null; + $relations = null; + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.content') { + $languages = Language::ALL; + if ($request->query->has('languages')) { + $languages = explode(',', $request->query->get('languages')); + } + + $contentVersion = $this->repository->getContentService()->loadContent($contentId, $languages); + $relations = $this->repository->getContentService()->loadRelations($contentVersion->getVersionInfo()); + } + + $restContent = new Values\RestContent( + $contentInfo, + $mainLocation, + $contentVersion, + $contentType, + $relations, + $request->getPathInfo() + ); + + if ($contentInfo->mainLocationId === null) { + return $restContent; + } + + return new Values\CachedValue( + $restContent, + ['locationId' => $contentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentMetadataUpdateController.php b/src/lib/Server/Controller/Content/ContentMetadataUpdateController.php new file mode 100644 index 000000000..7f69517a0 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentMetadataUpdateController.php @@ -0,0 +1,166 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/objects/{contentId}', + name: 'Update content', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'This method updates the content metadata which is independent from a version. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, all information for the content item (excluding the current version) is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-match', + in: 'header', + required: true, + description: 'Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The ContentUpdate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfo', + ], + ], + 'application/vnd.ibexa.api.ContentUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.ContentInfo+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfo', + ], + ], + 'application/vnd.ibexa.api.ContentInfo+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/PATCH/ContentInfo.xml.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update this object.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content ID does not exist.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the one provided in the If-Match header.', + ], + Response::HTTP_UNSUPPORTED_MEDIA_TYPE => [ + 'description' => 'Error - the media-type is not one of those specified in headers.', + ], + ], + ), +)] +class ContentMetadataUpdateController extends RestController +{ + /** + * Updates a content's metadata. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\RestContent + */ + public function updateContentMetadata($contentId, Request $request) + { + $updateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + // update section + if ($updateStruct->sectionId !== null) { + $section = $this->repository->getSectionService()->loadSection($updateStruct->sectionId); + $this->repository->getSectionService()->assignSection($contentInfo, $section); + $updateStruct->sectionId = null; + } + + // @todo Consider refactoring! ContentService::updateContentMetadata throws the same exception + // in case the updateStruct is empty and if remoteId already exists. Since REST version of update struct + // includes section ID in addition to other fields, we cannot throw exception if only sectionId property + // is set, so we must skip updating content in that case instead of allowing propagation of the exception. + foreach ($updateStruct as $propertyName => $propertyValue) { + if ($propertyName !== 'sectionId' && $propertyValue !== null) { + // update content + $this->repository->getContentService()->updateContentMetadata($contentInfo, $updateStruct); + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + break; + } + } + + try { + $locationInfo = $this->repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } catch (NotFoundException $e) { + $locationInfo = null; + } + + return new Values\RestContent( + $contentInfo, + $locationInfo + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentRedirectController.php b/src/lib/Server/Controller/Content/ContentRedirectController.php new file mode 100644 index 000000000..24871d58d --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentRedirectController.php @@ -0,0 +1,66 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects', + name: 'Load content by remote ID', + openapi: new Model\Operation( + summary: 'Loads content item for a given remote ID.', + tags: [ + 'Objects', + ], + parameters: [ + ], + responses: [ + Response::HTTP_TEMPORARY_REDIRECT => [ + 'description' => 'Temporary redirect.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content with the given remote ID does not exist.', + ], + ], + ), +)] +class ContentRedirectController extends RestController +{ + /** + * Loads a content info by remote ID. + * + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + * + * @return \Ibexa\Rest\Server\Values\TemporaryRedirect + */ + public function redirectContent(Request $request) + { + if (!$request->query->has('remoteId')) { + throw new BadRequestException("'remoteId' parameter is required."); + } + + $contentInfo = $this->repository->getContentService()->loadContentInfoByRemoteId( + $request->query->get('remoteId') + ); + + return new Values\TemporaryRedirect( + $this->router->generate( + 'ibexa.rest.load_content', + [ + 'contentId' => $contentInfo->id, + ] + ) + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentRelationCreateController.php b/src/lib/Server/Controller/Content/ContentRelationCreateController.php new file mode 100644 index 000000000..cc56e7b7f --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentRelationCreateController.php @@ -0,0 +1,153 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}/relations', + name: 'Create new Relation', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Relation of type COMMON for the given draft.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated version is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RelationCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RelationCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example', + ], + 'application/vnd.ibexa.api.RelationCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Relation+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Relation', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example', + ], + 'application/vnd.ibexa.api.Relation+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example', + ], + ], + ], + ], + ), +)] +class ContentRelationCreateController extends RestController +{ + /** + * Creates a new relation of type COMMON for the given draft. + * + * @param mixed $contentId + * @param int $versionNumber + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if version $versionNumber isn't a draft + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if a relation to the same content already exists + * + * @return \Ibexa\Rest\Server\Values\CreatedRelation + */ + public function createRelation($contentId, $versionNumber, Request $request) + { + $destinationContentId = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + $versionInfo = $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber); + if (!$versionInfo->isDraft()) { + throw new ForbiddenException('Relation of type COMMON can only be added to drafts'); + } + + try { + $destinationContentInfo = $this->repository->getContentService()->loadContentInfo($destinationContentId); + } catch (NotFoundException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + $existingRelations = $this->repository->getContentService()->loadRelations($versionInfo); + foreach ($existingRelations as $existingRelation) { + if ($existingRelation->getDestinationContentInfo()->id == $destinationContentId) { + throw new ForbiddenException('Relation of type COMMON to the selected destination content ID already exists'); + } + } + + $relation = $this->repository->getContentService()->addRelation($versionInfo, $destinationContentInfo); + + return new Values\CreatedRelation( + [ + 'relation' => new Values\RestRelation($relation, $contentId, $versionNumber), + ] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentRevealController.php b/src/lib/Server/Controller/Content/ContentRevealController.php new file mode 100644 index 000000000..e38043a32 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentRevealController.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/reveal', + name: 'Reveal content item', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Makes or keep the content item visible', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'OK - Object item is revealed.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to change Object item visibility.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content item was not found.', + ], + ], + ), +)] +class ContentRevealController extends RestController +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function revealContent(int $contentId): Values\NoContent + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + $this->repository->getContentService()->revealContent($contentInfo); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentTranslationDeleteController.php b/src/lib/Server/Controller/Content/ContentTranslationDeleteController.php new file mode 100644 index 000000000..72c567cf3 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentTranslationDeleteController.php @@ -0,0 +1,95 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objects/{contentId}/translations/{languageCode}', + name: 'Delete translation (permanently)', + openapi: new Model\Operation( + summary: 'Permanently deletes a translation from all versions of a content item.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'languageCode', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete content item (content/remove policy).', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item was not found.', + ], + Response::HTTP_NOT_ACCEPTABLE => [ + 'description' => 'Error - the given translation does not exist for the content item.', + ], + Response::HTTP_CONFLICT => [ + 'description' => 'Error - the specified translation is the only one any version has or is the main translation.', + ], + ], + ), +)] +class ContentTranslationDeleteController extends RestController +{ + /** + * Deletes a translation from all the Versions of the given Content Object. + * + * If any non-published Version contains only the Translation to be deleted, that entire Version will be deleted + * + * @param int $contentId + * @param string $languageCode + * + * @return \Ibexa\Rest\Server\Values\NoContent + * + * @throws \Exception + */ + public function deleteContentTranslation($contentId, $languageCode) + { + $contentService = $this->repository->getContentService(); + + $this->repository->beginTransaction(); + try { + $contentInfo = $contentService->loadContentInfo($contentId); + $contentService->deleteTranslation( + $contentInfo, + $languageCode + ); + + $this->repository->commit(); + + return new Values\NoContent(); + } catch (\Exception $e) { + $this->repository->rollback(); + throw $e; + } + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionDeleteController.php b/src/lib/Server/Controller/Content/ContentVersionDeleteController.php new file mode 100644 index 000000000..52d357b78 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionDeleteController.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}', + name: 'Delete content version', + openapi: new Model\Operation( + summary: 'Deletes the content version.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - the version is deleted.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item or version were not found.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this version.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the version is in published state.', + ], + ], + ), +)] +class ContentVersionDeleteController extends RestController +{ + /** + * The version is deleted. + * + * @param mixed $contentId + * @param mixed $versionNumber + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteContentVersion($contentId, $versionNumber) + { + $versionInfo = $this->repository->getContentService()->loadVersionInfo( + $this->repository->getContentService()->loadContentInfo($contentId), + $versionNumber + ); + + if ($versionInfo->isPublished()) { + throw new ForbiddenException('Versions with PUBLISHED status cannot be deleted'); + } + + $this->repository->getContentService()->deleteVersion( + $versionInfo + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionPublishController.php b/src/lib/Server/Controller/Content/ContentVersionPublishController.php new file mode 100644 index 000000000..b905180c8 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionPublishController.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; + +class ContentVersionPublishController extends RestController +{ + /** + * The content version is published. + * + * @param mixed $contentId + * @param mixed $versionNumber + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException if version $versionNumber isn't a draft + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function publishVersion($contentId, $versionNumber) + { + $versionInfo = $this->repository->getContentService()->loadVersionInfo( + $this->repository->getContentService()->loadContentInfo($contentId), + $versionNumber + ); + + if (!$versionInfo->isDraft()) { + throw new ForbiddenException('Only versions with DRAFT status can be published'); + } + + $this->repository->getContentService()->publishVersion( + $versionInfo + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionRelationDeleteController.php b/src/lib/Server/Controller/Content/ContentVersionRelationDeleteController.php new file mode 100644 index 000000000..e2b663cb8 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionRelationDeleteController.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}/relations/{relationId}', + name: 'Delete Relation', + openapi: new Model\Operation( + summary: 'Deletes a Relation of the given draft.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'relationId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - deleted a Relation of the given draft.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this Relation.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the Relation is not of type COMMON or the given version is not a draft.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - content item or the Relation were not found in the given version.', + ], + ], + ), +)] +class ContentVersionRelationDeleteController extends RestController +{ + /** + * Deletes a relation of the given draft. + * + * @param mixed $contentId + * @param int $versionNumber + * @param mixed $relationId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function removeRelation($contentId, $versionNumber, $relationId, Request $request) + { + $versionInfo = $this->repository->getContentService()->loadVersionInfo( + $this->repository->getContentService()->loadContentInfo($contentId), + $versionNumber + ); + + $versionRelations = $this->repository->getContentService()->loadRelations($versionInfo); + foreach ($versionRelations as $relation) { + if ($relation->id == $relationId) { + if ($relation->type !== Relation::COMMON) { + throw new ForbiddenException('Relation is not of type COMMON'); + } + + if (!$versionInfo->isDraft()) { + throw new ForbiddenException('Relation of type COMMON can only be removed from drafts'); + } + + $this->repository->getContentService()->deleteRelation($versionInfo, $relation->getDestinationContentInfo()); + + return new Values\NoContent(); + } + } + + throw new Exceptions\NotFoundException("Could not find Relation '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionRelationLoadByIdController.php b/src/lib/Server/Controller/Content/ContentVersionRelationLoadByIdController.php new file mode 100644 index 000000000..1b44c6593 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionRelationLoadByIdController.php @@ -0,0 +1,126 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}/relations/{relationId}', + name: 'Load Relation', + openapi: new Model\Operation( + summary: 'Loads a Relation for the given content item.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Relation is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'relationId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - loads a Relation for the given content item.', + 'content' => [ + 'application/vnd.ibexa.api.Relation+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Relation', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example', + ], + 'application/vnd.ibexa.api.Relation+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item with the given ID or the Relation does not exist.', + ], + ], + ), +)] +class ContentVersionRelationLoadByIdController extends RestController +{ + /** + * Loads a relation for the given content object and version. + * + * @param mixed $contentId + * @param int $versionNumber + * @param mixed $relationId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\RestRelation + */ + public function loadVersionRelation($contentId, $versionNumber, $relationId, Request $request) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + $relationList = $this->repository->getContentService()->loadRelations( + $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) + ); + + foreach ($relationList as $relation) { + if ($relation->id == $relationId) { + $relation = new Values\RestRelation($relation, $contentId, $versionNumber); + + if ($contentInfo->mainLocationId === null) { + return $relation; + } + + return new Values\CachedValue( + $relation, + ['locationId' => $contentInfo->mainLocationId] + ); + } + } + + throw new Exceptions\NotFoundException("Relation not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionUpdateController.php b/src/lib/Server/Controller/Content/ContentVersionUpdateController.php new file mode 100644 index 000000000..4e98a7ec2 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionUpdateController.php @@ -0,0 +1,199 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Exceptions\ContentFieldValidationException as RESTContentFieldValidationException; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}', + name: 'Update version', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'A specific draft is updated. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated version is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-match', + in: 'header', + required: true, + description: 'Performs the patch only if the specified ETag is the current one.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The VersionUpdate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.VersionUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example', + ], + 'application/vnd.ibexa.api.VersionUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionUpdateWrapper', + ], + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Version+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Version', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example', + ], + 'application/vnd.ibexa.api.Version+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/GET/Version.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update this version.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the version is not allowed to change - i.e. version is not a DRAFT.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content ID or version ID does not exist.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the one provided in the If-Match header.', + ], + ], + ), +)] +class ContentVersionUpdateController extends RestController +{ + /** + * A specific draft is updated. + * + * @param mixed $contentId + * @param mixed $versionNumber + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + * + * @return \Ibexa\Rest\Server\Values\Version + */ + public function updateVersion($contentId, $versionNumber, Request $request) + { + $contentUpdateStruct = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + 'Url' => $this->router->generate( + 'ibexa.rest.update_version', + [ + 'contentId' => $contentId, + 'versionNumber' => $versionNumber, + ] + ), + ], + $request->getContent() + ) + ); + + $versionInfo = $this->repository->getContentService()->loadVersionInfo( + $this->repository->getContentService()->loadContentInfo($contentId), + $versionNumber + ); + + if (!$versionInfo->isDraft()) { + throw new ForbiddenException('Only versions with DRAFT status can be updated'); + } + + try { + $this->repository->getContentService()->updateContent($versionInfo, $contentUpdateStruct); + } catch (ContentValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (ContentFieldValidationException $e) { + throw new RESTContentFieldValidationException($e); + } + + $languages = null; + if ($request->query->has('languages')) { + $languages = explode(',', $request->query->get('languages')); + } + + // Reload the content to handle languages GET parameter + $content = $this->repository->getContentService()->loadContent( + $contentId, + $languages, + $versionInfo->versionNo + ); + $contentType = $this->repository->getContentTypeService()->loadContentType( + $content->getVersionInfo()->getContentInfo()->contentTypeId + ); + + return new Values\Version( + $content, + $contentType, + $this->repository->getContentService()->loadRelations($content->getVersionInfo()), + $request->getPathInfo() + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionsListController.php b/src/lib/Server/Controller/Content/ContentVersionsListController.php new file mode 100644 index 000000000..d11276e76 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionsListController.php @@ -0,0 +1,86 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/versions', + name: 'List versions', + openapi: new Model\Operation( + summary: 'Returns a list of all versions of the content item. This method does not include fields and relations in the version elements of the response.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the version list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.VersionList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.xml.example', + ], + 'application/vnd.ibexa.api.VersionList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/GET/VersionList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read the versions.', + ], + ], + ), +)] +class ContentVersionsListController extends RestController +{ + /** + * Returns a list of all versions of the content. This method does not + * include fields and relations in the Version elements of the response. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\VersionList + */ + public function loadContentVersions($contentId, Request $request) + { + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + + return new Values\VersionList( + $this->repository->getContentService()->loadVersions($contentInfo), + $request->getPathInfo() + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentVersionsRelationsListController.php b/src/lib/Server/Controller/Content/ContentVersionsRelationsListController.php new file mode 100644 index 000000000..ee2578d77 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentVersionsRelationsListController.php @@ -0,0 +1,120 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/versions/{versionNo}/relations', + name: 'Load Relations of content item version', + openapi: new Model\Operation( + summary: 'Loads the Relations of the given version.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Relation is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'versionNo', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RelationList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example', + ], + 'application/vnd.ibexa.api.RelationList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RelationListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item was not found.', + ], + ], + ), +)] +class ContentVersionsRelationsListController extends RestController +{ + /** + * Loads the relations of the given version. + * + * @param mixed $contentId + * @param mixed $versionNumber + * + * @return \Ibexa\Rest\Server\Values\RelationList + */ + public function loadVersionRelations($contentId, $versionNumber, Request $request) + { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; + + $contentInfo = $this->repository->getContentService()->loadContentInfo($contentId); + $relationList = $this->repository->getContentService()->loadRelations( + $this->repository->getContentService()->loadVersionInfo($contentInfo, $versionNumber) + ); + + $relationList = array_slice( + $relationList, + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : null + ); + + $relationListValue = new Values\RelationList( + $relationList, + $contentId, + $versionNumber, + $request->getPathInfo() + ); + + if ($contentInfo->mainLocationId === null) { + return $relationListValue; + } + + return new Values\CachedValue( + $relationListValue, + ['locationId' => $contentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/Content/ContentViewController.php b/src/lib/Server/Controller/Content/ContentViewController.php new file mode 100644 index 000000000..14d0eb1f2 --- /dev/null +++ b/src/lib/Server/Controller/Content/ContentViewController.php @@ -0,0 +1,90 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Content; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +#[Post( + uriTemplate: '/content/views', + name: 'Create View (deprecated)', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Executes a query and returns View including the results. The View input reflects the criteria model of the public PHP API. Deprecated as of eZ Platform 1.0 and will respond 301, use POST /views instead.', + tags: [ + 'Views', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'The View in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The View input in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_MOVED_PERMANENTLY => [ + 'description' => 'Moved permanently.', + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + ], + ), +)] +class ContentViewController extends RestController +{ + /** + * Creates and executes a content view. + * + * @deprecated Since platform 1.0. Forwards the request to the new /views location, but returns a 301. + * + * @return \Ibexa\Rest\Server\Values\RestExecutedView + */ + public function createView() + { + $response = $this->forward('ezpublish_rest.controller.views:createView'); + + // Add 301 status code and location href + $response->setStatusCode(301); + $response->headers->set('Location', $this->router->generate('ibexa.rest.views.create')); + + return $response; + } + + /** + * @param string $controller + * + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function forward($controller) + { + $path['_controller'] = $controller; + $subRequest = $this->container->get('request_stack')->getCurrentRequest()->duplicate(null, null, $path); + + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } +} diff --git a/src/lib/Server/Controller/ContentType.php b/src/lib/Server/Controller/ContentType.php deleted file mode 100644 index 94d03a405..000000000 --- a/src/lib/Server/Controller/ContentType.php +++ /dev/null @@ -1,1003 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\ContentTypeService; -use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; -use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException; -use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeValidationException; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\Values\Content\Language; -use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType as APIContentType; -use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use Ibexa\Contracts\Rest\Exceptions; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\BadRequestException; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * ContentType controller. - */ -class ContentType extends RestController -{ - /** - * Content type service. - * - * @var \Ibexa\Contracts\Core\Repository\ContentTypeService - */ - protected $contentTypeService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\ContentTypeService $contentTypeService - */ - public function __construct(ContentTypeService $contentTypeService) - { - $this->contentTypeService = $contentTypeService; - } - - /** - * Creates a new content type group. - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedContentTypeGroup - */ - public function createContentTypeGroup(Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - return new Values\CreatedContentTypeGroup( - [ - 'contentTypeGroup' => $this->contentTypeService->createContentTypeGroup($createStruct), - ] - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - /** - * Updates a content type group. - * - * @param $contentTypeGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup - */ - public function updateContentTypeGroup($contentTypeGroupId, Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - $this->contentTypeService->updateContentTypeGroup( - $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId), - $this->mapToGroupUpdateStruct($createStruct) - ); - - return $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - /** - * Returns a list of content types of the group. - * - * @param string $contentTypeGroupId - * - * @return \Ibexa\Rest\Server\Values\ContentTypeList|\Ibexa\Rest\Server\Values\ContentTypeInfoList - */ - public function listContentTypesForGroup($contentTypeGroupId, Request $request) - { - $contentTypes = $this->contentTypeService->loadContentTypes( - $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL), - Language::ALL - ); - - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.contenttypelist') { - return new Values\ContentTypeList($contentTypes, $request->getPathInfo()); - } - - return new Values\ContentTypeInfoList($contentTypes, $request->getPathInfo()); - } - - /** - * The given content type group is deleted. - * - * @param mixed $contentTypeGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteContentTypeGroup($contentTypeGroupId) - { - $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); - - $contentTypes = $this->contentTypeService->loadContentTypes($contentTypeGroup); - if (!empty($contentTypes)) { - throw new ForbiddenException('Only empty content type groups can be deleted'); - } - - $this->contentTypeService->deleteContentTypeGroup($contentTypeGroup); - - return new Values\NoContent(); - } - - /** - * Returns a list of all content type groups. - * - * @return \Ibexa\Rest\Server\Values\ContentTypeGroupList - */ - public function loadContentTypeGroupList(Request $request) - { - if ($request->query->has('identifier')) { - $contentTypeGroup = $this->contentTypeService->loadContentTypeGroupByIdentifier( - $request->query->get('identifier') - ); - - return new Values\TemporaryRedirect( - $this->router->generate( - 'ibexa.rest.load_content_type_group', - [ - 'contentTypeGroupId' => $contentTypeGroup->id, - ] - ) - ); - } - - return new Values\ContentTypeGroupList( - $this->contentTypeService->loadContentTypeGroups(Language::ALL) - ); - } - - /** - * Returns the content type group given by id. - * - * @param $contentTypeGroupId - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup - */ - public function loadContentTypeGroup($contentTypeGroupId) - { - return $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL); - } - - /** - * Loads a content type. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\RestContentType - */ - public function loadContentType($contentTypeId) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); - - return new Values\RestContentType( - $contentType, - $contentType->getFieldDefinitions()->toArray() - ); - } - - /** - * Returns a list of content types. - * - * @return \Ibexa\Rest\Server\Values\ContentTypeList|\Ibexa\Rest\Server\Values\ContentTypeInfoList - */ - public function listContentTypes(Request $request) - { - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.contenttypelist') { - $return = new Values\ContentTypeList([], $request->getPathInfo()); - } else { - $return = new Values\ContentTypeInfoList([], $request->getPathInfo()); - } - - if ($request->query->has('identifier')) { - $return->contentTypes = [$this->loadContentTypeByIdentifier($request)]; - - return $return; - } - - if ($request->query->has('remoteId')) { - $return->contentTypes = [ - $this->loadContentTypeByRemoteId($request), - ]; - - return $return; - } - - $limit = null; - if ($request->query->has('limit')) { - $limit = (int)$request->query->get('limit', null); - if ($limit <= 0) { - throw new BadRequestException('wrong value for limit parameter'); - } - } - $contentTypes = $this->getContentTypeList(); - $sort = $request->query->get('sort'); - if ($request->query->has('orderby')) { - $orderby = $request->query->get('orderby'); - $this->sortContentTypeList($contentTypes, $orderby, $sort); - } - $offset = $request->query->get('offset', 0); - $return->contentTypes = array_slice($contentTypes, $offset, $limit); - - return $return; - } - - /** - * Loads a content type by its identifier. - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType - */ - public function loadContentTypeByIdentifier(Request $request) - { - return $this->contentTypeService->loadContentTypeByIdentifier( - $request->query->get('identifier'), - Language::ALL - ); - } - - /** - * Loads a content type by its remote ID. - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType - */ - public function loadContentTypeByRemoteId(Request $request) - { - return $this->contentTypeService->loadContentTypeByRemoteId( - $request->query->get('remoteId'), - Language::ALL - ); - } - - /** - * Creates a new content type draft in the given content type group. - * - * @param $contentTypeGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - * - * @return \Ibexa\Rest\Server\Values\CreatedContentType - */ - public function createContentType($contentTypeGroupId, Request $request) - { - $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); - $publish = ($request->query->has('publish') && $request->query->get('publish') === 'true'); - - try { - $contentTypeDraft = $this->contentTypeService->createContentType( - $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - // @todo Needs refactoring! Temporary solution so parser has access to get parameters - '__publish' => $publish, - ], - $request->getContent() - ) - ), - [$contentTypeGroup] - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (ContentTypeValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (ContentTypeFieldDefinitionValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (Exceptions\Parser $e) { - throw new BadRequestException($e->getMessage()); - } - - if ($publish) { - $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $this->contentTypeService->loadContentType($contentTypeDraft->id, Language::ALL); - - return new Values\CreatedContentType( - [ - 'contentType' => new Values\RestContentType( - $contentType, - $contentType->getFieldDefinitions()->toArray() - ), - ] - ); - } - - return new Values\CreatedContentType( - [ - 'contentType' => new Values\RestContentType( - $contentTypeDraft, - $contentTypeDraft->getFieldDefinitions()->toArray() - ), - ] - ); - } - - /** - * Copies a content type. The identifier of the copy is changed to - * copy_of_<originalBaseIdentifier>_<newTypeId> and a new remoteId is generated. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\ResourceCreated - */ - public function copyContentType($contentTypeId) - { - $copiedContentType = $this->contentTypeService->copyContentType( - $this->contentTypeService->loadContentType($contentTypeId) - ); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_content_type', - ['contentTypeId' => $copiedContentType->id] - ) - ); - } - - /** - * Creates a draft and updates it with the given data. - * - * @param $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedContentType - */ - public function createContentTypeDraft($contentTypeId, Request $request) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - - try { - $contentTypeDraft = $this->contentTypeService->createContentTypeDraft( - $contentType - ); - } catch (BadStateException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - $contentTypeUpdateStruct = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - ], - $request->getContent() - ) - ); - - try { - $this->contentTypeService->updateContentTypeDraft( - $contentTypeDraft, - $contentTypeUpdateStruct - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\CreatedContentType( - [ - 'contentType' => new Values\RestContentType( - // Reload the content type draft to get the updated values - $this->contentTypeService->loadContentTypeDraft( - $contentTypeDraft->id - ) - ), - ] - ); - } - - /** - * Loads a content type draft. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\RestContentType - */ - public function loadContentTypeDraft($contentTypeId) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - - return new Values\RestContentType( - $contentTypeDraft, - $contentTypeDraft->getFieldDefinitions()->toArray() - ); - } - - /** - * Updates meta data of a draft. This method does not handle field definitions. - * - * @param $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\RestContentType - */ - public function updateContentTypeDraft($contentTypeId, Request $request) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - $contentTypeUpdateStruct = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - ], - $request->getContent() - ) - ); - - try { - $this->contentTypeService->updateContentTypeDraft( - $contentTypeDraft, - $contentTypeUpdateStruct - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\RestContentType( - // Reload the content type draft to get the updated values - $this->contentTypeService->loadContentTypeDraft( - $contentTypeDraft->id - ) - ); - } - - /** - * Creates a new field definition for the given content type draft. - * - * @param $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\CreatedFieldDefinition - */ - public function addContentTypeDraftFieldDefinition($contentTypeId, Request $request) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - $fieldDefinitionCreate = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - ], - $request->getContent() - ) - ); - - try { - $this->contentTypeService->addFieldDefinition( - $contentTypeDraft, - $fieldDefinitionCreate - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (ContentTypeFieldDefinitionValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (BadStateException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - $updatedDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - foreach ($updatedDraft->getFieldDefinitions() as $fieldDefinition) { - if ($fieldDefinition->identifier == $fieldDefinitionCreate->identifier) { - return new Values\CreatedFieldDefinition( - [ - 'fieldDefinition' => new Values\RestFieldDefinition($updatedDraft, $fieldDefinition), - ] - ); - } - } - - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - /** - * Loads field definitions for a given content type. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\FieldDefinitionList - * - * @todo Check why this isn't in the specs - */ - public function loadContentTypeFieldDefinitionList($contentTypeId) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); - - return new Values\FieldDefinitionList( - $contentType, - $contentType->getFieldDefinitions()->toArray() - ); - } - - /** - * Returns the field definition given by id. - * - * @param $contentTypeId - * @param $fieldDefinitionId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\RestFieldDefinition - */ - public function loadContentTypeFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); - - foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { - if ($fieldDefinition->id == $fieldDefinitionId) { - return new Values\RestFieldDefinition( - $contentType, - $fieldDefinition - ); - } - } - - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - /** - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function loadContentTypeFieldDefinitionByIdentifier( - int $contentTypeId, - string $fieldDefinitionIdentifier, - Request $request - ): Values\RestFieldDefinition { - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); - $path = $this->router->generate( - 'ibexa.rest.load_content_type_field_definition_by_identifier', - [ - 'contentTypeId' => $contentType->id, - 'fieldDefinitionIdentifier' => $fieldDefinitionIdentifier, - ] - ); - - if ($fieldDefinition === null) { - throw new Exceptions\NotFoundException( - sprintf("Field definition not found: '%s'.", $request->getPathInfo()) - ); - } - - return new Values\RestFieldDefinition( - $contentType, - $fieldDefinition, - $path - ); - } - - /** - * Loads field definitions for a given content type draft. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\FieldDefinitionList - */ - public function loadContentTypeDraftFieldDefinitionList($contentTypeId) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - - return new Values\FieldDefinitionList( - $contentTypeDraft, - $contentTypeDraft->getFieldDefinitions() - ); - } - - /** - * Returns the draft field definition given by id. - * - * @param $contentTypeId - * @param $fieldDefinitionId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\RestFieldDefinition - */ - public function loadContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - - foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDefinition) { - if ($fieldDefinition->id == $fieldDefinitionId) { - return new Values\RestFieldDefinition( - $contentTypeDraft, - $fieldDefinition - ); - } - } - - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - /** - * Updates the attributes of a field definition. - * - * @param $contentTypeId - * @param $fieldDefinitionId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\FieldDefinitionList - */ - public function updateContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - $fieldDefinitionUpdate = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - // @todo Needs refactoring! Temporary solution so parser has access to URL - 'Url' => $request->getPathInfo(), - ], - $request->getContent() - ) - ); - - $fieldDefinition = null; - foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDef) { - if ($fieldDef->id == $fieldDefinitionId) { - $fieldDefinition = $fieldDef; - } - } - - if ($fieldDefinition === null) { - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - try { - $this->contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $fieldDefinition, - $fieldDefinitionUpdate - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - $updatedDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - foreach ($updatedDraft->getFieldDefinitions() as $fieldDef) { - if ($fieldDef->id == $fieldDefinitionId) { - return new Values\RestFieldDefinition($updatedDraft, $fieldDef); - } - } - - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - /** - * Deletes a field definition from a content type draft. - * - * @param $contentTypeId - * @param $fieldDefinitionId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function removeContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - - $fieldDefinition = null; - foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDef) { - if ($fieldDef->id == $fieldDefinitionId) { - $fieldDefinition = $fieldDef; - } - } - - if ($fieldDefinition === null) { - throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); - } - - $this->contentTypeService->removeFieldDefinition( - $contentTypeDraft, - $fieldDefinition - ); - - return new Values\NoContent(); - } - - /** - * Publishes a content type draft. - * - * @param $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\RestContentType - */ - public function publishContentTypeDraft($contentTypeId) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - - $fieldDefinitions = $contentTypeDraft->getFieldDefinitions(); - if (empty($fieldDefinitions)) { - throw new ForbiddenException('Cannot publish an empty content type draft'); - } - - $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $publishedContentType = $this->contentTypeService->loadContentType($contentTypeDraft->id, Language::ALL); - - return new Values\RestContentType( - $publishedContentType, - $publishedContentType->getFieldDefinitions()->toArray() - ); - } - - /** - * The given content type is deleted. - * - * @param $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteContentType($contentTypeId) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - - try { - $this->contentTypeService->deleteContentType($contentType); - } catch (BadStateException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\NoContent(); - } - - /** - * The given content type draft is deleted. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteContentTypeDraft($contentTypeId) - { - $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); - $this->contentTypeService->deleteContentType($contentTypeDraft); - - return new Values\NoContent(); - } - - /** - * Returns the content type groups the content type belongs to. - * - * @param $contentTypeId - * - * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList - */ - public function loadGroupsOfContentType($contentTypeId) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); - - return new Values\ContentTypeGroupRefList( - $contentType, - $contentType->getContentTypeGroups() - ); - } - - /** - * Links a content type group to the content type and returns the updated group list. - * - * @param mixed $contentTypeId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - * - * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList - */ - public function linkContentTypeToGroup($contentTypeId, Request $request) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - - try { - $contentTypeGroupId = $this->requestParser->parseHref( - $request->query->get('group'), - 'contentTypeGroupId' - ); - } catch (Exceptions\InvalidArgumentException $e) { - // Group URI does not match the required value - throw new BadRequestException($e->getMessage()); - } - - $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); - - $existingContentTypeGroups = $contentType->getContentTypeGroups(); - $contentTypeInGroup = false; - foreach ($existingContentTypeGroups as $existingGroup) { - if ($existingGroup->id == $contentTypeGroup->id) { - $contentTypeInGroup = true; - break; - } - } - - if ($contentTypeInGroup) { - throw new ForbiddenException('The content type is already linked to the provided group'); - } - - $this->contentTypeService->assignContentTypeGroup( - $contentType, - $contentTypeGroup - ); - - $existingContentTypeGroups[] = $contentTypeGroup; - - return new Values\ContentTypeGroupRefList( - $contentType, - $existingContentTypeGroups - ); - } - - /** - * Removes the given group from the content type and returns the updated group list. - * - * @param $contentTypeId - * @param $contentTypeGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList - */ - public function unlinkContentTypeFromGroup($contentTypeId, $contentTypeGroupId) - { - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); - - $existingContentTypeGroups = $contentType->getContentTypeGroups(); - $contentTypeInGroup = false; - foreach ($existingContentTypeGroups as $existingGroup) { - if ($existingGroup->id == $contentTypeGroup->id) { - $contentTypeInGroup = true; - break; - } - } - - if (!$contentTypeInGroup) { - throw new Exceptions\NotFoundException('The content type is not in the provided group'); - } - - if (count($existingContentTypeGroups) == 1) { - throw new ForbiddenException('Cannot unlink the content type from its only remaining group'); - } - - $this->contentTypeService->unassignContentTypeGroup( - $contentType, - $contentTypeGroup - ); - - $contentType = $this->contentTypeService->loadContentType($contentTypeId); - - return new Values\ContentTypeGroupRefList( - $contentType, - $contentType->getContentTypeGroups() - ); - } - - /** - * Converts the provided ContentTypeGroupCreateStruct to ContentTypeGroupUpdateStruct. - * - * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct $createStruct - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct - */ - private function mapToGroupUpdateStruct(ContentTypeGroupCreateStruct $createStruct) - { - return new ContentTypeGroupUpdateStruct( - [ - 'identifier' => $createStruct->identifier, - 'modifierId' => $createStruct->creatorId, - 'modificationDate' => $createStruct->creationDate, - ] - ); - } - - /** - * @param array &$contentTypes - * @param string $orderby - * - * @return mixed - * - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - */ - protected function sortContentTypeList(array &$contentTypes, $orderby, $sort = 'asc') - { - switch ($orderby) { - case 'name': - if ($sort === 'asc' || $sort === null) { - usort( - $contentTypes, - static function (APIContentType $contentType1, APIContentType $contentType2) { - return strcasecmp($contentType1->identifier, $contentType2->identifier); - } - ); - } elseif ($sort === 'desc') { - usort( - $contentTypes, - static function (APIContentType $contentType1, APIContentType $contentType2) { - return strcasecmp($contentType1->identifier, $contentType2->identifier) * -1; - } - ); - } else { - throw new BadRequestException('wrong value for sort parameter'); - } - break; - case 'lastmodified': - if ($sort === 'asc' || $sort === null) { - usort( - $contentTypes, - static function ($timeObj3, $timeObj4) { - $timeObj3 = strtotime($timeObj3->modificationDate->format('Y-m-d H:i:s')); - $timeObj4 = strtotime($timeObj4->modificationDate->format('Y-m-d H:i:s')); - - return $timeObj3 > $timeObj4; - } - ); - } elseif ($sort === 'desc') { - usort( - $contentTypes, - static function ($timeObj3, $timeObj4) { - $timeObj3 = strtotime($timeObj3->modificationDate->format('Y-m-d H:i:s')); - $timeObj4 = strtotime($timeObj4->modificationDate->format('Y-m-d H:i:s')); - - return $timeObj3 < $timeObj4; - } - ); - } else { - throw new BadRequestException('wrong value for sort parameter'); - } - break; - default: - throw new BadRequestException('wrong value for orderby parameter'); - break; - } - } - - /** - * @return ContentType[] - */ - protected function getContentTypeList() - { - $contentTypes = []; - foreach ($this->contentTypeService->loadContentTypeGroups() as $contentTypeGroup) { - $contentTypes = array_merge( - $contentTypes, - $this->contentTypeService->loadContentTypes($contentTypeGroup, Language::ALL) - ); - } - - return $contentTypes; - } -} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeCopyController.php b/src/lib/Server/Controller/ContentType/ContentTypeCopyController.php new file mode 100644 index 000000000..f8d836732 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeCopyController.php @@ -0,0 +1,44 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; + +class ContentTypeCopyController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Copies a content type. The identifier of the copy is changed to + * copy_of_<originalBaseIdentifier>_<newTypeId> and a new remoteId is generated. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\ResourceCreated + */ + public function copyContentType($contentTypeId) + { + $copiedContentType = $this->contentTypeService->copyContentType( + $this->contentTypeService->loadContentType($contentTypeId) + ); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_content_type', + ['contentTypeId' => $copiedContentType->id] + ) + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeCreateController.php b/src/lib/Server/Controller/ContentType/ContentTypeCreateController.php new file mode 100644 index 000000000..b370fc4c0 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeCreateController.php @@ -0,0 +1,183 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/typegroups/{contentTypeGroupId}/types', + name: 'Create content type', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new content type draft in the given content type group.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new content type or draft is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content type Create schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentTypeCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Content type created.', + 'content' => [ + 'application/vnd.ibexa.api.ContentType+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentType', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example', + ], + 'application/vnd.ibexa.api.ContentType+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeWrapper', + ], + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition. Validation on a Field definition fails. Validation of the content type fails, e.g. multiple Fields of a same singular Field Type are provided. Publish is set to true and the input is not complete e.g. no Field definitions are provided.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create this content type.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A content type with same identifier already exists.', + ], + ], + ), +)] +class ContentTypeCreateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Creates a new content type draft in the given content type group. + * + * @param $contentTypeGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + * + * @return \Ibexa\Rest\Server\Values\CreatedContentType + */ + public function createContentType($contentTypeGroupId, Request $request) + { + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); + $publish = ($request->query->has('publish') && $request->query->get('publish') === 'true'); + + try { + $contentTypeDraft = $this->contentTypeService->createContentType( + $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + // @todo Needs refactoring! Temporary solution so parser has access to get parameters + '__publish' => $publish, + ], + $request->getContent() + ) + ), + [$contentTypeGroup] + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (ContentTypeValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (ContentTypeFieldDefinitionValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (Exceptions\Parser $e) { + throw new BadRequestException($e->getMessage()); + } + + if ($publish) { + $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $this->contentTypeService->loadContentType($contentTypeDraft->id, Language::ALL); + + return new Values\CreatedContentType( + [ + 'contentType' => new Values\RestContentType( + $contentType, + $contentType->getFieldDefinitions()->toArray() + ), + ] + ); + } + + return new Values\CreatedContentType( + [ + 'contentType' => new Values\RestContentType( + $contentTypeDraft, + $contentTypeDraft->getFieldDefinitions()->toArray() + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDeleteController.php b/src/lib/Server/Controller/ContentType/ContentTypeDeleteController.php new file mode 100644 index 000000000..f74d35161 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDeleteController.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/types/{contentTypeId}', + name: 'Delete content type', + openapi: new Model\Operation( + summary: 'Deletes the provided content type.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - content type deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete this content type.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - There are object instances of this content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist.', + ], + ], + ), +)] +class ContentTypeDeleteController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * The given content type is deleted. + * + * @param $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteContentType($contentTypeId) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + + try { + $this->contentTypeService->deleteContentType($contentType); + } catch (BadStateException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftCreateController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftCreateController.php new file mode 100644 index 000000000..966ee29f8 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftCreateController.php @@ -0,0 +1,167 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/types/{contentTypeId}', + name: 'Create Draft', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a draft and updates it with the given data.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new content type draft is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content type Update schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentTypeUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Draft created.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeInfo+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfo', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeInfo+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create the draft.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A content type with the given new identifier already exists. A draft already exists.', + ], + ], + ), +)] +class ContentTypeDraftCreateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Creates a draft and updates it with the given data. + * + * @param $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedContentType + */ + public function createContentTypeDraft($contentTypeId, Request $request) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + + try { + $contentTypeDraft = $this->contentTypeService->createContentTypeDraft( + $contentType + ); + } catch (BadStateException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + $contentTypeUpdateStruct = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + ], + $request->getContent() + ) + ); + + try { + $this->contentTypeService->updateContentTypeDraft( + $contentTypeDraft, + $contentTypeUpdateStruct + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\CreatedContentType( + [ + 'contentType' => new Values\RestContentType( + // Reload the content type draft to get the updated values + $this->contentTypeService->loadContentTypeDraft( + $contentTypeDraft->id + ) + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftDeleteController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftDeleteController.php new file mode 100644 index 000000000..294e79c23 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftDeleteController.php @@ -0,0 +1,71 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/types/{contentTypeId}/draft', + name: 'Delete content type draft', + openapi: new Model\Operation( + summary: 'Deletes the provided content type draft.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - content type draft deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete this content type draft.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type draft does not exist.', + ], + ], + ), +)] +class ContentTypeDraftDeleteController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * The given content type draft is deleted. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteContentTypeDraft($contentTypeId) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + $this->contentTypeService->deleteContentType($contentTypeDraft); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftFeildDefinitionAddController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftFeildDefinitionAddController.php new file mode 100644 index 000000000..090aaa00d --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftFeildDefinitionAddController.php @@ -0,0 +1,166 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/types/{contentTypeId}/draft/fieldDefinitions', + name: 'Add content type Draft Field definition', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Field definition for the given content type.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Field definition is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Field Definition Create schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.FieldDefinitionCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinitionCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionCreateWrapper', + ], + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Field definition created.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinition+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinition', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinition+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition or validation on the Field definition fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to add a Field definition.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A Field definition with the same identifier already exists in the given content type. The Field definition is of singular type, already existing in the given content type. The Field definition you want to add is of a type that can\'t be added to a content type that already has content instances.', + ], + ], + ), +)] +class ContentTypeDraftFeildDefinitionAddController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Creates a new field definition for the given content type draft. + * + * @param $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\CreatedFieldDefinition + */ + public function addContentTypeDraftFieldDefinition($contentTypeId, Request $request) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + $fieldDefinitionCreate = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + ], + $request->getContent() + ) + ); + + try { + $this->contentTypeService->addFieldDefinition( + $contentTypeDraft, + $fieldDefinitionCreate + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (ContentTypeFieldDefinitionValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (BadStateException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + $updatedDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + foreach ($updatedDraft->getFieldDefinitions() as $fieldDefinition) { + if ($fieldDefinition->identifier == $fieldDefinitionCreate->identifier) { + return new Values\CreatedFieldDefinition( + [ + 'fieldDefinition' => new Values\RestFieldDefinition($updatedDraft, $fieldDefinition), + ] + ); + } + } + + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionDeleteController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionDeleteController.php new file mode 100644 index 000000000..b71dcd60c --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionDeleteController.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId}', + name: 'Delete content type Draft Field definition', + openapi: new Model\Operation( + summary: 'Deletes the provided Field definition.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'fieldDefinitionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - Field definition deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete this content type.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - There is no draft of the content type assigned to the authenticated user.', + ], + ], + ), +)] +class ContentTypeDraftFieldDefinitionDeleteController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Deletes a field definition from a content type draft. + * + * @param $contentTypeId + * @param $fieldDefinitionId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function removeContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + + $fieldDefinition = null; + foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDef) { + if ($fieldDef->id == $fieldDefinitionId) { + $fieldDefinition = $fieldDef; + } + } + + if ($fieldDefinition === null) { + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } + + $this->contentTypeService->removeFieldDefinition( + $contentTypeDraft, + $fieldDefinition + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionListController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionListController.php new file mode 100644 index 000000000..586a2616f --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionListController.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/draft/fieldDefinitions', + name: 'Get Draft Field definition list', + openapi: new Model\Operation( + summary: 'Returns all Field definitions of the provided content type Draft.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Field definitions are returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - return a list of Field definitions.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinitionList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitions', + ], + ], + 'application/vnd.ibexa.api.FieldDefinitionList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionsWrapper', + ], + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type draft does not exist.', + ], + ], + ), +)] +class ContentTypeDraftFieldDefinitionListController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Loads field definitions for a given content type draft. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\FieldDefinitionList + */ + public function loadContentTypeDraftFieldDefinitionList($contentTypeId) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + + return new Values\FieldDefinitionList( + $contentTypeDraft, + $contentTypeDraft->getFieldDefinitions() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionLoadByIdController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionLoadByIdController.php new file mode 100644 index 000000000..1a214aa05 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionLoadByIdController.php @@ -0,0 +1,115 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId}', + name: 'Get content type Draft Field definition', + openapi: new Model\Operation( + summary: 'Returns the Field definition by the given ID.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Field definition is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'fieldDefinitionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the Field definition.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinition+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinition', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinition+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read the content type draft.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type or draft does not exist.', + ], + ], + ), +)] +class ContentTypeDraftFieldDefinitionLoadByIdController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns the draft field definition given by id. + * + * @param $contentTypeId + * @param $fieldDefinitionId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\RestFieldDefinition + */ + public function loadContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + + foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDefinition) { + if ($fieldDefinition->id == $fieldDefinitionId) { + return new Values\RestFieldDefinition( + $contentTypeDraft, + $fieldDefinition + ); + } + } + + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionUpdateController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionUpdateController.php new file mode 100644 index 000000000..79ca6eb67 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftFieldDefinitionUpdateController.php @@ -0,0 +1,178 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId}', + name: 'Update content type Draft Field definition', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates the attributes of a Field definition.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Field definition is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Field definition update schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'fieldDefinitionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.FieldDefinitionUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinitionUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionUpdateWrapper', + ], + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - attributes updated.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinition+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinition', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinition+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to update the Field definition.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A Field definition with the given identifier already exists in the given content type.', + ], + ], + ), +)] +class ContentTypeDraftFieldDefinitionUpdateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Updates the attributes of a field definition. + * + * @param $contentTypeId + * @param $fieldDefinitionId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\FieldDefinitionList + */ + public function updateContentTypeDraftFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + $fieldDefinitionUpdate = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + // @todo Needs refactoring! Temporary solution so parser has access to URL + 'Url' => $request->getPathInfo(), + ], + $request->getContent() + ) + ); + + $fieldDefinition = null; + foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDef) { + if ($fieldDef->id == $fieldDefinitionId) { + $fieldDefinition = $fieldDef; + } + } + + if ($fieldDefinition === null) { + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } + + try { + $this->contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $fieldDefinition, + $fieldDefinitionUpdate + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + $updatedDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + foreach ($updatedDraft->getFieldDefinitions() as $fieldDef) { + if ($fieldDef->id == $fieldDefinitionId) { + return new Values\RestFieldDefinition($updatedDraft, $fieldDef); + } + } + + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftLoadController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftLoadController.php new file mode 100644 index 000000000..c1a1f77a4 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftLoadController.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/draft', + name: 'Get content type draft', + openapi: new Model\Operation( + summary: 'Returns the draft of the content type with the provided ID.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the content type is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the content type.', + 'content' => [ + 'application/vnd.ibexa.api.ContentType+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentType', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example', + ], + 'application/vnd.ibexa.api.ContentType+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist or does not have a draft.', + ], + ], + ), +)] +class ContentTypeDraftLoadController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Loads a content type draft. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\RestContentType + */ + public function loadContentTypeDraft($contentTypeId) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + + return new Values\RestContentType( + $contentTypeDraft, + $contentTypeDraft->getFieldDefinitions()->toArray() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftPublishController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftPublishController.php new file mode 100644 index 000000000..7c6aee72a --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftPublishController.php @@ -0,0 +1,52 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; + +class ContentTypeDraftPublishController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Publishes a content type draft. + * + * @param $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\RestContentType + */ + public function publishContentTypeDraft($contentTypeId) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + + $fieldDefinitions = $contentTypeDraft->getFieldDefinitions(); + if (empty($fieldDefinitions)) { + throw new ForbiddenException('Cannot publish an empty content type draft'); + } + + $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $publishedContentType = $this->contentTypeService->loadContentType($contentTypeDraft->id, Language::ALL); + + return new Values\RestContentType( + $publishedContentType, + $publishedContentType->getFieldDefinitions()->toArray() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeDraftUpdateController.php b/src/lib/Server/Controller/ContentType/ContentTypeDraftUpdateController.php new file mode 100644 index 000000000..7c4d59275 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeDraftUpdateController.php @@ -0,0 +1,156 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/types/{contentTypeId}/draft', + name: 'Update content type draft', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates metadata of a draft. This method does not handle Field definitions. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new content type draft is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content type update schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentTypeUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'Draft metadata updated.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeInfo+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfo', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeInfo+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to update the draft.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A content type with the given new identifier already exists.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - There is no draft for this content type.', + ], + ], + ), +)] +class ContentTypeDraftUpdateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Updates meta data of a draft. This method does not handle field definitions. + * + * @param $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\RestContentType + */ + public function updateContentTypeDraft($contentTypeId, Request $request) + { + $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); + $contentTypeUpdateStruct = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + ], + $request->getContent() + ) + ); + + try { + $this->contentTypeService->updateContentTypeDraft( + $contentTypeDraft, + $contentTypeUpdateStruct + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\RestContentType( + // Reload the content type draft to get the updated values + $this->contentTypeService->loadContentTypeDraft( + $contentTypeDraft->id + ) + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionListController.php b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionListController.php new file mode 100644 index 000000000..ad8da4bfa --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionListController.php @@ -0,0 +1,94 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/fieldDefinitions', + name: 'Get Field definition list', + openapi: new Model\Operation( + summary: 'Returns all Field definitions of the provided content type.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Field definitions are returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - return a list of Field definitions.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinitionList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitions', + ], + ], + 'application/vnd.ibexa.api.FieldDefinitionList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionsWrapper', + ], + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist.', + ], + ], + ), +)] +class ContentTypeFieldDefinitionListController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Loads field definitions for a given content type. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\FieldDefinitionList + * + * @todo Check why this isn't in the specs + */ + public function loadContentTypeFieldDefinitionList($contentTypeId) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); + + return new Values\FieldDefinitionList( + $contentType, + $contentType->getFieldDefinitions()->toArray() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdController.php b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdController.php new file mode 100644 index 000000000..6d9d24b3c --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdController.php @@ -0,0 +1,116 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/fieldDefinitions/{fieldDefinitionId}', + name: 'Get Field definition', + openapi: new Model\Operation( + summary: 'Returns the Field definition by the given ID.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Field definition is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'fieldDefinitionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the Field definition.', + 'content' => [ + 'application/vnd.ibexa.api.FieldDefinition+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinition', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example', + ], + 'application/vnd.ibexa.api.FieldDefinition+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/FieldDefinitionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read the content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist.', + ], + ], + ), +)] +class ContentTypeFieldDefinitionLoadByIdController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns the field definition given by id. + * + * @param $contentTypeId + * @param $fieldDefinitionId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\RestFieldDefinition + */ + public function loadContentTypeFieldDefinition($contentTypeId, $fieldDefinitionId, Request $request) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); + + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + if ($fieldDefinition->id == $fieldDefinitionId) { + return new Values\RestFieldDefinition( + $contentType, + $fieldDefinition + ); + } + } + + throw new Exceptions\NotFoundException("Field definition not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdentifierController.php b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdentifierController.php new file mode 100644 index 000000000..e851f9dc3 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeFieldDefinitionLoadByIdentifierController.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class ContentTypeFieldDefinitionLoadByIdentifierController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadContentTypeFieldDefinitionByIdentifier( + int $contentTypeId, + string $fieldDefinitionIdentifier, + Request $request + ): Values\RestFieldDefinition { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + $path = $this->router->generate( + 'ibexa.rest.load_content_type_field_definition_by_identifier', + [ + 'contentTypeId' => $contentType->id, + 'fieldDefinitionIdentifier' => $fieldDefinitionIdentifier, + ] + ); + + if ($fieldDefinition === null) { + throw new Exceptions\NotFoundException( + sprintf("Field definition not found: '%s'.", $request->getPathInfo()) + ); + } + + return new Values\RestFieldDefinition( + $contentType, + $fieldDefinition, + $path + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupCreateController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupCreateController.php new file mode 100644 index 000000000..f8cba878a --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupCreateController.php @@ -0,0 +1,133 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/typegroups', + name: 'Create content type group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new content type group.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new content type group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content type group input schema encoded in XML or JSON.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentTypeGroupInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Content type group created.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create this content type group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A content type group with the same identifier already exists.', + ], + ], + ), +)] +class ContentTypeGroupCreateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Creates a new content type group. + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedContentTypeGroup + */ + public function createContentTypeGroup(Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + return new Values\CreatedContentTypeGroup( + [ + 'contentTypeGroup' => $this->contentTypeService->createContentTypeGroup($createStruct), + ] + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupDeleteController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupDeleteController.php new file mode 100644 index 000000000..55108487e --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupDeleteController.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/typegroups/{contentTypeGroupId}', + name: 'Delete content type group', + openapi: new Model\Operation( + summary: 'Deletes the provided content type group.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'contentTypeGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - content type group deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete this content type group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - The content type group is not empty.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type group does not exist.', + ], + ], + ), +)] +class ContentTypeGroupDeleteController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * The given content type group is deleted. + * + * @param mixed $contentTypeGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteContentTypeGroup($contentTypeGroupId) + { + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); + + $contentTypes = $this->contentTypeService->loadContentTypes($contentTypeGroup); + if (!empty($contentTypes)) { + throw new ForbiddenException('Only empty content type groups can be deleted'); + } + + $this->contentTypeService->deleteContentTypeGroup($contentTypeGroup); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupListController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupListController.php new file mode 100644 index 000000000..ace3a7949 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupListController.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}/groups', + name: 'Get groups of content type', + openapi: new Model\Operation( + summary: 'Returns the content type group to which content type belongs to.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the content type group list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist.', + ], + ], + ), +)] +class ContentTypeGroupListController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns the content type groups the content type belongs to. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList + */ + public function loadGroupsOfContentType($contentTypeId) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); + + return new Values\ContentTypeGroupRefList( + $contentType, + $contentType->getContentTypeGroups() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupLoadByIdController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupLoadByIdController.php new file mode 100644 index 000000000..9936d9461 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupLoadByIdController.php @@ -0,0 +1,100 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/typegroups/{contentTypeGroupId}', + name: 'Get content type group', + openapi: new Model\Operation( + summary: 'Returns the content type group with provided ID.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the content type group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the content type group.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this content type group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type group does not exist.', + ], + ], + ), +)] +class ContentTypeGroupLoadByIdController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns the content type group given by id. + * + * @param $contentTypeGroupId + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup + */ + public function loadContentTypeGroup($contentTypeGroupId) + { + return $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupUpdateController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupUpdateController.php new file mode 100644 index 000000000..5f53c6f99 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupUpdateController.php @@ -0,0 +1,176 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/typegroups/{contentTypeGroupId}', + name: 'Update content type group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a content type group. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated content type group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content type group input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag causes patching only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentTypeGroupInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroupInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'Content type group updated.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/POST/ContentTypeGroup.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create this content type group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A content type group with the given identifier already exists.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - The current ETag does not match the one provided in the If-Match header.', + ], + ], + ), +)] +class ContentTypeGroupUpdateController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Updates a content type group. + * + * @param $contentTypeGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup + */ + public function updateContentTypeGroup($contentTypeGroupId, Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + $this->contentTypeService->updateContentTypeGroup( + $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId), + $this->mapToGroupUpdateStruct($createStruct) + ); + + return $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } + + /** + * Converts the provided ContentTypeGroupCreateStruct to ContentTypeGroupUpdateStruct. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct + */ + private function mapToGroupUpdateStruct(ContentTypeGroupCreateStruct $createStruct) + { + return new ContentTypeGroupUpdateStruct( + [ + 'identifier' => $createStruct->identifier, + 'modifierId' => $createStruct->creatorId, + 'modificationDate' => $createStruct->creationDate, + ] + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeGroupsListController.php b/src/lib/Server/Controller/ContentType/ContentTypeGroupsListController.php new file mode 100644 index 000000000..d887c4a87 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeGroupsListController.php @@ -0,0 +1,103 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/typegroups', + name: 'Get content type groups', + openapi: new Model\Operation( + summary: 'Returns a list of all content type groups. If an identifier is provided, loads the content type group for this identifier.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the content type group list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list of content type groups.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroupList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/GET/ContentTypeGroupList.json.example', + ], + ], + ], + Response::HTTP_TEMPORARY_REDIRECT => [ + 'description' => 'Temporary redirect.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read content types.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type group with the given identifier does not exist.', + ], + ], + ), +)] +class ContentTypeGroupsListController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns a list of all content type groups. + * + * @return \Ibexa\Rest\Server\Values\ContentTypeGroupList + */ + public function loadContentTypeGroupList(Request $request) + { + if ($request->query->has('identifier')) { + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroupByIdentifier( + $request->query->get('identifier') + ); + + return new Values\TemporaryRedirect( + $this->router->generate( + 'ibexa.rest.load_content_type_group', + [ + 'contentTypeGroupId' => $contentTypeGroup->id, + ] + ) + ); + } + + return new Values\ContentTypeGroupList( + $this->contentTypeService->loadContentTypeGroups(Language::ALL) + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeLinkToGroupController.php b/src/lib/Server/Controller/ContentType/ContentTypeLinkToGroupController.php new file mode 100644 index 000000000..f997391b5 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeLinkToGroupController.php @@ -0,0 +1,140 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/types/{contentTypeId}/groups', + name: 'Link group to content type', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Links a content type group to the content type and returns the updated group list.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated content type group list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to add a group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - The content type is already assigned to the group.', + ], + ], + ), +)] +class ContentTypeLinkToGroupController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Links a content type group to the content type and returns the updated group list. + * + * @param mixed $contentTypeId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + * + * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList + */ + public function linkContentTypeToGroup($contentTypeId, Request $request) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + + try { + $contentTypeGroupId = $this->requestParser->parseHref( + $request->query->get('group'), + 'contentTypeGroupId' + ); + } catch (Exceptions\InvalidArgumentException $e) { + // Group URI does not match the required value + throw new BadRequestException($e->getMessage()); + } + + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); + + $existingContentTypeGroups = $contentType->getContentTypeGroups(); + $contentTypeInGroup = false; + foreach ($existingContentTypeGroups as $existingGroup) { + if ($existingGroup->id == $contentTypeGroup->id) { + $contentTypeInGroup = true; + break; + } + } + + if ($contentTypeInGroup) { + throw new ForbiddenException('The content type is already linked to the provided group'); + } + + $this->contentTypeService->assignContentTypeGroup( + $contentType, + $contentTypeGroup + ); + + $existingContentTypeGroups[] = $contentTypeGroup; + + return new Values\ContentTypeGroupRefList( + $contentType, + $existingContentTypeGroups + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeListController.php b/src/lib/Server/Controller/ContentType/ContentTypeListController.php new file mode 100644 index 000000000..d91faecb7 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeListController.php @@ -0,0 +1,231 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType as APIContentType; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types', + name: 'List content types', + openapi: new Model\Operation( + summary: 'Returns a list of content types.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the list of content type info objects or content types (including Field definitions) is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list of content types.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeInfoList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeInfoList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example', + ], + 'application/vnd.ibexa.api.ContentTypeList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeList', + ], + ], + 'application/vnd.ibexa.api.ContentTypeList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeListWrapper', + ], + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read the content types.', + ], + ], + ), +)] +class ContentTypeListController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns a list of content types. + * + * @return \Ibexa\Rest\Server\Values\ContentTypeList|\Ibexa\Rest\Server\Values\ContentTypeInfoList + */ + public function listContentTypes(Request $request) + { + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.contenttypelist') { + $return = new Values\ContentTypeList([], $request->getPathInfo()); + } else { + $return = new Values\ContentTypeInfoList([], $request->getPathInfo()); + } + + if ($request->query->has('identifier')) { + $return->contentTypes = [$this->loadContentTypeByIdentifier($request)]; + + return $return; + } + + if ($request->query->has('remoteId')) { + $return->contentTypes = [ + $this->loadContentTypeByRemoteId($request), + ]; + + return $return; + } + + $limit = null; + if ($request->query->has('limit')) { + $limit = (int)$request->query->get('limit', null); + if ($limit <= 0) { + throw new BadRequestException('wrong value for limit parameter'); + } + } + $contentTypes = $this->getContentTypeList(); + $sort = $request->query->get('sort'); + if ($request->query->has('orderby')) { + $orderby = $request->query->get('orderby'); + $this->sortContentTypeList($contentTypes, $orderby, $sort); + } + $offset = $request->query->get('offset', 0); + $return->contentTypes = array_slice($contentTypes, $offset, $limit); + + return $return; + } + + /** + * Loads a content type by its identifier. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + public function loadContentTypeByIdentifier(Request $request) + { + return $this->contentTypeService->loadContentTypeByIdentifier( + $request->query->get('identifier'), + Language::ALL + ); + } + + /** + * Loads a content type by its remote ID. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + public function loadContentTypeByRemoteId(Request $request) + { + return $this->contentTypeService->loadContentTypeByRemoteId( + $request->query->get('remoteId'), + Language::ALL + ); + } + + /** + * @param array &$contentTypes + * @param string $orderby + * + * @return mixed + * + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + */ + protected function sortContentTypeList(array &$contentTypes, $orderby, $sort = 'asc') + { + switch ($orderby) { + case 'name': + if ($sort === 'asc' || $sort === null) { + usort( + $contentTypes, + static function (APIContentType $contentType1, APIContentType $contentType2) { + return strcasecmp($contentType1->identifier, $contentType2->identifier); + } + ); + } elseif ($sort === 'desc') { + usort( + $contentTypes, + static function (APIContentType $contentType1, APIContentType $contentType2) { + return strcasecmp($contentType1->identifier, $contentType2->identifier) * -1; + } + ); + } else { + throw new BadRequestException('wrong value for sort parameter'); + } + break; + case 'lastmodified': + if ($sort === 'asc' || $sort === null) { + usort( + $contentTypes, + static function ($timeObj3, $timeObj4) { + $timeObj3 = strtotime($timeObj3->modificationDate->format('Y-m-d H:i:s')); + $timeObj4 = strtotime($timeObj4->modificationDate->format('Y-m-d H:i:s')); + + return $timeObj3 > $timeObj4; + } + ); + } elseif ($sort === 'desc') { + usort( + $contentTypes, + static function ($timeObj3, $timeObj4) { + $timeObj3 = strtotime($timeObj3->modificationDate->format('Y-m-d H:i:s')); + $timeObj4 = strtotime($timeObj4->modificationDate->format('Y-m-d H:i:s')); + + return $timeObj3 < $timeObj4; + } + ); + } else { + throw new BadRequestException('wrong value for sort parameter'); + } + break; + default: + throw new BadRequestException('wrong value for orderby parameter'); + break; + } + } + + /** + * @return ContentType[] + */ + protected function getContentTypeList() + { + $contentTypes = []; + foreach ($this->contentTypeService->loadContentTypeGroups() as $contentTypeGroup) { + $contentTypes = array_merge( + $contentTypes, + $this->contentTypeService->loadContentTypes($contentTypeGroup, Language::ALL) + ); + } + + return $contentTypes; + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeListForGroupController.php b/src/lib/Server/Controller/ContentType/ContentTypeListForGroupController.php new file mode 100644 index 000000000..06f5ebecd --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeListForGroupController.php @@ -0,0 +1,111 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/typegroups/{contentTypeGroupId}/types', + name: 'List content types for group', + openapi: new Model\Operation( + summary: 'Returns a list of content types in the provided group.', + tags: [ + 'Type Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the list of content type info objects or content types (including Field definitions) is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list on content types.', + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeInfoList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeInfoList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeInfoListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example', + ], + 'application/vnd.ibexa.api.ContentTypeList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/GET/ContentTypeInfoList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read the content types.', + ], + ], + ), +)] +class ContentTypeListForGroupController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Returns a list of content types of the group. + * + * @param string $contentTypeGroupId + * + * @return \Ibexa\Rest\Server\Values\ContentTypeList|\Ibexa\Rest\Server\Values\ContentTypeInfoList + */ + public function listContentTypesForGroup($contentTypeGroupId, Request $request) + { + $contentTypes = $this->contentTypeService->loadContentTypes( + $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId, Language::ALL), + Language::ALL + ); + + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.contenttypelist') { + return new Values\ContentTypeList($contentTypes, $request->getPathInfo()); + } + + return new Values\ContentTypeInfoList($contentTypes, $request->getPathInfo()); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeLoadByIdController.php b/src/lib/Server/Controller/ContentType/ContentTypeLoadByIdController.php new file mode 100644 index 000000000..309c7e608 --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeLoadByIdController.php @@ -0,0 +1,106 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/types/{contentTypeId}', + name: 'Get content type', + openapi: new Model\Operation( + summary: 'Returns the content type with the provided ID.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the content type is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the content type.', + 'content' => [ + 'application/vnd.ibexa.api.ContentType+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentType', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example', + ], + 'application/vnd.ibexa.api.ContentType+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/GET/ContentType.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content type does not exist.', + ], + ], + ), +)] +class ContentTypeLoadByIdController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Loads a content type. + * + * @param $contentTypeId + * + * @return \Ibexa\Rest\Server\Values\RestContentType + */ + public function loadContentType($contentTypeId) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId, Language::ALL); + + return new Values\RestContentType( + $contentType, + $contentType->getFieldDefinitions()->toArray() + ); + } +} diff --git a/src/lib/Server/Controller/ContentType/ContentTypeUnlinkFromGroupController.php b/src/lib/Server/Controller/ContentType/ContentTypeUnlinkFromGroupController.php new file mode 100644 index 000000000..ee70d408e --- /dev/null +++ b/src/lib/Server/Controller/ContentType/ContentTypeUnlinkFromGroupController.php @@ -0,0 +1,137 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ContentType; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/types/{contentTypeId}/groups/{id}', + name: 'Unlink group from content type', + openapi: new Model\Operation( + summary: 'Removes the given group from the content type and returns the updated group list.', + tags: [ + 'Type', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated content type group list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentTypeId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.ContentTypeGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.ContentTypeGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentTypeGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete this content type.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - content type cannot be unlinked from the only remaining group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The resource does not exist.', + ], + ], + ), +)] +class ContentTypeUnlinkFromGroupController extends RestController +{ + protected ContentTypeService $contentTypeService; + + public function __construct(ContentTypeService $contentTypeService) + { + $this->contentTypeService = $contentTypeService; + } + + /** + * Removes the given group from the content type and returns the updated group list. + * + * @param $contentTypeId + * @param $contentTypeGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\ContentTypeGroupRefList + */ + public function unlinkContentTypeFromGroup($contentTypeId, $contentTypeGroupId) + { + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroup($contentTypeGroupId); + + $existingContentTypeGroups = $contentType->getContentTypeGroups(); + $contentTypeInGroup = false; + foreach ($existingContentTypeGroups as $existingGroup) { + if ($existingGroup->id == $contentTypeGroup->id) { + $contentTypeInGroup = true; + break; + } + } + + if (!$contentTypeInGroup) { + throw new Exceptions\NotFoundException('The content type is not in the provided group'); + } + + if (count($existingContentTypeGroups) == 1) { + throw new ForbiddenException('Cannot unlink the content type from its only remaining group'); + } + + $this->contentTypeService->unassignContentTypeGroup( + $contentType, + $contentTypeGroup + ); + + $contentType = $this->contentTypeService->loadContentType($contentTypeId); + + return new Values\ContentTypeGroupRefList( + $contentType, + $contentType->getContentTypeGroups() + ); + } +} diff --git a/src/lib/Server/Controller/JWT.php b/src/lib/Server/Controller/JWT.php index f1f705b3b..fabaf68af 100644 --- a/src/lib/Server/Controller/JWT.php +++ b/src/lib/Server/Controller/JWT.php @@ -8,9 +8,81 @@ namespace Ibexa\Rest\Server\Controller; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; use Ibexa\Rest\Server\Controller as RestController; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +#[Post( + uriTemplate: '/user/token/jwt', + name: 'Create JWT token', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates JWT authentication token.', + tags: [ + 'User Token', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the token is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The SessionInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.JWTInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/JWTInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.xml.example', + ], + 'application/vnd.ibexa.api.JWTInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/JWTInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/token/jwt/POST/JWTInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.JWT+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/JWT', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.xml.example', + ], + 'application/vnd.ibexa.api.JWT+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/JWTWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/token/jwt/POST/JWT.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - Unauthorized', + ], + ], + ), +)] final class JWT extends RestController { public function createToken(Request $request): void diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php deleted file mode 100644 index 9c005c800..000000000 --- a/src/lib/Server/Controller/Language.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\LanguageService; -use Ibexa\Contracts\Core\Repository\Values\Content\Language as ApiLanguage; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Values\LanguageList; -use Traversable; - -final class Language extends RestController -{ - private LanguageService $languageService; - - public function __construct(LanguageService $languageService) - { - $this->languageService = $languageService; - } - - public function listLanguages(): LanguageList - { - $languages = $this->languageService->loadLanguages(); - - if ($languages instanceof Traversable) { - $languages = iterator_to_array($languages); - } - - return new LanguageList($languages); - } - - public function loadLanguage(string $languageCode): ApiLanguage - { - return $this->languageService->loadLanguage($languageCode); - } -} diff --git a/src/lib/Server/Controller/Language/LanguageListController.php b/src/lib/Server/Controller/Language/LanguageListController.php new file mode 100644 index 000000000..9373ec053 --- /dev/null +++ b/src/lib/Server/Controller/Language/LanguageListController.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Language; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values\LanguageList; +use Symfony\Component\HttpFoundation\Response; +use Traversable; + +#[Get( + uriTemplate: '/languages', + name: 'Language list', + openapi: new Model\Operation( + summary: 'Lists languages', + tags: [ + 'Language', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.LanguageList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LanguageList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example', + ], + 'application/vnd.ibexa.api.LanguageList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LanguageListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example', + ], + ], + ], + ], + ), +)] +final class LanguageListController extends RestController +{ + private LanguageService $languageService; + + public function __construct(LanguageService $languageService) + { + $this->languageService = $languageService; + } + + public function listLanguages(): LanguageList + { + $languages = $this->languageService->loadLanguages(); + + if ($languages instanceof Traversable) { + $languages = iterator_to_array($languages); + } + + return new LanguageList($languages); + } +} diff --git a/src/lib/Server/Controller/Language/LanguageLoadByIdController.php b/src/lib/Server/Controller/Language/LanguageLoadByIdController.php new file mode 100644 index 000000000..708de672f --- /dev/null +++ b/src/lib/Server/Controller/Language/LanguageLoadByIdController.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Language; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language as ApiLanguage; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/languages/{code}', + name: 'Get language', + openapi: new Model\Operation( + tags: [ + 'Language', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the language is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'code', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Language+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Language', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example', + ], + 'application/vnd.ibexa.api.Language+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LanguageWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/code/GET/Language.json.example', + ], + ], + ], + ], + ), +)] +final class LanguageLoadByIdController extends RestController +{ + private LanguageService $languageService; + + public function __construct(LanguageService $languageService) + { + $this->languageService = $languageService; + } + + public function loadLanguage(string $languageCode): ApiLanguage + { + return $this->languageService->loadLanguage($languageCode); + } +} diff --git a/src/lib/Server/Controller/Location.php b/src/lib/Server/Controller/Location.php deleted file mode 100644 index 8f60e6f2b..000000000 --- a/src/lib/Server/Controller/Location.php +++ /dev/null @@ -1,516 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Contracts\Core\Repository\TrashService; -use Ibexa\Contracts\Core\Repository\URLAliasService; -use Ibexa\Contracts\Rest\Exceptions; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\BadRequestException; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * Location controller. - */ -class Location extends RestController -{ - /** - * Location service. - * - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Content service. - * - * @var \Ibexa\Contracts\Core\Repository\ContentService - */ - protected $contentService; - - /** - * Trash service. - * - * @var \Ibexa\Contracts\Core\Repository\TrashService - */ - protected $trashService; - - /** - * URLAlias Service. - * - * @var \Ibexa\Contracts\Core\Repository\URLAliasService - */ - protected $urlAliasService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService - * @param \Ibexa\Contracts\Core\Repository\TrashService $trashService - * @param \Ibexa\Contracts\Core\Repository\URLAliasService $urlAliasService - */ - public function __construct( - LocationService $locationService, - ContentService $contentService, - TrashService $trashService, - URLAliasService $urlAliasService - ) { - $this->locationService = $locationService; - $this->contentService = $contentService; - $this->trashService = $trashService; - $this->urlAliasService = $urlAliasService; - } - - /** - * Loads the location for a given ID (x)or remote ID. - * - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException - * - * @return \Ibexa\Rest\Server\Values\TemporaryRedirect - */ - public function redirectLocation(Request $request) - { - if ($request->query->has('id')) { - $location = $this->locationService->loadLocation($request->query->get('id')); - } elseif ($request->query->has('remoteId')) { - $location = $this->locationService->loadLocationByRemoteId($request->query->get('remoteId')); - } elseif ($request->query->has('urlAlias')) { - $urlAlias = $this->urlAliasService->lookup($request->query->get('urlAlias')); - $location = $this->locationService->loadLocation($urlAlias->destination); - } else { - throw new BadRequestException("At least one of 'id', 'remoteId' or 'urlAlias' parameters is required."); - } - - return new Values\TemporaryRedirect( - $this->router->generate( - 'ibexa.rest.load_location', - [ - 'locationPath' => trim($location->pathString, '/'), - ] - ) - ); - } - - /** - * Creates a new location for object with id $contentId. - * - * @param mixed $contentId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedLocation - */ - public function createLocation($contentId, Request $request) - { - $locationCreateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $contentInfo = $this->contentService->loadContentInfo($contentId); - - try { - $createdLocation = $this->locationService->createLocation($contentInfo, $locationCreateStruct); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\CreatedLocation(['restLocation' => new Values\RestLocation($createdLocation, 0)]); - } - - /** - * Loads a location. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\RestLocation - */ - public function loadLocation($locationPath) - { - $location = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath) - ); - - if (trim($location->pathString, '/') != $locationPath) { - throw new Exceptions\NotFoundException( - "Could not find a Location with path string $locationPath" - ); - } - - return new Values\CachedValue( - new Values\RestLocation( - $location, - $this->locationService->getLocationChildCount($location) - ), - ['locationId' => $location->id] - ); - } - - /** - * Deletes a location. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteSubtree($locationPath) - { - $location = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath) - ); - $this->locationService->deleteLocation($location); - - return new Values\NoContent(); - } - - /** - * Copies a subtree to a new destination. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\ResourceCreated - */ - public function copySubtree($locationPath, Request $request) - { - $location = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath) - ); - - $destinationLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath( - $this->requestParser->parseHref( - $request->headers->get('Destination'), - 'locationPath' - ) - ) - ); - - $newLocation = $this->locationService->copySubtree($location, $destinationLocation); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_location', - [ - 'locationPath' => trim($newLocation->pathString, '/'), - ] - ) - ); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function copy(string $locationPath, Request $request): Values\ResourceCreated - { - $locationId = $this->extractLocationIdFromPath($locationPath); - $location = $this->locationService->loadLocation($locationId); - - $destinationLocation = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent(), - ), - ); - - $newLocation = $this->locationService->copySubtree($location, $destinationLocation); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_location', - [ - 'locationPath' => trim($newLocation->pathString, '/'), - ], - ) - ); - } - - /** - * Moves a subtree to a new location. - * - * @param string $locationPath - * - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException if the Destination header cannot be parsed as location or trash - * - * @return \Ibexa\Rest\Server\Values\ResourceCreated|\Ibexa\Rest\Server\Values\NoContent - */ - public function moveSubtree($locationPath, Request $request) - { - $locationToMove = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath) - ); - - $destinationLocationId = null; - $destinationHref = $request->headers->get('Destination'); - try { - // First check to see if the destination is for moving within another subtree - $destinationLocationId = $this->extractLocationIdFromPath( - $this->requestParser->parseHref($destinationHref, 'locationPath') - ); - - // We're moving the subtree - $destinationLocation = $this->locationService->loadLocation($destinationLocationId); - $this->locationService->moveSubtree($locationToMove, $destinationLocation); - - // Reload the location to get the new position is subtree - $locationToMove = $this->locationService->loadLocation($locationToMove->id); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_location', - [ - 'locationPath' => trim($locationToMove->pathString, '/'), - ] - ) - ); - } catch (Exceptions\InvalidArgumentException $e) { - // If parsing of destination fails, let's try to see if destination is trash - try { - $route = $this->requestParser->parse($destinationHref); - if (!isset($route['_route']) || $route['_route'] !== 'ibexa.rest.load_trash_items') { - throw new Exceptions\InvalidArgumentException(''); - } - // Trash the subtree - $trashItem = $this->trashService->trash($locationToMove); - - if (isset($trashItem)) { - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_trash_item', - ['trashItemId' => $trashItem->id] - ) - ); - } else { - // Only a location has been trashed and not the object - return new Values\NoContent(); - } - } catch (Exceptions\InvalidArgumentException $e) { - // If that fails, the Destination header is not formatted right - // so just throw the BadRequestException - throw new BadRequestException("{$destinationHref} is not an acceptable destination"); - } - } - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function moveLocation(Request $request, string $locationPath): Values\ResourceCreated - { - $destinationLocation = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent(), - ), - ); - - $locationToMove = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath), - ); - - $this->locationService->moveSubtree($locationToMove, $destinationLocation); - - // Reload the location to get a new subtree position - $locationToMove = $this->locationService->loadLocation($locationToMove->id); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_location', - [ - 'locationPath' => trim($locationToMove->getPathString(), '/'), - ], - ), - ); - } - - /** - * Swaps a location with another one. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function swapLocation($locationPath, Request $request) - { - $locationId = $this->extractLocationIdFromPath($locationPath); - $location = $this->locationService->loadLocation($locationId); - - $destinationLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath( - $this->requestParser->parseHref( - $request->headers->get('Destination'), - 'locationPath' - ) - ) - ); - - $this->locationService->swapLocation($location, $destinationLocation); - - return new Values\NoContent(); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function swap(Request $request, string $locationPath): Values\NoContent - { - $locationId = $this->extractLocationIdFromPath($locationPath); - $location = $this->locationService->loadLocation($locationId); - - $destinationLocation = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent(), - ), - ); - - $this->locationService->swapLocation($location, $destinationLocation); - - return new Values\NoContent(); - } - - /** - * Loads a location by remote ID. - * - * @todo remove, or use in loadLocation with filter - * - * @return \Ibexa\Rest\Server\Values\LocationList - */ - public function loadLocationByRemoteId(Request $request) - { - return new Values\LocationList( - [ - new Values\RestLocation( - $location = $this->locationService->loadLocationByRemoteId( - $request->query->get('remoteId') - ), - $this->locationService->getLocationChildCount($location) - ), - ], - $request->getPathInfo() - ); - } - - /** - * Loads all locations for content object. - * - * @param mixed $contentId - * - * @return \Ibexa\Rest\Server\Values\LocationList - */ - public function loadLocationsForContent($contentId, Request $request) - { - $restLocations = []; - $contentInfo = $this->contentService->loadContentInfo($contentId); - foreach ($this->locationService->loadLocations($contentInfo) as $location) { - $restLocations[] = new Values\RestLocation( - $location, - // @todo Remove, and make optional in VO. Not needed for a location list. - $this->locationService->getLocationChildCount($location) - ); - } - - return new Values\CachedValue( - new Values\LocationList($restLocations, $request->getPathInfo()), - ['locationId' => $contentInfo->mainLocationId] - ); - } - - /** - * Loads child locations of a location. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\LocationList - */ - public function loadLocationChildren($locationPath, Request $request) - { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 10; - - $restLocations = []; - $locationId = $this->extractLocationIdFromPath($locationPath); - $children = $this->locationService->loadLocationChildren( - $this->locationService->loadLocation($locationId), - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : 25 - )->locations; - foreach ($children as $location) { - $restLocations[] = new Values\RestLocation( - $location, - $this->locationService->getLocationChildCount($location) - ); - } - - return new Values\CachedValue( - new Values\LocationList($restLocations, $request->getPathInfo()), - ['locationId' => $locationId] - ); - } - - /** - * Extracts and returns an item id from a path, e.g. /1/2/58 => 58. - * - * @param string $path - * - * @return mixed - */ - private function extractLocationIdFromPath($path) - { - $pathParts = explode('/', $path); - - return array_pop($pathParts); - } - - /** - * Updates a location. - * - * @param string $locationPath - * - * @return \Ibexa\Rest\Server\Values\RestLocation - */ - public function updateLocation($locationPath, Request $request) - { - $locationUpdate = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $location = $this->locationService->loadLocation($this->extractLocationIdFromPath($locationPath)); - - // First handle hiding/unhiding so that updating location afterwards - // will return updated location with hidden/visible status correctly updated - // Exact check for true/false is needed as null signals that no hiding/unhiding - // is to be performed - if ($locationUpdate->hidden === true) { - $this->locationService->hideLocation($location); - } elseif ($locationUpdate->hidden === false) { - $this->locationService->unhideLocation($location); - } - - return new Values\RestLocation( - $location = $this->locationService->updateLocation($location, $locationUpdate->locationUpdateStruct), - $this->locationService->getLocationChildCount($location) - ); - } -} diff --git a/src/lib/Server/Controller/Location/LocationBaseController.php b/src/lib/Server/Controller/Location/LocationBaseController.php new file mode 100644 index 000000000..09088f504 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationBaseController.php @@ -0,0 +1,75 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class LocationBaseController extends RestController +{ + protected LocationService $locationService; + + protected ContentService $contentService; + + protected TrashService $trashService; + + protected URLAliasService $urlAliasService; + + public function __construct( + LocationService $locationService, + ContentService $contentService, + TrashService $trashService, + URLAliasService $urlAliasService + ) { + $this->locationService = $locationService; + $this->contentService = $contentService; + $this->trashService = $trashService; + $this->urlAliasService = $urlAliasService; + } + + /** + * Loads a location by remote ID. + * + * @todo remove, or use in loadLocation with filter + * + * @return \Ibexa\Rest\Server\Values\LocationList + */ + public function loadLocationByRemoteId(Request $request) + { + return new Values\LocationList( + [ + new Values\RestLocation( + $location = $this->locationService->loadLocationByRemoteId( + $request->query->get('remoteId') + ), + $this->locationService->getLocationChildCount($location) + ), + ], + $request->getPathInfo() + ); + } + + /** + * Extracts and returns an item id from a path, e.g. /1/2/58 => 58. + * + * @param string $path + * + * @return mixed + */ + protected function extractLocationIdFromPath($path) + { + $pathParts = explode('/', $path); + + return array_pop($pathParts); + } +} diff --git a/src/lib/Server/Controller/Location/LocationChildrenListController.php b/src/lib/Server/Controller/Location/LocationChildrenListController.php new file mode 100644 index 000000000..00dd20b0d --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationChildrenListController.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/locations/{path}/children', + name: 'Get child Locations.', + openapi: new Model\Operation( + summary: 'Loads all child Locations for the given parent Location.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Location list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.LocationList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.xml.example', + ], + 'application/vnd.ibexa.api.LocationList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationListWrapper', + ], + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item with the given ID does not exist.', + ], + ], + ), +)] +class LocationChildrenListController extends LocationBaseController +{ + /** + * Loads child locations of a location. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\LocationList + */ + public function loadLocationChildren($locationPath, Request $request) + { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 10; + + $restLocations = []; + $locationId = $this->extractLocationIdFromPath($locationPath); + $children = $this->locationService->loadLocationChildren( + $this->locationService->loadLocation($locationId), + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : 25 + )->locations; + foreach ($children as $location) { + $restLocations[] = new Values\RestLocation( + $location, + $this->locationService->getLocationChildCount($location) + ); + } + + return new Values\CachedValue( + new Values\LocationList($restLocations, $request->getPathInfo()), + ['locationId' => $locationId] + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationCreateController.php b/src/lib/Server/Controller/Location/LocationCreateController.php new file mode 100644 index 000000000..006714636 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationCreateController.php @@ -0,0 +1,133 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objects/{contentId}/locations', + name: 'Create new Location for content item', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Location for the given content item.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Location is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The LocationCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.LocationCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example', + ], + 'application/vnd.ibexa.api.LocationCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/LocationCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Location+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Location', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example', + ], + 'application/vnd.ibexa.api.Location+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this Location.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - a Location under the given parent ID already exists.', + ], + ], + ), +)] +class LocationCreateController extends LocationBaseController +{ + /** + * Creates a new location for object with id $contentId. + * + * @param mixed $contentId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedLocation + */ + public function createLocation($contentId, Request $request) + { + $locationCreateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $contentInfo = $this->contentService->loadContentInfo($contentId); + + try { + $createdLocation = $this->locationService->createLocation($contentInfo, $locationCreateStruct); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\CreatedLocation(['restLocation' => new Values\RestLocation($createdLocation, 0)]); + } +} diff --git a/src/lib/Server/Controller/Location/LocationForContentListController.php b/src/lib/Server/Controller/Location/LocationForContentListController.php new file mode 100644 index 000000000..577d37a86 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationForContentListController.php @@ -0,0 +1,104 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/locations', + name: 'Get Locations for content item', + openapi: new Model\Operation( + summary: 'Loads all Locations for the given content item.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Location list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.LocationList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.xml.example', + ], + 'application/vnd.ibexa.api.LocationList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/GET/LocationList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this content item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the content item with the given ID does not exist.', + ], + ], + ), +)] +class LocationForContentListController extends LocationBaseController +{ + /** + * Loads all locations for content object. + * + * @param mixed $contentId + * + * @return \Ibexa\Rest\Server\Values\LocationList + */ + public function loadLocationsForContent($contentId, Request $request) + { + $restLocations = []; + $contentInfo = $this->contentService->loadContentInfo($contentId); + foreach ($this->locationService->loadLocations($contentInfo) as $location) { + $restLocations[] = new Values\RestLocation( + $location, + // @todo Remove, and make optional in VO. Not needed for a location list. + $this->locationService->getLocationChildCount($location) + ); + } + + return new Values\CachedValue( + new Values\LocationList($restLocations, $request->getPathInfo()), + ['locationId' => $contentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationLoadByPathController.php b/src/lib/Server/Controller/Location/LocationLoadByPathController.php new file mode 100644 index 000000000..bee862cbd --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationLoadByPathController.php @@ -0,0 +1,107 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/locations/{path}', + name: 'Load Location', + openapi: new Model\Operation( + summary: 'Loads the Location for the given path e.g. \'/content/locations/1/2/61\'.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Location is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Location+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Location', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example', + ], + 'application/vnd.ibexa.api.Location+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to read this Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Location with the given path does not exist.', + ], + ], + ), +)] +class LocationLoadByPathController extends LocationBaseController +{ + /** + * Loads a location. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\RestLocation + */ + public function loadLocation($locationPath) + { + $location = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath) + ); + + if (trim($location->pathString, '/') != $locationPath) { + throw new Exceptions\NotFoundException( + "Could not find a Location with path string $locationPath" + ); + } + + return new Values\CachedValue( + new Values\RestLocation( + $location, + $this->locationService->getLocationChildCount($location) + ), + ['locationId' => $location->id] + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationRedirectController.php b/src/lib/Server/Controller/Location/LocationRedirectController.php new file mode 100644 index 000000000..72af79adf --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationRedirectController.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/locations', + name: 'Load Locations by id/remoteId/urlAlias', + openapi: new Model\Operation( + summary: 'Loads the Location for a given ID (x), remote ID or URL alias.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.LocationList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Location', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example', + ], + 'application/vnd.ibexa.api.LocationList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example', + ], + ], + ], + Response::HTTP_TEMPORARY_REDIRECT => [ + 'description' => 'Temporary redirect to the main resource URL.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Location with the given ID (remote ID or URL Alias) does not exist.', + ], + ], + ), +)] +class LocationRedirectController extends LocationBaseController +{ + /** + * Loads the location for a given ID (x)or remote ID. + * + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException + * + * @return \Ibexa\Rest\Server\Values\TemporaryRedirect + */ + public function redirectLocation(Request $request) + { + if ($request->query->has('id')) { + $location = $this->locationService->loadLocation($request->query->get('id')); + } elseif ($request->query->has('remoteId')) { + $location = $this->locationService->loadLocationByRemoteId($request->query->get('remoteId')); + } elseif ($request->query->has('urlAlias')) { + $urlAlias = $this->urlAliasService->lookup($request->query->get('urlAlias')); + $location = $this->locationService->loadLocation($urlAlias->destination); + } else { + throw new BadRequestException("At least one of 'id', 'remoteId' or 'urlAlias' parameters is required."); + } + + return new Values\TemporaryRedirect( + $this->router->generate( + 'ibexa.rest.load_location', + [ + 'locationPath' => trim($location->pathString, '/'), + ] + ) + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationSubtreeCopyController.php b/src/lib/Server/Controller/Location/LocationSubtreeCopyController.php new file mode 100644 index 000000000..8bac77905 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationSubtreeCopyController.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class LocationSubtreeCopyController extends LocationBaseController +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function copy(string $locationPath, Request $request): Values\ResourceCreated + { + $locationId = $this->extractLocationIdFromPath($locationPath); + $location = $this->locationService->loadLocation($locationId); + + $destinationLocation = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent(), + ), + ); + + $newLocation = $this->locationService->copySubtree($location, $destinationLocation); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_location', + [ + 'locationPath' => trim($newLocation->pathString, '/'), + ], + ) + ); + } + + /** + * Copies a subtree to a new destination. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\ResourceCreated + */ + public function copySubtree($locationPath, Request $request) + { + $location = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath) + ); + + $destinationLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath( + $this->requestParser->parseHref( + $request->headers->get('Destination'), + 'locationPath' + ) + ) + ); + + $newLocation = $this->locationService->copySubtree($location, $destinationLocation); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_location', + [ + 'locationPath' => trim($newLocation->pathString, '/'), + ] + ) + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationSubtreeDeleteController.php b/src/lib/Server/Controller/Location/LocationSubtreeDeleteController.php new file mode 100644 index 000000000..d4211a083 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationSubtreeDeleteController.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/locations/{path}', + name: 'Delete subtree', + openapi: new Model\Operation( + summary: 'Deletes the complete subtree for the given path. Every content item which does not have any other Location is deleted. Otherwise the deleted Location is removed from the content item. The children are recursively deleted.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this subtree.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Location with the given ID does not exist.', + ], + ], + ), +)] +class LocationSubtreeDeleteController extends LocationBaseController +{ + /** + * Deletes a location. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteSubtree($locationPath) + { + $location = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath) + ); + $this->locationService->deleteLocation($location); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Location/LocationSubtreeMoveController.php b/src/lib/Server/Controller/Location/LocationSubtreeMoveController.php new file mode 100644 index 000000000..4baa21805 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationSubtreeMoveController.php @@ -0,0 +1,118 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Get; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class LocationSubtreeMoveController extends LocationBaseController +{ + /** + * Moves a subtree to a new location. + * + * @param string $locationPath + * + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException if the Destination header cannot be parsed as location or trash + * + * @return \Ibexa\Rest\Server\Values\ResourceCreated|\Ibexa\Rest\Server\Values\NoContent + */ + public function moveSubtree($locationPath, Request $request) + { + $locationToMove = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath) + ); + + $destinationLocationId = null; + $destinationHref = $request->headers->get('Destination'); + try { + // First check to see if the destination is for moving within another subtree + $destinationLocationId = $this->extractLocationIdFromPath( + $this->requestParser->parseHref($destinationHref, 'locationPath') + ); + + // We're moving the subtree + $destinationLocation = $this->locationService->loadLocation($destinationLocationId); + $this->locationService->moveSubtree($locationToMove, $destinationLocation); + + // Reload the location to get the new position is subtree + $locationToMove = $this->locationService->loadLocation($locationToMove->id); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_location', + [ + 'locationPath' => trim($locationToMove->pathString, '/'), + ] + ) + ); + } catch (Exceptions\InvalidArgumentException $e) { + // If parsing of destination fails, let's try to see if destination is trash + try { + $route = $this->requestParser->parse($destinationHref); + if (!isset($route['_route']) || $route['_route'] !== 'ibexa.rest.load_trash_items') { + throw new Exceptions\InvalidArgumentException(''); + } + // Trash the subtree + $trashItem = $this->trashService->trash($locationToMove); + + if (isset($trashItem)) { + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_trash_item', + ['trashItemId' => $trashItem->id] + ) + ); + } else { + // Only a location has been trashed and not the object + return new Values\NoContent(); + } + } catch (Exceptions\InvalidArgumentException $e) { + // If that fails, the Destination header is not formatted right + // so just throw the BadRequestException + throw new BadRequestException("{$destinationHref} is not an acceptable destination"); + } + } + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function moveLocation(Request $request, string $locationPath): Values\ResourceCreated + { + $destinationLocation = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent(), + ), + ); + + $locationToMove = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath), + ); + + $this->locationService->moveSubtree($locationToMove, $destinationLocation); + + // Reload the location to get a new subtree position + $locationToMove = $this->locationService->loadLocation($locationToMove->id); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_location', + [ + 'locationPath' => trim($locationToMove->getPathString(), '/'), + ], + ), + ); + } +} diff --git a/src/lib/Server/Controller/Location/LocationSwapController.php b/src/lib/Server/Controller/Location/LocationSwapController.php new file mode 100644 index 000000000..514245ff1 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationSwapController.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; + +class LocationSwapController extends LocationBaseController +{ + /** + * Swaps a location with another one. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function swapLocation($locationPath, Request $request) + { + $locationId = $this->extractLocationIdFromPath($locationPath); + $location = $this->locationService->loadLocation($locationId); + + $destinationLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath( + $this->requestParser->parseHref( + $request->headers->get('Destination'), + 'locationPath' + ) + ) + ); + + $this->locationService->swapLocation($location, $destinationLocation); + + return new Values\NoContent(); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function swap(Request $request, string $locationPath): Values\NoContent + { + $locationId = $this->extractLocationIdFromPath($locationPath); + $location = $this->locationService->loadLocation($locationId); + + $destinationLocation = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent(), + ), + ); + + $this->locationService->swapLocation($location, $destinationLocation); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Location/LocationUpdateController.php b/src/lib/Server/Controller/Location/LocationUpdateController.php new file mode 100644 index 000000000..66e7659c7 --- /dev/null +++ b/src/lib/Server/Controller/Location/LocationUpdateController.php @@ -0,0 +1,141 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Location; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/locations/{path}', + name: 'Update Location', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates the Location. This method can also be used to hide/reveal a Location via the hidden field in the LocationUpdate. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Location is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The LocationUpdate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.LocationUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationUpdateStruct', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example', + ], + 'application/vnd.ibexa.api.LocationUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationUpdateStructWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/locations/location_id/PATCH/LocationUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Location+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Location', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.xml.example', + ], + 'application/vnd.ibexa.api.Location+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/LocationWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/locations/POST/Location.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update this Location.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Location with the given ID does not exist.', + ], + ], + ), +)] +class LocationUpdateController extends LocationBaseController +{ + /** + * Updates a location. + * + * @param string $locationPath + * + * @return \Ibexa\Rest\Server\Values\RestLocation + */ + public function updateLocation($locationPath, Request $request) + { + $locationUpdate = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $location = $this->locationService->loadLocation($this->extractLocationIdFromPath($locationPath)); + + // First handle hiding/unhiding so that updating location afterwards + // will return updated location with hidden/visible status correctly updated + // Exact check for true/false is needed as null signals that no hiding/unhiding + // is to be performed + if ($locationUpdate->hidden === true) { + $this->locationService->hideLocation($location); + } elseif ($locationUpdate->hidden === false) { + $this->locationService->unhideLocation($location); + } + + return new Values\RestLocation( + $location = $this->locationService->updateLocation($location, $locationUpdate->locationUpdateStruct), + $this->locationService->getLocationChildCount($location) + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState.php b/src/lib/Server/Controller/ObjectState.php deleted file mode 100644 index 9f6ab55f1..000000000 --- a/src/lib/Server/Controller/ObjectState.php +++ /dev/null @@ -1,344 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; -use Ibexa\Contracts\Core\Repository\ObjectStateService; -use Ibexa\Contracts\Core\Repository\Values\Content\Language; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Values\ContentObjectStates; -use Ibexa\Rest\Values\RestObjectState; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * ObjectState controller. - */ -class ObjectState extends RestController -{ - /** - * ObjectState service. - * - * @var \Ibexa\Contracts\Core\Repository\ObjectStateService - */ - protected $objectStateService; - - /** - * Content service. - * - * @var \Ibexa\Contracts\Core\Repository\ContentService - */ - protected $contentService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\ObjectStateService $objectStateService - * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService - */ - public function __construct(ObjectStateService $objectStateService, ContentService $contentService) - { - $this->objectStateService = $objectStateService; - $this->contentService = $contentService; - } - - /** - * Creates a new object state group. - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedObjectStateGroup - */ - public function createObjectStateGroup(Request $request) - { - try { - $createdStateGroup = $this->objectStateService->createObjectStateGroup( - $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */$e->getMessage()); - } - - return new Values\CreatedObjectStateGroup( - [ - 'objectStateGroup' => $createdStateGroup, - ] - ); - } - - /** - * Creates a new object state. - * - * @param $objectStateGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedObjectState - */ - public function createObjectState($objectStateGroupId, Request $request) - { - $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); - - try { - $createdObjectState = $this->objectStateService->createObjectState( - $objectStateGroup, - $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\CreatedObjectState( - [ - 'objectState' => new RestObjectState( - $createdObjectState, - $objectStateGroup->id - ), - ] - ); - } - - /** - * Loads an object state group. - * - * @param $objectStateGroupId - * - * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup - */ - public function loadObjectStateGroup($objectStateGroupId) - { - return $this->objectStateService->loadObjectStateGroup($objectStateGroupId, Language::ALL); - } - - /** - * Loads an object state. - * - * @param $objectStateGroupId - * @param $objectStateId - * - * @return \Ibexa\Rest\Values\RestObjectState - */ - public function loadObjectState($objectStateGroupId, $objectStateId) - { - return new RestObjectState( - $this->objectStateService->loadObjectState($objectStateId, Language::ALL), - $objectStateGroupId - ); - } - - /** - * Returns a list of all object state groups. - * - * @return \Ibexa\Rest\Server\Values\ObjectStateGroupList - */ - public function loadObjectStateGroups() - { - return new Values\ObjectStateGroupList( - $this->objectStateService->loadObjectStateGroups(0, -1, Language::ALL) - ); - } - - /** - * Returns a list of all object states of the given group. - * - * @param $objectStateGroupId - * - * @return \Ibexa\Rest\Server\Values\ObjectStateList - */ - public function loadObjectStates($objectStateGroupId) - { - $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); - - return new Values\ObjectStateList( - $this->objectStateService->loadObjectStates($objectStateGroup, Language::ALL), - $objectStateGroup->id - ); - } - - /** - * The given object state group including the object states is deleted. - * - * @param $objectStateGroupId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteObjectStateGroup($objectStateGroupId) - { - $this->objectStateService->deleteObjectStateGroup( - $this->objectStateService->loadObjectStateGroup($objectStateGroupId) - ); - - return new Values\NoContent(); - } - - /** - * The given object state is deleted. - * - * @param $objectStateId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteObjectState($objectStateId) - { - $this->objectStateService->deleteObjectState( - $this->objectStateService->loadObjectState($objectStateId) - ); - - return new Values\NoContent(); - } - - /** - * Updates an object state group. - * - * @param $objectStateGroupId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup - */ - public function updateObjectStateGroup($objectStateGroupId, Request $request) - { - $updateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); - - try { - $updatedStateGroup = $this->objectStateService->updateObjectStateGroup($objectStateGroup, $updateStruct); - - return $updatedStateGroup; - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - /** - * Updates an object state. - * - * @param $objectStateGroupId - * @param $objectStateId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Values\RestObjectState - */ - public function updateObjectState($objectStateGroupId, $objectStateId, Request $request) - { - $updateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $objectState = $this->objectStateService->loadObjectState($objectStateId); - - try { - $updatedObjectState = $this->objectStateService->updateObjectState($objectState, $updateStruct); - - return new RestObjectState($updatedObjectState, $objectStateGroupId); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - /** - * Returns the object states of content. - * - * @param $contentId - * - * @return \Ibexa\Rest\Values\ContentObjectStates - */ - public function getObjectStatesForContent($contentId) - { - $groups = $this->objectStateService->loadObjectStateGroups(); - $contentInfo = $this->contentService->loadContentInfo($contentId); - - $contentObjectStates = []; - - foreach ($groups as $group) { - try { - $state = $this->objectStateService->getContentState($contentInfo, $group); - $contentObjectStates[] = new RestObjectState($state, $group->id); - } catch (NotFoundException $e) { - // Do nothing - } - } - - return new ContentObjectStates($contentObjectStates); - } - - /** - * Updates object states of content - * An object state in the input overrides the state of the object state group. - * - * @param $contentId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Values\ContentObjectStates - */ - public function setObjectStatesForContent($contentId, Request $request) - { - $newObjectStates = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $countByGroups = []; - foreach ($newObjectStates as $newObjectState) { - $groupId = (int)$newObjectState->groupId; - if (array_key_exists($groupId, $countByGroups)) { - ++$countByGroups[$groupId]; - } else { - $countByGroups[$groupId] = 1; - } - } - - foreach ($countByGroups as $groupId => $count) { - if ($count > 1) { - throw new ForbiddenException( - /** @Ignore */ - "Multiple Object states provided for group with ID $groupId" - ); - } - } - - $contentInfo = $this->contentService->loadContentInfo($contentId); - - $contentObjectStates = []; - foreach ($newObjectStates as $newObjectState) { - $objectStateGroup = $this->objectStateService->loadObjectStateGroup($newObjectState->groupId); - $this->objectStateService->setContentState($contentInfo, $objectStateGroup, $newObjectState->objectState); - $contentObjectStates[(int)$objectStateGroup->id] = $newObjectState; - } - - return new ContentObjectStates($contentObjectStates); - } -} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateCreateController.php b/src/lib/Server/Controller/ObjectState/ObjectStateCreateController.php new file mode 100644 index 000000000..cdfb009a4 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateCreateController.php @@ -0,0 +1,156 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Values\RestObjectState; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}/objectstates', + name: 'Create Object state', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Object state.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Object state is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Object state input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ObjectStateCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Object state created.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectState+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectState', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example', + ], + 'application/vnd.ibexa.api.ObjectState+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create an Object state.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - An Object state with the same identifier already exists in the given group.', + ], + ], + ), +)] +class ObjectStateCreateController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Creates a new object state. + * + * @param $objectStateGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedObjectState + */ + public function createObjectState($objectStateGroupId, Request $request) + { + $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); + + try { + $createdObjectState = $this->objectStateService->createObjectState( + $objectStateGroup, + $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\CreatedObjectState( + [ + 'objectState' => new RestObjectState( + $createdObjectState, + $objectStateGroup->id + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateDeleteController.php b/src/lib/Server/Controller/ObjectState/ObjectStateDeleteController.php new file mode 100644 index 000000000..3692ec3ca --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateDeleteController.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId}', + name: 'Delete Object state', + openapi: new Model\Operation( + summary: 'Deletes provided Object state.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - Object state deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete an Object state.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The Object state does not exist.', + ], + ], + ), +)] +class ObjectStateDeleteController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * The given object state is deleted. + * + * @param $objectStateId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteObjectState($objectStateId) + { + $this->objectStateService->deleteObjectState( + $this->objectStateService->loadObjectState($objectStateId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateGroupCreateController.php b/src/lib/Server/Controller/ObjectState/ObjectStateGroupCreateController.php new file mode 100644 index 000000000..270a45f11 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateGroupCreateController.php @@ -0,0 +1,139 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/objectstategroups', + name: 'Create Object state group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Object state group.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Object state group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Object state group input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ObjectStateGroupCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroupCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Object state group created.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectStateGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create an Object state group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - An Object state group with the same identifier already exists.', + ], + ], + ), +)] +class ObjectStateGroupCreateController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Creates a new object state group. + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedObjectStateGroup + */ + public function createObjectStateGroup(Request $request) + { + try { + $createdStateGroup = $this->objectStateService->createObjectStateGroup( + $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */$e->getMessage()); + } + + return new Values\CreatedObjectStateGroup( + [ + 'objectStateGroup' => $createdStateGroup, + ] + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateGroupDeleteController.php b/src/lib/Server/Controller/ObjectState/ObjectStateGroupDeleteController.php new file mode 100644 index 000000000..379336399 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateGroupDeleteController.php @@ -0,0 +1,76 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}', + name: 'Delete Object state group', + openapi: new Model\Operation( + summary: 'Deletes the given Object state group including Object states.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - Object state group deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete an Object state group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The Object state group does not exist.', + ], + ], + ), +)] +class ObjectStateGroupDeleteController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * The given object state group including the object states is deleted. + * + * @param $objectStateGroupId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteObjectStateGroup($objectStateGroupId) + { + $this->objectStateService->deleteObjectStateGroup( + $this->objectStateService->loadObjectStateGroup($objectStateGroupId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateGroupListController.php b/src/lib/Server/Controller/ObjectState/ObjectStateGroupListController.php new file mode 100644 index 000000000..f0cee501a --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateGroupListController.php @@ -0,0 +1,94 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objectstategroups', + name: 'List Object state groups', + openapi: new Model\Operation( + summary: 'Returns a list of all Object state groups.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Object state group list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list of Object state groups.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectStateGroupList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroupList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read Object state groups.', + ], + ], + ), +)] +class ObjectStateGroupListController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Returns a list of all object state groups. + * + * @return \Ibexa\Rest\Server\Values\ObjectStateGroupList + */ + public function loadObjectStateGroups() + { + return new Values\ObjectStateGroupList( + $this->objectStateService->loadObjectStateGroups(0, -1, Language::ALL) + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateGroupLoadByIdController.php b/src/lib/Server/Controller/ObjectState/ObjectStateGroupLoadByIdController.php new file mode 100644 index 000000000..5b4abcfec --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateGroupLoadByIdController.php @@ -0,0 +1,104 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}', + name: 'Get Object state group', + openapi: new Model\Operation( + summary: 'Returns the Object state group with the provided ID.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Object state group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the Object state group.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectStateGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this Object state group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The Object state group does not exist.', + ], + ], + ), +)] +class ObjectStateGroupLoadByIdController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Loads an object state group. + * + * @param $objectStateGroupId + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + public function loadObjectStateGroup($objectStateGroupId) + { + return $this->objectStateService->loadObjectStateGroup($objectStateGroupId, Language::ALL); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateGroupUpdateController.php b/src/lib/Server/Controller/ObjectState/ObjectStateGroupUpdateController.php new file mode 100644 index 000000000..bae908913 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateGroupUpdateController.php @@ -0,0 +1,158 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}', + name: 'Update Object state group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates an Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Object state group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Object state group input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ObjectStateGroupUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroupUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Object stated group updated.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectStateGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to update an Object state group.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - An Object state group with the provided identifier already exists.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - The current ETag does not match the one provided in the If-Match header.', + ], + ], + ), +)] +class ObjectStateGroupUpdateController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Updates an object state group. + * + * @param $objectStateGroupId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + public function updateObjectStateGroup($objectStateGroupId, Request $request) + { + $updateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); + + try { + $updatedStateGroup = $this->objectStateService->updateObjectStateGroup($objectStateGroup, $updateStruct); + + return $updatedStateGroup; + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateListController.php b/src/lib/Server/Controller/ObjectState/ObjectStateListController.php new file mode 100644 index 000000000..f40577ce6 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateListController.php @@ -0,0 +1,107 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}/objectstates', + name: 'List Object states', + openapi: new Model\Operation( + summary: 'Returns a list of all Object states of the given group.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Object state list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list of Object states.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectStateList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read Object states.', + ], + ], + ), +)] +class ObjectStateListController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Returns a list of all object states of the given group. + * + * @param $objectStateGroupId + * + * @return \Ibexa\Rest\Server\Values\ObjectStateList + */ + public function loadObjectStates($objectStateGroupId) + { + $objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); + + return new Values\ObjectStateList( + $this->objectStateService->loadObjectStates($objectStateGroup, Language::ALL), + $objectStateGroup->id + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateLoadByIdController.php b/src/lib/Server/Controller/ObjectState/ObjectStateLoadByIdController.php new file mode 100644 index 000000000..d67504c0f --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateLoadByIdController.php @@ -0,0 +1,117 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Values\RestObjectState; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId}', + name: 'Get Object state', + openapi: new Model\Operation( + summary: 'Returns the Object state.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Object State is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the Object state.', + 'content' => [ + 'application/vnd.ibexa.api.ObjectState+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectState', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example', + ], + 'application/vnd.ibexa.api.ObjectState+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this Object state.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The Object state does not exist.', + ], + ], + ), +)] +class ObjectStateLoadByIdController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Loads an object state. + * + * @param $objectStateGroupId + * @param $objectStateId + * + * @return \Ibexa\Rest\Values\RestObjectState + */ + public function loadObjectState($objectStateGroupId, $objectStateId) + { + return new RestObjectState( + $this->objectStateService->loadObjectState($objectStateId, Language::ALL), + $objectStateGroupId + ); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStateUpdateController.php b/src/lib/Server/Controller/ObjectState/ObjectStateUpdateController.php new file mode 100644 index 000000000..6ea44a773 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStateUpdateController.php @@ -0,0 +1,168 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Values\RestObjectState; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId}', + name: 'Update Object state', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates an Object state. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Object State Groups', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Object state is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Object state input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateGroupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'objectStateId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ObjectStateUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example', + ], + 'application/vnd.ibexa.api.ObjectStateUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Object State updated', + 'content' => [ + 'application/vnd.ibexa.api.ObjectState+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectState', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example', + ], + 'application/vnd.ibexa.api.ObjectState+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ObjectStateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to update the Object state.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - An Object state with the provided identifier already exists in this group.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - The current ETag does not match the one provided in the If-Match header.', + ], + ], + ), +)] +class ObjectStateUpdateController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Updates an object state. + * + * @param $objectStateGroupId + * @param $objectStateId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Values\RestObjectState + */ + public function updateObjectState($objectStateGroupId, $objectStateId, Request $request) + { + $updateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $objectState = $this->objectStateService->loadObjectState($objectStateId); + + try { + $updatedObjectState = $this->objectStateService->updateObjectState($objectState, $updateStruct); + + return new RestObjectState($updatedObjectState, $objectStateGroupId); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStatesForContentListController.php b/src/lib/Server/Controller/ObjectState/ObjectStatesForContentListController.php new file mode 100644 index 000000000..6c06ac362 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStatesForContentListController.php @@ -0,0 +1,117 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Values\ContentObjectStates; +use Ibexa\Rest\Values\RestObjectState; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/objects/{contentId}/objectstates', + name: 'Get Object states of content item', + openapi: new Model\Operation( + summary: 'Returns the Object states of a content item', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Object states are returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the Object state.', + 'content' => [ + 'application/vnd.ibexa.api.ContentObjectStates+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStates', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example', + ], + 'application/vnd.ibexa.api.ContentObjectStates+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStatesWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example', + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The content item does not exist.', + ], + ], + ), +)] +class ObjectStatesForContentListController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Returns the object states of content. + * + * @param $contentId + * + * @return \Ibexa\Rest\Values\ContentObjectStates + */ + public function getObjectStatesForContent($contentId) + { + $groups = $this->objectStateService->loadObjectStateGroups(); + $contentInfo = $this->contentService->loadContentInfo($contentId); + + $contentObjectStates = []; + + foreach ($groups as $group) { + try { + $state = $this->objectStateService->getContentState($contentInfo, $group); + $contentObjectStates[] = new RestObjectState($state, $group->id); + } catch (NotFoundException $e) { + // Do nothing + } + } + + return new ContentObjectStates($contentObjectStates); + } +} diff --git a/src/lib/Server/Controller/ObjectState/ObjectStatesForContentUpdateController.php b/src/lib/Server/Controller/ObjectState/ObjectStatesForContentUpdateController.php new file mode 100644 index 000000000..62cf81136 --- /dev/null +++ b/src/lib/Server/Controller/ObjectState/ObjectStatesForContentUpdateController.php @@ -0,0 +1,179 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\ObjectState; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Values\ContentObjectStates; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/objects/{contentId}/objectstates', + name: 'Set Object states of content item', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates Object states of a content item. An Object state in the input overrides the state of the Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Objects', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Object state is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The content item Object states input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'contentId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ContentObjectStates+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStates', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example', + ], + 'application/vnd.ibexa.api.ContentObjectStates+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStatesWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'OK - Object state updated.', + 'content' => [ + 'application/vnd.ibexa.api.ContentObjectStates+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStates', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/PATCH/ContentObjectStates.response.xml.example', + ], + 'application/vnd.ibexa.api.ContentObjectStates+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ContentObjectStatesWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to set an Object state.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - The input contains multiple Object states of the same Object state group.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - The current ETag does not match the one provided in the If-Match header.', + ], + ], + ), +)] +class ObjectStatesForContentUpdateController extends RestController +{ + protected ObjectStateService $objectStateService; + + protected ContentService $contentService; + + public function __construct(ObjectStateService $objectStateService, ContentService $contentService) + { + $this->objectStateService = $objectStateService; + $this->contentService = $contentService; + } + + /** + * Updates object states of content + * An object state in the input overrides the state of the object state group. + * + * @param $contentId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Values\ContentObjectStates + */ + public function setObjectStatesForContent($contentId, Request $request) + { + $newObjectStates = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $countByGroups = []; + foreach ($newObjectStates as $newObjectState) { + $groupId = (int)$newObjectState->groupId; + if (array_key_exists($groupId, $countByGroups)) { + ++$countByGroups[$groupId]; + } else { + $countByGroups[$groupId] = 1; + } + } + + foreach ($countByGroups as $groupId => $count) { + if ($count > 1) { + throw new ForbiddenException( + /** @Ignore */ + "Multiple Object states provided for group with ID $groupId" + ); + } + } + + $contentInfo = $this->contentService->loadContentInfo($contentId); + + $contentObjectStates = []; + foreach ($newObjectStates as $newObjectState) { + $objectStateGroup = $this->objectStateService->loadObjectStateGroup($newObjectState->groupId); + $this->objectStateService->setContentState($contentInfo, $objectStateGroup, $newObjectState->objectState); + $contentObjectStates[(int)$objectStateGroup->id] = $newObjectState; + } + + return new ContentObjectStates($contentObjectStates); + } +} diff --git a/src/lib/Server/Controller/Role.php b/src/lib/Server/Controller/Role.php deleted file mode 100644 index ffea373e1..000000000 --- a/src/lib/Server/Controller/Role.php +++ /dev/null @@ -1,802 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; -use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; -use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Contracts\Core\Repository\RoleService; -use Ibexa\Contracts\Core\Repository\UserService; -use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; -use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; -use Ibexa\Contracts\Rest\Exceptions; -use Ibexa\Core\Base\Exceptions\ForbiddenException; -use Ibexa\Core\Base\Exceptions\InvalidArgumentException; -use Ibexa\Core\Base\Exceptions\UnauthorizedException; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\BadRequestException; -use Ibexa\Rest\Server\Values; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * Role controller. - */ -class Role extends RestController -{ - /** - * Role service. - * - * @var \Ibexa\Contracts\Core\Repository\RoleService - */ - protected $roleService; - - /** - * User service. - * - * @var \Ibexa\Contracts\Core\Repository\UserService - */ - protected $userService; - - /** - * Location service. - * - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\RoleService $roleService - * @param \Ibexa\Contracts\Core\Repository\UserService $userService - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - */ - public function __construct( - RoleService $roleService, - UserService $userService, - LocationService $locationService - ) { - $this->roleService = $roleService; - $this->userService = $userService; - $this->locationService = $locationService; - } - - /** - * Create new role. - * - * Defaults to publishing the role, but you can create a draft instead by setting the POST parameter publish=false - * - * @return \Ibexa\Rest\Server\Values\CreatedRole - */ - public function createRole(Request $request) - { - $publish = ( - !$request->query->has('publish') || - ($request->query->has('publish') && $request->query->get('publish') === 'true') - ); - - try { - $roleDraft = $this->roleService->createRole( - $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - // @todo Needs refactoring! Temporary solution so parser has access to get parameters - '__publish' => $publish, - ], - $request->getContent() - ) - ) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (UnauthorizedException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (Exceptions\Parser $e) { - throw new BadRequestException($e->getMessage()); - } - - if ($publish) { - @trigger_error( - "Create and publish role in the same operation is deprecated, and will be removed in the future.\n" . - 'Instead, publish the role draft using Role::publishRoleDraft().', - E_USER_DEPRECATED - ); - - $this->roleService->publishRoleDraft($roleDraft); - - $role = $this->roleService->loadRole($roleDraft->id); - - return new Values\CreatedRole(['role' => new Values\RestRole($role)]); - } - - return new Values\CreatedRole(['role' => new Values\RestRole($roleDraft)]); - } - - /** - * Creates a new RoleDraft for an existing Role. - * - * @since 6.2 - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException if the Role already has a Role Draft that will need to be removed first, - * or if the authenticated user is not allowed to create a role - * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException if a policy limitation in the $roleCreateStruct is not valid - * - * @return \Ibexa\Rest\Server\Values\CreatedRole - */ - public function createRoleDraft($roleId, Request $request) - { - try { - $roleDraft = $this->roleService->createRoleDraft( - $this->roleService->loadRole($roleId) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (UnauthorizedException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } catch (Exceptions\Parser $e) { - throw new BadRequestException($e->getMessage()); - } - - return new Values\CreatedRole(['role' => new Values\RestRole($roleDraft)]); - } - - /** - * Loads list of roles. - * - * @return \Ibexa\Rest\Server\Values\RoleList - */ - public function listRoles(Request $request) - { - $roles = []; - if ($request->query->has('identifier')) { - try { - $role = $this->roleService->loadRoleByIdentifier($request->query->get('identifier')); - $roles[] = $role; - } catch (APINotFoundException $e) { - // Do nothing - } - } else { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; - - $roles = array_slice( - $this->roleService->loadRoles(), - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : null - ); - } - - return new Values\RoleList($roles, $request->getPathInfo()); - } - - /** - * Loads role. - * - * @param $roleId - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\Role - */ - public function loadRole($roleId) - { - return $this->roleService->loadRole($roleId); - } - - /** - * Loads a role draft. - * - * @param mixed $roleId Original role ID, or ID of the role draft itself - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft - */ - public function loadRoleDraft($roleId) - { - try { - // First try to load the draft for given role. - return $this->roleService->loadRoleDraftByRoleId($roleId); - } catch (NotFoundException $e) { - // We might want a newly created role, so try to load it by its ID. - // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. - return $this->roleService->loadRoleDraft($roleId); - } - } - - /** - * Updates a role. - * - * @param $roleId - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\Role - */ - public function updateRole($roleId, Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - $roleDraft = $this->roleService->createRoleDraft( - $this->roleService->loadRole($roleId) - ); - $roleDraft = $this->roleService->updateRoleDraft( - $roleDraft, - $this->mapToUpdateStruct($createStruct) - ); - - $this->roleService->publishRoleDraft($roleDraft); - - return $this->roleService->loadRole($roleId); - } - - /** - * Updates a role draft. - * - * @param mixed $roleId Original role ID, or ID of the role draft itself - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft - */ - public function updateRoleDraft($roleId, Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - // First try to load the draft for given role. - $roleDraft = $this->roleService->loadRoleDraftByRoleId($roleId); - } catch (NotFoundException $e) { - // We might want a newly created role, so try to load it by its ID. - // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. - $roleDraft = $this->roleService->loadRoleDraft($roleId); - } - - return $this->roleService->updateRoleDraft($roleDraft, $this->mapToUpdateStruct($createStruct)); - } - - /** - * Publishes a role draft. - * - * @param mixed $roleId Original role ID, or ID of the role draft itself - * - * @return \Ibexa\Rest\Server\Values\PublishedRole - */ - public function publishRoleDraft($roleId) - { - try { - // First try to load the draft for given role. - $roleDraft = $this->roleService->loadRoleDraftByRoleId($roleId); - } catch (NotFoundException $e) { - // We might want a newly created role, so try to load it by its ID. - // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. - $roleDraft = $this->roleService->loadRoleDraft($roleId); - } - - $this->roleService->publishRoleDraft($roleDraft); - - $role = $this->roleService->loadRoleByIdentifier($roleDraft->identifier); - - return new Values\PublishedRole(['role' => new Values\RestRole($role)]); - } - - /** - * Delete a role by ID. - * - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteRole($roleId) - { - $this->roleService->deleteRole( - $this->roleService->loadRole($roleId) - ); - - return new Values\NoContent(); - } - - /** - * Delete a role draft by ID. - * - * @since 6.2 - * - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteRoleDraft($roleId) - { - $this->roleService->deleteRoleDraft( - $this->roleService->loadRoleDraft($roleId) - ); - - return new Values\NoContent(); - } - - /** - * Loads the policies for the role. - * - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\PolicyList - */ - public function loadPolicies($roleId, Request $request) - { - $loadedRole = $this->roleService->loadRole($roleId); - - return new Values\PolicyList($loadedRole->getPolicies(), $request->getPathInfo()); - } - - /** - * Deletes all policies from a role. - * - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deletePolicies($roleId) - { - $loadedRole = $this->roleService->loadRole($roleId); - $roleDraft = $this->roleService->createRoleDraft($loadedRole); - /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policyDraft */ - foreach ($roleDraft->getPolicies() as $policyDraft) { - $this->roleService->removePolicyByRoleDraft($roleDraft, $policyDraft); - } - $this->roleService->publishRoleDraft($roleDraft); - - return new Values\NoContent(); - } - - /** - * Loads a policy. - * - * @param $roleId - * @param $policyId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy - */ - public function loadPolicy($roleId, $policyId, Request $request) - { - $loadedRole = $this->roleService->loadRole($roleId); - foreach ($loadedRole->getPolicies() as $policy) { - if ($policy->id == $policyId) { - return $policy; - } - } - - throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); - } - - /** - * Adds a policy to role. - * - * @param int $roleId ID of a role draft - * - * @return \Ibexa\Rest\Server\Values\CreatedPolicy - */ - public function addPolicy($roleId, Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - // First try to treat $roleId as a role draft ID. - $role = $this->roleService->addPolicyByRoleDraft( - $this->roleService->loadRoleDraft($roleId), - $createStruct - ); - } catch (NotFoundException $e) { - // Then try to treat $roleId as a role ID. - $roleDraft = $this->roleService->createRoleDraft( - $this->roleService->loadRole($roleId) - ); - $roleDraft = $this->roleService->addPolicyByRoleDraft( - $roleDraft, - $createStruct - ); - $this->roleService->publishRoleDraft($roleDraft); - $role = $this->roleService->loadRole($roleId); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } - - return new Values\CreatedPolicy( - [ - 'policy' => $this->getLastAddedPolicy($role), - ] - ); - } - - /** - * Get the last added policy for $role. - * - * This is needed because the RoleService addPolicy() and addPolicyByRoleDraft() methods return the role, - * not the policy. - * - * @param $role \Ibexa\Contracts\Core\Repository\Values\User\Role - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy - */ - private function getLastAddedPolicy($role) - { - $policies = $role->getPolicies(); - - $policyToReturn = $policies[0]; - for ($i = 1, $count = count($policies); $i < $count; ++$i) { - if ($policies[$i]->id > $policyToReturn->id) { - $policyToReturn = $policies[$i]; - } - } - - return $policyToReturn; - } - - /** - * Updates a policy. - * - * @param int $roleId ID of a role draft - * @param int $policyId ID of a policy - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy - */ - public function updatePolicy($roleId, $policyId, Request $request) - { - $updateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - try { - // First try to treat $roleId as a role draft ID. - $roleDraft = $this->roleService->loadRoleDraft($roleId); - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->id == $policyId) { - try { - return $this->roleService->updatePolicyByRoleDraft( - $roleDraft, - $policy, - $updateStruct - ); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } - } - } - } catch (NotFoundException $e) { - // Then try to treat $roleId as a role ID. - $roleDraft = $this->roleService->createRoleDraft( - $this->roleService->loadRole($roleId) - ); - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->originalId == $policyId) { - try { - $policyDraft = $this->roleService->updatePolicyByRoleDraft( - $roleDraft, - $policy, - $updateStruct - ); - $this->roleService->publishRoleDraft($roleDraft); - $role = $this->roleService->loadRole($roleId); - - foreach ($role->getPolicies() as $newPolicy) { - if ($newPolicy->id == $policyDraft->id) { - return $newPolicy; - } - } - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } - } - } - } - - throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); - } - - /** - * Delete a policy from role. - * - * @param int $roleId ID of a role draft - * @param int $policyId ID of a policy - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deletePolicy($roleId, $policyId, Request $request) - { - try { - // First try to treat $roleId as a role draft ID. - $roleDraft = $this->roleService->loadRoleDraft($roleId); - $policy = null; - foreach ($roleDraft->getPolicies() as $rolePolicy) { - if ($rolePolicy->id == $policyId) { - $policy = $rolePolicy; - break; - } - } - if ($policy !== null) { - $this->roleService->removePolicyByRoleDraft($roleDraft, $policy); - - return new Values\NoContent(); - } - } catch (NotFoundException $e) { - // Then try to treat $roleId as a role ID. - $roleDraft = $this->roleService->createRoleDraft( - $this->roleService->loadRole($roleId) - ); - $policy = null; - foreach ($roleDraft->getPolicies() as $rolePolicy) { - if ($rolePolicy->originalId == $policyId) { - $policy = $rolePolicy; - break; - } - } - if ($policy !== null) { - $this->roleService->removePolicyByRoleDraft($roleDraft, $policy); - $this->roleService->publishRoleDraft($roleDraft); - - return new Values\NoContent(); - } - } - throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); - } - - /** - * Assigns role to user. - * - * @param $userId - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function assignRoleToUser($userId, Request $request) - { - $roleAssignment = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $user = $this->userService->loadUser($userId); - $role = $this->roleService->loadRole($roleAssignment->roleId); - - try { - $this->roleService->assignRoleToUser($role, $user, $roleAssignment->limitation); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } - - $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); - - return new Values\RoleAssignmentList($roleAssignments, $user->id); - } - - /** - * Assigns role to user group. - * - * @param $groupPath - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function assignRoleToUserGroup($groupPath, Request $request) - { - $roleAssignment = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $groupLocationParts = explode('/', $groupPath); - $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); - $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); - - $role = $this->roleService->loadRole($roleAssignment->roleId); - - try { - $this->roleService->assignRoleToUserGroup($role, $userGroup, $roleAssignment->limitation); - } catch (LimitationValidationException $e) { - throw new BadRequestException($e->getMessage()); - } - - $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); - - return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); - } - - /** - * Un-assigns role from user. - * - * @param $userId - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function unassignRoleFromUser($userId, $roleId) - { - $user = $this->userService->loadUser($userId); - - $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->role->id == $roleId) { - $this->roleService->removeRoleAssignment($roleAssignment); - } - } - $newRoleAssignments = $this->roleService->getRoleAssignmentsForUser($user); - - return new Values\RoleAssignmentList($newRoleAssignments, $user->id); - } - - /** - * Un-assigns role from user group. - * - * @param $groupPath - * @param $roleId - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function unassignRoleFromUserGroup($groupPath, $roleId) - { - $groupLocationParts = explode('/', $groupPath); - $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); - $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); - - $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->role->id == $roleId) { - $this->roleService->removeRoleAssignment($roleAssignment); - } - } - $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); - - return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); - } - - /** - * Loads role assignments for user. - * - * @param $userId - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function loadRoleAssignmentsForUser($userId) - { - $user = $this->userService->loadUser($userId); - - $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); - - return new Values\RoleAssignmentList($roleAssignments, $user->id); - } - - /** - * Loads role assignments for user group. - * - * @param $groupPath - * - * @return \Ibexa\Rest\Server\Values\RoleAssignmentList - */ - public function loadRoleAssignmentsForUserGroup($groupPath) - { - $groupLocationParts = explode('/', $groupPath); - $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); - $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); - - $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); - - return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); - } - - /** - * Returns a role assignment to the given user. - * - * @param $userId - * @param $roleId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\RestUserRoleAssignment - */ - public function loadRoleAssignmentForUser($userId, $roleId, Request $request) - { - $user = $this->userService->loadUser($userId); - $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); - - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->getRole()->id == $roleId) { - return new Values\RestUserRoleAssignment($roleAssignment, $userId); - } - } - - throw new Exceptions\NotFoundException("Role assignment not found: '{$request->getPathInfo()}'."); - } - - /** - * Returns a role assignment to the given user group. - * - * @param $groupPath - * @param $roleId - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * - * @return \Ibexa\Rest\Server\Values\RestUserGroupRoleAssignment - */ - public function loadRoleAssignmentForUserGroup($groupPath, $roleId, Request $request) - { - $groupLocationParts = explode('/', $groupPath); - $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); - $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); - - $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->getRole()->id == $roleId) { - return new Values\RestUserGroupRoleAssignment($roleAssignment, $groupPath); - } - } - - throw new Exceptions\NotFoundException("Role assignment not found: '{$request->getPathInfo()}'."); - } - - /** - * Search all policies which are applied to a given user. - * - * @return \Ibexa\Rest\Server\Values\PolicyList - */ - public function listPoliciesForUser(Request $request) - { - $user = $this->userService->loadUser((int)$request->query->get('userId')); - $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user, true); - - $policies = []; - foreach ($roleAssignments as $roleAssignment) { - $policies[] = $roleAssignment->getRole()->getPolicies(); - } - - return new Values\PolicyList( - !empty($policies) ? array_merge(...$policies) : [], - $request->getPathInfo() - ); - } - - /** - * Maps a RoleCreateStruct to a RoleUpdateStruct. - * - * Needed since both structs are encoded into the same media type on input. - * - * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $createStruct - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct - */ - protected function mapToUpdateStruct(RoleCreateStruct $createStruct) - { - return new RoleUpdateStruct( - [ - 'identifier' => $createStruct->identifier, - ] - ); - } -} diff --git a/src/lib/Server/Controller/Role/RoleAssignToUserController.php b/src/lib/Server/Controller/Role/RoleAssignToUserController.php new file mode 100644 index 000000000..0240f34b7 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignToUserController.php @@ -0,0 +1,130 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/users/{userId}/roles', + name: 'Assign Role to User', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Assigns a Role to a user.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleAssignInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RoleAssignInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - validation of limitation in RoleAssignInput fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to assign this Role.', + ], + ], + ), +)] +class RoleAssignToUserController extends RoleBaseController +{ + /** + * Assigns role to user. + * + * @param $userId + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function assignRoleToUser($userId, Request $request) + { + $roleAssignment = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $user = $this->userService->loadUser($userId); + $role = $this->roleService->loadRole($roleAssignment->roleId); + + try { + $this->roleService->assignRoleToUser($role, $user, $roleAssignment->limitation); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } + + $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); + + return new Values\RoleAssignmentList($roleAssignments, $user->id); + } +} diff --git a/src/lib/Server/Controller/Role/RoleAssignToUserGroupController.php b/src/lib/Server/Controller/Role/RoleAssignToUserGroupController.php new file mode 100644 index 000000000..9418ef9cb --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignToUserGroupController.php @@ -0,0 +1,133 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/groups/{path}/roles', + name: 'Assign Role to User Group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Assigns a Role to a User Group.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleAssignInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RoleAssignInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - validation of limitation in RoleAssignInput fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to assign this Role.', + ], + ], + ), +)] +class RoleAssignToUserGroupController extends RoleBaseController +{ + /** + * Assigns role to user group. + * + * @param $groupPath + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function assignRoleToUserGroup($groupPath, Request $request) + { + $roleAssignment = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + $groupLocationParts = explode('/', $groupPath); + $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); + $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); + + $role = $this->roleService->loadRole($roleAssignment->roleId); + + try { + $this->roleService->assignRoleToUserGroup($role, $userGroup, $roleAssignment->limitation); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } + + $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); + + return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); + } +} diff --git a/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupListController.php b/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupListController.php new file mode 100644 index 000000000..f223de509 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupListController.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/{path}/roles', + name: 'Load Roles for User Group', + openapi: new Model\Operation( + summary: 'Returns a list of all Roles assigned to the given User Group.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RoleAssignmentForUserGroupListController extends RoleBaseController +{ + /** + * Loads role assignments for user group. + * + * @param $groupPath + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function loadRoleAssignmentsForUserGroup($groupPath) + { + $groupLocationParts = explode('/', $groupPath); + $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); + $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); + + $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); + + return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); + } +} diff --git a/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupLoadByIdController.php b/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupLoadByIdController.php new file mode 100644 index 000000000..e7855aea0 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignmentForUserGroupLoadByIdController.php @@ -0,0 +1,103 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/{path}/roles/{roleId}', + name: 'Load User Group Role Assignment', + openapi: new Model\Operation( + summary: 'Returns a Role assignment of the given User Group.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'roleId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a Role assignment of the given User Group.', + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignment+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignment', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignment+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RoleAssignmentForUserGroupLoadByIdController extends RoleBaseController +{ + /** + * Returns a role assignment to the given user group. + * + * @param $groupPath + * @param $roleId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\RestUserGroupRoleAssignment + */ + public function loadRoleAssignmentForUserGroup($groupPath, $roleId, Request $request) + { + $groupLocationParts = explode('/', $groupPath); + $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); + $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); + + $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->getRole()->id == $roleId) { + return new Values\RestUserGroupRoleAssignment($roleAssignment, $groupPath); + } + } + + throw new Exceptions\NotFoundException("Role assignment not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Role/RoleAssignmentForUserListController.php b/src/lib/Server/Controller/Role/RoleAssignmentForUserListController.php new file mode 100644 index 000000000..a917d69e5 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignmentForUserListController.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users/{userId}/roles', + name: 'Load Roles for User', + openapi: new Model\Operation( + summary: 'Returns a list of all Roles assigned to the given User.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RoleAssignmentForUserListController extends RoleBaseController +{ + /** + * Loads role assignments for user. + * + * @param $userId + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function loadRoleAssignmentsForUser($userId) + { + $user = $this->userService->loadUser($userId); + + $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); + + return new Values\RoleAssignmentList($roleAssignments, $user->id); + } +} diff --git a/src/lib/Server/Controller/Role/RoleAssignmentForUserLoadByIdController.php b/src/lib/Server/Controller/Role/RoleAssignmentForUserLoadByIdController.php new file mode 100644 index 000000000..32227c743 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleAssignmentForUserLoadByIdController.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users/{userId}/roles/{roleId}', + name: 'Load User Role Assignment', + openapi: new Model\Operation( + summary: 'Returns a Role assignment to the given User.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'roleId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Role assignment to the given User Group.', + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignment+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignment', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignment+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RoleAssignmentForUserLoadByIdController extends RoleBaseController +{ + /** + * Returns a role assignment to the given user. + * + * @param $userId + * @param $roleId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\RestUserRoleAssignment + */ + public function loadRoleAssignmentForUser($userId, $roleId, Request $request) + { + $user = $this->userService->loadUser($userId); + $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); + + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->getRole()->id == $roleId) { + return new Values\RestUserRoleAssignment($roleAssignment, $userId); + } + } + + throw new Exceptions\NotFoundException("Role assignment not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Role/RoleBaseController.php b/src/lib/Server/Controller/Role/RoleBaseController.php new file mode 100644 index 000000000..5ff9369ff --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleBaseController.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; +use Ibexa\Rest\Server\Controller as RestController; + +class RoleBaseController extends RestController +{ + protected RoleService $roleService; + + protected UserService $userService; + + protected LocationService $locationService; + + public function __construct( + RoleService $roleService, + UserService $userService, + LocationService $locationService + ) { + $this->roleService = $roleService; + $this->userService = $userService; + $this->locationService = $locationService; + } + + /** + * Get the last added policy for $role. + * + * This is needed because the RoleService addPolicy() and addPolicyByRoleDraft() methods return the role, + * not the policy. + * + * @param $role \Ibexa\Contracts\Core\Repository\Values\User\Role + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy + */ + protected function getLastAddedPolicy($role) + { + $policies = $role->getPolicies(); + + $policyToReturn = $policies[0]; + for ($i = 1, $count = count($policies); $i < $count; ++$i) { + if ($policies[$i]->id > $policyToReturn->id) { + $policyToReturn = $policies[$i]; + } + } + + return $policyToReturn; + } + + /** + * Maps a RoleCreateStruct to a RoleUpdateStruct. + * + * Needed since both structs are encoded into the same media type on input. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct + */ + protected function mapToUpdateStruct(RoleCreateStruct $createStruct) + { + return new RoleUpdateStruct( + [ + 'identifier' => $createStruct->identifier, + ] + ); + } +} diff --git a/src/lib/Server/Controller/Role/RoleCreateController.php b/src/lib/Server/Controller/Role/RoleCreateController.php new file mode 100644 index 000000000..91b20c340 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleCreateController.php @@ -0,0 +1,152 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Core\Base\Exceptions\ForbiddenException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/roles', + name: 'Create Role or Role draft', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Role or Role draft.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new user is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleInput schema encoded in XML or JSON.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RoleInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example', + ], + 'application/vnd.ibexa.api.RoleInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Role+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Role', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example', + ], + 'application/vnd.ibexa.api.Role+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create a Role or a Role draft.', + ], + ], + ), +)] +class RoleCreateController extends RoleBaseController +{ + /** + * Create new role. + * + * Defaults to publishing the role, but you can create a draft instead by setting the POST parameter publish=false + * + * @return \Ibexa\Rest\Server\Values\CreatedRole + */ + public function createRole(Request $request) + { + $publish = ( + !$request->query->has('publish') || + ($request->query->has('publish') && $request->query->get('publish') === 'true') + ); + + try { + $roleDraft = $this->roleService->createRole( + $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + // @todo Needs refactoring! Temporary solution so parser has access to get parameters + '__publish' => $publish, + ], + $request->getContent() + ) + ) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (UnauthorizedException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (Exceptions\Parser $e) { + throw new BadRequestException($e->getMessage()); + } + + if ($publish) { + @trigger_error( + "Create and publish role in the same operation is deprecated, and will be removed in the future.\n" . + 'Instead, publish the role draft using Role::publishRoleDraft().', + E_USER_DEPRECATED + ); + + $this->roleService->publishRoleDraft($roleDraft); + + $role = $this->roleService->loadRole($roleDraft->id); + + return new Values\CreatedRole(['role' => new Values\RestRole($role)]); + } + + return new Values\CreatedRole(['role' => new Values\RestRole($roleDraft)]); + } +} diff --git a/src/lib/Server/Controller/Role/RoleDeleteController.php b/src/lib/Server/Controller/Role/RoleDeleteController.php new file mode 100644 index 000000000..1ec18962c --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDeleteController.php @@ -0,0 +1,60 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/roles/{id}', + name: 'Delete Role', + openapi: new Model\Operation( + summary: 'The given Role and all assignments to Users or User Groups are deleted.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the User is not authorized to delete this Role.', + ], + ], + ), +)] +class RoleDeleteController extends RoleBaseController +{ + /** + * Delete a role by ID. + * + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteRole($roleId) + { + $this->roleService->deleteRole( + $this->roleService->loadRole($roleId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Role/RoleDraftCreateController.php b/src/lib/Server/Controller/Role/RoleDraftCreateController.php new file mode 100644 index 000000000..e9748decc --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDraftCreateController.php @@ -0,0 +1,116 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Core\Base\Exceptions\ForbiddenException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/roles/{id}', + name: 'Create Role Draft', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Creates a new Role draft from an existing Role.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new user is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleInput schema encoded in XML or JSON.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleDraft+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleDraft', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/POST/RoleDraft.xml.example', + ], + 'application/vnd.ibexa.api.RoleDraft+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleDraftWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create a Role or a Role draft', + ], + ], + ), +)] +class RoleDraftCreateController extends RoleBaseController +{ + /** + * Creates a new RoleDraft for an existing Role. + * + * @since 6.2 + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException if the Role already has a Role Draft that will need to be removed first, + * or if the authenticated user is not allowed to create a role + * @throws \Ibexa\Rest\Server\Exceptions\BadRequestException if a policy limitation in the $roleCreateStruct is not valid + * + * @return \Ibexa\Rest\Server\Values\CreatedRole + */ + public function createRoleDraft($roleId, Request $request) + { + try { + $roleDraft = $this->roleService->createRoleDraft( + $this->roleService->loadRole($roleId) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (UnauthorizedException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } catch (Exceptions\Parser $e) { + throw new BadRequestException($e->getMessage()); + } + + return new Values\CreatedRole(['role' => new Values\RestRole($roleDraft)]); + } +} diff --git a/src/lib/Server/Controller/Role/RoleDraftDeleteController.php b/src/lib/Server/Controller/Role/RoleDraftDeleteController.php new file mode 100644 index 000000000..44c2f6b20 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDraftDeleteController.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/roles/{id}/draft', + name: 'Delete Role draft', + openapi: new Model\Operation( + summary: 'The given Role draft is deleted.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this Role.', + ], + ], + ), +)] +class RoleDraftDeleteController extends RoleBaseController +{ + /** + * Delete a role draft by ID. + * + * @since 6.2 + * + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteRoleDraft($roleId) + { + $this->roleService->deleteRoleDraft( + $this->roleService->loadRoleDraft($roleId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Role/RoleDraftLoadByRoleIdController.php b/src/lib/Server/Controller/Role/RoleDraftLoadByRoleIdController.php new file mode 100644 index 000000000..b27b577f8 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDraftLoadByRoleIdController.php @@ -0,0 +1,98 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/roles/{id}/draft', + name: 'Load Role draft', + openapi: new Model\Operation( + summary: 'Loads a Role draft by original Role ID.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the User list returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Role draft by original Role ID.', + 'content' => [ + 'application/vnd.ibexa.api.Role+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Role', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example', + ], + 'application/vnd.ibexa.api.Role+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - there is no draft or Role with the given ID.', + ], + ], + ), +)] +class RoleDraftLoadByRoleIdController extends RoleBaseController +{ + /** + * Loads a role draft. + * + * @param mixed $roleId Original role ID, or ID of the role draft itself + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + */ + public function loadRoleDraft($roleId) + { + try { + // First try to load the draft for given role. + return $this->roleService->loadRoleDraftByRoleId($roleId); + } catch (NotFoundException $e) { + // We might want a newly created role, so try to load it by its ID. + // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. + return $this->roleService->loadRoleDraft($roleId); + } + } +} diff --git a/src/lib/Server/Controller/Role/RoleDraftPublishController.php b/src/lib/Server/Controller/Role/RoleDraftPublishController.php new file mode 100644 index 000000000..454e533f8 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDraftPublishController.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Rest\Server\Values; + +class RoleDraftPublishController extends RoleBaseController +{ + /** + * Publishes a role draft. + * + * @param mixed $roleId Original role ID, or ID of the role draft itself + * + * @return \Ibexa\Rest\Server\Values\PublishedRole + */ + public function publishRoleDraft($roleId) + { + try { + // First try to load the draft for given role. + $roleDraft = $this->roleService->loadRoleDraftByRoleId($roleId); + } catch (NotFoundException $e) { + // We might want a newly created role, so try to load it by its ID. + // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. + $roleDraft = $this->roleService->loadRoleDraft($roleId); + } + + $this->roleService->publishRoleDraft($roleDraft); + + $role = $this->roleService->loadRoleByIdentifier($roleDraft->identifier); + + return new Values\PublishedRole(['role' => new Values\RestRole($role)]); + } +} diff --git a/src/lib/Server/Controller/Role/RoleDraftUpdateController.php b/src/lib/Server/Controller/Role/RoleDraftUpdateController.php new file mode 100644 index 000000000..db02f7f87 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleDraftUpdateController.php @@ -0,0 +1,142 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Rest\Message; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/user/roles/{id}/draft', + name: 'Update Role draft', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a Role draft. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Role is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'Performs a PATCH only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RoleInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example', + ], + 'application/vnd.ibexa.api.RoleInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Role draft updated.', + 'content' => [ + 'application/vnd.ibexa.api.Role+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Role', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example', + ], + 'application/vnd.ibexa.api.Role+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update the Role.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - there is no draft or Role with the given ID.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the one provided in the If-Match header.', + ], + ], + ), +)] +class RoleDraftUpdateController extends RoleBaseController +{ + /** + * Updates a role draft. + * + * @param mixed $roleId Original role ID, or ID of the role draft itself + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + */ + public function updateRoleDraft($roleId, Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + // First try to load the draft for given role. + $roleDraft = $this->roleService->loadRoleDraftByRoleId($roleId); + } catch (NotFoundException $e) { + // We might want a newly created role, so try to load it by its ID. + // loadRoleDraft() might throw a NotFoundException (wrong $roleId). If so, let it bubble up. + $roleDraft = $this->roleService->loadRoleDraft($roleId); + } + + return $this->roleService->updateRoleDraft($roleDraft, $this->mapToUpdateStruct($createStruct)); + } +} diff --git a/src/lib/Server/Controller/Role/RoleListController.php b/src/lib/Server/Controller/Role/RoleListController.php new file mode 100644 index 000000000..30ba8baae --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleListController.php @@ -0,0 +1,90 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/roles', + name: 'Load Roles', + openapi: new Model\Operation( + summary: 'Returns a list of all Roles.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the user list returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - list of all Roles.', + 'content' => [ + 'application/vnd.ibexa.api.RoleList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/GET/RoleList.xml.example', + ], + 'application/vnd.ibexa.api.RoleList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/GET/RoleList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RoleListController extends RoleBaseController +{ + /** + * Loads list of roles. + * + * @return \Ibexa\Rest\Server\Values\RoleList + */ + public function listRoles(Request $request) + { + $roles = []; + if ($request->query->has('identifier')) { + try { + $role = $this->roleService->loadRoleByIdentifier($request->query->get('identifier')); + $roles[] = $role; + } catch (APINotFoundException $e) { + // Do nothing + } + } else { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; + + $roles = array_slice( + $this->roleService->loadRoles(), + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : null + ); + } + + return new Values\RoleList($roles, $request->getPathInfo()); + } +} diff --git a/src/lib/Server/Controller/Role/RoleLoadByIdController.php b/src/lib/Server/Controller/Role/RoleLoadByIdController.php new file mode 100644 index 000000000..649226840 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleLoadByIdController.php @@ -0,0 +1,90 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/roles/{id}', + name: 'Load Role', + openapi: new Model\Operation( + summary: 'Loads a Role for the given ID.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the user list returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Role for the given ID.', + 'content' => [ + 'application/vnd.ibexa.api.Role+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Role', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example', + ], + 'application/vnd.ibexa.api.Role+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role does not exist.', + ], + ], + ), +)] +class RoleLoadByIdController extends RoleBaseController +{ + /** + * Loads role. + * + * @param $roleId + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + */ + public function loadRole($roleId) + { + return $this->roleService->loadRole($roleId); + } +} diff --git a/src/lib/Server/Controller/Role/RolePoliciesForUserListController.php b/src/lib/Server/Controller/Role/RolePoliciesForUserListController.php new file mode 100644 index 000000000..41e72e23f --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePoliciesForUserListController.php @@ -0,0 +1,81 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/policies', + name: 'List Policies for User', + openapi: new Model\Operation( + summary: 'Search all Policies which are applied to a given User.', + tags: [ + 'User Policy', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Policy list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Policies which are applied to a given User.', + 'content' => [ + 'application/vnd.ibexa.api.PolicyList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.xml.example', + ], + 'application/vnd.ibexa.api.PolicyList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + ], + ), +)] +class RolePoliciesForUserListController extends RoleBaseController +{ + /** + * Search all policies which are applied to a given user. + * + * @return \Ibexa\Rest\Server\Values\PolicyList + */ + public function listPoliciesForUser(Request $request) + { + $user = $this->userService->loadUser((int)$request->query->get('userId')); + $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user, true); + + $policies = []; + foreach ($roleAssignments as $roleAssignment) { + $policies[] = $roleAssignment->getRole()->getPolicies(); + } + + return new Values\PolicyList( + !empty($policies) ? array_merge(...$policies) : [], + $request->getPathInfo() + ); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyCreateController.php b/src/lib/Server/Controller/Role/RolePolicyCreateController.php new file mode 100644 index 000000000..99c36205e --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyCreateController.php @@ -0,0 +1,147 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/roles/{id}/policies', + name: 'Create Policy', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a Policy', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Policy is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'If set, the updated Policy is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.PolicyCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/POST/PolicyCreate.xml.example', + ], + 'application/vnd.ibexa.api.PolicyCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyCreateWrapper', + ], + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Policy+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Policy', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example', + ], + 'application/vnd.ibexa.api.Policy+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition or validation of limitation in PolicyCreate fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create the Policy.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role does not exist.', + ], + ], + ), +)] +class RolePolicyCreateController extends RoleBaseController +{ + /** + * Adds a policy to role. + * + * @param int $roleId ID of a role draft + * + * @return \Ibexa\Rest\Server\Values\CreatedPolicy + */ + public function addPolicy($roleId, Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + // First try to treat $roleId as a role draft ID. + $role = $this->roleService->addPolicyByRoleDraft( + $this->roleService->loadRoleDraft($roleId), + $createStruct + ); + } catch (NotFoundException $e) { + // Then try to treat $roleId as a role ID. + $roleDraft = $this->roleService->createRoleDraft( + $this->roleService->loadRole($roleId) + ); + $roleDraft = $this->roleService->addPolicyByRoleDraft( + $roleDraft, + $createStruct + ); + $this->roleService->publishRoleDraft($roleDraft); + $role = $this->roleService->loadRole($roleId); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } + + return new Values\CreatedPolicy( + [ + 'policy' => $this->getLastAddedPolicy($role), + ] + ); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyDeleteAllFromRoleController.php b/src/lib/Server/Controller/Role/RolePolicyDeleteAllFromRoleController.php new file mode 100644 index 000000000..f2b59e486 --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyDeleteAllFromRoleController.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/roles/{id}/policies', + name: 'Delete Policies', + openapi: new Model\Operation( + summary: 'All Policies of the given Role are deleted.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - all Policies of the given Role are deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this content type.', + ], + ], + ), +)] +class RolePolicyDeleteAllFromRoleController extends RoleBaseController +{ + /** + * Deletes all policies from a role. + * + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deletePolicies($roleId) + { + $loadedRole = $this->roleService->loadRole($roleId); + $roleDraft = $this->roleService->createRoleDraft($loadedRole); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policyDraft */ + foreach ($roleDraft->getPolicies() as $policyDraft) { + $this->roleService->removePolicyByRoleDraft($roleDraft, $policyDraft); + } + $this->roleService->publishRoleDraft($roleDraft); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyDeleteController.php b/src/lib/Server/Controller/Role/RolePolicyDeleteController.php new file mode 100644 index 000000000..469b6b140 --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyDeleteController.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/roles/{roleId}/policies/{policyId}', + name: 'Delete Policy', + openapi: new Model\Operation( + summary: 'Deletes given Policy.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'policyId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - the given Policy is deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this content type.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role or Policy does not exist.', + ], + ], + ), +)] +class RolePolicyDeleteController extends RoleBaseController +{ + /** + * Delete a policy from role. + * + * @param int $roleId ID of a role draft + * @param int $policyId ID of a policy + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deletePolicy($roleId, $policyId, Request $request) + { + try { + // First try to treat $roleId as a role draft ID. + $roleDraft = $this->roleService->loadRoleDraft($roleId); + $policy = null; + foreach ($roleDraft->getPolicies() as $rolePolicy) { + if ($rolePolicy->id == $policyId) { + $policy = $rolePolicy; + break; + } + } + if ($policy !== null) { + $this->roleService->removePolicyByRoleDraft($roleDraft, $policy); + + return new Values\NoContent(); + } + } catch (NotFoundException $e) { + // Then try to treat $roleId as a role ID. + $roleDraft = $this->roleService->createRoleDraft( + $this->roleService->loadRole($roleId) + ); + $policy = null; + foreach ($roleDraft->getPolicies() as $rolePolicy) { + if ($rolePolicy->originalId == $policyId) { + $policy = $rolePolicy; + break; + } + } + if ($policy !== null) { + $this->roleService->removePolicyByRoleDraft($roleDraft, $policy); + $this->roleService->publishRoleDraft($roleDraft); + + return new Values\NoContent(); + } + } + throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyListController.php b/src/lib/Server/Controller/Role/RolePolicyListController.php new file mode 100644 index 000000000..5f6a10802 --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyListController.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/roles/{id}/policies', + name: 'Load Policies', + openapi: new Model\Operation( + summary: 'Loads Policies for the given Role.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Policy list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.PolicyList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.xml.example', + ], + 'application/vnd.ibexa.api.PolicyList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/GET/PolicyList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role does not exist.', + ], + ], + ), +)] +class RolePolicyListController extends RoleBaseController +{ + /** + * Loads the policies for the role. + * + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\PolicyList + */ + public function loadPolicies($roleId, Request $request) + { + $loadedRole = $this->roleService->loadRole($roleId); + + return new Values\PolicyList($loadedRole->getPolicies(), $request->getPathInfo()); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyLoadByIdController.php b/src/lib/Server/Controller/Role/RolePolicyLoadByIdController.php new file mode 100644 index 000000000..b488bbc74 --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyLoadByIdController.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Rest\Exceptions; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/roles/{roleId}/policies/{policyId}', + name: 'Load Policy', + openapi: new Model\Operation( + summary: 'Loads a Policy for the given module and function.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Policy is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'policyId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Policy+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Policy', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example', + ], + 'application/vnd.ibexa.api.Policy+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Roles.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role or Policy does not exist.', + ], + ], + ), +)] +class RolePolicyLoadByIdController extends RoleBaseController +{ + /** + * Loads a policy. + * + * @param $roleId + * @param $policyId + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy + */ + public function loadPolicy($roleId, $policyId, Request $request) + { + $loadedRole = $this->roleService->loadRole($roleId); + foreach ($loadedRole->getPolicies() as $policy) { + if ($policy->id == $policyId) { + return $policy; + } + } + + throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Role/RolePolicyUpdateController.php b/src/lib/Server/Controller/Role/RolePolicyUpdateController.php new file mode 100644 index 000000000..a84fe1e5b --- /dev/null +++ b/src/lib/Server/Controller/Role/RolePolicyUpdateController.php @@ -0,0 +1,180 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Rest\Exceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\BadRequestException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/user/roles/{roleId}/policies/{policyId}', + name: 'Update Policy', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a Policy. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Policy is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'If set, the updated Policy is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'policyId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.PolicyUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example', + ], + 'application/vnd.ibexa.api.PolicyUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyUpdateWrapper', + ], + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Policy+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Policy', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/PATCH/Policy.xml.example', + ], + 'application/vnd.ibexa.api.Policy+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/PolicyWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/policies/id/GET/Policy.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition or validation of limitation in PolicyUpdate fails.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update the Policy.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Role does not exist.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the one provided in the If-Match header.', + ], + ], + ), +)] +class RolePolicyUpdateController extends RoleBaseController +{ + /** + * Updates a policy. + * + * @param int $roleId ID of a role draft + * @param int $policyId ID of a policy + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy + */ + public function updatePolicy($roleId, $policyId, Request $request) + { + $updateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + try { + // First try to treat $roleId as a role draft ID. + $roleDraft = $this->roleService->loadRoleDraft($roleId); + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->id == $policyId) { + try { + return $this->roleService->updatePolicyByRoleDraft( + $roleDraft, + $policy, + $updateStruct + ); + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } + } + } + } catch (NotFoundException $e) { + // Then try to treat $roleId as a role ID. + $roleDraft = $this->roleService->createRoleDraft( + $this->roleService->loadRole($roleId) + ); + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->originalId == $policyId) { + try { + $policyDraft = $this->roleService->updatePolicyByRoleDraft( + $roleDraft, + $policy, + $updateStruct + ); + $this->roleService->publishRoleDraft($roleDraft); + $role = $this->roleService->loadRole($roleId); + + foreach ($role->getPolicies() as $newPolicy) { + if ($newPolicy->id == $policyDraft->id) { + return $newPolicy; + } + } + } catch (LimitationValidationException $e) { + throw new BadRequestException($e->getMessage()); + } + } + } + } + + throw new Exceptions\NotFoundException("Policy not found: '{$request->getPathInfo()}'."); + } +} diff --git a/src/lib/Server/Controller/Role/RoleUnassignFromUserController.php b/src/lib/Server/Controller/Role/RoleUnassignFromUserController.php new file mode 100644 index 000000000..8db3d6a60 --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleUnassignFromUserController.php @@ -0,0 +1,97 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/users/{userId}/roles/{roleId}', + name: 'Unassign Role from User', + openapi: new Model\Operation( + summary: 'The given Role is removed from the user.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'roleId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this content type.', + ], + ], + ), +)] +class RoleUnassignFromUserController extends RoleBaseController +{ + /** + * Un-assigns role from user. + * + * @param $userId + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function unassignRoleFromUser($userId, $roleId) + { + $user = $this->userService->loadUser($userId); + + $roleAssignments = $this->roleService->getRoleAssignmentsForUser($user); + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->role->id == $roleId) { + $this->roleService->removeRoleAssignment($roleAssignment); + } + } + $newRoleAssignments = $this->roleService->getRoleAssignmentsForUser($user); + + return new Values\RoleAssignmentList($newRoleAssignments, $user->id); + } +} diff --git a/src/lib/Server/Controller/Role/RoleUnassignFromUserGroupController.php b/src/lib/Server/Controller/Role/RoleUnassignFromUserGroupController.php new file mode 100644 index 000000000..037e6c9ba --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleUnassignFromUserGroupController.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/groups/{path}/roles/{roleId}', + name: 'Unassign Role from User Group', + openapi: new Model\Operation( + summary: 'The given Role is removed from the User or User Group.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Role assignment list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'roleId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.RoleAssignmentList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example', + ], + 'application/vnd.ibexa.api.RoleAssignmentList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleAssignmentListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this Role assignment.', + ], + ], + ), +)] +class RoleUnassignFromUserGroupController extends RoleBaseController +{ + /** + * Un-assigns role from user group. + * + * @param $groupPath + * @param $roleId + * + * @return \Ibexa\Rest\Server\Values\RoleAssignmentList + */ + public function unassignRoleFromUserGroup($groupPath, $roleId) + { + $groupLocationParts = explode('/', $groupPath); + $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); + $userGroup = $this->userService->loadUserGroup($groupLocation->contentId); + + $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->role->id == $roleId) { + $this->roleService->removeRoleAssignment($roleAssignment); + } + } + $roleAssignments = $this->roleService->getRoleAssignmentsForUserGroup($userGroup); + + return new Values\RoleAssignmentList($roleAssignments, $groupPath, true); + } +} diff --git a/src/lib/Server/Controller/Role/RoleUpdateController.php b/src/lib/Server/Controller/Role/RoleUpdateController.php new file mode 100644 index 000000000..b2e55012c --- /dev/null +++ b/src/lib/Server/Controller/Role/RoleUpdateController.php @@ -0,0 +1,138 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Role; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Message; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/user/roles/{id}', + name: 'Update Role', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a Role. PATCH or POST with header X-HTTP-Method-Override PATCH', + tags: [ + 'User Role', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new user is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The RoleInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'id', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.RoleInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.xml.example', + ], + 'application/vnd.ibexa.api.RoleInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/POST/RoleInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Role updated', + 'content' => [ + 'application/vnd.ibexa.api.Role+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Role', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.xml.example', + ], + 'application/vnd.ibexa.api.Role+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RoleWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/roles/id/draft/PATCH/Role.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update the Role.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the provided one in the If-Match header.', + ], + ], + ), +)] +class RoleUpdateController extends RoleBaseController +{ + /** + * Updates a role. + * + * @param $roleId + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + */ + public function updateRole($roleId, Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + $roleDraft = $this->roleService->createRoleDraft( + $this->roleService->loadRole($roleId) + ); + $roleDraft = $this->roleService->updateRoleDraft( + $roleDraft, + $this->mapToUpdateStruct($createStruct) + ); + + $this->roleService->publishRoleDraft($roleDraft); + + return $this->roleService->loadRole($roleId); + } +} diff --git a/src/lib/Server/Controller/Root.php b/src/lib/Server/Controller/Root.php index f1743ff96..d643a4950 100644 --- a/src/lib/Server/Controller/Root.php +++ b/src/lib/Server/Controller/Root.php @@ -7,9 +7,51 @@ namespace Ibexa\Rest\Server\Controller; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; use Ibexa\Contracts\Rest\Exceptions\NotFoundException; use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; +#[Get( + uriTemplate: '/', + name: 'List of root resources', + openapi: new Model\Operation( + summary: 'Lists the root resources of the Ibexa Platform installation.', + tags: [ + 'Root', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the list is return in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Root+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Root', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/GET/Root.xml.example', + ], + 'application/vnd.ibexa.api.Root+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/RootWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/GET/Root.json.example', + ], + ], + ], + ], + ), +)] /** * Root controller. */ diff --git a/src/lib/Server/Controller/Section.php b/src/lib/Server/Controller/Section.php deleted file mode 100644 index 0f3454dc2..000000000 --- a/src/lib/Server/Controller/Section.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\SectionService; -use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; -use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Server\Values\NoContent; -use Symfony\Component\HttpFoundation\Request; - -/** - * Section controller. - */ -class Section extends RestController -{ - /** - * Section service. - * - * @var \Ibexa\Contracts\Core\Repository\SectionService - */ - protected $sectionService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\SectionService $sectionService - */ - public function __construct(SectionService $sectionService) - { - $this->sectionService = $sectionService; - } - - /** - * List sections. - * - * @return \Ibexa\Rest\Server\Values\SectionList - */ - public function listSections(Request $request) - { - if ($request->query->has('identifier')) { - $sections = [ - $this->loadSectionByIdentifier($request), - ]; - } else { - $sections = $this->sectionService->loadSections(); - } - - return new Values\SectionList($sections, $request->getPathInfo()); - } - - /** - * Loads section by identifier. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section - */ - public function loadSectionByIdentifier(Request $request) - { - return $this->sectionService->loadSectionByIdentifier( - // GET variable - $request->query->get('identifier') - ); - } - - /** - * Create new section. - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedSection - */ - public function createSection(Request $request) - { - try { - $createdSection = $this->sectionService->createSection( - $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\CreatedSection( - [ - 'section' => $createdSection, - ] - ); - } - - /** - * Loads a section. - * - * @param $sectionId - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section - */ - public function loadSection($sectionId) - { - return $this->sectionService->loadSection($sectionId); - } - - /** - * Updates a section. - * - * @param $sectionId - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section - */ - public function updateSection($sectionId, Request $request) - { - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - return $this->sectionService->updateSection( - $this->sectionService->loadSection($sectionId), - $this->mapToUpdateStruct($createStruct) - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - /** - * Delete a section by ID. - * - * @param $sectionId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteSection($sectionId) - { - $this->sectionService->deleteSection( - $this->sectionService->loadSection($sectionId) - ); - - return new NoContent(); - } - - /** - * Maps a SectionCreateStruct to a SectionUpdateStruct. - * - * Needed since both structs are encoded into the same media type on input. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct $createStruct - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct - */ - protected function mapToUpdateStruct(SectionCreateStruct $createStruct) - { - return new SectionUpdateStruct( - [ - 'name' => $createStruct->name, - 'identifier' => $createStruct->identifier, - ] - ); - } -} diff --git a/src/lib/Server/Controller/Section/SectionCreateController.php b/src/lib/Server/Controller/Section/SectionCreateController.php new file mode 100644 index 000000000..1f2aa66fa --- /dev/null +++ b/src/lib/Server/Controller/Section/SectionCreateController.php @@ -0,0 +1,124 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Section; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/sections', + name: 'Create new Section', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new Section.', + tags: [ + 'Section', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new Section is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Section input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.SectionInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/POST/SectionInput.xml.example', + ], + 'application/vnd.ibexa.api.SectionInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.Section+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Section', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example', + ], + 'application/vnd.ibexa.api.Section+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example', + ], + ], + ], + ], + ), +)] +class SectionCreateController extends RestController +{ + protected SectionService $sectionService; + + public function __construct(SectionService $sectionService) + { + $this->sectionService = $sectionService; + } + + /** + * Create new section. + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedSection + */ + public function createSection(Request $request) + { + try { + $createdSection = $this->sectionService->createSection( + $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\CreatedSection( + [ + 'section' => $createdSection, + ] + ); + } +} diff --git a/src/lib/Server/Controller/Section/SectionDeleteController.php b/src/lib/Server/Controller/Section/SectionDeleteController.php new file mode 100644 index 000000000..8336ea130 --- /dev/null +++ b/src/lib/Server/Controller/Section/SectionDeleteController.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Section; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values\NoContent; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/sections/{sectionId}', + name: 'Delete Section', + openapi: new Model\Operation( + summary: 'The given Section is deleted.', + tags: [ + 'Section', + ], + parameters: [ + new Model\Parameter( + name: 'sectionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - given Section is deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this Section.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Section does not exist.', + ], + ], + ), +)] +class SectionDeleteController extends RestController +{ + protected SectionService $sectionService; + + public function __construct(SectionService $sectionService) + { + $this->sectionService = $sectionService; + } + + /** + * Delete a section by ID. + * + * @param $sectionId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteSection($sectionId) + { + $this->sectionService->deleteSection( + $this->sectionService->loadSection($sectionId) + ); + + return new NoContent(); + } +} diff --git a/src/lib/Server/Controller/Section/SectionListController.php b/src/lib/Server/Controller/Section/SectionListController.php new file mode 100644 index 000000000..d1493c8bd --- /dev/null +++ b/src/lib/Server/Controller/Section/SectionListController.php @@ -0,0 +1,108 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Section; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/sections', + name: 'Get Sections', + openapi: new Model\Operation( + summary: 'Returns a list of all Sections.', + tags: [ + 'Section', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Section list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.SectionList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/GET/SectionList.xml.example', + ], + 'application/vnd.ibexa.api.SectionList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/GET/SectionList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read the Section.', + ], + ], + ), +)] +class SectionListController extends RestController +{ + protected SectionService $sectionService; + + public function __construct(SectionService $sectionService) + { + $this->sectionService = $sectionService; + } + + /** + * List sections. + * + * @return \Ibexa\Rest\Server\Values\SectionList + */ + public function listSections(Request $request) + { + if ($request->query->has('identifier')) { + $sections = [ + $this->loadSectionByIdentifier($request), + ]; + } else { + $sections = $this->sectionService->loadSections(); + } + + return new Values\SectionList($sections, $request->getPathInfo()); + } + + /** + * Loads section by identifier. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function loadSectionByIdentifier(Request $request) + { + return $this->sectionService->loadSectionByIdentifier( + // GET variable + $request->query->get('identifier') + ); + } +} diff --git a/src/lib/Server/Controller/Section/SectionLoadByIdController.php b/src/lib/Server/Controller/Section/SectionLoadByIdController.php new file mode 100644 index 000000000..046dda218 --- /dev/null +++ b/src/lib/Server/Controller/Section/SectionLoadByIdController.php @@ -0,0 +1,98 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Section; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/sections/{sectionId}', + name: 'Get Section', + openapi: new Model\Operation( + summary: 'Returns the Section by given Section ID.', + tags: [ + 'Section', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Section is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'sectionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Section+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Section', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example', + ], + 'application/vnd.ibexa.api.Section+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read this Section.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The Section does not exist.', + ], + ], + ), +)] +class SectionLoadByIdController extends RestController +{ + protected SectionService $sectionService; + + public function __construct(SectionService $sectionService) + { + $this->sectionService = $sectionService; + } + + /** + * Loads a section. + * + * @param $sectionId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function loadSection($sectionId) + { + return $this->sectionService->loadSection($sectionId); + } +} diff --git a/src/lib/Server/Controller/Section/SectionUpdateController.php b/src/lib/Server/Controller/Section/SectionUpdateController.php new file mode 100644 index 000000000..ac9a43258 --- /dev/null +++ b/src/lib/Server/Controller/Section/SectionUpdateController.php @@ -0,0 +1,173 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Section; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/content/sections/{sectionId}', + name: 'Update a Section', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a Section. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'Section', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated Section is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The Section input schema encoded in XML or JSON.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'sectionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.SectionInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/POST/SectionInput.xml.example', + ], + 'application/vnd.ibexa.api.SectionInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Section updated.', + 'content' => [ + 'application/vnd.ibexa.api.Section+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Section', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.xml.example', + ], + 'application/vnd.ibexa.api.Section+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SectionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/section_id/PATCH/Section.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this Section.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - a Section with the given identifier already exists.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the one provided in the If-Match header.', + ], + ], + ), +)] +class SectionUpdateController extends RestController +{ + protected SectionService $sectionService; + + public function __construct(SectionService $sectionService) + { + $this->sectionService = $sectionService; + } + + /** + * Updates a section. + * + * @param $sectionId + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function updateSection($sectionId, Request $request) + { + $createStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + return $this->sectionService->updateSection( + $this->sectionService->loadSection($sectionId), + $this->mapToUpdateStruct($createStruct) + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } + + /** + * Maps a SectionCreateStruct to a SectionUpdateStruct. + * + * Needed since both structs are encoded into the same media type on input. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct + */ + protected function mapToUpdateStruct(SectionCreateStruct $createStruct) + { + return new SectionUpdateStruct( + [ + 'name' => $createStruct->name, + 'identifier' => $createStruct->identifier, + ] + ); + } +} diff --git a/src/lib/Server/Controller/Services.php b/src/lib/Server/Controller/Services.php index 53969dea9..ab487806c 100644 --- a/src/lib/Server/Controller/Services.php +++ b/src/lib/Server/Controller/Services.php @@ -7,9 +7,50 @@ namespace Ibexa\Rest\Server\Controller; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; use Ibexa\Rest\Server\Controller as RestController; use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; +#[Get( + uriTemplate: '/services/countries', + name: 'Countries list', + openapi: new Model\Operation( + summary: 'Gives access to an ISO-3166 formatted list of world countries. It is useful when presenting a country options list from any application.', + tags: [ + 'Services', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the country list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.CountriesList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/CountryList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/services/countries/GET/CountriesList.xml.example', + ], + 'application/vnd.ibexa.api.CountriesList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/CountryListWrapper', + ], + ], + ], + ], + ], + ), +)] /** * Services controller. */ diff --git a/src/lib/Server/Controller/Session/SessionBaseController.php b/src/lib/Server/Controller/Session/SessionBaseController.php new file mode 100644 index 000000000..9a7265055 --- /dev/null +++ b/src/lib/Server/Controller/Session/SessionBaseController.php @@ -0,0 +1,98 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Session; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Contracts\Rest\Exceptions\UnauthorizedException; +use Ibexa\Rest\Server\Controller; +use Ibexa\Rest\Server\Security\CsrfTokenManager; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface as SecurityTokenStorageInterface; +use Symfony\Component\Security\Csrf\CsrfToken; + +class SessionBaseController extends Controller +{ + public function __construct( + protected readonly PermissionResolver $permissionResolver, + protected readonly UserService $userService, + protected readonly CsrfTokenManager $csrfTokenManager, + protected readonly SecurityTokenStorageInterface $securityTokenStorage, + protected readonly string $csrfTokenIntention, + protected readonly ConfigResolverInterface $configResolver, + ) { + } + + protected function hasStoredCsrfToken(): bool + { + return $this->csrfTokenManager->hasToken($this->csrfTokenIntention); + } + + /** + * Checks the presence / validity of the CSRF token. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the token is missing or invalid + */ + protected function checkCsrfToken(Request $request): void + { + if (!$request->headers->has('X-CSRF-Token')) { + throw $this->createInvalidCsrfTokenException($request); + } + + $csrfToken = new CsrfToken( + $this->csrfTokenIntention, + $request->headers->get('X-CSRF-Token') + ); + + if (!$this->csrfTokenManager->isTokenValid($csrfToken)) { + throw $this->createInvalidCsrfTokenException($request); + } + } + + protected function getCsrfToken(): string + { + return $this->csrfTokenManager->getToken($this->csrfTokenIntention)->getValue(); + } + + protected function createInvalidCsrfTokenException(Request $request): UnauthorizedException + { + return new UnauthorizedException('Missing or invalid CSRF token'); + } + + protected function logout(Request $request): Response + { + $path = '/'; + $domain = null; + + $session = $this->configResolver->getParameter('session'); + if (array_key_exists('cookie_domain', $session)) { + $domain = $session['cookie_domain']; + } + + if (array_key_exists('cookie_path', $session)) { + $path = $session['cookie_path']; + } + + $response = new Response(); + $requestSession = $request->getSession(); + + $response->headers->clearCookie( + $requestSession->getName(), + $path, + $domain + ); + + $response->setStatusCode(404); + $requestSession->clear(); + + return $response; + } +} diff --git a/src/lib/Server/Controller/Session/SessionCheckController.php b/src/lib/Server/Controller/Session/SessionCheckController.php new file mode 100644 index 000000000..d81098ee8 --- /dev/null +++ b/src/lib/Server/Controller/Session/SessionCheckController.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Session; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/sessions/current', + name: 'Get current session', + openapi: new Model\Operation( + summary: 'Get current user session, if any.', + tags: [ + 'User Session', + ], + parameters: [ + new Model\Parameter( + name: 'Cookie', + in: 'header', + required: true, + description: 'Only needed for session\'s checking {sessionName}={sessionID}.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the session is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'User is currently logged in and has a valid session.', + 'content' => [ + 'application/vnd.ibexa.api.Session+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Session', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example', + ], + 'application/vnd.ibexa.api.Session+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example', + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'User does not have a valid session, or it has expired.', + ], + ], + ), +)] +/** + * @internal + */ +final class SessionCheckController extends SessionBaseController +{ + /** + * @return \Ibexa\Rest\Server\Values\UserSession|\Symfony\Component\HttpFoundation\Response + */ + public function checkSessionAction(Request $request) + { + $session = $request->getSession(); + if ($session === null || !$session->isStarted()) { + return $this->logout($request); + } + + $currentUser = $this->userService->loadUser( + $this->permissionResolver->getCurrentUserReference()->getUserId() + ); + + return new Values\UserSession( + $currentUser, + $session->getName(), + $session->getId(), + $this->getCsrfToken(), + false + ); + } +} diff --git a/src/lib/Server/Controller/Session/SessionCreateController.php b/src/lib/Server/Controller/Session/SessionCreateController.php new file mode 100644 index 000000000..2742cfb41 --- /dev/null +++ b/src/lib/Server/Controller/Session/SessionCreateController.php @@ -0,0 +1,171 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Session; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Rest\Exceptions\UnauthorizedException; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\Exception\AuthenticationException; + +#[Post( + uriTemplate: '/user/sessions', + name: 'Create session (login a User)', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Performs a login for the user or checks if session exists and returns the session and session cookie. The client will need to remember both session name/ID and CSRF token as this is for security reasons not exposed via GET.', + tags: [ + 'User Session', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the session is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The SessionInput schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Cookie', + in: 'header', + required: true, + description: 'Only needed for session\'s checking {sessionName}={sessionID}.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'X-CSRF-Token', + in: 'header', + required: true, + description: 'Only needed for session\'s checking. The {csrfToken} needed on all unsafe HTTP methods with session.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.SessionInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.xml.example', + ], + 'application/vnd.ibexa.api.SessionInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/SessionInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'Session already exists.', + 'content' => [ + 'application/vnd.ibexa.api.Session+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Session', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example', + ], + 'application/vnd.ibexa.api.Session+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example', + ], + ], + ], + Response::HTTP_CREATED => [ + 'description' => 'Session is created.', + 'content' => [ + 'application/vnd.ibexa.api.Session+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Session', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example', + ], + 'application/vnd.ibexa.api.Session+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the authorization failed.', + ], + Response::HTTP_CONFLICT => [ + 'description' => 'Error - header contained a session cookie but different user was authorized.', + ], + ], + ), +)] +/** + * @internal + */ +final class SessionCreateController extends SessionBaseController +{ + /** + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function createSessionAction(Request $request): RestValue + { + try { + $session = $request->getSession(); + $csrfToken = $this->getCsrfToken(); + $token = $this->securityTokenStorage->getToken(); + + if ($token === null) { + throw new UnauthorizedException('The current user is not authenticated.'); + } + + /** @var \Ibexa\Core\MVC\Symfony\Security\User $user */ + $user = $token->getUser(); + + return new Values\UserSession( + $user->getAPIUser(), + $session->getName(), + $session->getId(), + $csrfToken, + !$token->hasAttribute('isFromSession') + ); + } catch (Exceptions\UserConflictException $e) { + // Already logged in with another user, this will be converted to HTTP status 409 + return new Values\Conflict(); + } catch (AuthenticationException $e) { + throw new UnauthorizedException('Invalid login or password'); + } catch (AccessDeniedException $e) { + throw new UnauthorizedException($e->getMessage()); + } + } +} diff --git a/src/lib/Server/Controller/Session/SessionDeleteController.php b/src/lib/Server/Controller/Session/SessionDeleteController.php new file mode 100644 index 000000000..4465a4bee --- /dev/null +++ b/src/lib/Server/Controller/Session/SessionDeleteController.php @@ -0,0 +1,85 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Session; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/sessions/{sessionId}', + name: 'Delete session (logout a User)', + openapi: new Model\Operation( + summary: 'The user session is removed i.e. the user is logged out.', + tags: [ + 'User Session', + ], + parameters: [ + new Model\Parameter( + name: 'Cookie', + in: 'header', + required: true, + description: '{sessionName}={sessionID}', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'X-CSRF-Token', + in: 'header', + required: true, + description: 'The {csrfToken} needed on all unsafe HTTP methods with session.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'sessionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'OK - session deleted.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the session does not exist.', + ], + ], + ), +)] +/** + * @internal + */ +final class SessionDeleteController extends SessionBaseController +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function deleteSessionAction(string $sessionId, Request $request): Values\DeletedUserSession|Response + { + /** @var \Symfony\Component\HttpFoundation\Session\Session $session */ + $session = $request->getSession(); + if (!$session->isStarted() || $session->getId() !== $sessionId || !$this->hasStoredCsrfToken()) { + return $this->logout($request); + } + + $this->checkCsrfToken($request); + + return new Values\DeletedUserSession( + $this->logout($request) + ); + } +} diff --git a/src/lib/Server/Controller/Session/SessionRefreshController.php b/src/lib/Server/Controller/Session/SessionRefreshController.php new file mode 100644 index 000000000..7eeadb062 --- /dev/null +++ b/src/lib/Server/Controller/Session/SessionRefreshController.php @@ -0,0 +1,127 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\Session; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/sessions/{sessionId}/refresh', + name: 'Refresh session (deprecated)', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Get the session\'s User information. Deprecated as of Ibexa DXP 4.6, use GET /user/sessions/current instead.', + tags: [ + 'User Session', + ], + parameters: [ + new Model\Parameter( + name: 'Cookie', + in: 'header', + required: true, + description: '{sessionName}={sessionID}', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'X-CSRF-Token', + in: 'header', + required: true, + description: 'The {csrfToken} needed on all unsafe HTTP methods with session.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'sessionId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Session+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Session', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/POST/Session.xml.example', + ], + 'application/vnd.ibexa.api.Session+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/SessionWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/sessions/session_id/refresh/POST/Session.json.example', + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the session does not exist.', + ], + ], + ), +)] +/** + * @internal + */ +final class SessionRefreshController extends SessionBaseController +{ + /** + * Refresh given session. + * + * @deprecated 5.0.0 The "SessionController::refreshSessionAction()" method is deprecated, will be removed in the next API version. Use SessionController::checkSessionAction() instead. + * + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function refreshSessionAction(string $sessionId, Request $request): Values\UserSession|Response + { + trigger_deprecation( + 'ibexa/rest', + '4.6.7', + sprintf('The %s() method is deprecated, will be removed in the next API version.', __METHOD__) + ); + + $session = $request->getSession(); + + if ($session === null || !$session->isStarted() || $session->getId() !== $sessionId || !$this->hasStoredCsrfToken()) { + return $this->logout($request); + } + + $this->checkCsrfToken($request); + $currentUser = $this->userService->loadUser( + $this->permissionResolver->getCurrentUserReference()->getUserId() + ); + + return new Values\UserSession( + $currentUser, + $session->getName(), + $session->getId(), + $request->headers->get('X-CSRF-Token') ?? '', + false + ); + } +} diff --git a/src/lib/Server/Controller/SessionController.php b/src/lib/Server/Controller/SessionController.php deleted file mode 100644 index c507b026a..000000000 --- a/src/lib/Server/Controller/SessionController.php +++ /dev/null @@ -1,217 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\PermissionResolver; -use Ibexa\Contracts\Core\Repository\UserService; -use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; -use Ibexa\Contracts\Rest\Exceptions\UnauthorizedException; -use Ibexa\Rest\Server\Controller; -use Ibexa\Rest\Server\Exceptions; -use Ibexa\Rest\Server\Security\CsrfTokenManager; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Value as RestValue; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface as SecurityTokenStorageInterface; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Csrf\CsrfToken; - -/** - * @internal - */ -final class SessionController extends Controller -{ - public function __construct( - private readonly PermissionResolver $permissionResolver, - private readonly UserService $userService, - private readonly CsrfTokenManager $csrfTokenManager, - private readonly SecurityTokenStorageInterface $securityTokenStorage, - private readonly string $csrfTokenIntention, - private readonly ConfigResolverInterface $configResolver, - ) { - } - - /** - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function createSessionAction(Request $request): RestValue - { - try { - $session = $request->getSession(); - $csrfToken = $this->getCsrfToken(); - $token = $this->securityTokenStorage->getToken(); - - if ($token === null) { - throw new UnauthorizedException('The current user is not authenticated.'); - } - - /** @var \Ibexa\Core\MVC\Symfony\Security\User $user */ - $user = $token->getUser(); - - return new Values\UserSession( - $user->getAPIUser(), - $session->getName(), - $session->getId(), - $csrfToken, - !$token->hasAttribute('isFromSession') - ); - } catch (Exceptions\UserConflictException $e) { - // Already logged in with another user, this will be converted to HTTP status 409 - return new Values\Conflict(); - } catch (AuthenticationException $e) { - throw new UnauthorizedException('Invalid login or password'); - } catch (AccessDeniedException $e) { - throw new UnauthorizedException($e->getMessage()); - } - } - - /** - * @return \Ibexa\Rest\Server\Values\UserSession|\Symfony\Component\HttpFoundation\Response - */ - public function checkSessionAction(Request $request) - { - $session = $request->getSession(); - if ($session === null || !$session->isStarted()) { - return $this->logout($request); - } - - $currentUser = $this->userService->loadUser( - $this->permissionResolver->getCurrentUserReference()->getUserId() - ); - - return new Values\UserSession( - $currentUser, - $session->getName(), - $session->getId(), - $this->getCsrfToken(), - false - ); - } - - /** - * Refresh given session. - * - * @deprecated 5.0.0 The "SessionController::refreshSessionAction()" method is deprecated, will be removed in the next API version. Use SessionController::checkSessionAction() instead. - * - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function refreshSessionAction(string $sessionId, Request $request): Values\UserSession|Response - { - trigger_deprecation( - 'ibexa/rest', - '4.6.7', - sprintf('The %s() method is deprecated, will be removed in the next API version.', __METHOD__) - ); - - $session = $request->getSession(); - - if ($session === null || !$session->isStarted() || $session->getId() !== $sessionId || !$this->hasStoredCsrfToken()) { - return $this->logout($request); - } - - $this->checkCsrfToken($request); - $currentUser = $this->userService->loadUser( - $this->permissionResolver->getCurrentUserReference()->getUserId() - ); - - return new Values\UserSession( - $currentUser, - $session->getName(), - $session->getId(), - $request->headers->get('X-CSRF-Token') ?? '', - false - ); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - */ - public function deleteSessionAction(string $sessionId, Request $request): Values\DeletedUserSession|Response - { - /** @var \Symfony\Component\HttpFoundation\Session\Session $session */ - $session = $request->getSession(); - if (!$session->isStarted() || $session->getId() !== $sessionId || !$this->hasStoredCsrfToken()) { - return $this->logout($request); - } - - $this->checkCsrfToken($request); - - return new Values\DeletedUserSession( - $this->logout($request) - ); - } - - private function hasStoredCsrfToken(): bool - { - return $this->csrfTokenManager->hasToken($this->csrfTokenIntention); - } - - /** - * Checks the presence / validity of the CSRF token. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the token is missing or invalid - */ - private function checkCsrfToken(Request $request): void - { - if (!$request->headers->has('X-CSRF-Token')) { - throw $this->createInvalidCsrfTokenException($request); - } - - $csrfToken = new CsrfToken( - $this->csrfTokenIntention, - $request->headers->get('X-CSRF-Token') - ); - - if (!$this->csrfTokenManager->isTokenValid($csrfToken)) { - throw $this->createInvalidCsrfTokenException($request); - } - } - - private function getCsrfToken(): string - { - return $this->csrfTokenManager->getToken($this->csrfTokenIntention)->getValue(); - } - - private function createInvalidCsrfTokenException(Request $request): UnauthorizedException - { - return new UnauthorizedException('Missing or invalid CSRF token'); - } - - private function logout(Request $request): Response - { - $path = '/'; - $domain = null; - - $session = $this->configResolver->getParameter('session'); - if (array_key_exists('cookie_domain', $session)) { - $domain = $session['cookie_domain']; - } - - if (array_key_exists('cookie_path', $session)) { - $path = $session['cookie_path']; - } - - $response = new Response(); - $requestSession = $request->getSession(); - - $response->headers->clearCookie( - $requestSession->getName(), - $path, - $domain - ); - - $response->setStatusCode(404); - $requestSession->clear(); - - return $response; - } -} diff --git a/src/lib/Server/Controller/Trash/LocationTrashController.php b/src/lib/Server/Controller/Trash/LocationTrashController.php new file mode 100644 index 000000000..2cf82e9a0 --- /dev/null +++ b/src/lib/Server/Controller/Trash/LocationTrashController.php @@ -0,0 +1,58 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Trash; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Webmozart\Assert\Assert; + +class LocationTrashController extends RestController +{ + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function trashLocation(string $locationPath): RestValue + { + $location = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath), + ); + + $trashItem = $this->trashService->trash($location); + + if ($trashItem === null) { + return new Values\NoContent(); + } + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_trash_item', + ['trashItemId' => $trashItem->getId()], + ), + ); + } + + private function extractLocationIdFromPath(string $path): int + { + $pathParts = explode('/', $path); + $lastPart = array_pop($pathParts); + + Assert::integerish($lastPart); + + return (int)$lastPart; + } +} diff --git a/src/lib/Server/Controller/Trash/TrashEmptyController.php b/src/lib/Server/Controller/Trash/TrashEmptyController.php new file mode 100644 index 000000000..30e3df6de --- /dev/null +++ b/src/lib/Server/Controller/Trash/TrashEmptyController.php @@ -0,0 +1,57 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Trash; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/trash', + name: 'Empty Trash', + openapi: new Model\Operation( + summary: 'Empties the Trash.', + tags: [ + 'Trash', + ], + parameters: [ + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - Trash emptied.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to empty all items from Trash.', + ], + ], + ), +)] +class TrashEmptyController extends RestController +{ + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { + } + + /** + * Empties the trash. + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function emptyTrash() + { + $this->trashService->emptyTrash(); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Trash/TrashItemDeleteController.php b/src/lib/Server/Controller/Trash/TrashItemDeleteController.php new file mode 100644 index 000000000..6f964de94 --- /dev/null +++ b/src/lib/Server/Controller/Trash/TrashItemDeleteController.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Trash; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/trash/{trashItemid}', + name: 'Delete Trash item', + openapi: new Model\Operation( + summary: 'Deletes the provided item from Trash.', + tags: [ + 'Trash', + ], + parameters: [ + new Model\Parameter( + name: 'trashItemid', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - item deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete the provided item.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The provided item does not exist in Trash.', + ], + ], + ), +)] +class TrashItemDeleteController extends RestController +{ + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { + } + + /** + * Deletes the given trash item. + * + * @param $trashItemId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteTrashItem($trashItemId) + { + $this->trashService->deleteTrashItem( + $this->trashService->loadTrashItem($trashItemId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/Trash/TrashItemListController.php b/src/lib/Server/Controller/Trash/TrashItemListController.php new file mode 100644 index 000000000..000a38fd8 --- /dev/null +++ b/src/lib/Server/Controller/Trash/TrashItemListController.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Trash; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/trash', + name: 'List Trash items', + openapi: new Model\Operation( + summary: 'Returns a list of all items in the Trash.', + tags: [ + 'Trash', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the Trash item list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the list of items in the Trash.', + 'content' => [ + 'application/vnd.ibexa.api.Trash+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/Trash', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/trash/GET/Trash.xml.example', + ], + 'application/vnd.ibexa.api.Trash+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/TrashWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/trash/GET/Trash.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read the Trash.', + ], + ], + ), +)] +class TrashItemListController extends RestController +{ + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { + } + + /** + * Returns a list of all trash items. + * + * @return \Ibexa\Rest\Server\Values\Trash + */ + public function loadTrashItems(Request $request) + { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; + + $query = new Query(); + $query->offset = $offset >= 0 ? $offset : null; + $query->limit = $limit >= 0 ? $limit : null; + + $trashItems = []; + + foreach ($this->trashService->findTrashItems($query)->items as $trashItem) { + $trashItems[] = new Values\RestTrashItem( + $trashItem, + $this->locationService->getLocationChildCount($trashItem) + ); + } + + return new Values\Trash( + $trashItems, + $request->getPathInfo() + ); + } +} diff --git a/src/lib/Server/Controller/Trash/TrashItemLoadByIdController.php b/src/lib/Server/Controller/Trash/TrashItemLoadByIdController.php new file mode 100644 index 000000000..2d363e0f3 --- /dev/null +++ b/src/lib/Server/Controller/Trash/TrashItemLoadByIdController.php @@ -0,0 +1,93 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\Trash; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/trash/{trashItemid}', + name: 'Get Trash item', + openapi: new Model\Operation( + summary: 'Returns the item in Trash with the provided ID.', + tags: [ + 'Trash', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the item in Trash is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'trashItemid', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.TrashItem+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/TrashItem', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.xml.example', + ], + 'application/vnd.ibexa.api.TrashItem+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/TrashItemWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/trash/trash_itemid/GET/TrashItem.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read the item in Trash.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - An item in Trash with the provided ID does not exist.', + ], + ], + ), +)] +class TrashItemLoadByIdController extends RestController +{ + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { + } + + /** + * Returns the trash item given by id. + * + * @param $trashItemId + * + * @return \Ibexa\Rest\Server\Values\RestTrashItem + */ + public function loadTrashItem($trashItemId) + { + return new Values\RestTrashItem( + $trashItem = $this->trashService->loadTrashItem($trashItemId), + $this->locationService->getLocationChildCount($trashItem) + ); + } +} diff --git a/src/lib/Server/Controller/Trash.php b/src/lib/Server/Controller/Trash/TrashItemRestoreController.php similarity index 52% rename from src/lib/Server/Controller/Trash.php rename to src/lib/Server/Controller/Trash/TrashItemRestoreController.php index c8c750b57..e7f33c073 100644 --- a/src/lib/Server/Controller/Trash.php +++ b/src/lib/Server/Controller/Trash/TrashItemRestoreController.php @@ -5,148 +5,26 @@ * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace Ibexa\Rest\Server\Controller; +namespace Ibexa\Rest\Server\Controller\Trash; use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\LocationService; use Ibexa\Contracts\Core\Repository\TrashService; -use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Rest\Message; use Ibexa\Rest\Server\Controller as RestController; use Ibexa\Rest\Server\Exceptions\ForbiddenException; use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Value as RestValue; use InvalidArgumentException; use JMS\TranslationBundle\Annotation\Ignore; use Symfony\Component\HttpFoundation\Request; -use Webmozart\Assert\Assert; -/** - * Trash controller. - */ -class Trash extends RestController +class TrashItemRestoreController extends RestController { - /** - * Trash service. - * - * @var \Ibexa\Contracts\Core\Repository\TrashService - */ - protected $trashService; - - /** - * Location service. - * - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\TrashService $trashService - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - */ - public function __construct(TrashService $trashService, LocationService $locationService) - { - $this->trashService = $trashService; - $this->locationService = $locationService; - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function trashLocation(string $locationPath): RestValue - { - $location = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath), - ); - - $trashItem = $this->trashService->trash($location); - - if ($trashItem === null) { - return new Values\NoContent(); - } - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_trash_item', - ['trashItemId' => $trashItem->getId()], - ), - ); - } - - /** - * Returns a list of all trash items. - * - * @return \Ibexa\Rest\Server\Values\Trash - */ - public function loadTrashItems(Request $request) - { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : -1; - - $query = new Query(); - $query->offset = $offset >= 0 ? $offset : null; - $query->limit = $limit >= 0 ? $limit : null; - - $trashItems = []; - - foreach ($this->trashService->findTrashItems($query)->items as $trashItem) { - $trashItems[] = new Values\RestTrashItem( - $trashItem, - $this->locationService->getLocationChildCount($trashItem) - ); - } - - return new Values\Trash( - $trashItems, - $request->getPathInfo() - ); - } - - /** - * Returns the trash item given by id. - * - * @param $trashItemId - * - * @return \Ibexa\Rest\Server\Values\RestTrashItem - */ - public function loadTrashItem($trashItemId) - { - return new Values\RestTrashItem( - $trashItem = $this->trashService->loadTrashItem($trashItemId), - $this->locationService->getLocationChildCount($trashItem) - ); - } - - /** - * Empties the trash. - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function emptyTrash() - { - $this->trashService->emptyTrash(); - - return new Values\NoContent(); - } - - /** - * Deletes the given trash item. - * - * @param $trashItemId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteTrashItem($trashItemId) - { - $this->trashService->deleteTrashItem( - $this->trashService->loadTrashItem($trashItemId) - ); - - return new Values\NoContent(); + public function __construct( + protected TrashService $trashService, + protected LocationService $locationService + ) { } /** @@ -245,14 +123,4 @@ public function restoreItem(int $trashItemId, Request $request): Values\Resource ) ); } - - private function extractLocationIdFromPath(string $path): int - { - $pathParts = explode('/', $path); - $lastPart = array_pop($pathParts); - - Assert::integerish($lastPart); - - return (int)$lastPart; - } } diff --git a/src/lib/Server/Controller/URLAlias.php b/src/lib/Server/Controller/URLAlias.php deleted file mode 100644 index a57dbb529..000000000 --- a/src/lib/Server/Controller/URLAlias.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Contracts\Core\Repository\URLAliasService; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * URLAlias controller. - */ -class URLAlias extends RestController -{ - /** - * URLAlias service. - * - * @var \Ibexa\Contracts\Core\Repository\URLAliasService - */ - protected $urlAliasService; - - /** - * Location service. - * - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\URLAliasService $urlAliasService - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - */ - public function __construct(URLAliasService $urlAliasService, LocationService $locationService) - { - $this->urlAliasService = $urlAliasService; - $this->locationService = $locationService; - } - - /** - * Returns the URL alias with the given ID. - * - * @param $urlAliasId - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias - */ - public function loadURLAlias($urlAliasId) - { - return $this->urlAliasService->load($urlAliasId); - } - - /** - * Returns the list of global URL aliases. - * - * @return \Ibexa\Rest\Server\Values\URLAliasRefList - */ - public function listGlobalURLAliases() - { - return new Values\URLAliasRefList( - $this->urlAliasService->listGlobalAliases(), - $this->router->generate('ibexa.rest.list_global_url_aliases') - ); - } - - /** - * Returns the list of URL aliases for a location. - * - * @param $locationPath - * - * @return \Ibexa\Rest\Server\Values\URLAliasRefList - */ - public function listLocationURLAliases($locationPath, Request $request) - { - $locationPathParts = explode('/', $locationPath); - - $location = $this->locationService->loadLocation( - array_pop($locationPathParts) - ); - - $custom = !($request->query->has('custom') && $request->query->get('custom') === 'false'); - - return new Values\CachedValue( - new Values\URLAliasRefList( - $this->urlAliasService->listLocationAliases($location, $custom), - $request->getPathInfo() - ), - ['locationId' => $location->id] - ); - } - - /** - * Creates a new URL alias. - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedURLAlias - */ - public function createURLAlias(Request $request) - { - $urlAliasCreate = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - if ($urlAliasCreate['_type'] === 'LOCATION') { - $locationPathParts = explode( - '/', - $this->requestParser->parseHref($urlAliasCreate['location']['_href'], 'locationPath') - ); - - $location = $this->locationService->loadLocation( - array_pop($locationPathParts) - ); - - try { - $createdURLAlias = $this->urlAliasService->createUrlAlias( - $location, - $urlAliasCreate['path'], - $urlAliasCreate['languageCode'], - $urlAliasCreate['forward'], - $urlAliasCreate['alwaysAvailable'] - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } else { - try { - $createdURLAlias = $this->urlAliasService->createGlobalUrlAlias( - $urlAliasCreate['resource'], - $urlAliasCreate['path'], - $urlAliasCreate['languageCode'], - $urlAliasCreate['forward'], - $urlAliasCreate['alwaysAvailable'] - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - } - - return new Values\CreatedURLAlias( - [ - 'urlAlias' => $createdURLAlias, - ] - ); - } - - /** - * The given URL alias is deleted. - * - * @param $urlAliasId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteURLAlias($urlAliasId) - { - $this->urlAliasService->removeAliases( - [ - $this->urlAliasService->load($urlAliasId), - ] - ); - - return new Values\NoContent(); - } -} diff --git a/src/lib/Server/Controller/URLAlias/URLAliasCreateController.php b/src/lib/Server/Controller/URLAlias/URLAliasCreateController.php new file mode 100644 index 000000000..6b80a7c1a --- /dev/null +++ b/src/lib/Server/Controller/URLAlias/URLAliasCreateController.php @@ -0,0 +1,164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLAlias; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/urlaliases', + name: 'Create URL alias', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a URL alias.', + tags: [ + 'Url Alias', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the created URL alias is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The URL alias input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UrlAliasCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.xml.example', + ], + 'application/vnd.ibexa.api.UrlAliasCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/POST/UrlAliasCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'URL alias created.', + 'content' => [ + 'application/vnd.ibexa.api.UrlAlias+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAlias', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example', + ], + 'application/vnd.ibexa.api.UrlAlias+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create a URL alias.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A URL alias with the same identifier already exists.', + ], + ], + ), +)] +class URLAliasCreateController extends RestController +{ + public function __construct( + protected URLAliasService $urlAliasService, + protected LocationService $locationService + ) { + } + + /** + * Creates a new URL alias. + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedURLAlias + */ + public function createURLAlias(Request $request) + { + $urlAliasCreate = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + if ($urlAliasCreate['_type'] === 'LOCATION') { + $locationPathParts = explode( + '/', + $this->requestParser->parseHref($urlAliasCreate['location']['_href'], 'locationPath') + ); + + $location = $this->locationService->loadLocation( + array_pop($locationPathParts) + ); + + try { + $createdURLAlias = $this->urlAliasService->createUrlAlias( + $location, + $urlAliasCreate['path'], + $urlAliasCreate['languageCode'], + $urlAliasCreate['forward'], + $urlAliasCreate['alwaysAvailable'] + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } else { + try { + $createdURLAlias = $this->urlAliasService->createGlobalUrlAlias( + $urlAliasCreate['resource'], + $urlAliasCreate['path'], + $urlAliasCreate['languageCode'], + $urlAliasCreate['forward'], + $urlAliasCreate['alwaysAvailable'] + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + } + + return new Values\CreatedURLAlias( + [ + 'urlAlias' => $createdURLAlias, + ] + ); + } +} diff --git a/src/lib/Server/Controller/URLAlias/URLAliasDeleteController.php b/src/lib/Server/Controller/URLAlias/URLAliasDeleteController.php new file mode 100644 index 000000000..189bbd3fe --- /dev/null +++ b/src/lib/Server/Controller/URLAlias/URLAliasDeleteController.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLAlias; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/urlaliases/{urlAliasId}', + name: 'Delete URL alias', + openapi: new Model\Operation( + summary: 'Deletes the provided URL alias.', + tags: [ + 'Url Alias', + ], + parameters: [ + new Model\Parameter( + name: 'urlAliasId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - URL alias deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete a URL alias.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The URL alias does not exist.', + ], + ], + ), +)] +class URLAliasDeleteController extends RestController +{ + public function __construct( + protected URLAliasService $urlAliasService, + protected LocationService $locationService + ) { + } + + /** + * The given URL alias is deleted. + * + * @param $urlAliasId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteURLAlias($urlAliasId) + { + $this->urlAliasService->removeAliases( + [ + $this->urlAliasService->load($urlAliasId), + ] + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/URLAlias/URLAliasListGlobalController.php b/src/lib/Server/Controller/URLAlias/URLAliasListGlobalController.php new file mode 100644 index 000000000..b872ac1cd --- /dev/null +++ b/src/lib/Server/Controller/URLAlias/URLAliasListGlobalController.php @@ -0,0 +1,81 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLAlias; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/urlaliases', + name: 'List global URL aliases', + openapi: new Model\Operation( + summary: 'Returns the list of global URL aliases.', + tags: [ + 'Url Alias', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the URL alias list contains only references and is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the list of URL aliases.', + 'content' => [ + 'application/vnd.ibexa.api.UrlAliasRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.xml.example', + ], + 'application/vnd.ibexa.api.UrlAliasRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read URL aliases.', + ], + ], + ), +)] +class URLAliasListGlobalController extends RestController +{ + public function __construct( + protected URLAliasService $urlAliasService, + protected LocationService $locationService + ) { + } + + /** + * Returns the list of global URL aliases. + * + * @return \Ibexa\Rest\Server\Values\URLAliasRefList + */ + public function listGlobalURLAliases() + { + return new Values\URLAliasRefList( + $this->urlAliasService->listGlobalAliases(), + $this->router->generate('ibexa.rest.list_global_url_aliases') + ); + } +} diff --git a/src/lib/Server/Controller/URLAlias/URLAliasListLocationController.php b/src/lib/Server/Controller/URLAlias/URLAliasListLocationController.php new file mode 100644 index 000000000..3773603aa --- /dev/null +++ b/src/lib/Server/Controller/URLAlias/URLAliasListLocationController.php @@ -0,0 +1,105 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLAlias; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/locations/{path}/urlaliases', + name: 'List URL aliases for Location', + openapi: new Model\Operation( + summary: 'Returns the list of URL aliases for a Location.', + tags: [ + 'Location', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the URL alias list contains only references and is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the list of URL aliases.', + 'content' => [ + 'application/vnd.ibexa.api.UrlAliasRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/GET/UrlAliasRefList.xml.example', + ], + 'application/vnd.ibexa.api.UrlAliasRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasRefListWrapper', + ], + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The user has no permission to read URL aliases.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The Location was not found.', + ], + ], + ), +)] +class URLAliasListLocationController extends RestController +{ + public function __construct( + protected URLAliasService $urlAliasService, + protected LocationService $locationService + ) { + } + + /** + * Returns the list of URL aliases for a location. + * + * @param $locationPath + * + * @return \Ibexa\Rest\Server\Values\URLAliasRefList + */ + public function listLocationURLAliases($locationPath, Request $request) + { + $locationPathParts = explode('/', $locationPath); + + $location = $this->locationService->loadLocation( + array_pop($locationPathParts) + ); + + $custom = !($request->query->has('custom') && $request->query->get('custom') === 'false'); + + return new Values\CachedValue( + new Values\URLAliasRefList( + $this->urlAliasService->listLocationAliases($location, $custom), + $request->getPathInfo() + ), + ['locationId' => $location->id] + ); + } +} diff --git a/src/lib/Server/Controller/URLAlias/URLAliasLoadByIdController.php b/src/lib/Server/Controller/URLAlias/URLAliasLoadByIdController.php new file mode 100644 index 000000000..76ffc9c29 --- /dev/null +++ b/src/lib/Server/Controller/URLAlias/URLAliasLoadByIdController.php @@ -0,0 +1,90 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLAlias; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/urlaliases/{urlAliasId}', + name: 'Get URL alias', + openapi: new Model\Operation( + summary: 'Returns the URL alias with the given ID.', + tags: [ + 'Url Alias', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the URL alias is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'urlAliasId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the URL alias.', + 'content' => [ + 'application/vnd.ibexa.api.UrlAlias+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAlias', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example', + ], + 'application/vnd.ibexa.api.UrlAlias+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlAliasWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read URL aliases.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The URL alias does not exist.', + ], + ], + ), +)] +class URLAliasLoadByIdController extends RestController +{ + public function __construct( + protected URLAliasService $urlAliasService, + protected LocationService $locationService + ) { + } + + /** + * Returns the URL alias with the given ID. + * + * @param $urlAliasId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function loadURLAlias($urlAliasId) + { + return $this->urlAliasService->load($urlAliasId); + } +} diff --git a/src/lib/Server/Controller/URLWildcard.php b/src/lib/Server/Controller/URLWildcard.php deleted file mode 100644 index b02c319a0..000000000 --- a/src/lib/Server/Controller/URLWildcard.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; -use Ibexa\Contracts\Core\Repository\URLWildcardService; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; - -/** - * URLWildcard controller. - */ -class URLWildcard extends RestController -{ - /** - * URLWildcard service. - * - * @var \Ibexa\Contracts\Core\Repository\URLWildcardService - */ - protected $urlWildcardService; - - /** - * Construct controller. - * - * @param \Ibexa\Contracts\Core\Repository\URLWildcardService $urlWildcardService - */ - public function __construct(URLWildcardService $urlWildcardService) - { - $this->urlWildcardService = $urlWildcardService; - } - - /** - * Returns the URL wildcard with the given id. - * - * @param $urlWildcardId - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard - */ - public function loadURLWildcard($urlWildcardId) - { - return $this->urlWildcardService->load($urlWildcardId); - } - - /** - * Returns the list of URL wildcards. - * - * @return \Ibexa\Rest\Server\Values\URLWildcardList - */ - public function listURLWildcards() - { - return new Values\URLWildcardList( - $this->urlWildcardService->loadAll() - ); - } - - /** - * Creates a new URL wildcard. - * - * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException - * - * @return \Ibexa\Rest\Server\Values\CreatedURLWildcard - */ - public function createURLWildcard(Request $request) - { - $urlWildcardCreate = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - $createdURLWildcard = $this->urlWildcardService->create( - $urlWildcardCreate['sourceUrl'], - $urlWildcardCreate['destinationUrl'], - $urlWildcardCreate['forward'] - ); - } catch (InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - return new Values\CreatedURLWildcard( - [ - 'urlWildcard' => $createdURLWildcard, - ] - ); - } - - /** - * The given URL wildcard is deleted. - * - * @param $urlWildcardId - * - * @return \Ibexa\Rest\Server\Values\NoContent - */ - public function deleteURLWildcard($urlWildcardId) - { - $this->urlWildcardService->remove( - $this->urlWildcardService->load($urlWildcardId) - ); - - return new Values\NoContent(); - } -} diff --git a/src/lib/Server/Controller/URLWildcard/URLWildcardCreateController.php b/src/lib/Server/Controller/URLWildcard/URLWildcardCreateController.php new file mode 100644 index 000000000..d009e5197 --- /dev/null +++ b/src/lib/Server/Controller/URLWildcard/URLWildcardCreateController.php @@ -0,0 +1,137 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLWildcard; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/content/urlwildcards', + name: 'Create URL wildcard', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new URL wildcard.', + tags: [ + 'Url Wildcard', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new URL wildcard is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The URL Wildcard input schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UrlWildcardCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example', + ], + 'application/vnd.ibexa.api.UrlWildcardCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'URL wildcard created.', + 'content' => [ + 'application/vnd.ibexa.api.UrlWildcard+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcard', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example', + ], + 'application/vnd.ibexa.api.UrlWildcard+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - The input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to create a URL wildcard.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - A URL wildcard with the same identifier already exists.', + ], + ], + ), +)] +class URLWildcardCreateController extends RestController +{ + public function __construct( + protected URLWildcardService $urlWildcardService + ) { + } + + /** + * Creates a new URL wildcard. + * + * @throws \Ibexa\Rest\Server\Exceptions\ForbiddenException + * + * @return \Ibexa\Rest\Server\Values\CreatedURLWildcard + */ + public function createURLWildcard(Request $request) + { + $urlWildcardCreate = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + $createdURLWildcard = $this->urlWildcardService->create( + $urlWildcardCreate['sourceUrl'], + $urlWildcardCreate['destinationUrl'], + $urlWildcardCreate['forward'] + ); + } catch (InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + return new Values\CreatedURLWildcard( + [ + 'urlWildcard' => $createdURLWildcard, + ] + ); + } +} diff --git a/src/lib/Server/Controller/URLWildcard/URLWildcardDeleteController.php b/src/lib/Server/Controller/URLWildcard/URLWildcardDeleteController.php new file mode 100644 index 000000000..dd1ad8ce0 --- /dev/null +++ b/src/lib/Server/Controller/URLWildcard/URLWildcardDeleteController.php @@ -0,0 +1,70 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLWildcard; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/content/urlwildcards/{wildcardId}', + name: 'Delete URL wildcard', + openapi: new Model\Operation( + summary: 'Deletes the given URL wildcard.', + tags: [ + 'Url Wildcard', + ], + parameters: [ + new Model\Parameter( + name: 'wildcardId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content - URL wildcard deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to delete a URL wildcard.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The URL wildcard does not exist.', + ], + ], + ), +)] +class URLWildcardDeleteController extends RestController +{ + public function __construct( + protected URLWildcardService $urlWildcardService + ) { + } + + /** + * The given URL wildcard is deleted. + * + * @param $urlWildcardId + * + * @return \Ibexa\Rest\Server\Values\NoContent + */ + public function deleteURLWildcard($urlWildcardId) + { + $this->urlWildcardService->remove( + $this->urlWildcardService->load($urlWildcardId) + ); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/URLWildcard/URLWildcardListController.php b/src/lib/Server/Controller/URLWildcard/URLWildcardListController.php new file mode 100644 index 000000000..32daa45dc --- /dev/null +++ b/src/lib/Server/Controller/URLWildcard/URLWildcardListController.php @@ -0,0 +1,78 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLWildcard; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/urlwildcards', + name: 'List URL wildcards', + openapi: new Model\Operation( + summary: 'Returns a list of URL wildcards.', + tags: [ + 'Url Wildcard', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the URL wildcard is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns a list of URL wildcards.', + 'content' => [ + 'application/vnd.ibexa.api.UrlWildcardList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.xml.example', + ], + 'application/vnd.ibexa.api.UrlWildcardList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/GET/UrlWildcardList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user has no permission to read URL wildcards.', + ], + ], + ), +)] +class URLWildcardListController extends RestController +{ + public function __construct( + protected URLWildcardService $urlWildcardService + ) { + } + + /** + * Returns the list of URL wildcards. + * + * @return \Ibexa\Rest\Server\Values\URLWildcardList + */ + public function listURLWildcards() + { + return new Values\URLWildcardList( + $this->urlWildcardService->loadAll() + ); + } +} diff --git a/src/lib/Server/Controller/URLWildcard/URLWildcardLoadByIdController.php b/src/lib/Server/Controller/URLWildcard/URLWildcardLoadByIdController.php new file mode 100644 index 000000000..69f272248 --- /dev/null +++ b/src/lib/Server/Controller/URLWildcard/URLWildcardLoadByIdController.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ + +namespace Ibexa\Rest\Server\Controller\URLWildcard; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Rest\Server\Controller as RestController; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/content/urlwildcards/{wildcardId}', + name: 'Get URL wildcard', + openapi: new Model\Operation( + summary: 'Returns the URL wildcard with the given ID.', + tags: [ + 'Url Wildcard', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the URL wildcard is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'wildcardId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - returns the URL wildcard.', + 'content' => [ + 'application/vnd.ibexa.api.UrlWildcard+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcard', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example', + ], + 'application/vnd.ibexa.api.UrlWildcard+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UrlWildcardWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - The user is not authorized to read URL wildcards.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - The URL wildcard does not exist.', + ], + ], + ), +)] +class URLWildcardLoadByIdController extends RestController +{ + public function __construct( + protected URLWildcardService $urlWildcardService + ) { + } + + /** + * Returns the URL wildcard with the given id. + * + * @param $urlWildcardId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard + */ + public function loadURLWildcard($urlWildcardId) + { + return $this->urlWildcardService->load($urlWildcardId); + } +} diff --git a/src/lib/Server/Controller/User.php b/src/lib/Server/Controller/User.php deleted file mode 100644 index a472eb6df..000000000 --- a/src/lib/Server/Controller/User.php +++ /dev/null @@ -1,935 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace Ibexa\Rest\Server\Controller; - -use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\ContentTypeService; -use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Contracts\Core\Repository\PermissionResolver; -use Ibexa\Contracts\Core\Repository\Repository; -use Ibexa\Contracts\Core\Repository\RoleService; -use Ibexa\Contracts\Core\Repository\SectionService; -use Ibexa\Contracts\Core\Repository\UserService; -use Ibexa\Contracts\Core\Repository\Values\Content\Language; -use Ibexa\Contracts\Core\Repository\Values\User\User as RepositoryUser; -use Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment; -use Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment; -use Ibexa\Contracts\Rest\Exceptions\NotFoundException; -use Ibexa\Core\Base\Exceptions\UnauthorizedException; -use Ibexa\Rest\Message; -use Ibexa\Rest\Server\Controller as RestController; -use Ibexa\Rest\Server\Exceptions; -use Ibexa\Rest\Server\Exceptions\ForbiddenException; -use Ibexa\Rest\Server\Values; -use Ibexa\Rest\Value as RestValue; -use JMS\TranslationBundle\Annotation\Ignore; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * User controller. - */ -final class User extends RestController -{ - protected UserService $userService; - - protected RoleService $roleService; - - protected ContentService $contentService; - - protected ContentTypeService $contentTypeService; - - protected LocationService $locationService; - - protected SectionService $sectionService; - - /** - * Repository. - * - * @var \Ibexa\Contracts\Core\Repository\Repository - */ - protected $repository; - - private PermissionResolver $permissionResolver; - - public function __construct( - UserService $userService, - RoleService $roleService, - ContentService $contentService, - ContentTypeService $contentTypeService, - LocationService $locationService, - SectionService $sectionService, - Repository $repository, - PermissionResolver $permissionResolver - ) { - $this->userService = $userService; - $this->roleService = $roleService; - $this->contentService = $contentService; - $this->contentTypeService = $contentTypeService; - $this->locationService = $locationService; - $this->sectionService = $sectionService; - $this->repository = $repository; - $this->permissionResolver = $permissionResolver; - } - - /** - * Redirects to the root user group. - */ - public function loadRootUserGroup(): Values\PermanentRedirect - { - //@todo Replace hardcoded value with one loaded from settings - return new Values\PermanentRedirect( - $this->router->generate('ibexa.rest.load_user_group', ['groupPath' => '/1/5']) - ); - } - - /** - * Loads a user group for the given path. - */ - public function loadUserGroup(string $groupPath): RestValue - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - if (trim($userGroupLocation->pathString, '/') !== $groupPath) { - throw new NotFoundException( - "Could not find a Location with path string $groupPath" - ); - } - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId, - Language::ALL - ); - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - return new Values\CachedValue( - new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ), - ['locationId' => $userGroupLocation->id] - ); - } - - public function loadUser(int $userId): RestValue - { - $user = $this->userService->loadUser($userId, Language::ALL); - - $userContentInfo = $user->getVersionInfo()->getContentInfo(); - $contentType = $this->contentTypeService->loadContentType($userContentInfo->contentTypeId); - - try { - $userMainLocation = $this->locationService->loadLocation($userContentInfo->mainLocationId); - $relations = $this->contentService->loadRelations($user->getVersionInfo()); - } catch (UnauthorizedException $e) { - // TODO: Hack for special case to allow current logged in user to load him/here self (but not relations) - if ($user->id == $this->permissionResolver->getCurrentUserReference()->getUserId()) { - $userMainLocation = $this->repository->sudo( - function () use ($userContentInfo) { - return $this->locationService->loadLocation($userContentInfo->mainLocationId); - } - ); - // user may not have permissions to read related content, for security reasons do not use sudo(). - $relations = []; - } else { - throw $e; - } - } - - return new Values\CachedValue( - new Values\RestUser( - $user, - $contentType, - $userContentInfo, - $userMainLocation, - $relations - ), - ['locationId' => $userContentInfo->mainLocationId] - ); - } - - /** - * @see \Symfony\Component\Security\Http\Controller\UserValueResolver - */ - public function redirectToCurrentUser(?UserInterface $user): Values\TemporaryRedirect - { - if ($user === null) { - throw new UnauthorizedHttpException('', 'Not logged in.'); - } - - $userReference = $this->permissionResolver->getCurrentUserReference(); - - return new Values\TemporaryRedirect( - $this->router->generate('ibexa.rest.load_user', ['userId' => $userReference->getUserId()]) - ); - } - - /** - * Create a new user group under the given parent - * To create a top level group use /user/groups/1/5/subgroups. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function createUserGroup(string $groupPath, Request $request): Values\CreatedUserGroup - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $createdUserGroup = $this->userService->createUserGroup( - $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ), - $this->userService->loadUserGroup( - $userGroupLocation->contentId - ) - ); - - $createdContentInfo = $createdUserGroup->getVersionInfo()->getContentInfo(); - $createdLocation = $this->locationService->loadLocation($createdContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($createdContentInfo->contentTypeId); - - return new Values\CreatedUserGroup( - [ - 'userGroup' => new Values\RestUserGroup( - $createdUserGroup, - $contentType, - $createdContentInfo, - $createdLocation, - $this->contentService->loadRelations($createdUserGroup->getVersionInfo()) - ), - ] - ); - } - - /** - * Create a new user group in the given group. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function createUser(string $groupPath, Request $request): Values\CreatedUser - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - $userGroup = $this->userService->loadUserGroup($userGroupLocation->contentId); - - $userCreateStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - try { - $createdUser = $this->userService->createUser($userCreateStruct, [$userGroup]); - } catch (ApiExceptions\InvalidArgumentException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage()); - } - - $createdContentInfo = $createdUser->getVersionInfo()->getContentInfo(); - $createdLocation = $this->locationService->loadLocation($createdContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($createdContentInfo->contentTypeId); - - return new Values\CreatedUser( - [ - 'user' => new Values\RestUser( - $createdUser, - $contentType, - $createdContentInfo, - $createdLocation, - $this->contentService->loadRelations($createdUser->getVersionInfo()) - ), - ] - ); - } - - public function updateUserGroup(string $groupPath, Request $request): Values\RestUserGroup - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - $updateStruct = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - // @todo Needs refactoring! Temporary solution so parser has access to URL - 'Url' => $request->getPathInfo(), - ], - $request->getContent() - ) - ); - - if ($updateStruct->sectionId !== null) { - $section = $this->sectionService->loadSection($updateStruct->sectionId); - $this->sectionService->assignSection( - $userGroup->getVersionInfo()->getContentInfo(), - $section - ); - } - - $updatedGroup = $this->userService->updateUserGroup($userGroup, $updateStruct->userGroupUpdateStruct); - $contentType = $this->contentTypeService->loadContentType( - $updatedGroup->getVersionInfo()->getContentInfo()->contentTypeId - ); - - return new Values\RestUserGroup( - $updatedGroup, - $contentType, - $updatedGroup->getVersionInfo()->getContentInfo(), - $userGroupLocation, - $this->contentService->loadRelations($updatedGroup->getVersionInfo()) - ); - } - - public function updateUser(int $userId, Request $request): Values\RestUser - { - $user = $this->userService->loadUser($userId); - - $updateStruct = $this->inputDispatcher->parse( - new Message( - [ - 'Content-Type' => $request->headers->get('Content-Type'), - // @todo Needs refactoring! Temporary solution so parser has access to URL - 'Url' => $request->getPathInfo(), - ], - $request->getContent() - ) - ); - - if ($updateStruct->sectionId !== null) { - $section = $this->sectionService->loadSection($updateStruct->sectionId); - $this->sectionService->assignSection( - $user->getVersionInfo()->getContentInfo(), - $section - ); - } - - $updatedUser = $this->userService->updateUser($user, $updateStruct->userUpdateStruct); - $updatedContentInfo = $updatedUser->getVersionInfo()->getContentInfo(); - $mainLocation = $this->locationService->loadLocation($updatedContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($updatedContentInfo->contentTypeId); - - return new Values\RestUser( - $updatedUser, - $contentType, - $updatedContentInfo, - $mainLocation, - $this->contentService->loadRelations($updatedUser->getVersionInfo()) - ); - } - - /** - * Given user group is deleted. - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function deleteUserGroup(string $groupPath): Values\NoContent - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - // Load one user to see if user group is empty or not - $users = $this->userService->loadUsersOfUserGroup($userGroup, 0, 1); - if (!empty($users)) { - throw new Exceptions\ForbiddenException('Cannot delete non-empty User Groups'); - } - - $this->userService->deleteUserGroup($userGroup); - - return new Values\NoContent(); - } - - /** - * Given user is deleted. - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function deleteUser(int $userId): Values\NoContent - { - $user = $this->userService->loadUser($userId); - - if ($user->id == $this->permissionResolver->getCurrentUserReference()->getUserId()) { - throw new Exceptions\ForbiddenException('Cannot delete the currently authenticated User'); - } - - $this->userService->deleteUser($user); - - return new Values\NoContent(); - } - - /** - * Loads users. - */ - public function loadUsers(Request $request): RestValue - { - $restUsers = []; - - try { - if ($request->query->has('roleId')) { - $restUsers = $this->loadUsersAssignedToRole( - $this->requestParser->parseHref($request->query->get('roleId'), 'roleId') - ); - } elseif ($request->query->has('remoteId')) { - $restUsers = [ - $this->buildRestUserObject( - $this->userService->loadUser( - $this->contentService->loadContentInfoByRemoteId($request->query->get('remoteId'))->id, - Language::ALL - ) - ), - ]; - } elseif ($request->query->has('login')) { - $restUsers = [ - $this->buildRestUserObject( - $this->userService->loadUserByLogin($request->query->get('login'), Language::ALL) - ), - ]; - } elseif ($request->query->has('email')) { - foreach ($this->userService->loadUsersByEmail($request->query->get('email'), Language::ALL) as $user) { - $restUsers[] = $this->buildRestUserObject($user); - } - } - } catch (ApiExceptions\UnauthorizedException $e) { - $restUsers = []; - } - - if (empty($restUsers)) { - throw new NotFoundException('Could not find Users with the given filter'); - } - - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.userlist') { - return new Values\UserList($restUsers, $request->getPathInfo()); - } - - return new Values\UserRefList($restUsers, $request->getPathInfo()); - } - - public function verifyUsers(Request $request): Values\OK - { - // We let the NotFoundException loadUsers throws if there are no results pass. - $this->loadUsers($request)->users; - - return new Values\OK(); - } - - /** - * Loads a list of users assigned to role. - * - * @param mixed $roleId - * - * @return \Ibexa\Rest\Server\Values\RestUser[] - */ - public function loadUsersAssignedToRole($roleId): array - { - $role = $this->roleService->loadRole($roleId); - $roleAssignments = $this->roleService->getRoleAssignments($role); - - $restUsers = []; - - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment instanceof UserRoleAssignment) { - $restUsers[] = $this->buildRestUserObject($roleAssignment->getUser()); - } - } - - return $restUsers; - } - - private function buildRestUserObject(RepositoryUser $user): Values\RestUser - { - return new Values\RestUser( - $user, - $this->contentTypeService->loadContentType($user->contentInfo->contentTypeId), - $user->contentInfo, - $this->locationService->loadLocation($user->contentInfo->mainLocationId), - $this->contentService->loadRelations($user->getVersionInfo()) - ); - } - - /** - * Loads user groups. - */ - public function loadUserGroups(Request $request): RestValue - { - $restUserGroups = []; - if ($request->query->has('id')) { - $userGroup = $this->userService->loadUserGroup($request->query->get('id'), Language::ALL); - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $userGroupMainLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - $restUserGroups = [ - new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupMainLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ), - ]; - } elseif ($request->query->has('roleId')) { - $restUserGroups = $this->loadUserGroupsAssignedToRole($request->query->get('roleId')); - } elseif ($request->query->has('remoteId')) { - $restUserGroups = [ - $this->loadUserGroupByRemoteId($request), - ]; - } - - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.usergrouplist') { - return new Values\UserGroupList($restUserGroups, $request->getPathInfo()); - } - - return new Values\UserGroupRefList($restUserGroups, $request->getPathInfo()); - } - - /** - * Loads a user group by its remote ID. - */ - public function loadUserGroupByRemoteId(Request $request): Values\RestUserGroup - { - $contentInfo = $this->contentService->loadContentInfoByRemoteId($request->query->get('remoteId')); - $userGroup = $this->userService->loadUserGroup($contentInfo->id, Language::ALL); - $userGroupLocation = $this->locationService->loadLocation($contentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($contentInfo->contentTypeId); - - return new Values\RestUserGroup( - $userGroup, - $contentType, - $contentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ); - } - - /** - * Loads a list of user groups assigned to role. - * - * @param mixed $roleId - * - * @return \Ibexa\Rest\Server\Values\RestUserGroup[] - */ - public function loadUserGroupsAssignedToRole($roleId): array - { - $role = $this->roleService->loadRole($roleId); - $roleAssignments = $this->roleService->getRoleAssignments($role); - - $restUserGroups = []; - - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment instanceof UserGroupRoleAssignment) { - $userGroup = $roleAssignment->getUserGroup(); - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - $restUserGroups[] = new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ); - } - } - - return $restUserGroups; - } - - /** - * Loads drafts assigned to user. - */ - public function loadUserDrafts(int $userId, Request $request): Values\VersionList - { - $contentDrafts = $this->contentService->loadContentDrafts( - $this->userService->loadUser($userId) - ); - - return new Values\VersionList($contentDrafts, $request->getPathInfo()); - } - - /** - * Moves the user group to another parent. - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function moveUserGroup(string $groupPath, Request $request): Values\ResourceCreated - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - $locationPath = $this->requestParser->parseHref( - $request->headers->get('Destination'), - 'groupPath' - ); - - try { - $destinationGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($locationPath) - ); - } catch (ApiExceptions\NotFoundException $e) { - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - try { - $destinationGroup = $this->userService->loadUserGroup($destinationGroupLocation->contentId); - } catch (ApiExceptions\NotFoundException $e) { - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - $this->userService->moveUserGroup($userGroup, $destinationGroup); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_user_group', - [ - 'groupPath' => trim($destinationGroupLocation->pathString, '/') . '/' . $userGroupLocation->id, - ] - ) - ); - } - - /** - * @throws \Ibexa\Contracts\Rest\Exceptions\ForbiddenException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function moveGroup(string $groupPath, Request $request): Values\ResourceCreated - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId, - ); - - try { - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $destinationLocation */ - $destinationLocation = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent(), - ), - ); - } catch (ApiExceptions\NotFoundException $e) { - throw new ForbiddenException(/** @Ignore */ $e->getMessage(), 1, $e); - } - - $destinationGroup = $this->userService->loadUserGroup( - $destinationLocation->getContent()->getId(), - ); - - $this->userService->moveUserGroup($userGroup, $destinationGroup); - - return new Values\ResourceCreated( - $this->router->generate( - 'ibexa.rest.load_user_group', - [ - 'groupPath' => trim($destinationLocation->pathString, '/') - . '/' - . $userGroupLocation->getId(), - ], - ) - ); - } - - /** - * Returns a list of the sub groups. - */ - public function loadSubUserGroups(string $groupPath, Request $request): RestValue - { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; - - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - $subGroups = $this->userService->loadSubUserGroups( - $userGroup, - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : 25, - Language::ALL - ); - - $restUserGroups = []; - foreach ($subGroups as $subGroup) { - $subGroupContentInfo = $subGroup->getVersionInfo()->getContentInfo(); - $subGroupLocation = $this->locationService->loadLocation($subGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($subGroupContentInfo->contentTypeId); - - $restUserGroups[] = new Values\RestUserGroup( - $subGroup, - $contentType, - $subGroupContentInfo, - $subGroupLocation, - $this->contentService->loadRelations($subGroup->getVersionInfo()) - ); - } - - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.usergrouplist') { - return new Values\CachedValue( - new Values\UserGroupList($restUserGroups, $request->getPathInfo()), - ['locationId' => $userGroupLocation->id] - ); - } - - return new Values\CachedValue( - new Values\UserGroupRefList($restUserGroups, $request->getPathInfo()), - ['locationId' => $userGroupLocation->id] - ); - } - - /** - * Returns a list of user groups the user belongs to. - * The returned list includes the resources for unassigning - * a user group if the user is in multiple groups. - */ - public function loadUserGroupsOfUser(int $userId, Request $request): RestValue - { - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; - - $user = $this->userService->loadUser($userId); - $userGroups = $this->userService->loadUserGroupsOfUser( - $user, - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : 25, - Language::ALL - ); - - $restUserGroups = []; - foreach ($userGroups as $userGroup) { - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - $restUserGroups[] = new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ); - } - - return new Values\CachedValue( - new Values\UserGroupRefList($restUserGroups, $request->getPathInfo(), $userId), - ['locationId' => $user->contentInfo->mainLocationId] - ); - } - - /** - * Loads the users of the group with the given path. - */ - public function loadUsersFromGroup(string $groupPath, Request $request): RestValue - { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($groupPath) - ); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; - $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; - - $users = $this->userService->loadUsersOfUserGroup( - $userGroup, - $offset >= 0 ? $offset : 0, - $limit >= 0 ? $limit : 25, - Language::ALL - ); - - $restUsers = []; - foreach ($users as $user) { - $userContentInfo = $user->getVersionInfo()->getContentInfo(); - $userLocation = $this->locationService->loadLocation($userContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userContentInfo->contentTypeId); - - $restUsers[] = new Values\RestUser( - $user, - $contentType, - $userContentInfo, - $userLocation, - $this->contentService->loadRelations($user->getVersionInfo()) - ); - } - - if ($this->getMediaType($request) === 'application/vnd.ibexa.api.userlist') { - return new Values\CachedValue( - new Values\UserList($restUsers, $request->getPathInfo()), - ['locationId' => $userGroupLocation->id] - ); - } - - return new Values\CachedValue( - new Values\UserRefList($restUsers, $request->getPathInfo()), - ['locationId' => $userGroupLocation->id] - ); - } - - /** - * Unassigns the user from a user group. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function unassignUserFromUserGroup(int $userId, string $groupPath): Values\UserGroupRefList - { - $user = $this->userService->loadUser($userId); - $userGroupLocation = $this->locationService->loadLocation((int)trim($groupPath, '/')); - - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - - try { - $this->userService->unAssignUserFromUserGroup($user, $userGroup); - } catch (ApiExceptions\InvalidArgumentException $e) { - // User is not in the group - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - $userGroups = $this->userService->loadUserGroupsOfUser($user); - $restUserGroups = []; - foreach ($userGroups as $userGroup) { - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - $restUserGroups[] = new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ); - } - - return new Values\UserGroupRefList( - $restUserGroups, - $this->router->generate( - 'ibexa.rest.load_user_groups_of_user', - ['userId' => $userId] - ), - $userId - ); - } - - /** - * Assigns the user to a user group. - * - * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException - */ - public function assignUserToUserGroup(int $userId, Request $request): Values\UserGroupRefList - { - $user = $this->userService->loadUser($userId); - - try { - $userGroupLocation = $this->locationService->loadLocation( - $this->extractLocationIdFromPath($request->query->get('group')) - ); - } catch (ApiExceptions\NotFoundException $e) { - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - try { - $userGroup = $this->userService->loadUserGroup( - $userGroupLocation->contentId - ); - } catch (ApiExceptions\NotFoundException $e) { - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - try { - $this->userService->assignUserToUserGroup($user, $userGroup); - } catch (ApiExceptions\NotFoundException $e) { - throw new Exceptions\ForbiddenException($e->getMessage()); - } - - $userGroups = $this->userService->loadUserGroupsOfUser($user); - $restUserGroups = []; - foreach ($userGroups as $userGroup) { - $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); - $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); - $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); - - $restUserGroups[] = new Values\RestUserGroup( - $userGroup, - $contentType, - $userGroupContentInfo, - $userGroupLocation, - $this->contentService->loadRelations($userGroup->getVersionInfo()) - ); - } - - return new Values\UserGroupRefList( - $restUserGroups, - $this->router->generate( - 'ibexa.rest.load_user_groups_of_user', - ['userId' => $userId] - ), - $userId - ); - } - - /** - * Extracts and returns an item id from a path, e.g. /1/2/58 => 58. - */ - private function extractLocationIdFromPath(string $path): int - { - $pathParts = explode('/', $path); - - return (int)array_pop($pathParts); - } -} diff --git a/src/lib/Server/Controller/User/UserAssignToUserGroupController.php b/src/lib/Server/Controller/User/UserAssignToUserGroupController.php new file mode 100644 index 000000000..96c6f6ab6 --- /dev/null +++ b/src/lib/Server/Controller/User/UserAssignToUserGroupController.php @@ -0,0 +1,161 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/users/{userId}/groups', + name: 'Assign User Group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapiContext: ['requestBody' => false], + openapi: new Model\Operation( + summary: 'Assigns the User to a User Group.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the link list of User Groups is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.UserGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to assign User Groups.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the new User Group does not exist or the User is already in this group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User does not exist.', + ], + ], + ), +)] +final class UserAssignToUserGroupController extends UserBaseController +{ + /** + * Assigns the user to a user group. + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function assignUserToUserGroup(int $userId, Request $request): Values\UserGroupRefList + { + $user = $this->userService->loadUser($userId); + + try { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($request->query->get('group')) + ); + } catch (ApiExceptions\NotFoundException $e) { + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + try { + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + } catch (ApiExceptions\NotFoundException $e) { + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + try { + $this->userService->assignUserToUserGroup($user, $userGroup); + } catch (ApiExceptions\NotFoundException $e) { + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + $userGroups = $this->userService->loadUserGroupsOfUser($user); + $restUserGroups = []; + foreach ($userGroups as $userGroup) { + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + $restUserGroups[] = new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ); + } + + return new Values\UserGroupRefList( + $restUserGroups, + $this->router->generate( + 'ibexa.rest.load_user_groups_of_user', + ['userId' => $userId] + ), + $userId + ); + } + + /** + * Loads a list of users assigned to role. + * + * @param mixed $roleId + * + * @return \Ibexa\Rest\Server\Values\RestUser[] + */ + public function loadUsersAssignedToRole($roleId): array + { + $role = $this->roleService->loadRole($roleId); + $roleAssignments = $this->roleService->getRoleAssignments($role); + + $restUsers = []; + + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment instanceof UserRoleAssignment) { + $restUsers[] = $this->buildRestUserObject($roleAssignment->getUser()); + } + } + + return $restUsers; + } +} diff --git a/src/lib/Server/Controller/User/UserBaseController.php b/src/lib/Server/Controller/User/UserBaseController.php new file mode 100644 index 000000000..6120f6f71 --- /dev/null +++ b/src/lib/Server/Controller/User/UserBaseController.php @@ -0,0 +1,138 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\User\User as RepositoryUser; +use Ibexa\Contracts\Rest\Exceptions\NotFoundException; +use Ibexa\Rest\Server\Controller as RestController; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; + +class UserBaseController extends RestController +{ + protected UserService $userService; + + protected RoleService $roleService; + + protected ContentService $contentService; + + protected ContentTypeService $contentTypeService; + + protected LocationService $locationService; + + protected SectionService $sectionService; + + /** + * Repository. + * + * @var \Ibexa\Contracts\Core\Repository\Repository + */ + protected $repository; + + protected PermissionResolver $permissionResolver; + + public function __construct( + UserService $userService, + RoleService $roleService, + ContentService $contentService, + ContentTypeService $contentTypeService, + LocationService $locationService, + SectionService $sectionService, + Repository $repository, + PermissionResolver $permissionResolver + ) { + $this->userService = $userService; + $this->roleService = $roleService; + $this->contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->locationService = $locationService; + $this->sectionService = $sectionService; + $this->repository = $repository; + $this->permissionResolver = $permissionResolver; + } + + /** + * Loads users. + */ + public function loadUsers(Request $request): RestValue + { + $restUsers = []; + + try { + if ($request->query->has('roleId')) { + $restUsers = $this->loadUsersAssignedToRole( + $this->requestParser->parseHref($request->query->get('roleId'), 'roleId') + ); + } elseif ($request->query->has('remoteId')) { + $restUsers = [ + $this->buildRestUserObject( + $this->userService->loadUser( + $this->contentService->loadContentInfoByRemoteId($request->query->get('remoteId'))->id, + Language::ALL + ) + ), + ]; + } elseif ($request->query->has('login')) { + $restUsers = [ + $this->buildRestUserObject( + $this->userService->loadUserByLogin($request->query->get('login'), Language::ALL) + ), + ]; + } elseif ($request->query->has('email')) { + foreach ($this->userService->loadUsersByEmail($request->query->get('email'), Language::ALL) as $user) { + $restUsers[] = $this->buildRestUserObject($user); + } + } + } catch (ApiExceptions\UnauthorizedException $e) { + $restUsers = []; + } + + if (empty($restUsers)) { + throw new NotFoundException('Could not find Users with the given filter'); + } + + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.userlist') { + return new Values\UserList($restUsers, $request->getPathInfo()); + } + + return new Values\UserRefList($restUsers, $request->getPathInfo()); + } + + protected function buildRestUserObject(RepositoryUser $user): Values\RestUser + { + return new Values\RestUser( + $user, + $this->contentTypeService->loadContentType($user->contentInfo->contentTypeId), + $user->contentInfo, + $this->locationService->loadLocation($user->contentInfo->mainLocationId), + $this->contentService->loadRelations($user->getVersionInfo()) + ); + } + + /** + * Extracts and returns an item id from a path, e.g. /1/2/58 => 58. + */ + protected function extractLocationIdFromPath(string $path): int + { + $pathParts = explode('/', $path); + + return (int)array_pop($pathParts); + } +} diff --git a/src/lib/Server/Controller/User/UserCreateController.php b/src/lib/Server/Controller/User/UserCreateController.php new file mode 100644 index 000000000..a79188f4a --- /dev/null +++ b/src/lib/Server/Controller/User/UserCreateController.php @@ -0,0 +1,153 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/groups/{path}/users', + name: 'Create User', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new User in the given Group.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new User is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The UserCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UserCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.xml.example', + ], + 'application/vnd.ibexa.api.UserCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/users/POST/UserCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.User+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/User', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.xml.example', + ], + 'application/vnd.ibexa.api.User+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this User.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - a User with the same login already exists.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the Group with the given ID does not exist.', + ], + ], + ), +)] +final class UserCreateController extends UserBaseController +{ + /** + * Create a new user group in the given group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function createUser(string $groupPath, Request $request): Values\CreatedUser + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + $userGroup = $this->userService->loadUserGroup($userGroupLocation->contentId); + + $userCreateStruct = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + + try { + $createdUser = $this->userService->createUser($userCreateStruct, [$userGroup]); + } catch (ApiExceptions\InvalidArgumentException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage()); + } + + $createdContentInfo = $createdUser->getVersionInfo()->getContentInfo(); + $createdLocation = $this->locationService->loadLocation($createdContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($createdContentInfo->contentTypeId); + + return new Values\CreatedUser( + [ + 'user' => new Values\RestUser( + $createdUser, + $contentType, + $createdContentInfo, + $createdLocation, + $this->contentService->loadRelations($createdUser->getVersionInfo()) + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserDeleteController.php b/src/lib/Server/Controller/User/UserDeleteController.php new file mode 100644 index 000000000..5e65ecd90 --- /dev/null +++ b/src/lib/Server/Controller/User/UserDeleteController.php @@ -0,0 +1,71 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/users/{userId}', + name: 'Delete User', + openapi: new Model\Operation( + summary: 'Deletes the given User.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No Content.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this User.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the user is the same as the authenticated User.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User does not exist.', + ], + ], + ), +)] +final class UserDeleteController extends UserBaseController +{ + /** + * Given user is deleted. + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function deleteUser(int $userId): Values\NoContent + { + $user = $this->userService->loadUser($userId); + + if ($user->id == $this->permissionResolver->getCurrentUserReference()->getUserId()) { + throw new Exceptions\ForbiddenException('Cannot delete the currently authenticated User'); + } + + $this->userService->deleteUser($user); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/User/UserDraftListController.php b/src/lib/Server/Controller/User/UserDraftListController.php new file mode 100644 index 000000000..e48408eae --- /dev/null +++ b/src/lib/Server/Controller/User/UserDraftListController.php @@ -0,0 +1,79 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users/{userId}/drafts', + name: 'Load user drafts', + openapi: new Model\Operation( + summary: 'Loads user\'s drafts', + tags: [ + '', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the version list is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - List the draft versions', + 'content' => [ + 'application/vnd.ibexa.api.VersionList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionList', + ], + ], + 'application/vnd.ibexa.api.VersionList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/VersionListWrapper', + ], + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the current user is not authorized to list the drafts of the given user.', + ], + ], + ), +)] +final class UserDraftListController extends UserBaseController +{ + /** + * Loads drafts assigned to user. + */ + public function loadUserDrafts(int $userId, Request $request): Values\VersionList + { + $contentDrafts = $this->contentService->loadContentDrafts( + $this->userService->loadUser($userId) + ); + + return new Values\VersionList($contentDrafts, $request->getPathInfo()); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupCreateController.php b/src/lib/Server/Controller/User/UserGroupCreateController.php new file mode 100644 index 000000000..d77c76815 --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupCreateController.php @@ -0,0 +1,216 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Post( + uriTemplate: '/user/groups/subgroups', + name: 'Create a top level User Group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a top level User Group under the root. To create a child group under a parent group use \'/user/groups/{path}/subgroups\'.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new User Group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The UserGroupCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UserGroupCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'description' => 'Created - the User Group has been created', + 'content' => [ + 'application/vnd.ibexa.api.UserGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example', + ], + 'application/vnd.ibexa.api.UserGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this User Group.', + ], + ], + ), +)] +#[Post( + uriTemplate: '/user/groups/{path}/subgroups', + name: 'Create User Group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Creates a new User Group under the given parent. To create a top level group use \'/user/groups/subgroups\'.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new User Group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The UserGroupCreate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UserGroupCreate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupCreate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupCreate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupCreateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_CREATED => [ + 'content' => [ + 'application/vnd.ibexa.api.UserGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example', + ], + 'application/vnd.ibexa.api.UserGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to create this User Group.', + ], + ], + ), +)] +final class UserGroupCreateController extends UserBaseController +{ + /** + * Create a new user group under the given parent + * To create a top level group use /user/groups/1/5/subgroups. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function createUserGroup(string $groupPath, Request $request): Values\CreatedUserGroup + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $createdUserGroup = $this->userService->createUserGroup( + $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ), + $this->userService->loadUserGroup( + $userGroupLocation->contentId + ) + ); + + $createdContentInfo = $createdUserGroup->getVersionInfo()->getContentInfo(); + $createdLocation = $this->locationService->loadLocation($createdContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($createdContentInfo->contentTypeId); + + return new Values\CreatedUserGroup( + [ + 'userGroup' => new Values\RestUserGroup( + $createdUserGroup, + $contentType, + $createdContentInfo, + $createdLocation, + $this->contentService->loadRelations($createdUserGroup->getVersionInfo()) + ), + ] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupDeleteController.php b/src/lib/Server/Controller/User/UserGroupDeleteController.php new file mode 100644 index 000000000..a0aea3ab6 --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupDeleteController.php @@ -0,0 +1,76 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/groups/{path}', + name: 'Delete User Group', + openapi: new Model\Operation( + summary: 'The given User Group is deleted.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_NO_CONTENT => [ + 'description' => 'No content - the given User Group is deleted.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to delete this content type.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the User Group is not empty.', + ], + ], + ), +)] +final class UserGroupDeleteController extends UserBaseController +{ + /** + * Given user group is deleted. + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function deleteUserGroup(string $groupPath): Values\NoContent + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + // Load one user to see if user group is empty or not + $users = $this->userService->loadUsersOfUserGroup($userGroup, 0, 1); + if (!empty($users)) { + throw new Exceptions\ForbiddenException('Cannot delete non-empty User Groups'); + } + + $this->userService->deleteUserGroup($userGroup); + + return new Values\NoContent(); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupListController.php b/src/lib/Server/Controller/User/UserGroupListController.php new file mode 100644 index 000000000..e9de7341e --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupListController.php @@ -0,0 +1,164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups', + name: 'Load User Groups', + openapi: new Model\Operation( + summary: 'Loads User Groups for either an an ID or a remote ID or a Role.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'UserGroupList - If set, the User Group List is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.UserGroupList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.json.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read User Groups.', + ], + ], + ), +)] +final class UserGroupListController extends UserBaseController +{ + /** + * Loads user groups. + */ + public function loadUserGroups(Request $request): RestValue + { + $restUserGroups = []; + if ($request->query->has('id')) { + $userGroup = $this->userService->loadUserGroup($request->query->get('id'), Language::ALL); + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $userGroupMainLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + $restUserGroups = [ + new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupMainLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ), + ]; + } elseif ($request->query->has('roleId')) { + $restUserGroups = $this->loadUserGroupsAssignedToRole($request->query->get('roleId')); + } elseif ($request->query->has('remoteId')) { + $restUserGroups = [ + $this->loadUserGroupByRemoteId($request), + ]; + } + + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.usergrouplist') { + return new Values\UserGroupList($restUserGroups, $request->getPathInfo()); + } + + return new Values\UserGroupRefList($restUserGroups, $request->getPathInfo()); + } + + /** + * Loads a user group by its remote ID. + */ + public function loadUserGroupByRemoteId(Request $request): Values\RestUserGroup + { + $contentInfo = $this->contentService->loadContentInfoByRemoteId($request->query->get('remoteId')); + $userGroup = $this->userService->loadUserGroup($contentInfo->id, Language::ALL); + $userGroupLocation = $this->locationService->loadLocation($contentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($contentInfo->contentTypeId); + + return new Values\RestUserGroup( + $userGroup, + $contentType, + $contentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ); + } + + /** + * Loads a list of user groups assigned to role. + * + * @param mixed $roleId + * + * @return \Ibexa\Rest\Server\Values\RestUserGroup[] + */ + public function loadUserGroupsAssignedToRole($roleId): array + { + $role = $this->roleService->loadRole($roleId); + $roleAssignments = $this->roleService->getRoleAssignments($role); + + $restUserGroups = []; + + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment instanceof UserGroupRoleAssignment) { + $userGroup = $roleAssignment->getUserGroup(); + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + $restUserGroups[] = new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ); + } + } + + return $restUserGroups; + } +} diff --git a/src/lib/Server/Controller/User/UserGroupLoadByPathController.php b/src/lib/Server/Controller/User/UserGroupLoadByPathController.php new file mode 100644 index 000000000..678dc3d5b --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupLoadByPathController.php @@ -0,0 +1,117 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Rest\Exceptions\NotFoundException; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/{path}', + name: 'Load User Group', + openapi: new Model\Operation( + summary: 'Loads User Groups for the given {path}.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new User Group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - loads User Groups.', + 'content' => [ + 'application/vnd.ibexa.api.UserGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example', + ], + 'application/vnd.ibexa.api.UserGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read User Groups.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User Group does not exist.', + ], + ], + ), +)] +final class UserGroupLoadByPathController extends UserBaseController +{ + /** + * Loads a user group for the given path. + */ + public function loadUserGroup(string $groupPath): RestValue + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + if (trim($userGroupLocation->pathString, '/') !== $groupPath) { + throw new NotFoundException( + "Could not find a Location with path string $groupPath" + ); + } + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId, + Language::ALL + ); + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + return new Values\CachedValue( + new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ), + ['locationId' => $userGroupLocation->id] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupMoveController.php b/src/lib/Server/Controller/User/UserGroupMoveController.php new file mode 100644 index 000000000..670040f8d --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupMoveController.php @@ -0,0 +1,111 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Exceptions\ForbiddenException; +use Ibexa\Rest\Server\Values; +use JMS\TranslationBundle\Annotation\Ignore; +use Symfony\Component\HttpFoundation\Request; + +final class UserGroupMoveController extends UserBaseController +{ + /** + * Moves the user group to another parent. + * + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function moveUserGroup(string $groupPath, Request $request): Values\ResourceCreated + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + $locationPath = $this->requestParser->parseHref( + $request->headers->get('Destination'), + 'groupPath' + ); + + try { + $destinationGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($locationPath) + ); + } catch (ApiExceptions\NotFoundException $e) { + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + try { + $destinationGroup = $this->userService->loadUserGroup($destinationGroupLocation->contentId); + } catch (ApiExceptions\NotFoundException $e) { + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + $this->userService->moveUserGroup($userGroup, $destinationGroup); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_user_group', + [ + 'groupPath' => trim($destinationGroupLocation->pathString, '/') . '/' . $userGroupLocation->id, + ] + ) + ); + } + + /** + * @throws \Ibexa\Contracts\Rest\Exceptions\ForbiddenException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function moveGroup(string $groupPath, Request $request): Values\ResourceCreated + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId, + ); + + try { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $destinationLocation */ + $destinationLocation = $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent(), + ), + ); + } catch (ApiExceptions\NotFoundException $e) { + throw new ForbiddenException(/** @Ignore */ $e->getMessage(), 1, $e); + } + + $destinationGroup = $this->userService->loadUserGroup( + $destinationLocation->getContent()->getId(), + ); + + $this->userService->moveUserGroup($userGroup, $destinationGroup); + + return new Values\ResourceCreated( + $this->router->generate( + 'ibexa.rest.load_user_group', + [ + 'groupPath' => trim($destinationLocation->pathString, '/') + . '/' + . $userGroupLocation->getId(), + ], + ) + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupOfRootLoadController.php b/src/lib/Server/Controller/User/UserGroupOfRootLoadController.php new file mode 100644 index 000000000..907cb5586 --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupOfRootLoadController.php @@ -0,0 +1,45 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/root', + name: 'Get root User Group', + openapi: new Model\Operation( + summary: 'Redirects to the root User Group.', + tags: [ + 'User Group', + ], + parameters: [ + ], + responses: [ + Response::HTTP_MOVED_PERMANENTLY => [ + 'description' => 'Moved permanently.', + ], + ], + ), +)] +final class UserGroupOfRootLoadController extends UserBaseController +{ + /** + * Redirects to the root user group. + */ + public function loadRootUserGroup(): Values\PermanentRedirect + { + //@todo Replace hardcoded value with one loaded from settings + return new Values\PermanentRedirect( + $this->router->generate('ibexa.rest.load_user_group', ['groupPath' => '/1/5']) + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupUpdateController.php b/src/lib/Server/Controller/User/UserGroupUpdateController.php new file mode 100644 index 000000000..a6147b50d --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupUpdateController.php @@ -0,0 +1,155 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/user/groups/{path}', + name: 'Update User Group', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a User Group. PATCH or POST with header X-HTTP-Method-Override PATCH.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the new User Group is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The UserGroupUpdate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'Performs the PATCH only if the specified ETag is the current one. Otherwise a 412 is returned.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UserGroupUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/PATCH/UserGroupUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - updated User Group.', + 'content' => [ + 'application/vnd.ibexa.api.UserGroup+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroup', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.xml.example', + ], + 'application/vnd.ibexa.api.UserGroup+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/path/subgroups/POST/UserGroup.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update the User Group.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - if the current ETag does not match with the one provided in the If-Match header.', + ], + ], + ), +)] +final class UserGroupUpdateController extends UserBaseController +{ + public function updateUserGroup(string $groupPath, Request $request): Values\RestUserGroup + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + $updateStruct = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + // @todo Needs refactoring! Temporary solution so parser has access to URL + 'Url' => $request->getPathInfo(), + ], + $request->getContent() + ) + ); + + if ($updateStruct->sectionId !== null) { + $section = $this->sectionService->loadSection($updateStruct->sectionId); + $this->sectionService->assignSection( + $userGroup->getVersionInfo()->getContentInfo(), + $section + ); + } + + $updatedGroup = $this->userService->updateUserGroup($userGroup, $updateStruct->userGroupUpdateStruct); + $contentType = $this->contentTypeService->loadContentType( + $updatedGroup->getVersionInfo()->getContentInfo()->contentTypeId + ); + + return new Values\RestUserGroup( + $updatedGroup, + $contentType, + $updatedGroup->getVersionInfo()->getContentInfo(), + $userGroupLocation, + $this->contentService->loadRelations($updatedGroup->getVersionInfo()) + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupUsersListController.php b/src/lib/Server/Controller/User/UserGroupUsersListController.php new file mode 100644 index 000000000..0c50762b0 --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupUsersListController.php @@ -0,0 +1,135 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/{path}/users', + name: 'Load Users of Group', + openapi: new Model\Operation( + summary: 'Loads the Users of the Group with the given ID.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'UserList - If set, the User list returned in XML or JSON format. UserRefList - If set, the link list of Users returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - the Users of the Group with the given ID.', + 'content' => [ + 'application/vnd.ibexa.api.UserList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserList', + ], + ], + 'application/vnd.ibexa.api.UserList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserList', + ], + ], + 'application/vnd.ibexa.api.UserRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/GET/UserRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/id/users/GET/UserRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read User Groups.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User Group does not exist.', + ], + ], + ), +)] +final class UserGroupUsersListController extends UserBaseController +{ + /** + * Loads the users of the group with the given path. + */ + public function loadUsersFromGroup(string $groupPath, Request $request): RestValue + { + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; + + $users = $this->userService->loadUsersOfUserGroup( + $userGroup, + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : 25, + Language::ALL + ); + + $restUsers = []; + foreach ($users as $user) { + $userContentInfo = $user->getVersionInfo()->getContentInfo(); + $userLocation = $this->locationService->loadLocation($userContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userContentInfo->contentTypeId); + + $restUsers[] = new Values\RestUser( + $user, + $contentType, + $userContentInfo, + $userLocation, + $this->contentService->loadRelations($user->getVersionInfo()) + ); + } + + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.userlist') { + return new Values\CachedValue( + new Values\UserList($restUsers, $request->getPathInfo()), + ['locationId' => $userGroupLocation->id] + ); + } + + return new Values\CachedValue( + new Values\UserRefList($restUsers, $request->getPathInfo()), + ['locationId' => $userGroupLocation->id] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserGroupsOfUserListController.php b/src/lib/Server/Controller/User/UserGroupsOfUserListController.php new file mode 100644 index 000000000..ef9a8e525 --- /dev/null +++ b/src/lib/Server/Controller/User/UserGroupsOfUserListController.php @@ -0,0 +1,112 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users/{userId}/groups', + name: 'Load Groups of User', + openapi: new Model\Operation( + summary: 'Returns a list of User Groups the User belongs to. The returned list includes the resources for unassigning a User Group if the User is in multiple groups.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the link list of User Groups is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.UserGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read User Groups.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the user does not exist.', + ], + ], + ), +)] +final class UserGroupsOfUserListController extends UserBaseController +{ + /** + * Returns a list of user groups the user belongs to. + * The returned list includes the resources for unassigning + * a user group if the user is in multiple groups. + */ + public function loadUserGroupsOfUser(int $userId, Request $request): RestValue + { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; + + $user = $this->userService->loadUser($userId); + $userGroups = $this->userService->loadUserGroupsOfUser( + $user, + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : 25, + Language::ALL + ); + + $restUserGroups = []; + foreach ($userGroups as $userGroup) { + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + $restUserGroups[] = new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ); + } + + return new Values\CachedValue( + new Values\UserGroupRefList($restUserGroups, $request->getPathInfo(), $userId), + ['locationId' => $user->contentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserListController.php b/src/lib/Server/Controller/User/UserListController.php new file mode 100644 index 000000000..348cd40bb --- /dev/null +++ b/src/lib/Server/Controller/User/UserListController.php @@ -0,0 +1,71 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users', + name: 'List Users', + openapi: new Model\Operation( + summary: 'Load Users either for a given remote ID or Role.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'UserList - If set, the User list is returned in XML or JSON format. UserRefList - If set, the link list of Users is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - Loads Users either for a given remote ID or Role.', + 'content' => [ + 'application/vnd.ibexa.api.UserList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example', + ], + 'application/vnd.ibexa.api.UserList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example', + ], + 'application/vnd.ibexa.api.UserRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/GET/UserRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserRefListWrapper', + ], + ], + ], + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'If there are no visibile Users matching the filter.', + ], + ], + ), +)] +final class UserListController extends UserBaseController +{ +} diff --git a/src/lib/Server/Controller/User/UserLoadByIdController.php b/src/lib/Server/Controller/User/UserLoadByIdController.php new file mode 100644 index 000000000..a63c7430e --- /dev/null +++ b/src/lib/Server/Controller/User/UserLoadByIdController.php @@ -0,0 +1,120 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/users/{userId}', + name: 'Load User', + openapi: new Model\Operation( + summary: 'Loads User with the given ID.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the User is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-None-Match', + in: 'header', + required: true, + description: 'ETag', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - the User with the given ID.', + 'content' => [ + 'application/vnd.ibexa.api.User+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example', + ], + 'application/vnd.ibexa.api.User+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Users.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User does not exist.', + ], + ], + ), +)] +final class UserLoadByIdController extends UserBaseController +{ + public function loadUser(int $userId): RestValue + { + $user = $this->userService->loadUser($userId, Language::ALL); + + $userContentInfo = $user->getVersionInfo()->getContentInfo(); + $contentType = $this->contentTypeService->loadContentType($userContentInfo->contentTypeId); + + try { + $userMainLocation = $this->locationService->loadLocation($userContentInfo->mainLocationId); + $relations = $this->contentService->loadRelations($user->getVersionInfo()); + } catch (UnauthorizedException $e) { + // TODO: Hack for special case to allow current logged in user to load him/here self (but not relations) + if ($user->id == $this->permissionResolver->getCurrentUserReference()->getUserId()) { + $userMainLocation = $this->repository->sudo( + function () use ($userContentInfo) { + return $this->locationService->loadLocation($userContentInfo->mainLocationId); + } + ); + // user may not have permissions to read related content, for security reasons do not use sudo(). + $relations = []; + } else { + throw $e; + } + } + + return new Values\CachedValue( + new Values\RestUser( + $user, + $contentType, + $userContentInfo, + $userMainLocation, + $relations + ), + ['locationId' => $userContentInfo->mainLocationId] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserRedirectToCurrentUserController.php b/src/lib/Server/Controller/User/UserRedirectToCurrentUserController.php new file mode 100644 index 000000000..12fd43d1c --- /dev/null +++ b/src/lib/Server/Controller/User/UserRedirectToCurrentUserController.php @@ -0,0 +1,78 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; +use Symfony\Component\Security\Core\User\UserInterface; + +#[Get( + uriTemplate: '/user/current', + name: 'Load current User', + openapi: new Model\Operation( + summary: 'Loads the current user.', + tags: [ + 'User Current', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the User is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - the User with the given ID.', + 'content' => [ + 'application/vnd.ibexa.api.User+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.xml.example', + ], + 'application/vnd.ibexa.api.User+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/GET/User.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read Users. For example, Anonymous user can\'t load oneself.', + ], + ], + ), +)] +final class UserRedirectToCurrentUserController extends UserBaseController +{ + /** + * @see \Symfony\Component\Security\Http\Controller\UserValueResolver + */ + public function redirectToCurrentUser(?UserInterface $user): Values\TemporaryRedirect + { + if ($user === null) { + throw new UnauthorizedHttpException('', 'Not logged in.'); + } + + $userReference = $this->permissionResolver->getCurrentUserReference(); + + return new Values\TemporaryRedirect( + $this->router->generate('ibexa.rest.load_user', ['userId' => $userReference->getUserId()]) + ); + } +} diff --git a/src/lib/Server/Controller/User/UserSubGroupListController.php b/src/lib/Server/Controller/User/UserSubGroupListController.php new file mode 100644 index 000000000..29d4de141 --- /dev/null +++ b/src/lib/Server/Controller/User/UserSubGroupListController.php @@ -0,0 +1,137 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Rest\Server\Values; +use Ibexa\Rest\Value as RestValue; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Get( + uriTemplate: '/user/groups/{path}/subgroups', + name: 'Load subgroups', + openapi: new Model\Operation( + summary: 'Returns a list of the subgroups.', + tags: [ + 'User Group', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'UserGroupList - If set, the User Group list is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Groups is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'path', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - list of the subgroups.', + 'content' => [ + 'application/vnd.ibexa.api.UserGroupList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/groups/GET/UserGroupList.json.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user has no permission to read User Groups.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User Group does not exist.', + ], + ], + ), +)] +final class UserSubGroupListController extends UserBaseController +{ + /** + * Returns a list of the sub groups. + */ + public function loadSubUserGroups(string $groupPath, Request $request): RestValue + { + $offset = $request->query->has('offset') ? (int)$request->query->get('offset') : 0; + $limit = $request->query->has('limit') ? (int)$request->query->get('limit') : 25; + + $userGroupLocation = $this->locationService->loadLocation( + $this->extractLocationIdFromPath($groupPath) + ); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + $subGroups = $this->userService->loadSubUserGroups( + $userGroup, + $offset >= 0 ? $offset : 0, + $limit >= 0 ? $limit : 25, + Language::ALL + ); + + $restUserGroups = []; + foreach ($subGroups as $subGroup) { + $subGroupContentInfo = $subGroup->getVersionInfo()->getContentInfo(); + $subGroupLocation = $this->locationService->loadLocation($subGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($subGroupContentInfo->contentTypeId); + + $restUserGroups[] = new Values\RestUserGroup( + $subGroup, + $contentType, + $subGroupContentInfo, + $subGroupLocation, + $this->contentService->loadRelations($subGroup->getVersionInfo()) + ); + } + + if ($this->getMediaType($request) === 'application/vnd.ibexa.api.usergrouplist') { + return new Values\CachedValue( + new Values\UserGroupList($restUserGroups, $request->getPathInfo()), + ['locationId' => $userGroupLocation->id] + ); + } + + return new Values\CachedValue( + new Values\UserGroupRefList($restUserGroups, $request->getPathInfo()), + ['locationId' => $userGroupLocation->id] + ); + } +} diff --git a/src/lib/Server/Controller/User/UserUnassignFromUserGroupController.php b/src/lib/Server/Controller/User/UserUnassignFromUserGroupController.php new file mode 100644 index 000000000..8802ac8cb --- /dev/null +++ b/src/lib/Server/Controller/User/UserUnassignFromUserGroupController.php @@ -0,0 +1,132 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Delete; +use ApiPlatform\OpenApi\Model; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Rest\Server\Exceptions; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Response; + +#[Delete( + uriTemplate: '/user/users/{userId}/groups/{groupId}', + name: 'Unassign User Group', + openapi: new Model\Operation( + summary: 'Unassigns the User from a User Group.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the link list of User Groups is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'groupId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.UserGroupRefList+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefList', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example', + ], + 'application/vnd.ibexa.api.UserGroupRefList+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserGroupRefListWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example', + ], + ], + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to unassign User Groups.', + ], + Response::HTTP_FORBIDDEN => [ + 'description' => 'Error - the User is not in the given group.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User does not exist.', + ], + ], + ), +)] +final class UserUnassignFromUserGroupController extends UserBaseController +{ + /** + * Unassigns the user from a user group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function unassignUserFromUserGroup(int $userId, string $groupPath): Values\UserGroupRefList + { + $user = $this->userService->loadUser($userId); + $userGroupLocation = $this->locationService->loadLocation((int)trim($groupPath, '/')); + + $userGroup = $this->userService->loadUserGroup( + $userGroupLocation->contentId + ); + + try { + $this->userService->unAssignUserFromUserGroup($user, $userGroup); + } catch (ApiExceptions\InvalidArgumentException $e) { + // User is not in the group + throw new Exceptions\ForbiddenException($e->getMessage()); + } + + $userGroups = $this->userService->loadUserGroupsOfUser($user); + $restUserGroups = []; + foreach ($userGroups as $userGroup) { + $userGroupContentInfo = $userGroup->getVersionInfo()->getContentInfo(); + $userGroupLocation = $this->locationService->loadLocation($userGroupContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($userGroupContentInfo->contentTypeId); + + $restUserGroups[] = new Values\RestUserGroup( + $userGroup, + $contentType, + $userGroupContentInfo, + $userGroupLocation, + $this->contentService->loadRelations($userGroup->getVersionInfo()) + ); + } + + return new Values\UserGroupRefList( + $restUserGroups, + $this->router->generate( + 'ibexa.rest.load_user_groups_of_user', + ['userId' => $userId] + ), + $userId + ); + } +} diff --git a/src/lib/Server/Controller/User/UserUpdateController.php b/src/lib/Server/Controller/User/UserUpdateController.php new file mode 100644 index 000000000..be4dc4609 --- /dev/null +++ b/src/lib/Server/Controller/User/UserUpdateController.php @@ -0,0 +1,152 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\Metadata\Patch; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; +use Ibexa\Rest\Message; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Patch( + uriTemplate: '/user/users/{userId}', + name: 'Update User', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Updates a User.', + tags: [ + 'User', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'If set, the updated User is returned in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The UserUpdate schema encoded in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'If-Match', + in: 'header', + required: true, + description: 'Performs a PATCH only if the specified ETag is the current one.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'userId', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.UserUpdate+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserUpdate', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.xml.example', + ], + 'application/vnd.ibexa.api.UserUpdate+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserUpdateWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/UserUpdate.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - User updated.', + 'content' => [ + 'application/vnd.ibexa.api.User+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/User', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.xml.example', + ], + 'application/vnd.ibexa.api.User+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/UserWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/user/users/user_id/PATCH/User.json.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + Response::HTTP_UNAUTHORIZED => [ + 'description' => 'Error - the user is not authorized to update the User.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - the User does not exist.', + ], + Response::HTTP_PRECONDITION_FAILED => [ + 'description' => 'Error - the current ETag does not match with the provided one in the If-Match header.', + ], + ], + ), +)] +final class UserUpdateController extends UserBaseController +{ + public function updateUser(int $userId, Request $request): Values\RestUser + { + $user = $this->userService->loadUser($userId); + + $updateStruct = $this->inputDispatcher->parse( + new Message( + [ + 'Content-Type' => $request->headers->get('Content-Type'), + // @todo Needs refactoring! Temporary solution so parser has access to URL + 'Url' => $request->getPathInfo(), + ], + $request->getContent() + ) + ); + + if ($updateStruct->sectionId !== null) { + $section = $this->sectionService->loadSection($updateStruct->sectionId); + $this->sectionService->assignSection( + $user->getVersionInfo()->getContentInfo(), + $section + ); + } + + $updatedUser = $this->userService->updateUser($user, $updateStruct->userUpdateStruct); + $updatedContentInfo = $updatedUser->getVersionInfo()->getContentInfo(); + $mainLocation = $this->locationService->loadLocation($updatedContentInfo->mainLocationId); + $contentType = $this->contentTypeService->loadContentType($updatedContentInfo->contentTypeId); + + return new Values\RestUser( + $updatedUser, + $contentType, + $updatedContentInfo, + $mainLocation, + $this->contentService->loadRelations($updatedUser->getVersionInfo()) + ); + } +} diff --git a/src/lib/Server/Controller/User/UserVerifyController.php b/src/lib/Server/Controller/User/UserVerifyController.php new file mode 100644 index 000000000..fb8082f82 --- /dev/null +++ b/src/lib/Server/Controller/User/UserVerifyController.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Rest\Server\Controller\User; + +use ApiPlatform\OpenApi\Model; +use Ibexa\Bundle\Rest\ApiPlatform\Head; +use Ibexa\Contracts\Rest\Exceptions\NotFoundException; +use Ibexa\Rest\Server\Values; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +#[Head( + uriTemplate: '/user/users', + name: 'Verify Users', + openapi: new Model\Operation( + summary: 'Verifies if there are Users matching given filter.', + tags: [ + 'User', + ], + parameters: [ + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'OK - verifies if there are Users matching the given filter.', + ], + Response::HTTP_NOT_FOUND => [ + 'description' => 'Error - there are no visibile Users matching the filter.', + ], + ], + ), +)] +final class UserVerifyController extends UserBaseController +{ + public function verifyUsers(Request $request): Values\OK + { + // We let the NotFoundException loadUsers throws if there are no results pass. + $this->loadUsers($request)->users; + + return new Values\OK(); + } +} diff --git a/src/lib/Server/Controller/Views.php b/src/lib/Server/Controller/Views.php index 1e29c3643..9f1d67954 100644 --- a/src/lib/Server/Controller/Views.php +++ b/src/lib/Server/Controller/Views.php @@ -7,6 +7,10 @@ namespace Ibexa\Rest\Server\Controller; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Post; +use ApiPlatform\OpenApi\Factory\OpenApiFactory; +use ApiPlatform\OpenApi\Model; use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Language; @@ -15,7 +19,72 @@ use Ibexa\Rest\Server\Controller; use Ibexa\Rest\Server\Values; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +#[Post( + uriTemplate: '/views', + name: 'Search content', + extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], + openapi: new Model\Operation( + summary: 'Executes a query and returns a View including the results. +View input reflects the criteria model of the public PHP API. +Refer to [Search Criteria Reference](/en/latest/search/criteria_reference/search_criteria_reference/)', + tags: [ + 'Views', + ], + parameters: [ + new Model\Parameter( + name: 'Accept', + in: 'header', + required: true, + description: 'The view in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Content-Type', + in: 'header', + required: true, + description: 'The view input in XML or JSON format.', + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.ViewInput+xml' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ViewInput', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/views/POST/ViewInput.xml.example', + ], + 'application/vnd.ibexa.api.ViewInput+json' => [ + 'schema' => [ + '$ref' => '#/components/schemas/ViewInputWrapper', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/views/POST/ViewInput.json.example', + ], + ]), + ), + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.View+xml; version=1.1' => [ + 'schema' => [ + '$ref' => '#/components/schemas/View', + ], + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/views/POST/View.xml.v11.example', + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Error - the input does not match the input schema definition.', + ], + ], + ), +)] /** * Controller for Repository Views (Search, mostly). */ diff --git a/tests/integration/IbexaTestKernel.php b/tests/integration/IbexaTestKernel.php index 6705424f6..888f98d56 100644 --- a/tests/integration/IbexaTestKernel.php +++ b/tests/integration/IbexaTestKernel.php @@ -8,6 +8,7 @@ namespace Ibexa\Tests\Integration\Rest; +use ApiPlatform\Symfony\Bundle\ApiPlatformBundle; use Hautelook\TemplatedUriBundle\HautelookTemplatedUriBundle; use Ibexa\Bundle\Rest\IbexaRestBundle; use Ibexa\Contracts\Rest\UriParser\UriParserInterface; @@ -25,6 +26,7 @@ public function registerBundles(): iterable yield new HautelookTemplatedUriBundle(); yield new IbexaRestBundle(); + yield new ApiPlatformBundle(); } public function registerContainerConfiguration(LoaderInterface $loader): void