├── assets
├── heart.png
└── fork-logo.png
├── src
├── exception
│ ├── NotAnImageException.php
│ ├── ImageNotSavedException.php
│ └── NotALocalFileException.php
├── connectors
│ ├── ConnectorInterface.php
│ ├── translation
│ │ ├── TranslatorInterface.php
│ │ ├── HuggingFaceT5SmallTranslator.php
│ │ ├── HuggingFaceOpusMtEnDeTranslator.php
│ │ ├── AbstractHuggingFaceTranslator.php
│ │ └── DeeplTranslator.php
│ ├── alttextgeneration
│ │ ├── AltTextGeneratorInterface.php
│ │ ├── HuggingFaceBlipBaseAltTextGenerator.php
│ │ ├── HuggingFaceBlipLargeAltTextGenerator.php
│ │ └── AbstractHuggingFaceAltTextGenerator.php
│ └── AbstractHuggingFaceConnector.php
├── events
│ ├── RegisterGeneratorsEvent.php
│ └── RegisterTranslatorsEvent.php
├── translations
│ └── de
│ │ └── altify.php
├── services
│ ├── AbstractConnectorService.php
│ ├── Generator.php
│ └── Translator.php
├── helpers
│ └── AssetHelper.php
├── jobs
│ └── GenerateAltText.php
├── controllers
│ ├── GenerateAltTextController.php
│ └── TranslateAltTextController.php
├── templates
│ └── _settings.twig
├── icon-mask.svg
├── elements
│ └── actions
│ │ ├── GenerateAltText.php
│ │ └── TranslateAltText.php
├── models
│ └── Settings.php
├── Plugin.php
└── icon.svg
├── LICENSE.md
└── composer.json
/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fork/craft-altify/main/assets/heart.png
--------------------------------------------------------------------------------
/assets/fork-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fork/craft-altify/main/assets/fork-logo.png
--------------------------------------------------------------------------------
/src/exception/NotAnImageException.php:
--------------------------------------------------------------------------------
1 | 'Alt text generieren',
5 | 'Generators' => 'Generatoren',
6 | 'Translate alt text' => 'Alt text übersetzen',
7 | 'Translators' => 'Übersetzer',
8 | '{class} must implement {interface}' => '{class} muss {interface} implementieren',
9 | ];
10 |
--------------------------------------------------------------------------------
/src/connectors/translation/TranslatorInterface.php:
--------------------------------------------------------------------------------
1 | De';
8 | protected string $handle = 't5SmallEnDe';
9 | protected string $modelPath = '/models/google-t5/t5-small';
10 | }
11 |
--------------------------------------------------------------------------------
/src/connectors/translation/HuggingFaceOpusMtEnDeTranslator.php:
--------------------------------------------------------------------------------
1 | De';
8 | protected string $handle = 'opusMtEnDe';
9 | protected string $modelPath = '/models/Helsinki-NLP/opus-mt-en-de';
10 | }
11 |
--------------------------------------------------------------------------------
/src/connectors/alttextgeneration/HuggingFaceBlipBaseAltTextGenerator.php:
--------------------------------------------------------------------------------
1 | getHandle()] = $classname;
19 | }
20 |
21 | return $data;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/connectors/AbstractHuggingFaceConnector.php:
--------------------------------------------------------------------------------
1 | name;
16 | }
17 |
18 | public function getHandle(): string
19 | {
20 | return $this->handle;
21 | }
22 |
23 | protected function getClient(): Client
24 | {
25 | return new Client([
26 | 'base_uri' => "https://api-inference.huggingface.co",
27 | 'headers' => [
28 | 'Authorization' => 'Bearer ' . Plugin::getInstance()->getSettings()->getHuggingFaceApiToken(),
29 | ],
30 | ]);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/helpers/AssetHelper.php:
--------------------------------------------------------------------------------
1 | kind !== Asset::KIND_IMAGE) {
27 | throw new NotAnImageException("Asset is not an image");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/jobs/GenerateAltText.php:
--------------------------------------------------------------------------------
1 | generator->generateAltTextForImage($this->assetId);
32 | }
33 |
34 | protected function defaultDescription(): ?string
35 | {
36 | return "Generate alt text";
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/connectors/translation/AbstractHuggingFaceTranslator.php:
--------------------------------------------------------------------------------
1 | alt;
19 |
20 | if (!empty($altText)) {
21 | $client = $this->getClient();
22 |
23 | $response = $client->post(
24 | $this->modelPath,
25 | ['json' => ['inputs' => $image->alt]]
26 | );
27 |
28 | $body = $response->getBody();
29 | $decoded = json_decode($body, true);
30 | $altText = !empty($decoded) ? $decoded[0]['translation_text'] : null;
31 | }
32 |
33 | return $altText;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Fork Unstable Media GmbH.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/connectors/translation/DeeplTranslator.php:
--------------------------------------------------------------------------------
1 | name;
18 | }
19 |
20 | public function getHandle(): string
21 | {
22 | return $this->handle;
23 | }
24 |
25 | /**
26 | * @param Asset $image
27 | * @return string|null
28 | * @throws DeepLException
29 | */
30 | public function translateAltTextForImage(Asset $image): ?string
31 | {
32 | $altText = $image->alt;
33 |
34 | if (!empty($altText)) {
35 | $translator = new Translator(Plugin::getInstance()->getSettings()->getDeeplApiKey());
36 | $result = $translator->translateText($altText, null, $image->site->language);
37 | $altText = $result->text;
38 | }
39 |
40 | return $altText;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/controllers/GenerateAltTextController.php:
--------------------------------------------------------------------------------
1 | requireLogin();
31 | $this->requireCpRequest();
32 | $assetId = Craft::$app->request->getBodyParam('assetId');
33 |
34 | try {
35 | Plugin::getInstance()->generator->generateAltTextForImage($assetId);
36 | } catch (Exception|Throwable $e) {
37 | Craft::$app->getSession()->setError($e->getMessage());
38 | }
39 |
40 | return $this->asSuccess();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/controllers/TranslateAltTextController.php:
--------------------------------------------------------------------------------
1 | requireLogin();
31 | $this->requireCpRequest();
32 | $assetId = Craft::$app->request->getBodyParam('assetId');
33 |
34 | try {
35 | Plugin::getInstance()->translator->translateAltTextForImage($assetId);
36 | } catch (Exception|Throwable $e) {
37 | Craft::$app->getSession()->setError($e->getMessage());
38 | }
39 |
40 | return $this->asSuccess();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/templates/_settings.twig:
--------------------------------------------------------------------------------
1 | {# @var plugin \fork\altify\Plugin #}
2 | {# @var settings \fork\altify\models\Settings #}
3 |
4 | {% import '_includes/forms.twig' as forms %}
5 |
6 | {{ forms.autosuggestField({
7 | label: "Alt text generator service"|t('altify'),
8 | id: 'altTextGenerator',
9 | name: 'altTextGenerator',
10 | suggestEnvVars: true,
11 | suggestions: settings.generatorSuggestions,
12 | value: settings.altTextGenerator,
13 | size: 60,
14 | errors: settings.getErrors('altTextGenerator'),
15 | }) }}
16 |
17 | {{ forms.autosuggestField({
18 | label: "Alt text translator service"|t('altify'),
19 | id: 'altTextTranslator',
20 | name: 'altTextTranslator',
21 | suggestEnvVars: true,
22 | suggestions: settings.translatorSuggestions,
23 | value: settings.altTextTranslator,
24 | size: 60,
25 | errors: settings.getErrors('altTextTranslator'),
26 | }) }}
27 |
28 | {{ forms.autosuggestField({
29 | label: "HuggingFace API token"|t('altfy'),
30 | id: 'huggingFaceApiToken',
31 | name: 'huggingFaceApiToken',
32 | suggestEnvVars: true,
33 | value: settings.huggingFaceApiToken,
34 | size: 60,
35 | errors: settings.getErrors('huggingFaceApiToken')
36 | }) }}
37 |
38 | {{ forms.autosuggestField({
39 | label: "DeepL API key"|t('altify'),
40 | id: 'deeplApiKey',
41 | name: 'deeplApiKey',
42 | suggestEnvVars: true,
43 | value: settings.deeplApiKey,
44 | size: 60,
45 | errors: settings.getErrors('deeplApiKey')
46 | }) }}
47 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fork/craft-altify",
3 | "description": "Generates alt texts for images using different services that can be chosen from.",
4 | "type": "craft-plugin",
5 | "license": "mit",
6 | "support": {
7 | "email": "obj@fork.de",
8 | "issues": "https://github.com/fork/craft-altify/issues?state=open",
9 | "source": "https://github.com/fork/craft-altify",
10 | "docs": "https://github.com/fork/craft-altify",
11 | "rss": "https://github.com/fork/craft-altify/releases.atom"
12 | },
13 | "require": {
14 | "php": ">=8.0.2",
15 | "craftcms/cms": "^4.5.0",
16 | "deeplcom/deepl-php": "^1.7"
17 | },
18 | "require-dev": {
19 | "craftcms/ecs": "dev-main",
20 | "craftcms/phpstan": "dev-main"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "fork\\altify\\": "src/"
25 | }
26 | },
27 | "extra": {
28 | "handle": "altify",
29 | "name": "Altify",
30 | "developer": "Fork",
31 | "documentationUrl": "https://github.com/fork/craft-altify"
32 | },
33 | "scripts": {
34 | "check-cs": "ecs check --ansi",
35 | "fix-cs": "ecs check --ansi --fix",
36 | "phpstan": "phpstan --memory-limit=1G"
37 | },
38 | "config": {
39 | "sort-packages": true,
40 | "platform": {
41 | "php": "8.0.2"
42 | },
43 | "allow-plugins": {
44 | "yiisoft/yii2-composer": true,
45 | "craftcms/plugin-installer": true,
46 | "php-http/discovery": true
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/icon-mask.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/connectors/alttextgeneration/AbstractHuggingFaceAltTextGenerator.php:
--------------------------------------------------------------------------------
1 | getVolume()->getFs();
28 | if (!($fs instanceof Local)) {
29 | throw new NotALocalFileException("Image is not a local file");
30 | }
31 |
32 | $fsPath = Craft::getAlias($fs->path);
33 | $absPath = $fsPath . DIRECTORY_SEPARATOR . $image->getPath();
34 |
35 | $client = $this->getClient();
36 |
37 | $body = Utils::tryFopen($absPath, 'r');
38 | $response = $client->post(
39 | $this->modelPath,
40 | ['body' => $body]
41 | );
42 |
43 | $body = $response->getBody();
44 | $decoded = json_decode($body, true);
45 | $first = !empty($decoded) ? $decoded[0] : null;
46 |
47 | return $first ? $this->filterWords($first['generated_text']) : null;
48 | }
49 |
50 | protected function filterWords(string $text): string
51 | {
52 | foreach (Plugin::getInstance()->getSettings()->wordsBlackList as $word) {
53 | $text = str_replace($word, '', $text);
54 | }
55 |
56 | return trim($text);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/elements/actions/GenerateAltText.php:
--------------------------------------------------------------------------------
1 | getView()->registerJsWithVars(fn($type) => << {
26 | new Craft.ElementActionTrigger({
27 | type: $type,
28 |
29 | // Whether this action should be available when multiple elements are selected
30 | bulk: true,
31 |
32 | // Return whether the action should be available depending on which elements are selected
33 | validateSelection: (selectedItems) {
34 | return true;
35 | },
36 |
37 | // Uncomment if the action should be handled by JavaScript:
38 | // activate: () => {
39 | // Craft.elementIndex.setIndexBusy();
40 | // const ids = Craft.elementIndex.getSelectedElementIds();
41 | // // ...
42 | // Craft.elementIndex.setIndexAvailable();
43 | // },
44 | });
45 | })();
46 | JS, [static::class]);
47 |
48 | return null;
49 | }
50 |
51 | /**
52 | * @param Craft\elements\db\ElementQueryInterface $query
53 | * @return bool
54 | * @throws MissingComponentException
55 | */
56 | public function performAction(Craft\elements\db\ElementQueryInterface $query): bool
57 | {
58 | $elements = $query->all();
59 | foreach ($elements as $element) {
60 | try {
61 | Plugin::getInstance()->generator->generateAltTextForImage($element->id);
62 | } catch (Exception|Throwable $e) {
63 | Craft::$app->getSession()->setError($e->getMessage());
64 | }
65 | }
66 |
67 | return true;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/elements/actions/TranslateAltText.php:
--------------------------------------------------------------------------------
1 | getView()->registerJsWithVars(fn($type) => << {
26 | new Craft.ElementActionTrigger({
27 | type: $type,
28 |
29 | // Whether this action should be available when multiple elements are selected
30 | bulk: true,
31 |
32 | // Return whether the action should be available depending on which elements are selected
33 | validateSelection: (selectedItems) {
34 | return true;
35 | },
36 |
37 | // Uncomment if the action should be handled by JavaScript:
38 | // activate: () => {
39 | // Craft.elementIndex.setIndexBusy();
40 | // const ids = Craft.elementIndex.getSelectedElementIds();
41 | // // ...
42 | // Craft.elementIndex.setIndexAvailable();
43 | // },
44 | });
45 | })();
46 | JS, [static::class]);
47 |
48 | return null;
49 | }
50 |
51 | /**
52 | * @param Craft\elements\db\ElementQueryInterface $query
53 | * @return bool
54 | * @throws MissingComponentException
55 | */
56 | public function performAction(Craft\elements\db\ElementQueryInterface $query): bool
57 | {
58 | $elements = $query->all();
59 | foreach ($elements as $element) {
60 | try {
61 | Plugin::getInstance()->translator->translateAltTextForImage($element->id);
62 | } catch (Exception|Throwable $e) {
63 | Craft::$app->getSession()->setError($e->getMessage());
64 | }
65 | }
66 |
67 | return true;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/services/Generator.php:
--------------------------------------------------------------------------------
1 | elements->getElementById($assetId);
45 | AssetHelper::validateImage($image);
46 | /** @var Asset $image */
47 | $image->alt = $this->generateAltText($image);
48 |
49 | if (!Craft::$app->elements->saveElement($image)) {
50 | throw new ImageNotSavedException(
51 | "Image could not be saved, reasons: " . json_encode($image->getErrors(), JSON_PRETTY_PRINT)
52 | );
53 | }
54 | }
55 |
56 | /**
57 | * @throws InvalidConfigException
58 | */
59 | public function generateAltText(Asset $image): string
60 | {
61 | return Plugin::getInstance()->getSettings()->getAltTextGenerator()->generateAltTextForImage($image);
62 | }
63 |
64 | /**
65 | * @return array
66 | */
67 | public function getAvailableGenerators(): array
68 | {
69 | $generators = self::buildConnectorArray(self::GENERATORS);
70 | $registerGeneratorsEvent = new RegisterGeneratorsEvent(['generators' => $generators]);
71 | $this->trigger(self::EVENT_REGISTER_GENERATORS, $registerGeneratorsEvent);
72 |
73 | return $registerGeneratorsEvent->generators;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/services/Translator.php:
--------------------------------------------------------------------------------
1 | elements->getElementById($assetId);
46 | AssetHelper::validateImage($image);
47 | /** @var Asset $image */
48 | $image->alt = $this->translateAltText($image);
49 |
50 | if (!Craft::$app->elements->saveElement($image)) {
51 | throw new ImageNotSavedException(
52 | "Image could not be saved, reasons: " . json_encode($image->getErrors(), JSON_PRETTY_PRINT)
53 | );
54 | }
55 | }
56 |
57 | /**
58 | * @throws InvalidConfigException
59 | */
60 | public function translateAltText(Asset $image): string
61 | {
62 | return Plugin::getInstance()->getSettings()->getAltTextTranslator()->translateAltTextForImage($image);
63 | }
64 |
65 | /**
66 | * @return array
67 | */
68 | public function getAvailableTranslators(): array
69 | {
70 | $translators = self::buildConnectorArray(self::TRANSLATORS);
71 | $registerTranslatorsEvent = new RegisterTranslatorsEvent(['translators' => $translators]);
72 | $this->trigger(self::EVENT_REGISTER_TRANSLATORS, $registerTranslatorsEvent);
73 |
74 | return $registerTranslatorsEvent->translators;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/models/Settings.php:
--------------------------------------------------------------------------------
1 | huggingFaceApiToken);
37 | }
38 |
39 | public function getDeeplApiKey(): ?string
40 | {
41 | return App::parseEnv($this->deeplApiKey);
42 | }
43 |
44 | /**
45 | * @return array[]
46 | * @noinspection PhpUnused
47 | */
48 | public function getGeneratorSuggestions(): array
49 | {
50 | $data = [];
51 |
52 | foreach (Plugin::getInstance()->generator->getAvailableGenerators() as $handle => $classname) {
53 | /** @var AltTextGeneratorInterface $obj */
54 | $obj = new $classname();
55 |
56 | $data[] = [
57 | 'name' => $handle,
58 | 'hint' => $obj->getName()
59 | ];
60 | }
61 |
62 | return [[
63 | 'label' => Craft::t('altify', 'Generators'),
64 | 'data' => $data
65 | ]];
66 | }
67 |
68 | /**
69 | * @return array[]
70 | * @noinspection PhpUnused
71 | */
72 | public function getTranslatorSuggestions(): array
73 | {
74 | $data = [];
75 |
76 | foreach (Plugin::getInstance()->translator->getAvailableTranslators() as $handle => $classname) {
77 | /** @var TranslatorInterface $obj */
78 | $obj = new $classname();
79 |
80 | $data[] = [
81 | 'name' => $handle,
82 | 'hint' => $obj->getName()
83 | ];
84 | }
85 |
86 | return [[
87 | 'label' => Craft::t('altify', 'Translators'),
88 | 'data' => $data
89 | ]];
90 | }
91 |
92 | /**
93 | * @throws InvalidConfigException
94 | */
95 | public function getAltTextGenerator(): AltTextGeneratorInterface
96 | {
97 | $altTextGenerator = App::parseEnv($this->altTextGenerator);
98 | $generators = Plugin::getInstance()->generator->getAvailableGenerators();
99 |
100 | if (key_exists($altTextGenerator, $generators)) {
101 | $className = $generators[$altTextGenerator];
102 | } else {
103 | $className = HuggingFaceBlipLargeAltTextGenerator::class;
104 | }
105 | if (!is_a($className, AltTextGeneratorInterface::class, true)) {
106 | throw new InvalidConfigException(Craft::t(
107 | 'altify',
108 | '{class} must implement {interface}',
109 | [
110 | 'class' => Plugin::getInstance()->getSettings()->altTextGenerator,
111 | 'interface' => AltTextGeneratorInterface::class
112 | ]
113 | ));
114 | }
115 |
116 | return new $className;
117 | }
118 |
119 | /**
120 | * @throws InvalidConfigException
121 | */
122 | public function getAltTextTranslator(): TranslatorInterface
123 | {
124 | $altTextTranslator = App::parseEnv($this->altTextTranslator);
125 | $translators = Plugin::getInstance()->translator->getAvailableTranslators();
126 |
127 | if (key_exists($altTextTranslator, $translators)) {
128 | $className = $translators[$altTextTranslator];
129 | } else {
130 | $className = HuggingFaceT5SmallTranslator::class;
131 | }
132 | if (!is_a($className, TranslatorInterface::class, true)) {
133 | throw new InvalidConfigException(Craft::t(
134 | 'altify',
135 | '{class} must implement {interface}',
136 | [
137 | 'class' => Plugin::getInstance()->getSettings()->altTextTranslator,
138 | 'interface' => TranslatorInterface::class
139 | ]
140 | ));
141 | }
142 |
143 | return new $className;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/Plugin.php:
--------------------------------------------------------------------------------
1 |
33 | * @copyright Fork
34 | * @license MIT
35 | * @property-read Generator $generator
36 | * @property-read Settings $settings
37 | * @property-read Translator $translator
38 | */
39 | class Plugin extends BasePlugin
40 | {
41 | public ?string $name = 'Altify';
42 | public string $schemaVersion = '1.0.0';
43 | public bool $hasCpSettings = true;
44 |
45 | public static function config(): array
46 | {
47 | return [
48 | 'components' => [
49 | 'generator' => Generator::class,
50 | 'translator' => Translator::class
51 | ],
52 | ];
53 | }
54 |
55 | public function init(): void
56 | {
57 | parent::init();
58 |
59 | // Defer most setup tasks until Craft is fully initialized
60 | Craft::$app->onInit(function() {
61 | $this->attachEventHandlers();
62 | });
63 | }
64 |
65 | /**
66 | * @throws InvalidConfigException
67 | */
68 | protected function createSettingsModel(): ?Model
69 | {
70 | return Craft::createObject(Settings::class);
71 | }
72 |
73 | /**
74 | * @throws SyntaxError
75 | * @throws Exception
76 | * @throws RuntimeError
77 | * @throws LoaderError
78 | */
79 | protected function settingsHtml(): ?string
80 | {
81 | $settings = $this->getSettings();
82 |
83 | return Craft::$app->view->renderTemplate('altify/_settings.twig', [
84 | 'plugin' => $this,
85 | 'settings' => $settings
86 | ]);
87 | }
88 |
89 | private function attachEventHandlers(): void
90 | {
91 | Event::on(
92 | Asset::class,
93 | Element::EVENT_AFTER_SAVE,
94 | function (ModelEvent $event) {
95 | $asset = $event->sender;
96 |
97 | if ($asset->firstSave && $asset->kind === Asset::KIND_IMAGE) {
98 | Craft::$app->getQueue()->push(new GenerateAltTextJob(['assetId' => $asset->id]));
99 | }
100 | }
101 | );
102 |
103 | Event::on(
104 | Asset::class,
105 | Element::EVENT_DEFINE_ADDITIONAL_BUTTONS,
106 | function (DefineHtmlEvent $event) {
107 | $this->appendAssetEditPageButtons($event);
108 | }
109 | );
110 |
111 | Event::on(
112 | Asset::class,
113 | Element::EVENT_REGISTER_ACTIONS,
114 | function (RegisterElementActionsEvent $event) {
115 | $event->actions[] = GenerateAltTextAction::class;
116 | $event->actions[] = TranslateAltTextAction::class;
117 | }
118 | );
119 | }
120 |
121 | /**
122 | * @param DefineHtmlEvent $event
123 | * @return void
124 | */
125 | private function appendAssetEditPageButtons(DefineHtmlEvent &$event): void
126 | {
127 | /** @see Asset::getAdditionalButtons() */
128 | $event->html = Html::beginTag('div', ['class' => 'btngroup']);
129 | $event->html .= Html::button(Craft::t('altify', 'Generate alt text'), [
130 | 'id' => 'generateAltText-btn',
131 | 'class' => 'btn',
132 | 'data' => [
133 | 'icon' => 'wand',
134 | ],
135 | 'aria' => [
136 | 'label' => Craft::t('altify', 'Generate alt text'),
137 | ],
138 | ]);
139 | $event->html .= Html::button(Craft::t('altify', 'Translate alt text'), [
140 | 'id' => 'translateAltText-btn',
141 | 'class' => 'btn',
142 | 'data' => [
143 | 'icon' => 'language',
144 | ],
145 | 'aria' => [
146 | 'label' => Craft::t('altify', 'Translate alt text'),
147 | ],
148 | ]);
149 | $js = << {
151 | let id = document.querySelector("input[name='elementId']").value;
152 | const \$form = Craft.createForm().appendTo(Garnish.\$bod);
153 | \$form.append(Craft.getCsrfInput());
154 | $('', {type: 'hidden', name: 'action', value: 'altify/generate-alt-text'}).appendTo(\$form);
155 | $('', {type: 'hidden', name: 'assetId', value: id}).appendTo(\$form);
156 | $('', {type: 'submit', value: 'Submit'}).appendTo(\$form);
157 | \$form.submit();
158 | \$form.remove();
159 | });
160 | $('#translateAltText-btn').on('click', () => {
161 | let id = document.querySelector("input[name='elementId']").value;
162 | const \$form = Craft.createForm().appendTo(Garnish.\$bod);
163 | \$form.append(Craft.getCsrfInput());
164 | $('', {type: 'hidden', name: 'action', value: 'altify/translate-alt-text'}).appendTo(\$form);
165 | $('', {type: 'hidden', name: 'assetId', value: id}).appendTo(\$form);
166 | $('', {type: 'submit', value: 'Submit'}).appendTo(\$form);
167 | \$form.submit();
168 | \$form.remove();
169 | });
170 | JS;
171 | Craft::$app->getView()->registerJs($js);
172 |
173 | $event->html .= Html::endTag('div');
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/icon.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------