Skip to content

IBX-9594: Describe creating a custom DAM connector #2642

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 24 commits into from
Apr 15, 2025
Merged

IBX-9594: Describe creating a custom DAM connector #2642

merged 24 commits into from
Apr 15, 2025

Conversation

dabrt
Copy link
Contributor

@dabrt dabrt commented Feb 26, 2025

Question Answer
JIRA Ticket IBX-9594
Versions 4.6 and up
Edition all

Describe creating a custom DAM connector based on an example from @adriendupuis :
https://github.com/adriendupuis/ezplatform-connector-commons

Checklist

  • Text renders correctly
  • Text has been checked with vale
  • Description metadata is up to date
  • Redirects cover removed/moved pages
  • Code samples are working
  • PHP code samples have been fixed with PHP CS fixer
  • Added link to this PR in relevant JIRA ticket or code PR

@dabrt dabrt requested a review from vidarl March 4, 2025 08:19
@dabrt dabrt requested a review from ViniTou March 5, 2025 16:36
@dabrt dabrt requested a review from vidarl March 11, 2025 14:21
@dabrt
Copy link
Contributor Author

dabrt commented Mar 11, 2025

@adriendupuis I think I won't be able to fix php code without your help. PHP Stan signals that in the handler, the fetchAsset() method must not return null, same for the transformation, build() must return a transformation object but not null.

@dabrt dabrt requested a review from adriendupuis March 17, 2025 12:07
Copy link
Contributor

@adriendupuis adriendupuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #2689

* DAM/Commons: Fix PHPStan
* DAM/Commons: Fix services w/out constructor.
* DAM/Commons: Fix service class names
* DAM/Commons: Add commons search tab
* DAM/Commons: Fix type hinting then simplify WikimediaCommonsHandler

---------

Co-authored-by: Tomasz Dąbrowski <64841871+dabrt@users.noreply.github.com>
@adriendupuis adriendupuis self-requested a review April 9, 2025 13:56
Copy link
Contributor

@adriendupuis adriendupuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adriendupuis I think I won't be able to fix php code without your help. PHP Stan signals that in the handler, the fetchAsset() method must not return null, same for the transformation, build() must return a transformation object but not null.

