diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 280caa54..e7a40a6c 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -2,7 +2,7 @@ api_platform: title: "%env(string:APP_TITLE)%" description: "%env(string:APP_DESCRIPTION)%" version: '%env(string:APP_VERSION)%' - enable_swagger_ui: false + enable_swagger_ui: true enable_re_doc: true metadata_backward_compatibility_layer: false graphql: diff --git a/lib/Documents/src/Models/DocumentTrait.php b/lib/Documents/src/Models/DocumentTrait.php index f2b568b4..1a675241 100644 --- a/lib/Documents/src/Models/DocumentTrait.php +++ b/lib/Documents/src/Models/DocumentTrait.php @@ -4,6 +4,7 @@ namespace RZ\Roadiz\Documents\Models; +use ApiPlatform\Metadata\ApiProperty; use Symfony\Component\Serializer\Annotation as SymfonySerializer; trait DocumentTrait @@ -277,6 +278,10 @@ protected function initDocumentTrait(): void #[ SymfonySerializer\Groups(["document", "document_display", "nodes_sources", "tag", "attribute"]), SymfonySerializer\SerializedName("processable"), + ApiProperty( + description: 'Document can be processed as an image for resampling and other image operations.', + writable: false, + ) ] public function isProcessable(): bool { diff --git a/lib/RoadizCoreBundle/src/Entity/Document.php b/lib/RoadizCoreBundle/src/Entity/Document.php index 92eeb139..0ee2b0ad 100644 --- a/lib/RoadizCoreBundle/src/Entity/Document.php +++ b/lib/RoadizCoreBundle/src/Entity/Document.php @@ -6,6 +6,7 @@ use ApiPlatform\Doctrine\Orm\Filter as BaseFilter; use ApiPlatform\Metadata\ApiFilter; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Serializer\Filter\PropertyFilter; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -79,6 +80,9 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[ORM\Column(name: 'copyright_valid_since', type: 'datetime', nullable: true)] #[SymfonySerializer\Groups(['document_copyright'])] #[Serializer\Groups(['document_copyright'])] + #[ApiProperty( + description: 'Document copyright starting date', + )] protected ?\DateTime $copyrightValidSince = null; /** @@ -87,6 +91,9 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[ORM\Column(name: 'copyright_valid_until', type: 'datetime', nullable: true)] #[SymfonySerializer\Groups(['document_copyright'])] #[Serializer\Groups(['document_copyright'])] + #[ApiProperty( + description: 'Document copyright expiry date', + )] protected ?\DateTime $copyrightValidUntil = null; /** @@ -143,6 +150,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type("string")] + #[ApiProperty( + description: 'Embed ID on external platforms', + example: 'FORSwsjtQSE', + )] #[Assert\Length(max: 250)] protected ?string $embedId = null; @@ -167,6 +178,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('string')] #[Assert\Length(max: 100)] + #[ApiProperty( + description: 'Embed platform name', + example: 'youtube', + )] protected ?string $embedPlatform = null; /** * @var Collection @@ -235,6 +250,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('string')] #[Assert\Length(max: 255)] + #[ApiProperty( + description: 'Document file mime type', + example: 'image/jpeg', + )] private ?string $mimeType = null; /** * @var Collection @@ -267,6 +286,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('int')] + #[ApiProperty( + description: 'When document has visual size: width in pixels', + example: '1280', + )] private int $imageWidth = 0; /** * @var integer @@ -275,6 +298,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('int')] + #[ApiProperty( + description: 'When document has visual size: height in pixels', + example: '800', + )] private int $imageHeight = 0; /** * @var integer @@ -283,6 +310,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[SymfonySerializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('int')] + #[ApiProperty( + description: 'When document is audio or video: duration in seconds', + example: '300', + )] private int $mediaDuration = 0; /** * @var string|null @@ -292,6 +323,10 @@ class Document extends AbstractDateTimed implements AdvancedDocumentInterface, H #[Serializer\Groups(['document', 'document_display', 'nodes_sources', 'tag', 'attribute'])] #[Serializer\Type('string')] #[Assert\Length(max: 7)] + #[ApiProperty( + description: 'When document is image: average color in hexadecimal format', + example: '#ffffff' + )] private ?string $imageAverageColor = null; /** * @var int|null The filesize in bytes. @@ -619,6 +654,10 @@ public function setFilesize(?int $filesize): static Serializer\SerializedName("alt"), SymfonySerializer\Groups(["document", "document_display", "nodes_sources", "tag", "attribute"]), SymfonySerializer\SerializedName("alt"), + ApiProperty( + description: 'Document alternative text, for img HTML tag.', + writable: false, + ) ] public function getAlternativeText(): string { diff --git a/lib/RoadizCoreBundle/src/Entity/Node.php b/lib/RoadizCoreBundle/src/Entity/Node.php index d47d4fd5..d53c7ae4 100644 --- a/lib/RoadizCoreBundle/src/Entity/Node.php +++ b/lib/RoadizCoreBundle/src/Entity/Node.php @@ -5,6 +5,7 @@ namespace RZ\Roadiz\CoreBundle\Entity; use ApiPlatform\Doctrine\Orm\Filter as BaseFilter; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Serializer\Filter\PropertyFilter; use ApiPlatform\Metadata\ApiFilter; use Doctrine\Common\Collections\ArrayCollection; @@ -92,6 +93,10 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu #[Assert\NotNull] #[Assert\NotBlank] #[Assert\Length(max: 255)] + #[ApiProperty( + description: 'Unique node name (slug) used to build content URL', + example: 'this-is-a-node-name', + )] private string $nodeName = ''; #[ORM\Column(name: 'dynamic_node_name', type: 'boolean', nullable: false, options: ['default' => true])] @@ -107,6 +112,10 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu #[SymfonySerializer\Groups(['nodes_sources_base', 'nodes_sources', 'node'])] #[Serializer\Groups(['nodes_sources_base', 'nodes_sources', 'node'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Is this node visible in website navigation?', + example: 'true', + )] private bool $visible = true; /** @@ -128,6 +137,10 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu #[SymfonySerializer\Groups(['node'])] #[Serializer\Groups(['node'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Is this node locked to prevent deletion and renaming?', + example: 'false', + )] private bool $locked = false; /** @@ -143,12 +156,20 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu #[SymfonySerializer\Groups(['node'])] #[Serializer\Groups(['node'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Does this node act as a container for other nodes?', + example: 'false', + )] private bool $hideChildren = false; #[ORM\Column(type: 'boolean', nullable: false, options: ['default' => false])] #[SymfonySerializer\Groups(['node'])] #[Serializer\Groups(['node'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Can this node hold other nodes inside?', + example: 'false', + )] private bool $sterile = false; #[ORM\Column(name: 'children_order', type: 'string', length: 50)] @@ -156,13 +177,32 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu #[Serializer\Groups(['node', 'node_listing'])] #[Assert\Length(max: 50)] #[Gedmo\Versioned] + #[ApiProperty( + description: 'This node children will be sorted by a given field', + example: 'position', + schema: [ + 'type' => 'string', + 'enum' => ['position', 'nodeName', 'createdAt', 'updatedAt', 'publishedAt'], + 'example' => 'position' + ], + )] private string $childrenOrder = 'position'; #[ORM\Column(name: 'children_order_direction', type: 'string', length: 4)] #[SymfonySerializer\Groups(['node', 'node_listing'])] #[Serializer\Groups(['node', 'node_listing'])] #[Assert\Length(max: 4)] + #[Assert\Choice(choices: ['ASC', 'DESC'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'This node children will be sorted ascendant or descendant', + example: 'ASC', + schema: [ + 'type' => 'string', + 'enum' => ['ASC', 'DESC'], + 'example' => 'ASC' + ], + )] private string $childrenOrderDirection = 'ASC'; /** diff --git a/lib/RoadizCoreBundle/src/Entity/NodesSources.php b/lib/RoadizCoreBundle/src/Entity/NodesSources.php index ca91b957..dd173ede 100644 --- a/lib/RoadizCoreBundle/src/Entity/NodesSources.php +++ b/lib/RoadizCoreBundle/src/Entity/NodesSources.php @@ -5,6 +5,7 @@ namespace RZ\Roadiz\CoreBundle\Entity; use ApiPlatform\Doctrine\Orm\Filter as BaseFilter; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Serializer\Filter\PropertyFilter; use ApiPlatform\Metadata\ApiFilter; use Doctrine\Common\Collections\ArrayCollection; @@ -73,6 +74,10 @@ class NodesSources extends AbstractEntity implements Loggable #[Serializer\Groups(['nodes_sources', 'nodes_sources_base', 'log_sources'])] #[Gedmo\Versioned] #[Assert\Length(max: 250)] + #[ApiProperty( + description: 'Content title', + example: 'This is a title', + )] protected ?string $title = null; #[ApiFilter(BaseFilter\DateFilter::class)] @@ -82,6 +87,9 @@ class NodesSources extends AbstractEntity implements Loggable #[SymfonySerializer\Groups(['nodes_sources', 'nodes_sources_base'])] #[Serializer\Groups(['nodes_sources', 'nodes_sources_base'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Content publication date and time', + )] protected ?\DateTime $publishedAt = null; #[ApiFilter(BaseFilter\SearchFilter::class, strategy: "partial")] @@ -90,6 +98,10 @@ class NodesSources extends AbstractEntity implements Loggable #[Serializer\Groups(['nodes_sources'])] #[Gedmo\Versioned] #[Assert\Length(max: 150)] + #[ApiProperty( + description: 'Title for search engine optimization, used in HTML title tag', + example: 'This is a title', + )] protected string $metaTitle = ''; #[ORM\Column(name: 'meta_keywords', type: 'text')] @@ -103,6 +115,10 @@ class NodesSources extends AbstractEntity implements Loggable #[SymfonySerializer\Groups(['nodes_sources'])] #[Serializer\Groups(['nodes_sources'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Description for search engine optimization, used in HTML meta description tag', + example: 'This is a description', + )] protected string $metaDescription = ''; #[ApiFilter(BaseFilter\BooleanFilter::class)] @@ -110,6 +126,10 @@ class NodesSources extends AbstractEntity implements Loggable #[SymfonySerializer\Groups(['nodes_sources'])] #[Serializer\Groups(['nodes_sources'])] #[Gedmo\Versioned] + #[ApiProperty( + description: 'Do not allow robots to index this content, used in HTML meta robots tag', + example: 'false', + )] protected bool $noIndex = false; #[ApiFilter(BaseFilter\SearchFilter::class, properties: [ diff --git a/lib/RoadizCoreBundle/src/Entity/Translation.php b/lib/RoadizCoreBundle/src/Entity/Translation.php index f4b2e95f..1fba916d 100644 --- a/lib/RoadizCoreBundle/src/Entity/Translation.php +++ b/lib/RoadizCoreBundle/src/Entity/Translation.php @@ -4,6 +4,7 @@ namespace RZ\Roadiz\CoreBundle\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Serializer\Filter\PropertyFilter; use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Doctrine\Orm\Filter as BaseFilter; @@ -543,11 +544,12 @@ class Translation extends AbstractDateTimed implements TranslationInterface protected Collection $folderTranslations; /** - * Language locale + * ISO 639-1 Language locale. * * fr or en for example * * @var string + * @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes * @Serializer\Groups({"translation", "document", "nodes_sources", "tag", "attribute", "folder", "log_sources"}) * @Serializer\Type("string") */ @@ -556,6 +558,10 @@ class Translation extends AbstractDateTimed implements TranslationInterface #[Assert\NotBlank] #[Assert\NotNull] #[Assert\Length(max: 10)] + #[ApiProperty( + description: 'Translation ISO 639-1 locale. See https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes', + example: 'fr', + )] private string $locale = ''; /** @@ -566,6 +572,10 @@ class Translation extends AbstractDateTimed implements TranslationInterface #[ORM\Column(name: 'override_locale', type: 'string', length: 10, unique: true, nullable: true)] #[SymfonySerializer\Ignore] #[Assert\Length(max: 10)] + #[ApiProperty( + description: 'Override standard locale with an other one (for example, `uk` instead of `en`)', + example: 'uk', + )] private ?string $overrideLocale = null; /** @@ -578,6 +588,10 @@ class Translation extends AbstractDateTimed implements TranslationInterface #[Assert\NotNull] #[Assert\NotBlank] #[Assert\Length(max: 250)] + #[ApiProperty( + description: 'Translation display name', + example: 'French', + )] private string $name = ''; /** @@ -587,6 +601,10 @@ class Translation extends AbstractDateTimed implements TranslationInterface */ #[ORM\Column(name: 'default_translation', type: 'boolean', nullable: false, options: ['default' => false])] #[SymfonySerializer\Groups(['translation', 'translation_base'])] + #[ApiProperty( + description: 'Is translation default one?', + example: 'true', + )] private bool $defaultTranslation = false; /** @@ -596,6 +614,10 @@ class Translation extends AbstractDateTimed implements TranslationInterface */ #[ORM\Column(type: 'boolean', nullable: false, options: ['default' => true])] #[SymfonySerializer\Groups(['translation', 'translation_base'])] + #[ApiProperty( + description: 'Is translation available publicly?', + example: 'true', + )] private bool $available = true; /** @@ -794,6 +816,10 @@ public function setOverrideLocale(?string $overrideLocale): Translation */ #[SymfonySerializer\SerializedName('locale')] #[SymfonySerializer\Groups(['translation_base'])] + #[ApiProperty( + description: 'Translation ISO 639-1 locale. See https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes', + example: 'fr', + )] public function getPreferredLocale(): string { return !empty($this->overrideLocale) ? $this->overrideLocale : $this->locale;