Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5245364
feat: update composer php requirements
erdnaxelaweb Apr 7, 2023
fe223b0
try to fix
RemyNovactive Apr 26, 2023
01b7fce
try to fix
RemyNovactive Apr 26, 2023
29a6d27
Merge pull request #1 from Novactive/main-ibx4-fix
yhanini Apr 27, 2023
7c314c7
try to fix
RemyNovactive Apr 27, 2023
cb06bfb
try to fix
RemyNovactive Apr 27, 2023
e12a039
try to fix
RemyNovactive Apr 28, 2023
41628ad
try to fix
RemyNovactive Apr 28, 2023
40f0c95
remove TODO
RemyNovactive Apr 28, 2023
e3b075e
Merge pull request #2 from Novactive/main-ibx4-fix
yhanini Apr 28, 2023
21d2998
ibexadesign
RemyNovactive May 4, 2023
d7e7259
feat: ibx4 migration
erdnaxelaweb May 26, 2023
ccbc62a
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 2, 2023
8ba5b13
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 2, 2023
1b8f153
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 2, 2023
040fae6
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 2, 2023
397b7dc
Merge pull request #4 from Novactive/fix-111471-MIG-GOUV
RemyNovactive Nov 2, 2023
9481c36
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 7, 2023
ad09c80
#111471 - [MIG-GOUV] Creation de contenu : dysfonctionnement dans la …
RemyNovactive Nov 7, 2023
543b175
Merge pull request #5 from Novactive/fix-111471-MIG-GOUV2
RemyNovactive Nov 7, 2023
b32e325
Add composer.lock to .gitignore
RemyNovactive Nov 7, 2023
64c1dc2
Merge remote-tracking branch 'origin/main-ibx4' into main-ibx4
RemyNovactive Nov 7, 2023
4e0a2e9
Add .idea to .gitignore
RemyNovactive Nov 7, 2023
df5de14
Set Calameo optional (#6)
RemyNovactive Dec 1, 2023
0c25e3f
Remove lazy
RemyNovactive Dec 3, 2024
9284599
Merge branch 'main-ibx4' into remove-lazy
RemyNovactive Dec 5, 2024
a157a7a
Merge pull request #7 from Novactive/remove-lazy
RemyNovactive Dec 5, 2024
e545a49
Ibexa5
RemyNovactive Feb 10, 2026
9a86ce6
symfony/var-exporter
RemyNovactive Feb 11, 2026
15dfccd
symfony/var-exporter
RemyNovactive Feb 11, 2026
f960d42
Ibexa 5
RemyNovactive Feb 19, 2026
68d3e58
Catch all exception in CalameoTwigExtension
RemyNovactive Mar 6, 2026
c906ef8
Merge branch 'refs/heads/main-ibx4' into main-ibx5
RemyNovactive Mar 6, 2026
9b882f2
Fix Client error: `GET http://api.calameo.com/1.0?order=Name&way=UP&s…
RemyNovactive Apr 2, 2026
6534681
Gestion d'exception
RemyNovactive Apr 2, 2026
e892121
Fix AlmaviaCX\Calameo\API\HttpClient
RemyNovactive Jun 3, 2026
cae533c
services.yml
RemyNovactive Jun 3, 2026
f78edbb
code
RemyNovactive Jun 3, 2026
5d3e318
services.yml
RemyNovactive Jun 3, 2026
2439230
Fix ->setParameter(':
RemyNovactive Jun 8, 2026
c1e7087
add CalameoController.php
RemyNovactive Jul 1, 2026
fdfed57
add link to https://www.calameo.com/account/dashboard
RemyNovactive Jul 1, 2026
9a5c7c0
Fix JS
RemyNovactive Jul 1, 2026
87cd5e2
Add almaviacx.calameo.delete_book_enable
RemyNovactive Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/vendor
/composer.lock
/.idea
29 changes: 17 additions & 12 deletions composer.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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": {
Expand All @@ -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
}
}
}
14 changes: 11 additions & 3 deletions src/bundles/APIBundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,14 +16,15 @@ services:
public: false

AlmaviaCX\Calameo\API\HttpClient:
lazy: true
arguments:
$APIKey: "%almaviacx.calameo.api.key%"
$APISecret: "%almaviacx.calameo.api.secret%"
$config: "%almaviacx.calameo.http_client.config%"

AlmaviaCX\Calameo\API\Serializer:
arguments:
$baseSerializer: "@jms_serializer"
$baseSerializer: '@JMS\Serializer\SerializerInterface'

AlmaviaCX\Calameo\API\Gateway\:
resource: "../../../../lib/API/Gateway/*"
Expand All @@ -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%"
272 changes: 272 additions & 0 deletions src/bundles/EzBundle/Controller/Admin/CalameoController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
<?php

declare(strict_types=1);

namespace AlmaviaCX\Bundle\Calameo\EzBundle\Controller\Admin;

use Doctrine\DBAL\Connection;
use Ibexa\ActivityLog\REST\Input\Parser\SortClause\SortClause;
use Ibexa\Contracts\Core\Repository\ContentTypeService;
use Ibexa\Contracts\Core\Repository\SearchService;
use Ibexa\Contracts\Core\Repository\Values\Content\Query;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/admin/calameo', name: 'almaviacx_calameo_admin_')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
final class CalameoController extends AbstractController
{
private const string FIELD_TYPE_IDENTIFIER = 'calameo_publication';
private const string TABLE_NAME = 'calameo_publication';

public function __construct(
private readonly Connection $connection,
private readonly ContentTypeService $contentTypeService,
private readonly SearchService $searchService,
private readonly string $APIKey,
private readonly string $APISecret,
private readonly array $config,
private readonly bool $deleteBookEnable,
) {
}

#[Route('', name: 'index', methods: ['GET'])]
public function index(): Response
{
$tableExists = $this->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(
<<<SQL
SELECT
contentobject_attribute_id,
version,
publication_id,
folder_id
FROM calameo_publication
ORDER BY contentobject_attribute_id DESC, version DESC
SQL,
);

return $this->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(
<<<SQL
SELECT
publication_id,
folder_id
FROM calameo_publication
WHERE contentobject_attribute_id = :field_id
AND version = :version
SQL,
[
'field_id' => $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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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',
];

Expand Down
2 changes: 1 addition & 1 deletion src/bundles/EzBundle/Resources/config/field_templates.yml
Original file line number Diff line number Diff line change
@@ -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 }
Loading