All my changes have been applied.

  • It works on 4.6.18 Commerce; Only the tab will have to wait for .20 to appear correctly (ibexa/connector-dam#72).
  • PHPStan analysis succeeded.

Copy link
Contributor

@mnocon mnocon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works, thank you for preparing this! 🎉

A couple of suggestions from me - the AssetURI example and the translation filename are the most important ones to me

$identifier: 'commons'
$source: 'commons'
$name: 'Wikimedia Commons'
$searchFormType: 'Ibexa\Platform\Connector\Dam\Form\Search\GenericSearchType'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$searchFormType: 'Ibexa\Platform\Connector\Dam\Form\Search\GenericSearchType'
$searchFormType: 'Ibexa\Connector\Dam\Form\Search\GenericSearchType'

Same as above

dabrt and others added 3 commits April 15, 2025 12:51
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
@dabrt dabrt requested a review from mnocon April 15, 2025 11:08
Copy link
Contributor

@mnocon mnocon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/back_office/images/config/packages/views.yaml


code_samples/back_office/images/config/packages/views.yaml

docs/content_management/images/add_image_asset_from_dam.md@180:```yaml
docs/content_management/images/add_image_asset_from_dam.md@181:[[= include_file('code_samples/back_office/images/config/packages/views.yaml') =]]
docs/content_management/images/add_image_asset_from_dam.md@182:```

001⫶parameters:
002⫶ ibexa.site_access.config.<scope>.image_asset_view_defaults:
003⫶ full:
004⫶ commons:
005⫶ template: '@@ibexadesign/commons_asset_view.html.twig'
006⫶ match:
007⫶ SourceBasedViewMatcher: commons
008⫶ default:
009⫶ template: '@@ibexadesign/ui/field_type/image_asset_view.html.twig'
010⫶ match: []


code_samples/back_office/images/config/services.yaml


code_samples/back_office/images/config/services.yaml

docs/content_management/images/add_image_asset_from_dam.md@109:```yaml
docs/content_management/images/add_image_asset_from_dam.md@110:[[= include_file('code_samples/back_office/images/config/services.yaml', 9, 12) =]]
docs/content_management/images/add_image_asset_from_dam.md@111:```

001⫶ App\Connector\Dam\Handler\WikimediaCommonsHandler:
002⫶ tags:
003⫶ - { name: 'ibexa.platform.connector.dam.handler', source: 'commons' }

docs/content_management/images/add_image_asset_from_dam.md@128:```yaml
docs/content_management/images/add_image_asset_from_dam.md@129:[[= include_file('code_samples/back_office/images/config/services.yaml', 13, 16) =]]
docs/content_management/images/add_image_asset_from_dam.md@130:```

001⫶ App\Connector\Dam\Transformation\WikimediaCommonsTransformationFactory:
002⫶ tags:
003⫶ - { name: 'ibexa.platform.connector.dam.transformation_factory', source: 'commons' }

docs/content_management/images/add_image_asset_from_dam.md@147:```yaml
docs/content_management/images/add_image_asset_from_dam.md@148:[[= include_file('code_samples/back_office/images/config/services.yaml', 17, 21) =]]
docs/content_management/images/add_image_asset_from_dam.md@149:```

001⫶ commons_asset_variation_generator:
002⫶ class: Ibexa\Connector\Dam\Variation\URLBasedVariationGenerator
003⫶ tags:
004⫶ - { name: 'ibexa.platform.connector.dam.variation_generator', source: 'commons' }

docs/content_management/images/add_image_asset_from_dam.md@163:```yaml
docs/content_management/images/add_image_asset_from_dam.md@164:[[= include_file('code_samples/back_office/images/config/services.yaml', 22, 33) =]]
docs/content_management/images/add_image_asset_from_dam.md@165:```

001⫶ commons_search_tab:
002⫶ class: Ibexa\Connector\Dam\View\Search\Tab\GenericSearchTab
003⫶ public: false
004⫶ arguments:
005⫶ $identifier: 'commons'
006⫶ $source: 'commons'
007⫶ $name: 'Wikimedia Commons'
008⫶ $searchFormType: 'Ibexa\Connector\Dam\Form\Search\GenericSearchType'
009⫶ $formFactory: '@form.factory'
010⫶ tags:
011⫶ - { name: 'ibexa.admin_ui.tab', group: 'connector-dam-search' }

docs/content_management/images/images.md@217:```yaml
docs/content_management/images/images.md@217:```yaml
docs/content_management/images/images.md@218:[[= include_file('code_samples/back_office/images/config/services.yaml') =]]
docs/content_management/images/images.md@218:[[= include_file('code_samples/back_office/images/config/services.yaml', 0, 8) =]]
docs/content_management/images/images.md@219:```

001⫶services:
002⫶ # ...
003⫶ App\Controller\SvgController:
004⫶ public: true
005⫶ arguments:
006⫶ - '@ibexa.api.service.content'
007⫶ - '@ibexa.field_type.ezbinaryfile.io_service'
008⫶ - '@Ibexa\Core\Helper\TranslationHelper'


code_samples/back_office/images/src/Connector/Dam/Handler/WikimediaCommonsHandler.php

docs/content_management/images/images.md@219:```

001⫶services:
002⫶ # ...
003⫶ App\Controller\SvgController:
004⫶ public: true
005⫶ arguments:
006⫶ - '@ibexa.api.service.content'
007⫶ - '@ibexa.field_type.ezbinaryfile.io_service'
008⫶ - '@Ibexa\Core\Helper\TranslationHelper'


code_samples/back_office/images/src/Connector/Dam/Handler/WikimediaCommonsHandler.php

docs/content_management/images/add_image_asset_from_dam.md@103:```php
docs/content_management/images/add_image_asset_from_dam.md@104:[[= include_file('code_samples/back_office/images/src/Connector/Dam/Handler/WikimediaCommonsHandler.php') =]]
docs/content_management/images/add_image_asset_from_dam.md@105:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Connector\Dam\Handler;
004⫶
005⫶use Ibexa\Contracts\Connector\Dam\Asset;
006⫶use Ibexa\Contracts\Connector\Dam\AssetCollection;
007⫶use Ibexa\Contracts\Connector\Dam\AssetIdentifier;
008⫶use Ibexa\Contracts\Connector\Dam\AssetMetadata;
009⫶use Ibexa\Contracts\Connector\Dam\AssetSource;
010⫶use Ibexa\Contracts\Connector\Dam\AssetUri;
011⫶use Ibexa\Contracts\Connector\Dam\Handler\Handler as HandlerInterface;
012⫶use Ibexa\Contracts\Connector\Dam\Search\AssetSearchResult;
013⫶use Ibexa\Contracts\Connector\Dam\Search\Query;
014⫶
015⫶class WikimediaCommonsHandler implements HandlerInterface
016⫶{
017⫶ public function search(Query $query, int $offset = 0, int $limit = 20): AssetSearchResult
018⫶ {
019⫶ $searchUrl = 'https://commons.wikimedia.org/w/api.php?action=query&list=search&format=json&srnamespace=6'
020⫶ . '&srsearch=' . urlencode($query->getPhrase())
021⫶ . '&sroffset=' . $offset
022⫶ . '&srlimit=' . $limit
023⫶ ;
024⫶
025⫶ $jsonResponse = file_get_contents($searchUrl);
026⫶ if ($jsonResponse === false) {
027⫶ return new AssetSearchResult(0, new AssetCollection([]));
028⫶ }
029⫶
030⫶ $response = json_decode($jsonResponse, true);
031⫶ if (!isset($response['query']['search'])) {
032⫶ return new AssetSearchResult(0, new AssetCollection([]));
033⫶ }
034⫶
035⫶ $assets = [];
036⫶ foreach ($response['query']['search'] as $result) {
037⫶ $identifier = str_replace('File:', '', $result['title']);
038⫶ $assets[] = $this->fetchAsset($identifier);
039⫶ }
040⫶
041⫶ return new AssetSearchResult(
042⫶ (int) ($response['query']['searchinfo']['totalhits'] ?? 0),
043⫶ new AssetCollection($assets)
044⫶ );
045⫶ }
046⫶
047⫶ public function fetchAsset(string $id): Asset
048⫶ {
049⫶ $metadataUrl = 'https://commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=extmetadata&format=json'
050⫶ . '&titles=File%3a' . urlencode($id)
051⫶ ;
052⫶
053⫶ $jsonResponse = file_get_contents($metadataUrl);
054⫶ if ($jsonResponse === false) {
055⫶ throw new \RuntimeException('Couldn\'t retrieve asset metadata');
056⫶ }
057⫶
058⫶ $response = json_decode($jsonResponse, true);
059⫶ if (!isset($response['query']['pages'])) {
060⫶ throw new \RuntimeException('Couldn\'t parse asset metadata');
061⫶ }
062⫶
063⫶ $pageData = array_values($response['query']['pages'])[0] ?? null;
064⫶ if (!isset($pageData['imageinfo'][0]['extmetadata'])) {
065⫶ throw new \RuntimeException('Couldn\'t parse image asset metadata');
066⫶ }
067⫶
068⫶ $imageInfo = $pageData['imageinfo'][0]['extmetadata'];
069⫶
070⫶ return new Asset(
071⫶ new AssetIdentifier($id),
072⫶ new AssetSource('commons'),
073⫶ new AssetUri('https://commons.wikimedia.org/w/index.php?title=Special:Redirect/file/' . urlencode($id)),
074⫶ new AssetMetadata([
075⫶ 'page_url' => "https://commons.wikimedia.org/wiki/File:$id",
076⫶ 'author' => $imageInfo['Artist']['value'] ?? null,
077⫶ 'license' => $imageInfo['LicenseShortName']['value'] ?? null,
078⫶ 'license_url' => $imageInfo['LicenseUrl']['value'] ?? null,
079⫶ ])
080⫶ );
081⫶ }
082⫶}


code_samples/back_office/images/src/Connector/Dam/Transformation/WikimediaCommonsTransformationFactory.php


code_samples/back_office/images/src/Connector/Dam/Transformation/WikimediaCommonsTransformationFactory.php

docs/content_management/images/add_image_asset_from_dam.md@122:```php
docs/content_management/images/add_image_asset_from_dam.md@123:[[= include_file('code_samples/back_office/images/src/Connector/Dam/Transformation/WikimediaCommonsTransformationFactory.php') =]]
docs/content_management/images/add_image_asset_from_dam.md@124:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Connector\Dam\Transformation;
004⫶
005⫶use Ibexa\Contracts\Connector\Dam\Variation\Transformation;
006⫶use Ibexa\Contracts\Connector\Dam\Variation\TransformationFactory as TransformationFactoryInterface;
007⫶
008⫶class WikimediaCommonsTransformationFactory implements TransformationFactoryInterface
009⫶{
010⫶ /** @param array<string, scalar> $transformationParameters */
011⫶ public function build(?string $transformationName = null, array $transformationParameters = []): Transformation
012⫶ {
013⫶ if (null === $transformationName) {
014⫶ return new Transformation(null, array_map('strval', $transformationParameters));
015⫶ }
016⫶
017⫶ $transformations = $this->buildAll();
018⫶
019⫶ if (array_key_exists($transformationName, $transformations)) {
020⫶ return $transformations[$transformationName];
021⫶ }
022⫶
023⫶ throw new \InvalidArgumentException(sprintf('Unknown transformation "%s".', $transformationName));
024⫶ }
025⫶
026⫶ public function buildAll(): array
027⫶ {
028⫶ return [
029⫶ 'reference' => new Transformation('reference', []),
030⫶ 'tiny' => new Transformation('tiny', ['width' => '30']),
031⫶ 'small' => new Transformation('small', ['width' => '100']),
032⫶ 'medium' => new Transformation('medium', ['width' => '200']),
033⫶ 'large' => new Transformation('large', ['width' => '300']),
034⫶ ];
035⫶ }
036⫶}


code_samples/back_office/images/templates/themes/standard/commons_asset_view.html.twig


code_samples/back_office/images/templates/themes/standard/commons_asset_view.html.twig

docs/content_management/images/add_image_asset_from_dam.md@173:```html+twig
docs/content_management/images/add_image_asset_from_dam.md@174:[[= include_file('code_samples/back_office/images/templates/themes/standard/commons_asset_view.html.twig') =]]
docs/content_management/images/add_image_asset_from_dam.md@175:```

001⫶{% extends '@ibexadesign/ui/field_type/image_asset_view.html.twig' %}
002⫶
003⫶{% block asset_preview %}
004⫶ {{ parent() }}
005⫶ <div>
006⫶ <a href="{{ asset.assetMetadata.page_url }}">Image</a>
007⫶ {% if asset.assetMetadata.author %} by {{ asset.assetMetadata.author }}{% endif %}
008⫶ {% if asset.assetMetadata.license and asset.assetMetadata.license_url %}
009⫶ under <a href="{{ asset.assetMetadata.license_url }}">{{ asset.assetMetadata.license }}</a>
010⫶ {% endif %}.
011⫶ </div>
012⫶{% endblock %}


code_samples/back_office/images/translations/ibexa_fieldtypes_preview.en.yaml


code_samples/back_office/images/translations/ibexa_fieldtypes_preview.en.yaml

docs/content_management/images/add_image_asset_from_dam.md@189:```yaml
docs/content_management/images/add_image_asset_from_dam.md@190:[[= include_file('code_samples/back_office/images/translations/ibexa_fieldtypes_preview.en.yaml') =]]
docs/content_management/images/add_image_asset_from_dam.md@191:```

001⫶ezimageasset.dam_asset.page_url: Image page
002⫶ezimageasset.dam_asset.author: Image author
003⫶ezimageasset.dam_asset.license: License
004⫶ezimageasset.dam_asset.license_url: License page

Download colorized diff

@dabrt dabrt merged commit b25f502 into master Apr 15, 2025
7 checks passed
@dabrt dabrt deleted the IBX-9594 branch April 15, 2025 11:39
dabrt added a commit that referenced this pull request Apr 15, 2025
* IBX-9594: Describe creating a custom DAM connector

---------

Co-authored-by: dabrt <dabrt@users.noreply.github.com>
Co-authored-by: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com>
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants