diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e36ec8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +/composer.lock +/.idea diff --git a/composer.json b/composer.json index 6dfa380..baa566c 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,13 @@ { "name": "almaviacx/calameobundle", - "description": "AlmaviaCX Calaméo Bundle provide a client for the Rest API and a new field type for eZPlatform.", + "description": "AlmaviaCX Calaméo Bundle provide a client for the Rest API and a new field type for Ibexa.", "keywords": [ "calameo", - "ezplatform", + "ibexa", "almaviacx" ], "homepage": "https://github.com/Novactive/AlmaviaCXCalameoBundle", - "type": "ezplatform-bundle", + "type": "ibexa-bundle", "authors": [ { "name": "AlmaviaCX", @@ -19,15 +19,15 @@ "MIT" ], "require": { - "php": "^7.2", + "php": ">=8.3", "ext-json": "*", - "doctrine/cache": "^1.10", - "ezsystems/ezpublish-kernel": "^7.5", - "ezsystems/repository-forms": "^2.5", - "guzzlehttp/guzzle": "^6.0", - "jms/serializer": "^1.0", - "jms/serializer-bundle": "^2.0", - "symfony/symfony": "^3.4" + "ext-pdo": "*", + "guzzlehttp/guzzle": "^7.10", + "guzzlehttp/promises": "^2.3", + "ibexa/oss": "^5", + "jms/serializer-bundle": "^5.5", + "nyholm/psr7": "^1.8", + "symfony/var-exporter": "^6.0|^7.0|^8.0" }, "autoload": { "psr-4": { @@ -46,6 +46,11 @@ "phpunit/phpunit": "^9.5" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "ibexa/post-install": true + } } } diff --git a/src/bundles/APIBundle/Resources/config/services.yml b/src/bundles/APIBundle/Resources/config/services.yml index e0a1eba..0a46a7d 100644 --- a/src/bundles/APIBundle/Resources/config/services.yml +++ b/src/bundles/APIBundle/Resources/config/services.yml @@ -1,7 +1,8 @@ parameters: - almaviacx.calameo.api.key: ~ - almaviacx.calameo.api.secret: ~ + almaviacx.calameo.api.key: '' + almaviacx.calameo.api.secret: '' almaviacx.calameo.http_client.config: [] + almaviacx.calameo.delete_book_enable: false services: # default configuration for services in *this* file @@ -15,6 +16,7 @@ services: public: false AlmaviaCX\Calameo\API\HttpClient: + lazy: true arguments: $APIKey: "%almaviacx.calameo.api.key%" $APISecret: "%almaviacx.calameo.api.secret%" @@ -22,7 +24,7 @@ services: AlmaviaCX\Calameo\API\Serializer: arguments: - $baseSerializer: "@jms_serializer" + $baseSerializer: '@JMS\Serializer\SerializerInterface' AlmaviaCX\Calameo\API\Gateway\: resource: "../../../../lib/API/Gateway/*" @@ -32,3 +34,9 @@ services: AlmaviaCX\Calameo\API\Service\: resource: "../../../../lib/API/Service/*" + + AlmaviaCX\Calameo\Exception\ExceptionThrower: + + AlmaviaCX\Calameo\API\Repository\PublicationRepository: + arguments: + $deleteBookEnable: "%almaviacx.calameo.delete_book_enable%" diff --git a/src/bundles/EzBundle/Controller/Admin/CalameoController.php b/src/bundles/EzBundle/Controller/Admin/CalameoController.php new file mode 100644 index 0000000..2988a82 --- /dev/null +++ b/src/bundles/EzBundle/Controller/Admin/CalameoController.php @@ -0,0 +1,272 @@ +tableExists(self::TABLE_NAME); + + return $this->render('@EzCalameo/admin/calameo/index.html.twig', [ + 'field_type_identifier' => self::FIELD_TYPE_IDENTIFIER, + 'table_name' => self::TABLE_NAME, + 'table_exists' => $tableExists, + 'configuration' => [ + 'almaviacx.calameo.api.key' => $this->APIKey, + 'almaviacx.calameo.api.secret' => str_repeat('*', strlen($this->APISecret)), + 'almaviacx.calameo.http_client.config' => $this->config, + 'almaviacx.calameo.delete_book_enable' => $this->deleteBookEnable, + ], + 'stats' => [ + 'field_definitions' => $this->countCalameoFieldDefinitions(), + 'contents' => $this->countContentsWithCalameoField(), + 'publications' => $tableExists ? $this->countCalameoPublications() : null, + ], + ]); + } + + #[Route('/contents', name: 'content_list', methods: ['GET'])] + public function contentList(Request $request): Response + { + $page = max(1, $request->query->getInt('page', 1)); + $limit = min(100, max(1, $request->query->getInt('limit', 25))); + $offset = ($page - 1) * $limit; + + $calameoContentTypes = $this->getContentTypesWithCalameoPublicationField(); + + if ($calameoContentTypes === []) { + return $this->render('@EzCalameo/admin/calameo/content_list.html.twig', [ + 'items' => [], + 'table_exists' => $this->tableExists(self::TABLE_NAME), + 'pagination' => [ + 'page' => $page, + 'limit' => $limit, + 'total' => 0, + 'pages' => 0, + 'has_previous' => false, + 'has_next' => false, + ], + ]); + } + + $query = new Query([ + 'filter' => new Criterion\ContentTypeIdentifier( + array_column($calameoContentTypes, 'identifier') + ), + 'limit' => $limit, + 'offset' => $offset, + ]); + $query->sortClauses = [new Query\SortClause\ContentId(Query::SORT_DESC)]; + + $searchResult = $this->searchService->findContent($query); + + $items = []; + $tableExists = $this->tableExists(self::TABLE_NAME); + + foreach ($searchResult->searchHits as $searchHit) { + $content = $searchHit->valueObject; + $contentTypeId = $content->contentInfo->contentTypeId; + + if (!isset($calameoContentTypes[$contentTypeId])) { + continue; + } + + foreach ($calameoContentTypes[$contentTypeId]['fields'] as $fieldIdentifier) { + $field = $content->getField($fieldIdentifier); + + $publicationReference = null; + + if ($field !== null && $tableExists) { + $publicationReference = $this->getCalameoPublicationReference( + (int) $field->id, + (int) $content->versionInfo->versionNo, + ); + } + + $items[] = [ + 'content_id' => $content->contentInfo->id, + 'content_name' => $content->contentInfo->name, + 'content_type_identifier' => $calameoContentTypes[$contentTypeId]['identifier'], + 'field_identifier' => $fieldIdentifier, + 'field_id' => $field?->id, + 'version' => $content->versionInfo->versionNo, + 'publication_id' => $publicationReference['publication_id'] ?? null, + 'folder_id' => $publicationReference['folder_id'] ?? null, + 'content_view_url' => sprintf('/view/content/%d', $content->contentInfo->id), + ]; + } + } + + $total = $searchResult->totalCount; + $pages = (int) ceil($total / $limit); + + return $this->render('@EzCalameo/admin/calameo/content_list.html.twig', [ + 'items' => $items, + 'table_exists' => $tableExists, + 'pagination' => [ + 'page' => $page, + 'limit' => $limit, + 'total' => $total, + 'pages' => $pages, + 'has_previous' => $page > 1, + 'has_next' => $page < $pages, + ], + ]); + } + + #[Route('/publications', name: 'publication_list', methods: ['GET'])] + public function calameoPublicationList(): Response + { + if (!$this->tableExists(self::TABLE_NAME)) { + return $this->render('@EzCalameo/admin/calameo/publication_list.html.twig', [ + 'items' => [], + 'table_exists' => false, + ]); + } + + $items = $this->connection->fetchAllAssociative( + <<render('@EzCalameo/admin/calameo/publication_list.html.twig', [ + 'items' => $items, + 'table_exists' => true, + ]); + } + + private function countCalameoPublications(): int + { + return (int) $this->connection->fetchOne( + 'SELECT COUNT(*) FROM calameo_publication', + ); + } + + private function tableExists(string $tableName): bool + { + try { + return $this->connection + ->createSchemaManager() + ->tablesExist([$tableName]); + } catch (\Throwable) { + return false; + } + } + + private function getContentTypesWithCalameoPublicationField(): array + { + $result = []; + + foreach ($this->contentTypeService->loadContentTypeGroups() as $contentTypeGroup) { + foreach ($this->contentTypeService->loadContentTypes($contentTypeGroup) as $contentType) { + $fieldIdentifiers = []; + + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + if ($fieldDefinition->fieldTypeIdentifier !== self::FIELD_TYPE_IDENTIFIER) { + continue; + } + + $fieldIdentifiers[] = $fieldDefinition->identifier; + } + + if ($fieldIdentifiers === []) { + continue; + } + + $result[$contentType->id] = [ + 'id' => $contentType->id, + 'identifier' => $contentType->identifier, + 'fields' => $fieldIdentifiers, + ]; + } + } + + return $result; + } + + private function getCalameoPublicationReference(int $fieldId, int $version): ?array + { + return $this->connection->fetchAssociative( + << $fieldId, + 'version' => $version, + ], + ) ?: null; + } + + private function countCalameoFieldDefinitions(): int + { + $count = 0; + + foreach ($this->getContentTypesWithCalameoPublicationField() as $contentTypeData) { + $count += count($contentTypeData['fields']); + } + + return $count; + } + + private function countContentsWithCalameoField(): int + { + $calameoContentTypes = $this->getContentTypesWithCalameoPublicationField(); + + if ($calameoContentTypes === []) { + return 0; + } + + $query = new Query([ + 'filter' => new Criterion\ContentTypeIdentifier( + array_column($calameoContentTypes, 'identifier') + ), + 'limit' => 0, + ]); + + return $this->searchService->findContent($query)->totalCount; + } +} diff --git a/src/bundles/EzBundle/DependencyInjection/EzCalameoExtension.php b/src/bundles/EzBundle/DependencyInjection/EzCalameoExtension.php index 7b54025..827f1a8 100644 --- a/src/bundles/EzBundle/DependencyInjection/EzCalameoExtension.php +++ b/src/bundles/EzBundle/DependencyInjection/EzCalameoExtension.php @@ -35,12 +35,12 @@ public function load(array $configs, ContainerBuilder $container): void /** * Allow an extension to prepend the extension configurations. * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param ContainerBuilder $container */ public function prepend(ContainerBuilder $container) { $configs = [ - 'field_templates.yml' => 'ezpublish', + 'field_templates.yml' => 'ibexa', 'twig.yml' => 'twig', ]; diff --git a/src/bundles/EzBundle/Resources/config/field_templates.yml b/src/bundles/EzBundle/Resources/config/field_templates.yml index fb7b361..fd19000 100644 --- a/src/bundles/EzBundle/Resources/config/field_templates.yml +++ b/src/bundles/EzBundle/Resources/config/field_templates.yml @@ -1,4 +1,4 @@ system: default: field_templates: - - { template: '@ezdesign/field/calameo_publication_field.html.twig', priority: 10 } + - { template: '@ibexadesign/field/calameo_publication_field.html.twig', priority: 10 } diff --git a/src/bundles/EzBundle/Resources/config/fieldtypes.yml b/src/bundles/EzBundle/Resources/config/fieldtypes.yml index e847c8b..b094c64 100644 --- a/src/bundles/EzBundle/Resources/config/fieldtypes.yml +++ b/src/bundles/EzBundle/Resources/config/fieldtypes.yml @@ -14,17 +14,20 @@ services: AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Type: tags: - { name: ezpublish.fieldType, alias: calameo_publication } - - { name: ezpublish.fieldType.nameable, alias: calameo_publication } + - { name: ibexa.field_type, alias: calameo_publication } AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\LegacyConverter: tags: - { name: ezpublish.storageEngine.legacy.converter, alias: calameo_publication } + - { name: ibexa.field_type.storage.legacy.converter, alias: calameo_publication } AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\FieldStorage: tags: - { name: ezpublish.fieldType.externalStorageHandler, alias: calameo_publication } + - { name: ibexa.field_type.storage.external.handler, alias: calameo_publication } almaviacx.calameo.fieldtype.indexable.calameo_publication: - class: "%ezpublish.fieldType.indexable.unindexed.class%" + class: Ibexa\Core\FieldType\Unindexed tags: - { name: ezpublish.fieldType.indexable, alias: calameo_publication } + - { name: ibexa.field_type.indexable, alias: calameo_publication } diff --git a/src/bundles/EzBundle/Resources/config/routes.yaml b/src/bundles/EzBundle/Resources/config/routes.yaml new file mode 100644 index 0000000..1356293 --- /dev/null +++ b/src/bundles/EzBundle/Resources/config/routes.yaml @@ -0,0 +1,3 @@ +almaviacx_calameo_admin: + resource: '../../Controller/Admin/' + type: attribute diff --git a/src/bundles/EzBundle/Resources/config/services.yml b/src/bundles/EzBundle/Resources/config/services.yml index b568d8d..c4a8e06 100644 --- a/src/bundles/EzBundle/Resources/config/services.yml +++ b/src/bundles/EzBundle/Resources/config/services.yml @@ -11,22 +11,41 @@ services: # if you need to do this, you can override this setting on individual services public: false + AlmaviaCX\Calameo\: + # * AlmaviaCXCalameoBundle/src/ + # ** bundles/EzBundle/Resources/config/services.yml + # ** lib + # + # namespace AlmaviaCX\Calameo\Ez\Form\Type\FieldType; + resource: '../../../../lib/*' +# exclude: +# - '../../../../lib/**/Value/' +# - '../../../../lib/**/Exception/' + AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\FormMapper: tags: - { name: ez.fieldFormMapper.definition, fieldType: calameo_publication } - { name: ez.fieldFormMapper.value, fieldType: calameo_publication } + - { name: ibexa.admin_ui.field_type.form.mapper.definition, fieldType: calameo_publication } + - { name: ibexa.admin_ui.field_type.form.mapper.value, fieldType: calameo_publication } AlmaviaCX\Calameo\Ez\Form\Type\FieldType\CalameoPublicationFieldType: AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Gateway\DoctrineStorage: - almaviacx.calameo.httpclient.siteaccessaware: + AlmaviaCX\Calameo\API\HttpClientFactory: ~ + + AlmaviaCX\Calameo\API\HttpClient: lazy: true - class: AlmaviaCX\Calameo\API\HttpClient - decorates: AlmaviaCX\Calameo\API\HttpClient - arguments: - $APIKey: "$calameo.api.key;almaviacx$" - $APISecret: "$calameo.api.secret;almaviacx$" - $config: "$calameo.config;almaviacx$" + factory: ['@AlmaviaCX\Calameo\API\HttpClientFactory', 'create'] AlmaviaCX\Calameo\Ez\Twig\CalameoTwigExtension: + + AlmaviaCX\Bundle\Calameo\EzBundle\Controller\: + resource: '../../Controller/' + arguments: + $APIKey: "%almaviacx.calameo.api.key%" + $APISecret: "%almaviacx.calameo.api.secret%" + $config: "%almaviacx.calameo.http_client.config%" + $deleteBookEnable: "%almaviacx.calameo.delete_book_enable%" + tags: ['controller.service_arguments'] diff --git a/src/bundles/EzBundle/Resources/config/twig.yml b/src/bundles/EzBundle/Resources/config/twig.yml index 0ad5134..908ebd4 100644 --- a/src/bundles/EzBundle/Resources/config/twig.yml +++ b/src/bundles/EzBundle/Resources/config/twig.yml @@ -1,2 +1,2 @@ form_themes: - - '@ezdesign/fieldtypes/edit/calameo_publication.html.twig' + - '@ibexadesign/fieldtypes/edit/calameo_publication.html.twig' diff --git a/src/bundles/EzBundle/Resources/encore/ez.config.manager.js b/src/bundles/EzBundle/Resources/encore/ez.config.manager.js deleted file mode 100644 index 5688f8b..0000000 --- a/src/bundles/EzBundle/Resources/encore/ez.config.manager.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * NovaeZMenuManagerBundle. - * - * @package NovaeZMenuManagerBundle - * - * @author florian - * @copyright 2019 Novactive - * @license https://github.com/Novactive/NovaeZMenuManagerBundle/blob/master/LICENSE - * - */ - -const path = require('path') - -module.exports = (eZConfig, eZConfigManager) => { - eZConfigManager.add({ - eZConfig, - entryName: 'ezplatform-admin-ui-content-edit-parts-css', - newItems: [ - path.resolve(__dirname, '../public/scss/fieldType/edit/calameo_publication.scss') - ] - }) - eZConfigManager.add({ - eZConfig, - entryName: 'ezplatform-admin-ui-content-edit-parts-js', - newItems: [ - path.resolve(__dirname, '../public/js/scripts/fieldType/calameo_publication.js') - ] - }) -} diff --git a/src/bundles/EzBundle/Resources/encore/ez.config.js b/src/bundles/EzBundle/Resources/encore/ibexa.config.js similarity index 100% rename from src/bundles/EzBundle/Resources/encore/ez.config.js rename to src/bundles/EzBundle/Resources/encore/ibexa.config.js diff --git a/src/bundles/EzBundle/Resources/encore/ibexa.config.manager.js b/src/bundles/EzBundle/Resources/encore/ibexa.config.manager.js new file mode 100644 index 0000000..723ce0f --- /dev/null +++ b/src/bundles/EzBundle/Resources/encore/ibexa.config.manager.js @@ -0,0 +1,19 @@ +const path = require('path'); + +module.exports = (ibexaConfig, ibexaConfigManager) => { + ibexaConfigManager.add({ + ibexaConfig, + entryName: 'ibexa-admin-ui-content-edit-parts-css', + newItems: [ + path.resolve(__dirname, '../public/scss/fieldType/edit/calameo_publication.scss'), + ], + }); + + ibexaConfigManager.add({ + ibexaConfig, + entryName: 'ibexa-admin-ui-content-edit-parts-js', + newItems: [ + path.resolve(__dirname, '../public/js/scripts/fieldType/calameo_publication.js'), + ], + }); +}; diff --git a/src/bundles/EzBundle/Resources/public/js/scripts/fieldType/calameo_publication.js b/src/bundles/EzBundle/Resources/public/js/scripts/fieldType/calameo_publication.js index 38513f9..a9e2f3e 100644 --- a/src/bundles/EzBundle/Resources/public/js/scripts/fieldType/calameo_publication.js +++ b/src/bundles/EzBundle/Resources/public/js/scripts/fieldType/calameo_publication.js @@ -1,54 +1,68 @@ (function (global) { - const SELECTOR_FIELD = '.ez-field-edit--calameo_publication'; - const SELECTOR_LABEL_WRAPPER = '.ez-field-edit__label-wrapper'; - - class CalameoPublicationPreviewField extends global.eZ.BasePreviewField { - /** - * Loads dropped file preview - * - * @param {Event} event - */ + console.log('calameo_publication.js') + const ibexa = global.ibexa || global.eZ; + + if (!ibexa || !ibexa.BasePreviewField || !ibexa.BaseFileFieldValidator) { + console.warn('[Calameo] Ibexa file field helpers are not available.'); + return; + } + + const SELECTOR_FIELD = '.ibexa-field-edit--calameo_publication'; + const SELECTOR_LABEL_WRAPPER = '.ibexa-field-edit__label-wrapper'; + + class CalameoPublicationPreviewField extends ibexa.BasePreviewField { loadDroppedFilePreview(event) { - const preview = this.fieldContainer.querySelector('.ez-field-edit__preview'); - const nameContainer = preview.querySelector('.ez-field-edit-preview__file-name'); - const files = [].slice.call(event.target.files); + const preview = this.fieldContainer.querySelector('.ibexa-field-edit__preview'); - nameContainer.innerHTML = files[0].name; - nameContainer.title = files[0].name; + if (!preview || !event.target.files || !event.target.files.length) { + return; + } - preview.querySelector('.ez-field-edit-preview__action--preview').href = URL.createObjectURL(files[0]); + const file = event.target.files[0]; + const nameContainer = preview.querySelector('.ibexa-field-edit-preview__file-name'); + const previewLink = preview.querySelector('.ibexa-field-edit-preview__action--preview'); + + if (nameContainer) { + nameContainer.innerHTML = file.name; + nameContainer.title = file.name; + } + + if (previewLink) { + previewLink.href = URL.createObjectURL(file); + } } } - [...document.querySelectorAll(SELECTOR_FIELD)].forEach(fieldContainer => { - const validator = new global.eZ.BaseFileFieldValidator({ + [...document.querySelectorAll(SELECTOR_FIELD)].forEach((fieldContainer) => { + const validator = new ibexa.BaseFileFieldValidator({ classInvalid: 'is-invalid', fieldContainer, eventsMap: [ { - selector: `input[type="file"]`, + selector: 'input[type="file"]', eventName: 'change', callback: 'validateInput', errorNodeSelectors: [SELECTOR_LABEL_WRAPPER], }, { isValueValidator: false, - selector: `input[type="file"]`, + selector: 'input[type="file"]', eventName: 'invalidFileSize', callback: 'showFileSizeError', errorNodeSelectors: [SELECTOR_LABEL_WRAPPER], }, ], }); + const previewField = new CalameoPublicationPreviewField({ validator, - fieldContainer + fieldContainer, }); previewField.init(); - global.eZ.fieldTypeValidators = global.eZ.fieldTypeValidators ? - [...global.eZ.fieldTypeValidators, validator] : - [validator]; - }) + ibexa.fieldTypeValidators = ibexa.fieldTypeValidators + ? [...ibexa.fieldTypeValidators, validator] + : [validator]; + }); })(window); diff --git a/src/bundles/EzBundle/Resources/public/scss/fieldType/edit/calameo_publication.scss b/src/bundles/EzBundle/Resources/public/scss/fieldType/edit/calameo_publication.scss index eca4962..59f1f92 100644 --- a/src/bundles/EzBundle/Resources/public/scss/fieldType/edit/calameo_publication.scss +++ b/src/bundles/EzBundle/Resources/public/scss/fieldType/edit/calameo_publication.scss @@ -1,34 +1,73 @@ -.ez-field-edit--calameo_publication { - &.ez-field-edit--with-preview { - .ez-field-edit-preview { - width: 500px; - height: 80px; - line-height: 80px; +$ibexa-color-danger: #db0032; +$ibexa-color-danger-100: #fbe5ea; - &__visual { - display: flex; - } +$base-font-size: 16px; + +@function div($number1, $number2) { + @return $number1 / $number2; +} + +@function calculateRem($size) { + $remSize: div($size, $base-font-size); + + @return $remSize + 0rem; // + 0rem converts value to proper `rem` +} + + +@mixin area-invalid { + color: $ibexa-color-danger; + border-color: $ibexa-color-danger; + background-color: $ibexa-color-danger-100; + + .ibexa-btn { + background-color: $ibexa-color-danger-100; + } +} - &__actions { - flex: 0 0 100px; +.ibexa-field-edit--calameo_publication { + &.is-invalid { + .ibexa-data-source { + @include area-invalid(); + } + } + + &.ibexa-field-edit--with-preview { + .ibexa-field-edit-preview { + &__file-icon { + flex: 0 0 calculateRem(32px); display: flex; align-items: center; - flex-direction: row; - justify-content: space-between; + justify-content: center; } - &__action--preview { - margin-top: 0; + &__visual { + grid-template-rows: auto; } - &__file-name { - flex: 1 1 auto; - font-size: .75rem; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: flex; - align-items: center; + &__media-wrapper { + justify-content: start; + flex-direction: column; + align-items: flex-start; + padding: calculateRem(24px); + } + + &__file-name-wrapper, + &__file-size-wrapper { + display: inline-block; + font-size: calculateRem(16px); + margin-right: calculateRem(40px); + + .ibexa-label { + margin-bottom: 0; + } + } + + &__file-name-wrapper { + max-width: calculateRem(500px); + } + + &__file-size-wrapper { + max-width: calculateRem(150px); } } } diff --git a/src/bundles/EzBundle/Resources/views/admin/calameo/content_list.html.twig b/src/bundles/EzBundle/Resources/views/admin/calameo/content_list.html.twig new file mode 100644 index 0000000..087429f --- /dev/null +++ b/src/bundles/EzBundle/Resources/views/admin/calameo/content_list.html.twig @@ -0,0 +1,118 @@ +{# Resources/views/admin/calameo/content_list.html.twig #} +{% extends '@ibexadesign/ui/layout.html.twig' %} + +{% block title %}Contenus avec champ Calaméo{% endblock %} + +{% block page_title %} + {% include '@ibexadesign/ui/page_title.html.twig' with { + title: 'Contenus avec champ Calaméo', + icon_name: 'catalog' + } %} +{% endblock %} + +{% block breadcrumbs %} + {% include '@ibexadesign/ui/breadcrumbs.html.twig' with { + items: [ + { + value: 'Configuration CALAMEO', + url: path('almaviacx_calameo_admin_index') + }, + { + value: 'Contenus avec champ Calaméo', + url: path('almaviacx_calameo_admin_publication_list') + }, + ] + } %} +{% endblock %} + + +{% block content %} +

+ {{ pagination.total }} contenu{{ pagination.total > 1 ? 's' : '' }} trouvé{{ pagination.total > 1 ? 's' : '' }}. + Page {{ pagination.page }}{% if pagination.pages > 0 %} / {{ pagination.pages }}{% endif %}. +

+ + + + + + + + + + + + + + + + {% for item in items %} + + + + + + + + + + + {% else %} + + + + {% endfor %} + +
Content IDNomTypeChampField IDVersionPublication IDFolder ID
+ + {{ item.content_id }} + + {{ item.content_name }}{{ item.content_type_identifier }}{{ item.field_identifier }}{{ item.field_id }}{{ item.version }} + {% if item.publication_id %} + + {% endif %} + {{ item.folder_id }}
Aucun contenu trouvé.
+ +{% if pagination.pages > 1 %} + +{% endif %} + +{% endblock %} diff --git a/src/bundles/EzBundle/Resources/views/admin/calameo/index.html.twig b/src/bundles/EzBundle/Resources/views/admin/calameo/index.html.twig new file mode 100644 index 0000000..309675f --- /dev/null +++ b/src/bundles/EzBundle/Resources/views/admin/calameo/index.html.twig @@ -0,0 +1,77 @@ +{# Resources/views/admin/calameo/index.html.twig #} + +{% extends '@ibexadesign/ui/layout.html.twig' %} + +{% block title %}Calaméo{% endblock %} + +{% block page_title %} + {% include '@ibexadesign/ui/page_title.html.twig' with { + title: 'Calaméo', + icon_name: 'catalog' + } %} +{% endblock %} + +{% block breadcrumbs %} + {% include '@ibexadesign/ui/breadcrumbs.html.twig' with { + items: [ + { + value: 'Calaméo', + url: path('almaviacx_calameo_admin_index') + }, + ] + } %} +{% endblock %} + + +{% block content %} + + +

Configuration

+ +https://www.calameo.com/account/dashboard + + + + + + + + + + + + + + + + + {% for key, value in configuration %} + + + + + {% endfor %} + +
FieldType{{ field_type_identifier }}
Table{{ table_name }}
Table présente{{ table_exists ? 'oui' : 'non' }}
{{ key }} + {% if value is iterable %} +
{{ value|json_encode(constant('JSON_PRETTY_PRINT')) }}
+ {% else %} + {{ value ?? 'non configuré' }} + {% endif %} +
+ +

Statistiques

+ + + +{% endblock %} diff --git a/src/bundles/EzBundle/Resources/views/admin/calameo/publication_list.html.twig b/src/bundles/EzBundle/Resources/views/admin/calameo/publication_list.html.twig new file mode 100644 index 0000000..8d0d38a --- /dev/null +++ b/src/bundles/EzBundle/Resources/views/admin/calameo/publication_list.html.twig @@ -0,0 +1,66 @@ +{# Resources/views/admin/calameo/publication_list.html.twig #} +{% extends '@ibexadesign/ui/layout.html.twig' %} + +{% block title %}Table calameo_publication{% endblock %} + +{% block page_title %} + {% include '@ibexadesign/ui/page_title.html.twig' with { + title: 'Table calameo_publication', + icon_name: 'catalog' + } %} +{% endblock %} + +{% block breadcrumbs %} + {% include '@ibexadesign/ui/breadcrumbs.html.twig' with { + items: [ + { + value: 'Configuration CALAMEO', + url: path('almaviacx_calameo_admin_index') + }, + { + value: 'Table calameo_publication', + url: path('almaviacx_calameo_admin_content_list') + }, + ] + } %} +{% endblock %} + + +{% block content %} +{% if not table_exists %} +

La table calameo_publication n’existe pas.

+{% else %} + + + + + + + + + + + {% for item in items %} + + + + + + + {% else %} + + + + {% endfor %} + +
Attribute IDVersionPublication IDFolder ID
{{ item.contentobject_attribute_id }}{{ item.version }} + {% if item.publication_id %} + + {% endif %} + {{ item.folder_id }}
Aucune entrée trouvée.
+{% endif %} + +{% endblock %} diff --git a/src/bundles/EzBundle/Resources/views/themes/admin/field/calameo_publication_field.html.twig b/src/bundles/EzBundle/Resources/views/themes/admin/field/calameo_publication_field.html.twig index 9a34abf..d54d89c 100644 --- a/src/bundles/EzBundle/Resources/views/themes/admin/field/calameo_publication_field.html.twig +++ b/src/bundles/EzBundle/Resources/views/themes/admin/field/calameo_publication_field.html.twig @@ -1,5 +1,5 @@ {# @var publication \AlmaviaCX\Calameo\API\Value\Publication #} -{% extends '@EzPublishCore/content_fields.html.twig' %} +{% extends '@IbexaCore/content_fields.html.twig' %} {% block calameo_publication_field %} {% set publication = loadCalameoPublication(field.value) %} @@ -9,5 +9,11 @@ {% else %} {{ publication.name }} ({{ publication.id }}) {% endif %} + {% else %} +

ERREUR CALAMEO

+
+            publicationId : {{ field.value.publicationId|default }}
+            folderId : {{ field.value.folderId|default }}
+        
{% endif %} {% endblock %} diff --git a/src/bundles/EzBundle/Resources/views/themes/admin/fieldtypes/edit/calameo_publication.html.twig b/src/bundles/EzBundle/Resources/views/themes/admin/fieldtypes/edit/calameo_publication.html.twig index 6e5c538..306073a 100644 --- a/src/bundles/EzBundle/Resources/views/themes/admin/fieldtypes/edit/calameo_publication.html.twig +++ b/src/bundles/EzBundle/Resources/views/themes/admin/fieldtypes/edit/calameo_publication.html.twig @@ -1,7 +1,7 @@ {# @var publication \AlmaviaCX\Calameo\API\Value\Publication #} {% trans_default_domain 'ezrepoforms_content' %} -{% use '@ezdesign/fieldtypes/edit/binary_base.html.twig' %} +{% use '@ibexadesign/ui/field_type/edit/binary_base.html.twig' %} {%- block ezplatform_fieldtype_calameo_publication_row -%} {% set widget_container_block_name = 'calameo_publication_widget_container' %} @@ -12,23 +12,38 @@ {%- endblock -%} {% block calameo_publication_preview %} + {% set readonly = attr.readonly|default(false) %} {% set publication = loadCalameoPublication(form.parent.vars.value.value) %} -
-
-
{{ publication ? publication.name }}
-
-
- - - - - - +
+
+
+
+ +

{{ publication ? publication.name }}

+
+
+ + + + + {{ 'ezbinaryfile.action.preview'|trans|desc('Preview') }} + + +
+
{% endblock %} @@ -36,12 +51,18 @@ {% block calameo_publication_widget_container %} {{- form_widget(form.publicationId) -}} {{- form_widget(form.folderId) -}} + + + {% set fieldtype = form.parent %} + {% set field_type_descriptions = fieldtype.vars.value.fieldDefinition.descriptions %} + {{ block(preview_block_name) }}
-
+
{{ block('binary_base_widget') }}
-{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/src/bundles/EzBundle/Resources/views/themes/standard/field/calameo_publication_field.html.twig b/src/bundles/EzBundle/Resources/views/themes/standard/field/calameo_publication_field.html.twig index 7e1d9ed..b809cef 100644 --- a/src/bundles/EzBundle/Resources/views/themes/standard/field/calameo_publication_field.html.twig +++ b/src/bundles/EzBundle/Resources/views/themes/standard/field/calameo_publication_field.html.twig @@ -1,5 +1,5 @@ {# @var publication \AlmaviaCX\Calameo\API\Value\Publication #} -{% extends '@EzPublishCore/content_fields.html.twig' %} +{% extends '@IbexaCore/content_fields.html.twig' %} {% block calameo_publication_field %} {% set publicationId = field.value.publicationId %} diff --git a/src/lib/API/Gateway/AbstractGateway.php b/src/lib/API/Gateway/AbstractGateway.php index ae4cf31..ce61b37 100644 --- a/src/lib/API/Gateway/AbstractGateway.php +++ b/src/lib/API/Gateway/AbstractGateway.php @@ -21,23 +21,10 @@ abstract class AbstractGateway { + protected HttpClient $client; + protected Serializer $serializer; + protected ExceptionThrower $exceptionThrower; - /** @var HttpClient */ - protected $client; - - /** @var Serializer */ - protected $serializer; - - /** @var ExceptionThrower */ - protected $exceptionThrower; - - /** - * AbstractGateway constructor. - * - * @param HttpClient $client - * @param Serializer $serializer - * @param ExceptionThrower $exceptionThrower - */ public function __construct( HttpClient $client, Serializer $serializer, @@ -49,11 +36,11 @@ public function __construct( } /** - * @param string $action + * @param string $action * @param string|null $responseContentType - * @param array $requestParameters - * @param string $method - * + * @param array $requestParameters + * @param string $method + * @param array $options * @return Response * @throws ApiResponseErrorException * @throws GuzzleException diff --git a/src/lib/API/Gateway/GenericGateway.php b/src/lib/API/Gateway/GenericGateway.php index 5cfa066..fb686f8 100644 --- a/src/lib/API/Gateway/GenericGateway.php +++ b/src/lib/API/Gateway/GenericGateway.php @@ -14,7 +14,7 @@ abstract class GenericGateway extends AbstractGateway { - public const ENDPOINT = 'http://api.calameo.com/1.0'; + public const string ENDPOINT = 'http://api.calameo.com/1.0'; protected function getEndpoint(): string { diff --git a/src/lib/API/Gateway/UploadGateway.php b/src/lib/API/Gateway/UploadGateway.php index 24a9924..2408991 100644 --- a/src/lib/API/Gateway/UploadGateway.php +++ b/src/lib/API/Gateway/UploadGateway.php @@ -21,7 +21,7 @@ class UploadGateway extends AbstractGateway { - public const ENDPOINT = 'http://upload.calameo.com/1.0'; + public const string ENDPOINT = 'http://upload.calameo.com/1.0'; /** * @param int $subscriptionId diff --git a/src/lib/API/HttpClient.php b/src/lib/API/HttpClient.php index 368fbe2..11201e4 100644 --- a/src/lib/API/HttpClient.php +++ b/src/lib/API/HttpClient.php @@ -20,24 +20,20 @@ class HttpClient extends Client { /** * Calameo API Key - * - * @var string */ - protected $APIKey; + protected ?string $APIKey; /** * CalameoAPI Secret - * - * @var string */ - protected $APISecret; + protected ?string $APISecret; /** * HttpClient constructor. * - * @param string $APIKey - * @param string $APISecret - * @param array $config + * @param string|null $APIKey + * @param string|null $APISecret + * @param array $config */ public function __construct(?string $APIKey, ?string $APISecret, array $config = []) { diff --git a/src/lib/API/HttpClientFactory.php b/src/lib/API/HttpClientFactory.php new file mode 100644 index 0000000..5b0d988 --- /dev/null +++ b/src/lib/API/HttpClientFactory.php @@ -0,0 +1,26 @@ +configResolver = $configResolver; + } + + public function create(): HttpClient + { + $key = $this->configResolver->getParameter('calameo.api.key', 'almaviacx'); + $secret = $this->configResolver->getParameter('calameo.api.secret', 'almaviacx'); + $config = $this->configResolver->getParameter('calameo.config', 'almaviacx'); + + return new HttpClient($key, $secret, $config); + } +} diff --git a/src/lib/API/Repository/AccountRepository.php b/src/lib/API/Repository/AccountRepository.php index 4f193b2..f6f9c0e 100644 --- a/src/lib/API/Repository/AccountRepository.php +++ b/src/lib/API/Repository/AccountRepository.php @@ -21,13 +21,8 @@ class AccountRepository { - /** @var AccountGateway */ - protected $gateway; + protected AccountGateway $gateway; - /** - * AccountRepository constructor. - * @param AccountGateway $gateway - */ public function __construct(AccountGateway $gateway) { $this->gateway = $gateway; diff --git a/src/lib/API/Repository/FolderRepository.php b/src/lib/API/Repository/FolderRepository.php index 6ccd6d7..e31d2de 100644 --- a/src/lib/API/Repository/FolderRepository.php +++ b/src/lib/API/Repository/FolderRepository.php @@ -22,13 +22,8 @@ class FolderRepository { - /** @var FolderGateway */ - protected $gateway; + protected FolderGateway $gateway; - /** - * SubscriptionRepository constructor. - * @param FolderGateway $gateway - */ public function __construct(FolderGateway $gateway) { $this->gateway = $gateway; diff --git a/src/lib/API/Repository/PublicationRepository.php b/src/lib/API/Repository/PublicationRepository.php index d06dc09..af51628 100644 --- a/src/lib/API/Repository/PublicationRepository.php +++ b/src/lib/API/Repository/PublicationRepository.php @@ -16,20 +16,16 @@ use AlmaviaCX\Calameo\API\Value\Publication; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; use GuzzleHttp\Exception\GuzzleException; +use Psr\Log\LoggerInterface; class PublicationRepository { - - /** @var PublicationGateway */ - protected $gateway; - - /** - * PublicationRepository constructor. - * @param PublicationGateway $gateway - */ - public function __construct(PublicationGateway $gateway) + public function __construct( + protected PublicationGateway $gateway, + protected bool $deleteBookEnable = false, + private readonly ?LoggerInterface $logger = null, + ) { - $this->gateway = $gateway; } /** @@ -45,13 +41,23 @@ public function getPublicationInfos(string $publicationId): Publication /** * @param string $publicationId - * @return bool + * @return bool Return false if deleteBookEnable is false * @throws ApiResponseErrorException * @throws GuzzleException */ public function deletePublication(string $publicationId): bool { + if (!$this->deleteBookEnable) { + $this->logger?->info(sprintf( + '[Calameo] Suppression distante ignorée pour la publication "%s" : almaviacx.calameo.delete_book_enable=false.', + $publicationId + )); + + return false; + } + $this->gateway->deleteBook($publicationId); + return true; } } diff --git a/src/lib/API/Serializer.php b/src/lib/API/Serializer.php index 8c07694..b86c919 100644 --- a/src/lib/API/Serializer.php +++ b/src/lib/API/Serializer.php @@ -13,20 +13,13 @@ namespace AlmaviaCX\Calameo\API; use AlmaviaCX\Calameo\API\Value\Response\Response; +use JMS\Serializer\SerializerInterface; use Psr\Http\Message\ResponseInterface; -class Serializer +readonly class Serializer { - /** @var \JMS\Serializer\Serializer */ - protected $baseSerializer; - - /** - * Serializer constructor. - * @param \JMS\Serializer\Serializer $baseSerializer - */ - public function __construct(\JMS\Serializer\Serializer $baseSerializer) + public function __construct(protected SerializerInterface $baseSerializer) { - $this->baseSerializer = $baseSerializer; } public function deserializeResponse( diff --git a/src/lib/API/Service/PublishingService.php b/src/lib/API/Service/PublishingService.php index 8f05dc5..6e926c4 100644 --- a/src/lib/API/Service/PublishingService.php +++ b/src/lib/API/Service/PublishingService.php @@ -15,19 +15,15 @@ use AlmaviaCX\Calameo\API\Gateway\UploadGateway; use AlmaviaCX\Calameo\API\Value\Publication; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; +use AlmaviaCX\Calameo\Exception\CalameoResponseContentMustBePublication; use AlmaviaCX\Calameo\Exception\NotImplementedException; use GuzzleHttp\Exception\GuzzleException; use SplFileInfo; class PublishingService { - /** @var UploadGateway */ - protected $gateway; + protected UploadGateway $gateway; - /** - * PublishingService constructor. - * @param UploadGateway $gateway - */ public function __construct(UploadGateway $gateway) { $this->gateway = $gateway; @@ -39,6 +35,7 @@ public function __construct(UploadGateway $gateway) * @return Publication * @throws ApiResponseErrorException * @throws GuzzleException + * @throws CalameoResponseContentMustBePublication */ public function publish( int $folderId, @@ -50,7 +47,10 @@ public function publish( $file, $options ); - return $response->content; + if ($response->content instanceof Publication) { + return $response->content; + } + throw new CalameoResponseContentMustBePublication(); } /** @@ -60,6 +60,7 @@ public function publish( * @return Publication * @throws ApiResponseErrorException * @throws GuzzleException + * @throws CalameoResponseContentMustBePublication */ public function publishFromUrl(int $folderId, string $url, array $options = []): Publication { @@ -68,7 +69,10 @@ public function publishFromUrl(int $folderId, string $url, array $options = []): $url, $options ); - return $response->content; + if ($response->content instanceof Publication) { + return $response->content; + } + throw new CalameoResponseContentMustBePublication(); } /** @@ -88,6 +92,7 @@ public function publishFromText(int $folderId, string $text): Publication * @return Publication * @throws ApiResponseErrorException * @throws GuzzleException + * @throws CalameoResponseContentMustBePublication */ public function revise(string $publicationId, SplFileInfo $file): Publication { @@ -95,7 +100,10 @@ public function revise(string $publicationId, SplFileInfo $file): Publication $publicationId, $file ); - return $response->content; + if ($response->content instanceof Publication) { + return $response->content; + } + throw new CalameoResponseContentMustBePublication(); } /** @@ -104,7 +112,7 @@ public function revise(string $publicationId, SplFileInfo $file): Publication * @param int $folderId * @return Publication * @throws ApiResponseErrorException - * @throws GuzzleException + * @throws GuzzleException|CalameoResponseContentMustBePublication */ public function reviseFromUrl(string $publicationId, string $url, int $folderId): Publication { @@ -113,7 +121,10 @@ public function reviseFromUrl(string $publicationId, string $url, int $folderId) $url, $folderId ); - return $response->content; + if ($response->content instanceof Publication) { + return $response->content; + } + throw new CalameoResponseContentMustBePublication(); } /** diff --git a/src/lib/API/Value/ContentList.php b/src/lib/API/Value/ContentList.php index 2d91ea4..d493e1d 100644 --- a/src/lib/API/Value/ContentList.php +++ b/src/lib/API/Value/ContentList.php @@ -22,17 +22,17 @@ abstract class ContentList extends ResponseContent * @var int * @Serializer\Type("int") */ - public $total; + public int $total; /** * @var int * @Serializer\Type("int") */ - public $start; + public int $start; /** * @var int * @Serializer\Type("int") */ - public $step; + public int $step; } diff --git a/src/lib/API/Value/Folder.php b/src/lib/API/Value/Folder.php index 2cab44d..decdd38 100644 --- a/src/lib/API/Value/Folder.php +++ b/src/lib/API/Value/Folder.php @@ -19,13 +19,13 @@ class Folder extends ResponseContent { /** @var string Title of the publication */ - public const SORT_NAME = 'Name'; + public const string SORT_NAME = 'Name'; /** @var string Date of creation */ - public const SORT_CREATION = 'Creation'; + public const string SORT_CREATION = 'Creation'; /** @var string Date the folder was last modified */ - public const SORT_MODIFICATION = 'Modification'; + public const string SORT_MODIFICATION = 'Modification'; /** * Unique identifying key for the folder @@ -33,7 +33,7 @@ class Folder extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("ID") */ - public $id; + public int $id; /** * Unique identifying key for the account of the folder @@ -41,7 +41,7 @@ class Folder extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("AccountID") */ - public $accountId; + public int $accountId; /** * Title of the folder @@ -49,7 +49,7 @@ class Folder extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("Name") */ - public $name; + public string $name; /** * Description of the folder @@ -57,7 +57,7 @@ class Folder extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("Description") */ - public $description; + public string $description; /** * Available publications inside the folder @@ -65,7 +65,7 @@ class Folder extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Books") */ - public $books; + public int $books; /** * Available subscribers inside the folder (only returned for your account's folder) @@ -73,7 +73,7 @@ class Folder extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Subscribers") */ - public $subscribers; + public int $subscribers; /** * Date of the folder's creation @@ -81,7 +81,7 @@ class Folder extends ResponseContent * @Serializer\Type("DateTime<'Y-m-d H:i:s'>") * @Serializer\SerializedName("Creation") */ - public $creation; + public DateTime $creation; /** * Date of the folder's last modification @@ -89,7 +89,7 @@ class Folder extends ResponseContent * @Serializer\Type("DateTime<'Y-m-d H:i:s'>") * @Serializer\SerializedName("Modification") */ - public $modification; + public DateTime $modification; /** * Absolute URL for the folder's overview @@ -97,5 +97,5 @@ class Folder extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("PublicUrl") */ - public $publicUrl; + public string $publicUrl; } diff --git a/src/lib/API/Value/FolderList.php b/src/lib/API/Value/FolderList.php index 001442c..ae12909 100644 --- a/src/lib/API/Value/FolderList.php +++ b/src/lib/API/Value/FolderList.php @@ -20,5 +20,5 @@ class FolderList extends ContentList * @var Folder[] * @Serializer\Type("array") */ - public $items; + public array $items; } diff --git a/src/lib/API/Value/Publication.php b/src/lib/API/Value/Publication.php index fcbb876..876cad0 100644 --- a/src/lib/API/Value/Publication.php +++ b/src/lib/API/Value/Publication.php @@ -15,39 +15,40 @@ use DateTime; use JMS\Serializer\Annotation as Serializer; use AlmaviaCX\Calameo\API\Value\Response\ResponseContent; -use SplFileInfo; -use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\DependencyInjection\Loader\Configurator\Traits\LazyTrait; class Publication extends ResponseContent { + use LazyTrait; + /** @var string Title of the publication */ - public const SORT_NAME = "Name"; + public const string SORT_NAME = "Name"; /** @var string Number of pages of the publication */ - public const SORT_PAGES = "Pages"; + public const string SORT_PAGES = "Pages"; /** @var string Number of comments on the publication */ - public const SORT_COMMENTS = "Comments"; + public const string SORT_COMMENTS = "Comments"; /** @var string Number of times the publication was read */ - public const SORT_VIEWS = "Views"; + public const string SORT_VIEWS = "Views"; /** @var string Date of publication */ - public const SORT_DATE = "Date"; + public const string SORT_DATE = "Date"; /** @var string Date of creation */ - public const SORT_CREATION = "Creation"; + public const string SORT_CREATION = "Creation"; /** @var string Date the publication was last modified */ - public const SORT_MODIFICATION = "Modification"; + public const string SORT_MODIFICATION = "Modification"; /** @var string waiting to be converted */ - public const STATUS_QUEUE = 'QUEUE'; + public const string STATUS_QUEUE = 'QUEUE'; /** @var string processing document */ - public const STATUS_PROCESS = 'PROCESS'; + public const string STATUS_PROCESS = 'PROCESS'; /** @var string converting document */ - public const STATUS_STORE = 'STORE'; + public const string STATUS_STORE = 'STORE'; /** @var string error during conversion */ - public const STATUS_ERROR = 'ERROR'; + public const string STATUS_ERROR = 'ERROR'; /** @var string publication ready */ - public const STATUS_DONE = 'DONE'; + public const string STATUS_DONE = 'DONE'; - public const PUBLISHING_MODE_PUBLIC = 1; - public const PUBLISHING_MODE_PRIVATE = 2; + public const int PUBLISHING_MODE_PUBLIC = 1; + public const int PUBLISHING_MODE_PRIVATE = 2; /** * ID of the publication @@ -56,7 +57,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("ID") */ - public $id; + public string $id; /** * Publication's owner account ID (should be your account ID) @@ -65,7 +66,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("AccountID") */ - public $accountId; + public int $accountId; /** * Publication's owner subscription ID @@ -74,7 +75,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("SubscriptionID") */ - public $folderId; + public int $folderId; /** * Title of the publication @@ -83,7 +84,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("Name") */ - public $name; + public string $name; /** * Description of the publication @@ -92,7 +93,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("Description") */ - public $description; + public string $description; /** * Conversion status of the publication. @@ -106,7 +107,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("Status") */ - public $status; + public string $status; /** * Sends 1 if the publication is private and 0 if not @@ -115,7 +116,7 @@ class Publication extends ResponseContent * @Serializer\Type("bool") * @Serializer\SerializedName("IsPrivate") */ - public $isPrivate; + public bool $isPrivate; /** * Authentication parameter for private URLs (authid) @@ -124,7 +125,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("AuthID") */ - public $authId; + public string $authId; /** * Sends 1 if the publication allows access to the miniCalaméo and 0 if not @@ -133,7 +134,7 @@ class Publication extends ResponseContent * @Serializer\Type("bool") * @Serializer\SerializedName("AllowMini") */ - public $allowMini; + public bool $allowMini; /** * Number of pages of the publication @@ -142,7 +143,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Pages") */ - public $pages; + public int $pages; /** * Width of a page of the publication @@ -151,7 +152,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Width") */ - public $width; + public int $width; /** * Height of a page of the publication @@ -160,7 +161,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Height") */ - public $height; + public int $height; /** * Number of views of the publication @@ -169,7 +170,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Views") */ - public $views; + public int $views; /** * Number of downloads of the publication @@ -178,7 +179,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Downloads") */ - public $downloads; + public int $downloads; /** * Number of comments of the publication @@ -187,7 +188,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Comments") */ - public $comments; + public int $comments; /** * Number of favorites from the publication @@ -196,7 +197,7 @@ class Publication extends ResponseContent * @Serializer\Type("int") * @Serializer\SerializedName("Favorites") */ - public $favorites; + public int $favorites; /** * Reference date of the publication @@ -206,7 +207,7 @@ class Publication extends ResponseContent * @Serializer\SerializedName("Date") * @Serializer\Accessor(setter="setDate") */ - public $date; + public DateTime $date; /** * Date of creation of the publication * @@ -214,7 +215,7 @@ class Publication extends ResponseContent * @Serializer\Type("DateTime<'Y-m-d H:i:s'>") * @Serializer\SerializedName("Creation") */ - public $creation; + public DateTime $creation; /** * Scheduled date of the publication (UTC) * @@ -222,7 +223,7 @@ class Publication extends ResponseContent * @Serializer\Type("DateTime<'Y-m-d H:i:s'>") * @Serializer\SerializedName("Publication") */ - public $publication; + public DateTime $publication; /** * Date of the last modification of the publication * @@ -230,7 +231,7 @@ class Publication extends ResponseContent * @Serializer\Type("DateTime<'Y-m-d H:i:s'>") * @Serializer\SerializedName("Modification") */ - public $modification; + public DateTime $modification; /** * Absolute URL for the publication's larger cover * @@ -238,7 +239,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("PosterUrl") */ - public $posterUrl; + public string $posterUrl; /** * Absolute URL for the publication's cover * @@ -246,7 +247,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("PictureUrl") */ - public $pictureUrl; + public string $pictureUrl; /** * Absolute URL for the publication's thumbnail * @@ -254,7 +255,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("ThumbUrl") */ - public $thumbUrl; + public string $thumbUrl; /** * Absolute URL for the publication's overview * @@ -262,7 +263,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("PublicUrl") */ - public $publicUrl; + public string $publicUrl; /** * Absolute URL for the publication's reading page * @@ -270,7 +271,7 @@ class Publication extends ResponseContent * @Serializer\Type("string") * @Serializer\SerializedName("ViewUrl") */ - public $viewUrl; + public string $viewUrl; /** * @param DateTime $date diff --git a/src/lib/API/Value/PublicationList.php b/src/lib/API/Value/PublicationList.php index e20d6f2..6b5ebcc 100644 --- a/src/lib/API/Value/PublicationList.php +++ b/src/lib/API/Value/PublicationList.php @@ -20,5 +20,5 @@ class PublicationList extends ContentList * @var Publication[] * @Serializer\Type("array") */ - public $items; + public array $items; } diff --git a/src/lib/API/Value/Request/SortOrder.php b/src/lib/API/Value/Request/SortOrder.php index 3ea15bb..d8694b9 100644 --- a/src/lib/API/Value/Request/SortOrder.php +++ b/src/lib/API/Value/Request/SortOrder.php @@ -14,6 +14,6 @@ class SortOrder { - public const UP = "UP"; - public const DOWN = "DOWN"; + public const string UP = "UP"; + public const string DOWN = "DOWN"; } diff --git a/src/lib/API/Value/Response/Error.php b/src/lib/API/Value/Response/Error.php index 78ae266..b85686d 100644 --- a/src/lib/API/Value/Response/Error.php +++ b/src/lib/API/Value/Response/Error.php @@ -20,11 +20,11 @@ class Error * @var int * @Serializer\Type("int") */ - public $code; + public int $code; /** * @var string * @Serializer\Type("string") */ - public $message; + public string $message; } diff --git a/src/lib/API/Value/Response/Response.php b/src/lib/API/Value/Response/Response.php index dce38b0..af8df9b 100644 --- a/src/lib/API/Value/Response/Response.php +++ b/src/lib/API/Value/Response/Response.php @@ -16,42 +16,42 @@ class Response { - public const TYPE_OK ="ok"; - public const TYPE_ERROR ="error"; + public const string TYPE_OK ="ok"; + public const string TYPE_ERROR ="error"; /** * @var string * @Serializer\Type("string") */ - public $status; + public string $status; /** * @var int * @Serializer\Type("int") */ - public $version; + public int $version; /** * @var string * @Serializer\Type("string") */ - public $requestid; + public string $requestid; /** * @var int * @Serializer\Type("int") */ - public $requests; + public int $requests; /** * @var Error|null * @Serializer\Type("AlmaviaCX\Calameo\API\Value\Response\Error") */ - public $error; + public ?Error $error; /** * @var ResponseContent * @Serializer\Type("AlmaviaCX\Calameo\API\Value\Response\ResponseContent") */ - public $content; + public ResponseContent $content; } diff --git a/src/lib/Exception/CalameoResponseContentMustBePublication.php b/src/lib/Exception/CalameoResponseContentMustBePublication.php new file mode 100644 index 0000000..7b0fa05 --- /dev/null +++ b/src/lib/Exception/CalameoResponseContentMustBePublication.php @@ -0,0 +1,15 @@ + Response\TooManyRequestsException::class, 101 => Response\InvalidSignatureException::class, 102 => Response\InvalidEndpointException::class, diff --git a/src/lib/Ez/FieldType/CalameoPublication/FieldStorage.php b/src/lib/Ez/FieldType/CalameoPublication/FieldStorage.php index 60ac511..c97881a 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/FieldStorage.php +++ b/src/lib/Ez/FieldType/CalameoPublication/FieldStorage.php @@ -16,140 +16,151 @@ use AlmaviaCX\Calameo\API\Service\PublishingService; use AlmaviaCX\Calameo\API\Value\Publication; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; -use AlmaviaCX\Calameo\Exception\NotImplementedException; -use AlmaviaCX\Calameo\Exception\Response\ApiResponseException; +use AlmaviaCX\Calameo\Exception\CalameoResponseContentMustBePublication; use AlmaviaCX\Calameo\Exception\Response\UnknownBookIDException; use AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Gateway\DoctrineStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use eZ\Publish\SPI\FieldType\FieldStorage as FieldStorageInterface; +use Doctrine\DBAL\Exception; use GuzzleHttp\Exception\GuzzleException; +use Ibexa\Contracts\Core\FieldType\FieldStorage as FieldStorageInterface; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Psr\Log\LoggerInterface; use SplFileInfo; -use Symfony\Component\HttpFoundation\File\UploadedFile; -class FieldStorage implements FieldStorageInterface +readonly class FieldStorage implements FieldStorageInterface { - /** @var PublicationRepository */ - public $publicationRepository; - - /** @var PublishingService */ - public $publishingService; - - /** @var DoctrineStorage */ - public $gateway; - - /** @var LoggerInterface */ - public $logger; - - /** - * @param PublicationRepository $publicationRepository - * @param PublishingService $publishingService - * @param DoctrineStorage $gateway - * @param LoggerInterface $logger - */ public function __construct( - PublicationRepository $publicationRepository, - PublishingService $publishingService, - DoctrineStorage $gateway, - LoggerInterface $logger + protected PublicationRepository $publicationRepository, + protected PublishingService $publishingService, + protected DoctrineStorage $gateway, + protected LoggerInterface $logger, + protected ConfigResolverInterface $configResolver, ) { - $this->publicationRepository = $publicationRepository; - $this->publishingService = $publishingService; - $this->gateway = $gateway; - $this->logger = $logger; } /** * @param VersionInfo $versionInfo * @param Field $field - * @param array $context * @return bool - * @throws ApiResponseErrorException - * @throws GuzzleException + * @throws ApiResponseErrorException|GuzzleException|Exception|CalameoResponseContentMustBePublication */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) + public function storeFieldData(VersionInfo $versionInfo, Field $field): bool { $inputUri = $field->value->externalData['inputUri'] ?? null; if ($inputUri) { $file = new SplFileInfo($inputUri); - try { - if ($field->value->externalData['publicationId'] === null) { - throw new UnknownBookIDException(); + if ($field->value->externalData['publicationId'] === null) { + $folderId = $field->value->externalData['folderId']; + if (!$folderId) { // null ou 0 + // Est-ce possible ? + $this->logger->error(sprintf('[Calameo] FolderId is %s', + $folderId === null ? 'null' : $folderId + )); } - $publication = $this->publishingService->revise( - $field->value->externalData['publicationId'], - $file - ); - } catch (UnknownBookIDException $exception) { + + $name = $versionInfo->contentInfo->name; + // $name === '' car le contenu n'est pas encore enregistré. + // Du coup calaméo va mettre : "Custom Filename" + if (!$name) { + $name = 'c' . $versionInfo->contentInfo->id; // + } + + // Création $publication = $this->publishingService->publish( - $field->value->externalData['folderId'], + $folderId, $file, [ - 'name' => $versionInfo->contentInfo->name, + 'name' => $name, // expected to be of type "string" 'is_published' => 1, 'publishing_mode' => Publication::PUBLISHING_MODE_PUBLIC, ] ); $field->value->externalData['publicationId'] = $publication->id; + } else { + // Modification + $this->publishingService->revise( + $field->value->externalData['publicationId'], + $file + ); } } $this->gateway->storePublicationReference($versionInfo, $field); + return true; } /** * @param VersionInfo $versionInfo * @param Field $field - * @param array $context - * @throws ApiResponseErrorException - * @throws GuzzleException + * @throws Exception */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context): void + public function getFieldData(VersionInfo $versionInfo, Field $field,): void { - $repository = $this->publicationRepository; - - $publicationReferenceData = $this->gateway->getPublicationReferenceData($field->id, $versionInfo->versionNo); - if ($publicationReferenceData === null || !$publicationReferenceData['publicationId']) { - return; + $publicationReferenceData = $this->gateway->getPublicationReferenceData($field->id, $versionInfo->versionNo); + if ($publicationReferenceData + && !empty($publicationReferenceData['publicationId']) + && !empty($publicationReferenceData['folderId']) + ) { + $field->value->externalData = $publicationReferenceData; } - - $field->value->externalData = $publicationReferenceData; - $field->value->externalData['publicationLoader'] = static function () use ($repository, $field) { - try { - return $repository->getPublicationInfos($field->value->externalData['publicationId']); - } catch (UnknownBookIDException $exception) { - return; - } - }; } /** * @param VersionInfo $versionInfo * @param array $fieldIds - * @param array $context - * @return bool|void + * @return void + * @throws GuzzleException|Exception */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context): void + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds): void { if (empty($fieldIds)) { return; } - $publicationIds = $this->gateway->getReferencedPublications($fieldIds, $versionInfo->versionNo); + $publicationIds = $this->gateway->getReferencedPublications($fieldIds); $versionPublicationId = $publicationIds[$versionInfo->versionNo] ?? null; + + // On supprime toujours la référence locale. + // La suppression dans Ibexa ne doit pas être bloquée par une config API Calaméo absente. $this->gateway->removePublicationReferences($fieldIds, $versionInfo->versionNo); $versionWithPublication = array_keys($publicationIds, $versionPublicationId); - if (count($versionWithPublication) <= 1 && $versionPublicationId) { - try { - $this->publicationRepository->deletePublication($versionPublicationId); - } catch (UnknownBookIDException $exception) { - return ; - } catch (ApiResponseErrorException $exception) { - $this->logger->error($exception->getMessage()); - return ; - } + + if (count($versionWithPublication) > 1 || !$versionPublicationId) { + return; + } + + if (!$this->isCalameoApiConfigured()) { + $this->logger->warning(sprintf( + '[Calameo] Suppression distante ignorée pour la publication "%s" : API key/secret non configurés.', + $versionPublicationId + )); + + return; + } + + if (!$this->isCalameoDeleteBookEnable()) { + $this->logger->warning(sprintf( + '[Calameo] Suppression distante ignorée pour la publication "%s" : delete_book_enable=false.', + $versionPublicationId + )); + + return; + } + + try { + $this->publicationRepository->deletePublication($versionPublicationId); + } catch (UnknownBookIDException) { + return; + } catch (ApiResponseErrorException|GuzzleException $exception) { + $this->logger->error(sprintf( + '[Calameo] Impossible de supprimer la publication distante "%s" : %s', + $versionPublicationId, + $exception->getMessage() + )); + + return; } } @@ -165,27 +176,64 @@ public function hasFieldData(): bool * @param VersionInfo $versionInfo * @param Field $field * @param array $context - * @return \eZ\Publish\SPI\Search\Field[]|void + * @return \Ibexa\Contracts\Core\Search\Field[]|void */ public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) { } - public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) - { -// if ($field->id !== $originalField->id) { -// var_dump([ -// $versionInfo, -// $field, -// $originalField, -// $context -// ]); -// die; -// } - if ($originalField->value->externalData === null) { + public function copyLegacyField( + VersionInfo $versionInfo, + Field $field, + Field $originalField, + array $context = [] + ): bool { + $externalData = $originalField->value->externalData ?? null; + + if (!is_array($externalData)) { + return false; + } + + if (empty($externalData['publicationId']) || empty($externalData['folderId'])) { return false; } - return $this->gateway->storePublicationReference($versionInfo, $field); + $field->value->externalData = [ + 'publicationId' => $externalData['publicationId'], + 'folderId' => $externalData['folderId'], + ]; + + $this->gateway->storePublicationReference($versionInfo, $field); + + return true; + } + + private function isCalameoApiConfigured(): bool + { + return $this->getCalameoConfigString('api.key') !== '' + && $this->getCalameoConfigString('api.secret') !== ''; + } + + private function isCalameoDeleteBookEnable(): bool + { + return $this->getCalameoConfigString('delete_book_enable') === '1'; + } + + private function getCalameoConfigString(string $name): string + { + try { + $value = $this->configResolver->getParameter( + sprintf('calameo.%s', $name), + 'almaviacx' + ); + } catch (\Throwable) { + return ''; + } + + if (!is_scalar($value)) { + return ''; + } + + return trim((string) $value); } } diff --git a/src/lib/Ez/FieldType/CalameoPublication/FormMapper.php b/src/lib/Ez/FieldType/CalameoPublication/FormMapper.php index 958284c..bfa72e6 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/FormMapper.php +++ b/src/lib/Ez/FieldType/CalameoPublication/FormMapper.php @@ -15,47 +15,27 @@ use AlmaviaCX\Calameo\API\Repository\AccountRepository; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; use AlmaviaCX\Calameo\Ez\Form\Type\FieldType\CalameoPublicationFieldType; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\Core\FieldType\BinaryFile\Value; -use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface; -use EzSystems\RepositoryForms\Data\Content\FieldData; -use EzSystems\RepositoryForms\Data\FieldDefinitionData; -use EzSystems\RepositoryForms\FieldType\DataTransformer\BinaryFileValueTransformer; -use EzSystems\RepositoryForms\FieldType\FieldDefinitionFormMapperInterface; -use EzSystems\RepositoryForms\FieldType\FieldValueFormMapperInterface; -use EzSystems\RepositoryForms\Form\Type\FieldType\BinaryFileFieldType; -use GuzzleHttp\Exception\GuzzleException; +use Ibexa\AdminUi\FieldType\FieldDefinitionFormMapperInterface; +use Ibexa\AdminUi\Form\Data\FieldDefinitionData; +use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface; +use Ibexa\Contracts\ContentForms\Data\Content\FieldData; +use Ibexa\Contracts\ContentForms\FieldType\FieldValueFormMapperInterface; +use Ibexa\Contracts\Core\Repository\FieldTypeService; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class FormMapper implements FieldValueFormMapperInterface, FieldDefinitionFormMapperInterface { - /** @var FieldTypeService */ - protected $fieldTypeService; - - /** @var AccountRepository */ - protected $accountRepository; - - /** @var NotificationHandlerInterface */ - protected $notificationHandler; - - /** - * @param FieldTypeService $fieldTypeService - * @param AccountRepository $accountRepository - * @param NotificationHandlerInterface $notificationHandler - */ public function __construct( - FieldTypeService $fieldTypeService, - AccountRepository $accountRepository, - NotificationHandlerInterface $notificationHandler + protected readonly FieldTypeService $fieldTypeService, + protected readonly AccountRepository $accountRepository, + protected readonly NotificationHandlerInterface $notificationHandler ) { - $this->fieldTypeService = $fieldTypeService; - $this->accountRepository = $accountRepository; - $this->notificationHandler = $notificationHandler; } - public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data) + // f + public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data): void { $folderChoices = []; $offset = 0; @@ -90,9 +70,9 @@ public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, Field ); } - public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) + public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data): void { - $fieldDefinition = $data->fieldDefinition; + $fieldDefinition = $data->getFieldDefinition(); $formConfig = $fieldForm->getConfig(); $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->fieldTypeIdentifier); @@ -119,7 +99,7 @@ public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) ); } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults( diff --git a/src/lib/Ez/FieldType/CalameoPublication/Gateway/DoctrineStorage.php b/src/lib/Ez/FieldType/CalameoPublication/Gateway/DoctrineStorage.php index a31bfe3..532d7ce 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/Gateway/DoctrineStorage.php +++ b/src/lib/Ez/FieldType/CalameoPublication/Gateway/DoctrineStorage.php @@ -13,17 +13,17 @@ namespace AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Gateway; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\FieldType\StorageGateway; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; use PDO; class DoctrineStorage extends StorageGateway { - /** @var Connection */ - protected $connection; + protected Connection $connection; /** * DoctrineStorage constructor. @@ -37,12 +37,13 @@ public function __construct(Connection $connection) /** * Store the file reference in $field for $versionNo. * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param VersionInfo $versionInfo + * @param Field $field * * @return bool + * @throws Exception */ - public function storePublicationReference(VersionInfo $versionInfo, Field $field) + public function storePublicationReference(VersionInfo $versionInfo, Field $field): bool { $referencedData = $this->getPublicationReferenceData($field->id, $versionInfo->versionNo); @@ -62,48 +63,49 @@ public function storePublicationReference(VersionInfo $versionInfo, Field $field * add additional columns to be set in the database. Please do not forget * to call the parent when overwriting this method. * - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param QueryBuilder $queryBuilder + * @param VersionInfo $versionInfo + * @param Field $field */ - protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field): void { $queryBuilder ->setValue('contentobject_attribute_id', ':fieldId') ->setValue('publication_id', ':publicationId') ->setValue('folder_id', ':folderId') ->setValue('version', ':versionNo') - ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) - ->setParameter(':publicationId', $field->value->externalData['publicationId'], PDO::PARAM_STR) - ->setParameter(':folderId', $field->value->externalData['folderId'], PDO::PARAM_INT) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $field->id, PDO::PARAM_INT) + ->setParameter('publicationId', $field->value->externalData['publicationId'], PDO::PARAM_STR) + ->setParameter('folderId', $field->value->externalData['folderId'], PDO::PARAM_INT) + ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) ; } /** - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param QueryBuilder $queryBuilder + * @param VersionInfo $versionInfo + * @param Field $field */ - protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field): void { $queryBuilder ->set('contentobject_attribute_id', ':fieldId') ->set('publication_id', ':publicationId') ->set('folder_id', ':folderId') ->set('version', ':versionNo') - ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) - ->setParameter(':publicationId', $field->value->externalData['publicationId'], PDO::PARAM_STR) - ->setParameter(':folderId', $field->value->externalData['folderId'], PDO::PARAM_INT) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $field->id, PDO::PARAM_INT) + ->setParameter('publicationId', $field->value->externalData['publicationId'], PDO::PARAM_STR) + ->setParameter('folderId', $field->value->externalData['folderId'], PDO::PARAM_INT) + ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) ; } /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param VersionInfo $versionInfo + * @param Field $field + * @throws Exception */ - protected function updateFieldData(VersionInfo $versionInfo, Field $field) + protected function updateFieldData(VersionInfo $versionInfo, Field $field): void { $updateQuery = $this->connection->createQueryBuilder(); $updateQuery->update( @@ -124,18 +126,19 @@ protected function updateFieldData(VersionInfo $versionInfo, Field $field) ) ) ) - ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $field->id, PDO::PARAM_INT) + ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) ; $updateQuery->execute(); } /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param VersionInfo $versionInfo + * @param Field $field + * @throws Exception */ - protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) + protected function storeNewFieldData(VersionInfo $versionInfo, Field $field): void { $insertQuery = $this->connection->createQueryBuilder(); $insertQuery->insert( @@ -155,7 +158,7 @@ protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) * * @return mixed */ - protected function castToPropertyValue($value, $columnName) + protected function castToPropertyValue(mixed $value, string $columnName): mixed { $propertyMap = $this->getPropertyMapping(); $castFunction = $propertyMap[$columnName]['cast']; @@ -170,11 +173,11 @@ protected function castToPropertyValue($value, $columnName) * add additional columns to be fetched from the database. Please do not * forget to call the parent when overwriting this method. * - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder + * @param QueryBuilder $queryBuilder * @param int $fieldId * @param int $versionNo */ - protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) + protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo): void { $queryBuilder->select( $this->connection->quoteIdentifier('publication_id'), @@ -189,8 +192,9 @@ protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versio * @param int $versionNo * * @return array|null + * @throws Exception */ - public function getPublicationReferenceData(int $fieldId, int $versionNo) + public function getPublicationReferenceData(int $fieldId, int $versionNo): ?array { $selectQuery = $this->connection->createQueryBuilder(); @@ -210,8 +214,8 @@ public function getPublicationReferenceData(int $fieldId, int $versionNo) ) ) ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) ; $statement = $selectQuery->execute(); @@ -236,8 +240,9 @@ public function getPublicationReferenceData(int $fieldId, int $versionNo) * @param array $fieldIds * * @return array + * @throws Exception */ - public function getReferencedPublications(array $fieldIds) + public function getReferencedPublications(array $fieldIds): array { if (empty($fieldIds)) { return []; @@ -256,7 +261,7 @@ public function getReferencedPublications(array $fieldIds) ':fieldIds' ) ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY); + ->setParameter('fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY); $statement = $selectQuery->execute(); $publicationIds = []; @@ -274,7 +279,7 @@ public function getReferencedPublications(array $fieldIds) * * @return string */ - protected function toPropertyName($columnName) + protected function toPropertyName(string $columnName): string { $propertyMap = $this->getPropertyMapping(); @@ -286,8 +291,9 @@ protected function toPropertyName($columnName) * * @param array $fieldIds * @param int $versionNo + * @throws Exception */ - public function removePublicationReferences(array $fieldIds, $versionNo) + public function removePublicationReferences(array $fieldIds, int $versionNo): void { if (empty($fieldIds)) { return; @@ -308,8 +314,8 @@ public function removePublicationReferences(array $fieldIds, $versionNo) ) ) ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) ; $deleteQuery->execute(); @@ -320,7 +326,7 @@ public function removePublicationReferences(array $fieldIds, $versionNo) * * @return array */ - protected function getPropertyMapping() + protected function getPropertyMapping(): array { return [ 'publication_id' => [ diff --git a/src/lib/Ez/FieldType/CalameoPublication/LegacyConverter.php b/src/lib/Ez/FieldType/CalameoPublication/LegacyConverter.php index 557e4ef..2190fa3 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/LegacyConverter.php +++ b/src/lib/Ez/FieldType/CalameoPublication/LegacyConverter.php @@ -12,11 +12,11 @@ namespace AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; class LegacyConverter implements Converter { @@ -29,20 +29,20 @@ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { } - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef): void { $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; $storageDef->dataText1 = implode('|', $fieldSettings['availableFolderIds'] ?? []); } - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef): void { $fieldDef->fieldTypeConstraints->fieldSettings = [ 'availableFolderIds' => explode('|', $storageDef->dataText1 ?? "") ]; } - public function getIndexColumn() + public function getIndexColumn(): string { return 'sort_key_string'; } diff --git a/src/lib/Ez/FieldType/CalameoPublication/Type.php b/src/lib/Ez/FieldType/CalameoPublication/Type.php index ef21dde..279c1b4 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/Type.php +++ b/src/lib/Ez/FieldType/CalameoPublication/Type.php @@ -12,15 +12,13 @@ namespace AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication; -use AlmaviaCX\Calameo\API\Value\Publication; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Nameable; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue as PersistenceValue; +use Ibexa\Contracts\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue as PersistenceValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\FieldType\FieldType; +use Ibexa\Core\FieldType\ValidationError; use RuntimeException; class Type extends FieldType @@ -35,7 +33,7 @@ class Type extends FieldType /** * @inheritDoc */ - public function validateFieldSettings($fieldSettings) + public function validateFieldSettings($fieldSettings): array { $validationErrors = []; @@ -99,23 +97,26 @@ protected function checkValueStructure(BaseValue $value) } } - public function getFieldTypeIdentifier() + public function getFieldTypeIdentifier(): string { return "calameo_publication"; } /** - * @param Value $value - * @return string|void + * @param BaseValue $value + * @param FieldDefinition $fieldDefinition + * @param string $languageCode + * @return string */ - public function getName(SPIValue $value) + public function getName(BaseValue $value, FieldDefinition $fieldDefinition, string $languageCode): string { + // return (string)$value->text; throw new RuntimeException( 'Name generation provided via NameableField set via "ezpublish.fieldType.nameable" service tag' ); } - public function getEmptyValue() + public function getEmptyValue(): Value { return new Value(); } @@ -133,7 +134,7 @@ public function fromHash($hash) * @param Value $value * @return array */ - public function toHash(SPIValue $value) + public function toHash(SPIValue $value): ?array { if ($this->isEmptyValue($value)) { return null; @@ -147,9 +148,9 @@ public function toHash(SPIValue $value) /** * @param Value $value - * @return string|null + * @return false */ - protected function getSortInfo(BaseValue $value) + protected function getSortInfo(BaseValue $value): bool { return false; } @@ -183,6 +184,6 @@ public function fromPersistenceValue(PersistenceValue $fieldValue): Value return $this->getEmptyValue(); } - return new Value($fieldValue->externalData ?? []); + return new Value($fieldValue->externalData); } } diff --git a/src/lib/Ez/FieldType/CalameoPublication/Value.php b/src/lib/Ez/FieldType/CalameoPublication/Value.php index 51882e4..4603e08 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/Value.php +++ b/src/lib/Ez/FieldType/CalameoPublication/Value.php @@ -13,56 +13,25 @@ namespace AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication; use AlmaviaCX\Calameo\API\Value\Publication; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Core\FieldType\Value as BaseValue; class Value extends BaseValue { - /** - * @var string|null - */ - public $publicationId; - - /** - * @var int|null - */ - public $folderId; - + public ?string $publicationId = null; + public ?int $folderId = null; /** * Input file URI, as a path to a file on a disk. * * @var string|null */ - public $inputUri; - - /** - * @var \Closure - */ - public $publicationLoader; - - /** @var Publication|null */ - protected $publication; + public ?string $inputUri = null; + protected ?Publication $publication = null; /** * Returns a string representation of the field value. - * - * @return string */ public function __toString(): string { return $this->publicationId; } - - /** - * @inheritDoc - */ - public function __get($property) - { - if ($property === "publication") { - if (!$this->publication && $this->publicationLoader) { - $loader = $this->publicationLoader; - $this->publication = $loader(); - } - } - return parent::__get($property); // TODO: Change the autogenerated stub - } } diff --git a/src/lib/Ez/FieldType/CalameoPublication/ValueTransformer.php b/src/lib/Ez/FieldType/CalameoPublication/ValueTransformer.php index 718d8bf..ad86521 100644 --- a/src/lib/Ez/FieldType/CalameoPublication/ValueTransformer.php +++ b/src/lib/Ez/FieldType/CalameoPublication/ValueTransformer.php @@ -12,25 +12,15 @@ namespace AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication; -use AlmaviaCX\Calameo\API\Value\Publication; -use eZ\Publish\API\Repository\FieldType; +use Ibexa\Contracts\Core\Repository\FieldType; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\HttpFoundation\File\UploadedFile; class ValueTransformer implements DataTransformerInterface { - /** @var FieldType */ - protected $fieldType; + protected FieldType $fieldType; + protected Value $initialValue; - /** @var Value */ - protected $initialValue; - - /** - * ValueTransformer constructor. - * @param FieldType $fieldType - * @param Value $initialValue - */ public function __construct(FieldType $fieldType, Value $initialValue) { $this->fieldType = $fieldType; @@ -38,7 +28,7 @@ public function __construct(FieldType $fieldType, Value $initialValue) } /** - * @param Value $value + * @param Value|null $value * @return array */ public function transform($value): array @@ -56,10 +46,10 @@ public function transform($value): array } /** - * @param array $value + * @param array|null $value * @return Value */ - public function reverseTransform($value) + public function reverseTransform($value): mixed { if (!is_array($value)) { throw new TransformationFailedException(sprintf('Expected a array got %s', gettype($value))); diff --git a/src/lib/Ez/Form/Type/FieldType/CalameoPublicationFieldType.php b/src/lib/Ez/Form/Type/FieldType/CalameoPublicationFieldType.php index 4166a28..5f578f9 100644 --- a/src/lib/Ez/Form/Type/FieldType/CalameoPublicationFieldType.php +++ b/src/lib/Ez/Form/Type/FieldType/CalameoPublicationFieldType.php @@ -13,12 +13,11 @@ namespace AlmaviaCX\Calameo\Ez\Form\Type\FieldType; use AlmaviaCX\Calameo\API\Repository\AccountRepository; -use AlmaviaCX\Calameo\API\Repository\FolderRepository; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; use AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Value; -use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface; -use EzSystems\RepositoryForms\Form\Type\FieldType\BinaryBaseFieldType; -use GuzzleHttp\Exception\GuzzleException; +use Ibexa\ContentForms\Form\Type\FieldType\BinaryBaseFieldType; +use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface; +use JMS\TranslationBundle\Annotation\Desc; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -31,33 +30,24 @@ class CalameoPublicationFieldType extends AbstractType { - /** @var AccountRepository */ - protected $accountRepository; - - /** @var NotificationHandlerInterface */ - protected $notificationHandler; - - /** - * @param AccountRepository $accountRepository - * @param NotificationHandlerInterface $notificationHandler - */ - public function __construct(AccountRepository $accountRepository, NotificationHandlerInterface $notificationHandler) + public function __construct( + protected readonly AccountRepository $accountRepository, + protected readonly NotificationHandlerInterface $notificationHandler + ) { - $this->accountRepository = $accountRepository; - $this->notificationHandler = $notificationHandler; } - public function getName() + public function getName(): string { return $this->getBlockPrefix(); } - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'ezplatform_fieldtype_calameo_publication'; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $folderChoices = []; $filteredFolderChoices = []; @@ -66,7 +56,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) do { try { $availableFolders = $this->accountRepository->fetchAccountFolders($limit, $offset); - } catch (ApiResponseErrorException $exception) { + } catch (\GuzzleHttp\Exception\ClientException|ApiResponseErrorException $exception) { $this->notificationHandler->error( sprintf("[Calameo] %s", $exception->getMessage()) ); @@ -135,12 +125,12 @@ static function (FormEvent $event) use ($folderChoices) { ); } - public function getParent() + public function getParent(): string { return BinaryBaseFieldType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults( [ diff --git a/src/lib/Ez/Twig/CalameoTwigExtension.php b/src/lib/Ez/Twig/CalameoTwigExtension.php index 9147092..a42e1f3 100644 --- a/src/lib/Ez/Twig/CalameoTwigExtension.php +++ b/src/lib/Ez/Twig/CalameoTwigExtension.php @@ -9,27 +9,29 @@ namespace AlmaviaCX\Calameo\Ez\Twig; +use AlmaviaCX\Calameo\API\Repository\PublicationRepository; use AlmaviaCX\Calameo\API\Value\Publication; use AlmaviaCX\Calameo\Exception\ApiResponseErrorException; +use AlmaviaCX\Calameo\Exception\Response\UnknownBookIDException; use AlmaviaCX\Calameo\Ez\FieldType\CalameoPublication\Value; -use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface; +use Ibexa\Contracts\AdminUi\Notification\NotificationHandlerInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; class CalameoTwigExtension extends AbstractExtension { - /** @var NotificationHandlerInterface */ - protected $notificationHandler; - - /** - * @param NotificationHandlerInterface $notificationHandler - */ - public function __construct(NotificationHandlerInterface $notificationHandler) - { + protected NotificationHandlerInterface $notificationHandler; + protected PublicationRepository $publicationRepository; + + public function __construct( + NotificationHandlerInterface $notificationHandler, + PublicationRepository $publicationRepository + ) { + $this->publicationRepository = $publicationRepository; $this->notificationHandler = $notificationHandler; } - public function getFunctions() + public function getFunctions(): array { return [ new TwigFunction('loadCalameoPublication', [$this, 'loadCalameoPublication']), @@ -38,12 +40,22 @@ public function getFunctions() public function loadCalameoPublication(Value $value): ?Publication { - try { - return $value->publication; - } catch (ApiResponseErrorException $exception) { - $this->notificationHandler->error( - sprintf("[Calameo] %s", $exception->getMessage()) - ); + if ($value->folderId && $value->publicationId) { + try { + return $this->publicationRepository->getPublicationInfos($value->publicationId); + } catch (UnknownBookIDException $unknownBookIDException) { + $this->notificationHandler->warning( + sprintf("[Calameo][UnknownBookIDException] %s", $unknownBookIDException->getMessage()) // Unknown book + ); + } catch (ApiResponseErrorException $exception) { + $this->notificationHandler->error( + sprintf("[Calameo][ApiResponseErrorException] %s", $exception->getMessage()) + ); + } catch (\Exception $exception) { + $this->notificationHandler->error( + sprintf("[Calameo] %s", $exception->getMessage()) + ); + } } return null; }