Skip to content

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>
@adriendupuis adriendupuis mentioned this pull request May 5, 2025
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants