From 35cf592a863efcfb43bc164df1696ffbb481a2fc Mon Sep 17 00:00:00 2001
From: Daniel Kestler <>
Date: Thu, 8 Sep 2022 08:00:43 +0200
Subject: [PATCH 1/4] FEATURE: Implement actual Canto tags to be used as Neos
tags
---
Classes/AssetSource/CantoAssetProxyQuery.php | 4 ++
Classes/Command/CantoCommandController.php | 56 ++++++++++++++++++++
Classes/Service/CantoClient.php | 25 +++++++++
Configuration/Settings.yaml | 7 +++
4 files changed, 92 insertions(+)
diff --git a/Classes/AssetSource/CantoAssetProxyQuery.php b/Classes/AssetSource/CantoAssetProxyQuery.php
index 1b58e0a..e82d663 100644
--- a/Classes/AssetSource/CantoAssetProxyQuery.php
+++ b/Classes/AssetSource/CantoAssetProxyQuery.php
@@ -248,6 +248,10 @@ public function prepareTagQuery(): void
}
}
}
+
+ if (!empty($this->mapping['tags'])) {
+ $this->tagQuery .= '&tags="' . $this->activeTag->getLabel() . '"';
+ }
}
/**
diff --git a/Classes/Command/CantoCommandController.php b/Classes/Command/CantoCommandController.php
index 334ed1c..6376a3f 100644
--- a/Classes/Command/CantoCommandController.php
+++ b/Classes/Command/CantoCommandController.php
@@ -207,4 +207,60 @@ public function importCustomFieldsAsCollectionsAndTagsCommand(string $assetSourc
!$quiet && $this->outputLine('Import done.');
}
+
+ /**
+ * Import Canto Tags as Tags
+ *
+ * @param string $assetSourceIdentifier Name of the canto asset source
+ * @param bool $quiet If set, only errors will be displayed.
+ * @throws GuzzleException
+ * @throws IllegalObjectTypeException
+ * @throws OAuthClientException
+ * @throws StopCommandException
+ * @throws AuthenticationFailedException
+ */
+ public function importTagsCommand(string $assetSourceIdentifier = CantoAssetSource::ASSET_SOURCE_IDENTIFIER, bool $quiet = true): void
+ {
+ !$quiet && $this->outputLine('Importing tags via Canto API');
+
+ $tagMapping = $this->mapping['tags'];
+ if (empty($tagMapping)) {
+ $this->outputLine('No tags mapping configured');
+ $this->quit(1);
+ }
+
+ try {
+ /** @var CantoAssetSource $cantoAssetSource */
+ $cantoAssetSource = $this->assetSourceService->getAssetSources()[$assetSourceIdentifier];
+ $cantoClient = $cantoAssetSource->getCantoClient();
+ $cantoClient->allowClientCredentialsAuthentication(true);
+ } catch (\Exception) {
+ $this->outputLine('Canto client could not be created');
+ $this->quit(1);
+ }
+
+ $tags = $cantoClient->getFacetValues($tagMapping['field']);
+ $relevantTags = array_chunk($tags, $tagMapping['limit']);
+ foreach ($relevantTags[0] as $tagLabel) {
+
+ if (!empty($tagMapping['include']) && !in_array($tagLabel, $tagMapping['include'], true)) {
+ continue;
+ }
+ if (!empty($tagMapping['exclude']) && in_array($tagLabel, $tagMapping['exclude'], true)) {
+ continue;
+ }
+
+ $tag = $this->tagRepository->findOneByLabel($tagLabel);
+
+ if ($tag === null) {
+ $tag = new Tag($tagLabel);
+ $this->tagRepository->add($tag);
+ !$quiet && $this->outputLine(' + %s', [$tagLabel]);
+ }
+
+ !$quiet && $this->outputLine();
+ }
+
+ !$quiet && $this->outputLine('Import done.');
+ }
}
diff --git a/Classes/Service/CantoClient.php b/Classes/Service/CantoClient.php
index 6aa5b7f..21be234 100644
--- a/Classes/Service/CantoClient.php
+++ b/Classes/Service/CantoClient.php
@@ -240,6 +240,31 @@ public function getCustomFields(): array
return [];
}
+ /**
+ * @throws AuthenticationFailedException
+ * @throws GuzzleException
+ * @throws HttpException
+ * @throws IdentityProviderException
+ * @throws MissingActionNameException
+ * @throws MissingClientSecretException
+ * @throws OAuthClientException
+ * @todo perhaps cache the result
+ */
+ public function getFacetValues(string $facetName): array
+ {
+ $response = $this->sendAuthenticatedRequest('search');
+ if ($response->getStatusCode() === 200) {
+ $result = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
+
+ foreach ($result['facets'] as $facet) {
+ if ($facet['name'] === $facetName) {
+ return $facet['value'];
+ }
+ }
+ }
+ return [];
+ }
+
/**
* @throws AuthenticationFailedException
* @throws GuzzleException
diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml
index 113c791..8e97b3b 100644
--- a/Configuration/Settings.yaml
+++ b/Configuration/Settings.yaml
@@ -1,8 +1,15 @@
Flownative:
Canto:
+ # At the moment, this is "either or", so use custom fields or tags for mapping, not both
mapping:
# map "Custom Fields" from Canto to Neos
customFields: []
+ # map "Tags" from Canto to Neos
+ tags:
+ field: 'tags'
+ limit: 20
+ include: []
+ exclude: ['Untagged']
webhook:
pathPrefix: '/flownative-canto/webhook/'
# A token that can be used to secure webhook invocations; used only if set
From 7d946fcb804a8670b325f21ce7353f905867808f Mon Sep 17 00:00:00 2001
From: Daniel Kestler <>
Date: Fri, 23 Sep 2022 11:40:26 +0200
Subject: [PATCH 2/4] Bugfix remove tags output for now
---
Classes/AssetSource/CantoAssetProxy.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Classes/AssetSource/CantoAssetProxy.php b/Classes/AssetSource/CantoAssetProxy.php
index 97ab72b..bfe4828 100644
--- a/Classes/AssetSource/CantoAssetProxy.php
+++ b/Classes/AssetSource/CantoAssetProxy.php
@@ -202,7 +202,8 @@ public function getLocalAssetIdentifier(): ?string
public function getTags(): array
{
- return $this->tags;
+ return [];
+ // return $this->tags;
}
public function isImported(): bool
From 1182e36ccdb381219285b45819dc0cc60fc0a32a Mon Sep 17 00:00:00 2001
From: Daniel Kestler <>
Date: Fri, 23 Sep 2022 11:46:51 +0200
Subject: [PATCH 3/4] Bugfix remove tag in object assignment
---
Classes/AssetSource/CantoAssetProxy.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/Classes/AssetSource/CantoAssetProxy.php b/Classes/AssetSource/CantoAssetProxy.php
index bfe4828..e33cce2 100644
--- a/Classes/AssetSource/CantoAssetProxy.php
+++ b/Classes/AssetSource/CantoAssetProxy.php
@@ -81,7 +81,6 @@ public static function fromJsonObject(stdClass $jsonObject, CantoAssetSource $as
$assetProxy->lastModified = \DateTime::createFromFormat('YmdHisv', $jsonObject->default->{'Date modified'});
$assetProxy->fileSize = (int)$jsonObject->size;
$assetProxy->mediaType = MediaTypes::getMediaTypeFromFilename($jsonObject->name);
- $assetProxy->tags = $jsonObject->tag ?? [];
$assetProxy->iptcProperties['CopyrightNotice'] = $jsonObject->copyright ?? ($jsonObject->default->Copyright ?? '');
From 18dd39a4890fe91a694e0351b45bfa19e9efda45 Mon Sep 17 00:00:00 2001
From: Daniel Kestler <>
Date: Wed, 19 Oct 2022 10:48:36 +0200
Subject: [PATCH 4/4] Bugfix: OAuth authorization flow not working
---
Classes/Service/CantoClient.php | 2 +-
Resources/Private/Templates/Canto/Index.html | 8 ++++++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/Classes/Service/CantoClient.php b/Classes/Service/CantoClient.php
index 21be234..15a29a2 100644
--- a/Classes/Service/CantoClient.php
+++ b/Classes/Service/CantoClient.php
@@ -148,7 +148,7 @@ private function getCurrentUri(): UriInterface
private function redirectToUri(string $uri): void
{
header('Location: ' . $uri);
- throw new StopActionException('Canto login required', 1625222167);
+ throw new AuthenticationFailedException('Canto login required', 1625222167);
}
/**
diff --git a/Resources/Private/Templates/Canto/Index.html b/Resources/Private/Templates/Canto/Index.html
index c3611ed..5e7fc5c 100644
--- a/Resources/Private/Templates/Canto/Index.html
+++ b/Resources/Private/Templates/Canto/Index.html
@@ -2,7 +2,7 @@
{neos:backend.translate(id: 'cantoConnectionSettings', source: 'Main', package: 'Flownative.Canto')}
-
+
- {authenticationError}
+
+
{authenticationError}
+
+