From 2378ef966372270125043a2673e5e6b81fe79f4e Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:31:11 +0100 Subject: [PATCH 01/19] make media action crop aspect ratio configurable --- .../language/en-GB/plg_media-action_crop.ini | 17 ++ .../plg_media-action_crop/joomla.asset.json | 17 ++ .../js/calculator.es6.js | 111 +++++++++++++ plugins/media-action/crop/crop.xml | 54 ++++++ .../crop/layouts/field/calculator.php | 75 +++++++++ .../media-action/crop/src/Extension/Crop.php | 157 ++++++++++++++++++ .../crop/src/Field/CalculatorField.php | 102 ++++++++++++ 7 files changed, 533 insertions(+) create mode 100644 build/media_source/plg_media-action_crop/joomla.asset.json create mode 100644 build/media_source/plg_media-action_crop/js/calculator.es6.js create mode 100644 plugins/media-action/crop/layouts/field/calculator.php create mode 100644 plugins/media-action/crop/src/Field/CalculatorField.php diff --git a/administrator/language/en-GB/plg_media-action_crop.ini b/administrator/language/en-GB/plg_media-action_crop.ini index 57c9846ad67a0..348d575861cd9 100644 --- a/administrator/language/en-GB/plg_media-action_crop.ini +++ b/administrator/language/en-GB/plg_media-action_crop.ini @@ -16,3 +16,20 @@ PLG_MEDIA-ACTION_CROP_PARAM_X="X-Axis" PLG_MEDIA-ACTION_CROP_PARAM_Y="Y-Axis" PLG_MEDIA-ACTION_CROP_QUALITY="Quality" PLG_MEDIA-ACTION_CROP_XML_DESCRIPTION="Adds crop functionality for images." +PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_LABEL="Aspect Ratios" +PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_DESC="Configure custom aspect ratios for the crop tool. Each ratio needs a label (e.g. '16:9'), a value (e.g. '1.7777777777777777'), and optionally a group (landscape/portrait)." +PLG_MEDIA-ACTION_CROP_RATIO_LABEL="Label" +PLG_MEDIA-ACTION_CROP_RATIO_LABEL_DESC="The label to display for this ratio (e.g. '16:9', '4:3', etc.)" +PLG_MEDIA-ACTION_CROP_RATIO_VALUE="Value" +PLG_MEDIA-ACTION_CROP_RATIO_VALUE_DESC="The calculated aspect ratio value (width/height). For example, 16:9 = 1.7777777777777777, 4:3 = 1.3333333333333333, 1:1 = 1" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP="Group" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP_DESC="Optionally group this ratio under landscape or portrait" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP_NONE="No Group" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_DESC="Use this calculator to determine the decimal value for your aspect ratios. Enter width and height, then copy the calculated value." +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_HEIGHT_LABEL="Height" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_WIDTH_LABEL="Width" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_LABEL="Calculated Value:" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_TITLE_LABEL="Click to copy" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_LABEL="Copy" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL="Copied" diff --git a/build/media_source/plg_media-action_crop/joomla.asset.json b/build/media_source/plg_media-action_crop/joomla.asset.json new file mode 100644 index 0000000000000..1ce229b3c33b7 --- /dev/null +++ b/build/media_source/plg_media-action_crop/joomla.asset.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json", + "name": "plg_media-action_crop", + "version": "6.1.0", + "description": "Web Assets for Media Action Crop Plugin", + "license": "GPL-2.0-or-later", + "assets": [ + { + "name": "plg_media-action_crop.calculator", + "type": "script", + "uri": "plg_media-action_crop/calculator.js", + "attributes": { + "defer": true + } + } + ] +} diff --git a/build/media_source/plg_media-action_crop/js/calculator.es6.js b/build/media_source/plg_media-action_crop/js/calculator.es6.js new file mode 100644 index 0000000000000..db4633992b886 --- /dev/null +++ b/build/media_source/plg_media-action_crop/js/calculator.es6.js @@ -0,0 +1,111 @@ +/** + * @copyright (C) 2017 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +/** + * Aspect Ratio Calculator Module + */ +(() => { + const initCalculator = () => { + const widthInput = document.getElementById('calc-width'); + const heightInput = document.getElementById('calc-height'); + const output = document.getElementById('calc-output'); + const copyBtn = document.getElementById('copy-calc-value'); + + if (!widthInput || !heightInput || !output || !copyBtn) { + return; + } + + /** + * Calculate aspect ratio from width and height + */ + const calculateRatio = () => { + const width = parseFloat(widthInput.value); + const height = parseFloat(heightInput.value); + + if (width > 0 && height > 0) { + const ratio = width / height; + output.textContent = ratio.toString(); + } else { + output.textContent = '0'; + } + }; + + /** + * Show copy feedback on button + */ + const showCopyFeedback = () => { + const originalHTML = copyBtn.innerHTML; + const copiedIcon = copyBtn.getAttribute('data-copied-icon'); + const copiedText = copyBtn.getAttribute('data-copied-text'); + + copyBtn.innerHTML = copiedIcon + copiedText; + copyBtn.disabled = true; + + setTimeout(() => { + copyBtn.innerHTML = originalHTML; + copyBtn.disabled = false; + }, 2000); + }; + + /** + * Fallback copy method for older browsers + * + * @param {string} value - The value to copy + */ + const fallbackCopy = (value) => { + const textArea = document.createElement('textarea'); + textArea.value = value; + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + document.body.appendChild(textArea); + textArea.select(); + + try { + document.execCommand('copy'); + showCopyFeedback(); + } catch (err) { + // Silent fail + } + + document.body.removeChild(textArea); + }; + + /** + * Copy the calculated value to clipboard + */ + const copyToClipboard = () => { + const value = output.textContent; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(value) + .then(() => { + showCopyFeedback(); + }) + .catch(() => { + fallbackCopy(value); + }); + } else { + fallbackCopy(value); + } + }; + + // Attach event listeners + widthInput.addEventListener('input', calculateRatio); + heightInput.addEventListener('input', calculateRatio); + copyBtn.addEventListener('click', copyToClipboard); + output.addEventListener('click', copyToClipboard); + + // Calculate initial ratio + calculateRatio(); + }; + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initCalculator); + } else { + // DOM already loaded, run with slight delay for form rendering + setTimeout(initCalculator, 100); + } +})(); diff --git a/plugins/media-action/crop/crop.xml b/plugins/media-action/crop/crop.xml index 4b914e679a0aa..5826636bc6e84 100644 --- a/plugins/media-action/crop/crop.xml +++ b/plugins/media-action/crop/crop.xml @@ -12,6 +12,7 @@ Joomla\Plugin\MediaAction\Crop form + layouts services src @@ -19,4 +20,57 @@ language/en-GB/plg_media-action_crop.ini language/en-GB/plg_media-action_crop.sys.ini + + +
+ + +
+ + + + + + + + + +
+
+
diff --git a/plugins/media-action/crop/layouts/field/calculator.php b/plugins/media-action/crop/layouts/field/calculator.php new file mode 100644 index 0000000000000..85481e639af3f --- /dev/null +++ b/plugins/media-action/crop/layouts/field/calculator.php @@ -0,0 +1,75 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +\defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Layout\LayoutHelper; + +extract($displayData); + +/** + * Layout variables + * ----------------- + * @var string $id The field ID + */ + +// Load the calculator JavaScript +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->getRegistry()->addExtensionRegistryFile('plg_media-action_crop'); +$wa->useScript('plg_media-action_crop.calculator'); + +// Prepare data for JavaScript +$copiedIcon = LayoutHelper::render('joomla.icon.iconclass', ['icon' => 'icon-check']); +$copiedText = Text::_('PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL'); +?> +
+
+

+ 'icon-wand']); ?> + +

+
+
+

+ +

+
+
+ + +
+
+ + +
+
+ +
+
diff --git a/plugins/media-action/crop/src/Extension/Crop.php b/plugins/media-action/crop/src/Extension/Crop.php index 8f56d62876889..c7437e4ae37f2 100644 --- a/plugins/media-action/crop/src/Extension/Crop.php +++ b/plugins/media-action/crop/src/Extension/Crop.php @@ -11,6 +11,7 @@ namespace Joomla\Plugin\MediaAction\Crop\Extension; use Joomla\CMS\Application\CMSWebApplicationInterface; +use Joomla\CMS\Form\Form; use Joomla\Component\Media\Administrator\Plugin\MediaActionPlugin; use Joomla\Event\SubscriberInterface; @@ -25,6 +26,162 @@ */ final class Crop extends MediaActionPlugin implements SubscriberInterface { + /** + * The form event. Load additional parameters when available into the field form. + * Override to dynamically inject aspect ratios from plugin settings. + * + * @param Form $form The form + * @param \stdClass $data The data + * + * @return void + * + * @since 4.0.0 + */ + public function onContentPrepareForm(Form $form, $data) + { + // Check if it is the right form + if ($form->getName() != 'com_media.file') { + return; + } + + $this->loadCss(); + $this->loadJs(); + + // The file with the params for the edit view + $paramsFile = JPATH_PLUGINS . '/media-action/' . $this->_name . '/form/' . $this->_name . '.xml'; + + // When the file exists, load it into the form + if (file_exists($paramsFile)) { + $form->loadFile($paramsFile); + + // Get the aspect ratios from plugin parameters + $aspectRatios = $this->params->get('aspect_ratios'); + + // Convert stdClass to array if needed + if (\is_object($aspectRatios)) { + $aspectRatios = json_decode(json_encode($aspectRatios), true); + } + + // If we have custom aspect ratios, modify the form field + if (!empty($aspectRatios) && \is_array($aspectRatios)) { + $this->injectAspectRatios($form, $aspectRatios); + } + // If no custom ratios configured, the form will use the hard-coded values from form/crop.xml + // This is intentional - allows fallback to defaults + + } + } + + /** + * Inject custom aspect ratios into the crop form + * + * @param Form $form The form object + * @param array $aspectRatios The aspect ratios from plugin settings + * + * @return void + * + * @since 4.0.0 + */ + protected function injectAspectRatios(Form $form, array $aspectRatios) + { + // Get the aspectRatio field (try without group first, then with 'crop' group) + $field = $form->getField('aspectRatio'); + + if (!$field) { + $field = $form->getField('aspectRatio', 'crop'); + } + + if (!$field) { + return; + } + + // Build new XML for the field with custom options + $xml = new \SimpleXMLElement(''); + $xml->addAttribute('name', 'aspectRatio'); + $xml->addAttribute('type', 'groupedlist'); + $xml->addAttribute('label', 'PLG_MEDIA-ACTION_CROP_PARAM_ASPECT'); + $xml->addAttribute('hiddenLabel', 'true'); + $xml->addAttribute('class', 'crop-aspect-ratio-options'); + $xml->addAttribute('default', '1.111'); + + // Add default options + $option = $xml->addChild('option', 'PLG_MEDIA-ACTION_CROP_PARAM_DEFAULT_RATIO'); + $option->addAttribute('class', 'crop-aspect-ratio-option'); + $option->addAttribute('value', '1.111'); + + $option = $xml->addChild('option', 'PLG_MEDIA-ACTION_CROP_PARAM_NO_RATIO'); + $option->addAttribute('class', 'crop-aspect-ratio-option'); + $option->addAttribute('value', ''); + + // Group ratios by landscape/portrait + $grouped = [ + '' => [], + 'landscape' => [], + 'portrait' => [], + ]; + + foreach ($aspectRatios as $ratio) { + // Handle both array and object formats + $label = \is_array($ratio) ? ($ratio['label'] ?? '') : ($ratio->label ?? ''); + $value = \is_array($ratio) ? ($ratio['value'] ?? '') : ($ratio->value ?? ''); + $group = \is_array($ratio) ? ($ratio['group'] ?? '') : ($ratio->group ?? ''); + + // Normalize group to lowercase + $group = strtolower(trim($group)); + + if (empty($label) || $value === '' || $value === null) { + continue; + } + + if (!isset($grouped[$group])) { + $grouped[$group] = []; + } + + $grouped[$group][] = [ + 'label' => $label, + 'value' => $value, + 'group' => $group, + ]; + } + + // Add ungrouped ratios + foreach ($grouped[''] as $ratio) { + $option = $xml->addChild('option', htmlspecialchars($ratio['label'], ENT_XML1, 'UTF-8')); + $option->addAttribute('class', 'crop-aspect-ratio-option'); + $option->addAttribute('value', $ratio['value']); + } + + // Add landscape group + if (!empty($grouped['landscape'])) { + $group = $xml->addChild('group'); + $group->addAttribute('label', 'PLG_MEDIA-ACTION_CROP_PARAM_LANDSCAPE'); + + foreach ($grouped['landscape'] as $ratio) { + $option = $group->addChild('option', htmlspecialchars($ratio['label'], ENT_XML1, 'UTF-8')); + $option->addAttribute('class', 'crop-aspect-ratio-option'); + $option->addAttribute('value', $ratio['value']); + } + } + + // Add portrait group + if (!empty($grouped['portrait'])) { + $group = $xml->addChild('group'); + $group->addAttribute('label', 'PLG_MEDIA-ACTION_CROP_PARAM_PORTRAIT'); + + foreach ($grouped['portrait'] as $ratio) { + $option = $group->addChild('option', htmlspecialchars($ratio['label'], ENT_XML1, 'UTF-8')); + $option->addAttribute('class', 'crop-aspect-ratio-option'); + $option->addAttribute('value', $ratio['value']); + } + } + + // Replace the field in the form + // Try setting in default group first, then 'crop' group + if (!$form->setField($xml, null, true)) { + $form->setField($xml, 'crop', true); + } + } + /** * Load the javascript files of the plugin. * diff --git a/plugins/media-action/crop/src/Field/CalculatorField.php b/plugins/media-action/crop/src/Field/CalculatorField.php new file mode 100644 index 0000000000000..34aa1e53a224f --- /dev/null +++ b/plugins/media-action/crop/src/Field/CalculatorField.php @@ -0,0 +1,102 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Plugin\MediaAction\Crop\Field; + +use Joomla\CMS\Factory; +use Joomla\CMS\Form\FormField; + +// phpcs:disable PSR1.Files.SideEffects +\defined('_JEXEC') or die; +// phpcs:enable PSR1.Files.SideEffects + +/** + * Aspect Ratio Calculator Field + * + * @since 4.0.0 + */ +class CalculatorField extends FormField +{ + /** + * The form field type. + * + * @var string + * @since 4.0.0 + */ + protected $type = 'Calculator'; + + /** + * The layout path to use for rendering the field. + * + * @var string + * @since 4.0.0 + */ + protected $layout = 'field.calculator'; + + /** + * Method to get the field label markup. + * + * @return string The field label markup. + * + * @since 4.0.0 + */ + protected function getLabel(): string + { + return ''; + } + + /** + * Method to get the field input markup. + * + * @return string The field input markup. + * + * @since 4.0.0 + */ + protected function getInput(): string + { + return $this->getRenderer($this->layout)->render($this->getLayoutData()); + } + + /** + * Get the layouts paths + * + * @return array An array of layout paths + * + * @since 4.0.0 + */ + protected function getLayoutPaths(): array + { + $template = Factory::getApplication()->getTemplate(); + + return [ + JPATH_ADMINISTRATOR . '/templates/' . $template . '/html/layouts/plugins/media-action/crop', + JPATH_PLUGINS . '/media-action/crop/layouts', + JPATH_SITE . '/layouts', + ]; + } + + /** + * Method to get the data to be passed to the layout for rendering. + * + * @return array The layout data. + * + * @since 4.0.0 + */ + protected function getLayoutData(): array + { + $data = parent::getLayoutData(); + + $extraData = [ + 'id' => $this->id, + ]; + + return array_merge($data, $extraData); + } +} From c5ecf44d908277ff148fb85355fd828d9a90c61b Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:41:36 +0100 Subject: [PATCH 02/19] :art: code styling --- plugins/media-action/crop/src/Extension/Crop.php | 14 +++++++------- .../crop/src/Field/CalculatorField.php | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/media-action/crop/src/Extension/Crop.php b/plugins/media-action/crop/src/Extension/Crop.php index c7437e4ae37f2..8901a1820c348 100644 --- a/plugins/media-action/crop/src/Extension/Crop.php +++ b/plugins/media-action/crop/src/Extension/Crop.php @@ -35,12 +35,12 @@ final class Crop extends MediaActionPlugin implements SubscriberInterface * * @return void * - * @since 4.0.0 + * @since 6.1.0 */ - public function onContentPrepareForm(Form $form, $data) + public function onContentPrepareForm(Form $form, $data): void { // Check if it is the right form - if ($form->getName() != 'com_media.file') { + if ($form->getName() !== 'com_media.file') { return; } @@ -80,9 +80,9 @@ public function onContentPrepareForm(Form $form, $data) * * @return void * - * @since 4.0.0 + * @since 6.1.0 */ - protected function injectAspectRatios(Form $form, array $aspectRatios) + protected function injectAspectRatios(Form $form, array $aspectRatios): void { // Get the aspectRatio field (try without group first, then with 'crop' group) $field = $form->getField('aspectRatio'); @@ -189,7 +189,7 @@ protected function injectAspectRatios(Form $form, array $aspectRatios) * * @since 4.0.0 */ - protected function loadJs() + protected function loadJs(): void { parent::loadJs(); @@ -207,7 +207,7 @@ protected function loadJs() * * @since 4.0.0 */ - protected function loadCss() + protected function loadCss(): void { parent::loadCss(); diff --git a/plugins/media-action/crop/src/Field/CalculatorField.php b/plugins/media-action/crop/src/Field/CalculatorField.php index 34aa1e53a224f..53b972e6cd507 100644 --- a/plugins/media-action/crop/src/Field/CalculatorField.php +++ b/plugins/media-action/crop/src/Field/CalculatorField.php @@ -20,7 +20,7 @@ /** * Aspect Ratio Calculator Field * - * @since 4.0.0 + * @since 6.1.0 */ class CalculatorField extends FormField { @@ -28,7 +28,7 @@ class CalculatorField extends FormField * The form field type. * * @var string - * @since 4.0.0 + * @since 6.1.0 */ protected $type = 'Calculator'; @@ -36,7 +36,7 @@ class CalculatorField extends FormField * The layout path to use for rendering the field. * * @var string - * @since 4.0.0 + * @since 6.1.0 */ protected $layout = 'field.calculator'; @@ -45,7 +45,7 @@ class CalculatorField extends FormField * * @return string The field label markup. * - * @since 4.0.0 + * @since 6.1.0 */ protected function getLabel(): string { @@ -57,7 +57,7 @@ protected function getLabel(): string * * @return string The field input markup. * - * @since 4.0.0 + * @since 6.1.0 */ protected function getInput(): string { @@ -69,7 +69,7 @@ protected function getInput(): string * * @return array An array of layout paths * - * @since 4.0.0 + * @since 6.1.0 */ protected function getLayoutPaths(): array { @@ -87,7 +87,7 @@ protected function getLayoutPaths(): array * * @return array The layout data. * - * @since 4.0.0 + * @since 6.1.0 */ protected function getLayoutData(): array { From 0a047f2b7988391d35f42613a82278e7760315c7 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:44:51 +0100 Subject: [PATCH 03/19] alpha sort --- .../language/en-GB/plg_media-action_crop.ini | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/administrator/language/en-GB/plg_media-action_crop.ini b/administrator/language/en-GB/plg_media-action_crop.ini index 348d575861cd9..9112d910949db 100644 --- a/administrator/language/en-GB/plg_media-action_crop.ini +++ b/administrator/language/en-GB/plg_media-action_crop.ini @@ -4,6 +4,8 @@ ; Note : All ini files need to be saved as UTF-8 PLG_MEDIA-ACTION_CROP="Media Action - Crop" +PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_LABEL="Aspect Ratios" +PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_DESC="Configure custom aspect ratios for the crop tool. Each ratio needs a label (e.g. '16:9'), a value (e.g. '1.7777777777777777'), and optionally a group (landscape/portrait)." PLG_MEDIA-ACTION_CROP_LABEL="Crop" PLG_MEDIA-ACTION_CROP_PARAM_ASPECT="Aspect Ratio" PLG_MEDIA-ACTION_CROP_PARAM_DEFAULT_RATIO="Default aspect ratio" @@ -15,21 +17,19 @@ PLG_MEDIA-ACTION_CROP_PARAM_WIDTH="Width" PLG_MEDIA-ACTION_CROP_PARAM_X="X-Axis" PLG_MEDIA-ACTION_CROP_PARAM_Y="Y-Axis" PLG_MEDIA-ACTION_CROP_QUALITY="Quality" -PLG_MEDIA-ACTION_CROP_XML_DESCRIPTION="Adds crop functionality for images." -PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_LABEL="Aspect Ratios" -PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_DESC="Configure custom aspect ratios for the crop tool. Each ratio needs a label (e.g. '16:9'), a value (e.g. '1.7777777777777777'), and optionally a group (landscape/portrait)." -PLG_MEDIA-ACTION_CROP_RATIO_LABEL="Label" -PLG_MEDIA-ACTION_CROP_RATIO_LABEL_DESC="The label to display for this ratio (e.g. '16:9', '4:3', etc.)" -PLG_MEDIA-ACTION_CROP_RATIO_VALUE="Value" -PLG_MEDIA-ACTION_CROP_RATIO_VALUE_DESC="The calculated aspect ratio value (width/height). For example, 16:9 = 1.7777777777777777, 4:3 = 1.3333333333333333, 1:1 = 1" -PLG_MEDIA-ACTION_CROP_RATIO_GROUP="Group" -PLG_MEDIA-ACTION_CROP_RATIO_GROUP_DESC="Optionally group this ratio under landscape or portrait" -PLG_MEDIA-ACTION_CROP_RATIO_GROUP_NONE="No Group" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_DESC="Use this calculator to determine the decimal value for your aspect ratios. Enter width and height, then copy the calculated value." PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_HEIGHT_LABEL="Height" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_WIDTH_LABEL="Width" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_LABEL="Calculated Value:" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_LABEL="Calculated value:" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_TITLE_LABEL="Click to copy" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_LABEL="Copy" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL="Copied" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP="Group" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP_DESC="Optionally group this ratio under landscape or portrait" +PLG_MEDIA-ACTION_CROP_RATIO_GROUP_NONE="No group" +PLG_MEDIA-ACTION_CROP_RATIO_LABEL="Label" +PLG_MEDIA-ACTION_CROP_RATIO_LABEL_DESC="The label to display for this ratio (e.g. '16:9', '4:3', etc.)" +PLG_MEDIA-ACTION_CROP_RATIO_VALUE="Value" +PLG_MEDIA-ACTION_CROP_RATIO_VALUE_DESC="The calculated aspect ratio value (width/height). For example, 16:9 = 1.7777777777777777, 4:3 = 1.3333333333333333, 1:1 = 1" +PLG_MEDIA-ACTION_CROP_XML_DESCRIPTION="Adds crop functionality for images." From 13e8aa0133d74e6dac67481cc45d237dacabf789 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:45:40 +0100 Subject: [PATCH 04/19] fix year --- build/media_source/plg_media-action_crop/js/calculator.es6.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/media_source/plg_media-action_crop/js/calculator.es6.js b/build/media_source/plg_media-action_crop/js/calculator.es6.js index db4633992b886..53ac79d296f49 100644 --- a/build/media_source/plg_media-action_crop/js/calculator.es6.js +++ b/build/media_source/plg_media-action_crop/js/calculator.es6.js @@ -1,5 +1,5 @@ /** - * @copyright (C) 2017 Open Source Matters, Inc. + * @copyright (C) 2025 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ From 2da2336903b8b75a0d95360c16bb7164775fd72e Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:47:14 +0100 Subject: [PATCH 05/19] apply __DEPLOY_VERSION__ to newly added code --- plugins/media-action/crop/src/Extension/Crop.php | 4 ++-- .../crop/src/Field/CalculatorField.php | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/media-action/crop/src/Extension/Crop.php b/plugins/media-action/crop/src/Extension/Crop.php index 8901a1820c348..8308cbd6c0fc3 100644 --- a/plugins/media-action/crop/src/Extension/Crop.php +++ b/plugins/media-action/crop/src/Extension/Crop.php @@ -35,7 +35,7 @@ final class Crop extends MediaActionPlugin implements SubscriberInterface * * @return void * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ public function onContentPrepareForm(Form $form, $data): void { @@ -80,7 +80,7 @@ public function onContentPrepareForm(Form $form, $data): void * * @return void * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected function injectAspectRatios(Form $form, array $aspectRatios): void { diff --git a/plugins/media-action/crop/src/Field/CalculatorField.php b/plugins/media-action/crop/src/Field/CalculatorField.php index 53b972e6cd507..27b527dccb94a 100644 --- a/plugins/media-action/crop/src/Field/CalculatorField.php +++ b/plugins/media-action/crop/src/Field/CalculatorField.php @@ -20,7 +20,7 @@ /** * Aspect Ratio Calculator Field * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ class CalculatorField extends FormField { @@ -28,7 +28,7 @@ class CalculatorField extends FormField * The form field type. * * @var string - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected $type = 'Calculator'; @@ -36,7 +36,7 @@ class CalculatorField extends FormField * The layout path to use for rendering the field. * * @var string - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected $layout = 'field.calculator'; @@ -45,7 +45,7 @@ class CalculatorField extends FormField * * @return string The field label markup. * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected function getLabel(): string { @@ -57,7 +57,7 @@ protected function getLabel(): string * * @return string The field input markup. * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected function getInput(): string { @@ -69,7 +69,7 @@ protected function getInput(): string * * @return array An array of layout paths * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected function getLayoutPaths(): array { @@ -87,7 +87,7 @@ protected function getLayoutPaths(): array * * @return array The layout data. * - * @since 6.1.0 + * @since __DEPLOY_VERSION__ */ protected function getLayoutData(): array { From daedb7c26aa3bf85a985fea1015293d0b921c3ae Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 11:50:40 +0100 Subject: [PATCH 06/19] fix year for newly added code --- plugins/media-action/crop/layouts/field/calculator.php | 2 +- plugins/media-action/crop/src/Field/CalculatorField.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/media-action/crop/layouts/field/calculator.php b/plugins/media-action/crop/layouts/field/calculator.php index 85481e639af3f..7ff74b6eb2f41 100644 --- a/plugins/media-action/crop/layouts/field/calculator.php +++ b/plugins/media-action/crop/layouts/field/calculator.php @@ -4,7 +4,7 @@ * @package Joomla.Plugin * @subpackage Media-Action.crop * - * @copyright (C) 2017 Open Source Matters, Inc. + * @copyright (C) 2025 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ diff --git a/plugins/media-action/crop/src/Field/CalculatorField.php b/plugins/media-action/crop/src/Field/CalculatorField.php index 27b527dccb94a..d755705c3a577 100644 --- a/plugins/media-action/crop/src/Field/CalculatorField.php +++ b/plugins/media-action/crop/src/Field/CalculatorField.php @@ -4,7 +4,7 @@ * @package Joomla.Plugin * @subpackage Media-Action.crop * - * @copyright (C) 2017 Open Source Matters, Inc. + * @copyright (C) 2025 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ From 436e14b70c76a40df771cab80784f654e69eed8e Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 12:02:52 +0100 Subject: [PATCH 07/19] another sort :-) --- administrator/language/en-GB/plg_media-action_crop.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/administrator/language/en-GB/plg_media-action_crop.ini b/administrator/language/en-GB/plg_media-action_crop.ini index 9112d910949db..4e7419d1904c7 100644 --- a/administrator/language/en-GB/plg_media-action_crop.ini +++ b/administrator/language/en-GB/plg_media-action_crop.ini @@ -4,8 +4,8 @@ ; Note : All ini files need to be saved as UTF-8 PLG_MEDIA-ACTION_CROP="Media Action - Crop" -PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_LABEL="Aspect Ratios" PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_DESC="Configure custom aspect ratios for the crop tool. Each ratio needs a label (e.g. '16:9'), a value (e.g. '1.7777777777777777'), and optionally a group (landscape/portrait)." +PLG_MEDIA-ACTION_CROP_ASPECT_RATIOS_LABEL="Aspect Ratios" PLG_MEDIA-ACTION_CROP_LABEL="Crop" PLG_MEDIA-ACTION_CROP_PARAM_ASPECT="Aspect Ratio" PLG_MEDIA-ACTION_CROP_PARAM_DEFAULT_RATIO="Default aspect ratio" @@ -17,14 +17,14 @@ PLG_MEDIA-ACTION_CROP_PARAM_WIDTH="Width" PLG_MEDIA-ACTION_CROP_PARAM_X="X-Axis" PLG_MEDIA-ACTION_CROP_PARAM_Y="Y-Axis" PLG_MEDIA-ACTION_CROP_QUALITY="Quality" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL="Copied" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_LABEL="Copy" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_DESC="Use this calculator to determine the decimal value for your aspect ratios. Enter width and height, then copy the calculated value." PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_HEIGHT_LABEL="Height" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_WIDTH_LABEL="Width" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_LABEL="Calculated value:" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_TITLE_LABEL="Click to copy" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_LABEL="Copy" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL="Copied" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_WIDTH_LABEL="Width" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" PLG_MEDIA-ACTION_CROP_RATIO_GROUP="Group" PLG_MEDIA-ACTION_CROP_RATIO_GROUP_DESC="Optionally group this ratio under landscape or portrait" PLG_MEDIA-ACTION_CROP_RATIO_GROUP_NONE="No group" From 985057210da3fd9f987687cbd05e7659809c58f8 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 12:06:23 +0100 Subject: [PATCH 08/19] remove fallback Copy --- .../js/calculator.es6.js | 41 ++++--------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/build/media_source/plg_media-action_crop/js/calculator.es6.js b/build/media_source/plg_media-action_crop/js/calculator.es6.js index 53ac79d296f49..0d51158858773 100644 --- a/build/media_source/plg_media-action_crop/js/calculator.es6.js +++ b/build/media_source/plg_media-action_crop/js/calculator.es6.js @@ -49,46 +49,19 @@ }, 2000); }; - /** - * Fallback copy method for older browsers - * - * @param {string} value - The value to copy - */ - const fallbackCopy = (value) => { - const textArea = document.createElement('textarea'); - textArea.value = value; - textArea.style.position = 'fixed'; - textArea.style.left = '-999999px'; - document.body.appendChild(textArea); - textArea.select(); - - try { - document.execCommand('copy'); - showCopyFeedback(); - } catch (err) { - // Silent fail - } - - document.body.removeChild(textArea); - }; - /** * Copy the calculated value to clipboard */ const copyToClipboard = () => { const value = output.textContent; - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(value) - .then(() => { - showCopyFeedback(); - }) - .catch(() => { - fallbackCopy(value); - }); - } else { - fallbackCopy(value); - } + navigator.clipboard.writeText(value) + .then(() => { + showCopyFeedback(); + }) + .catch((err) => { + console.error('Clipboard copy failed:', err); + }); }; // Attach event listeners From 59a8091362697b258f2ca12da0876297cdd559e7 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 12:06:47 +0100 Subject: [PATCH 09/19] Update administrator/language/en-GB/plg_media-action_crop.ini Co-authored-by: Brian Teeman --- administrator/language/en-GB/plg_media-action_crop.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/language/en-GB/plg_media-action_crop.ini b/administrator/language/en-GB/plg_media-action_crop.ini index 4e7419d1904c7..8dcff92fcd22a 100644 --- a/administrator/language/en-GB/plg_media-action_crop.ini +++ b/administrator/language/en-GB/plg_media-action_crop.ini @@ -21,10 +21,10 @@ PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_DONE_LABEL="Copied" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_COPY_LABEL="Copy" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_DESC="Use this calculator to determine the decimal value for your aspect ratios. Enter width and height, then copy the calculated value." PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_HEIGHT_LABEL="Height" +PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_LABEL="Calculated value:" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_OUTPUT_TITLE_LABEL="Click to copy" PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_WIDTH_LABEL="Width" -PLG_MEDIA-ACTION_CROP_RATIO_CALCULATOR_LABEL="Aspect Ratio Calculator" PLG_MEDIA-ACTION_CROP_RATIO_GROUP="Group" PLG_MEDIA-ACTION_CROP_RATIO_GROUP_DESC="Optionally group this ratio under landscape or portrait" PLG_MEDIA-ACTION_CROP_RATIO_GROUP_NONE="No group" From 1409e678d93cf4685d56dcfc34128252427aafb4 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Fri, 7 Nov 2025 15:13:32 +0100 Subject: [PATCH 10/19] PHPCBF FIX --- plugins/media-action/crop/src/Extension/Crop.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/media-action/crop/src/Extension/Crop.php b/plugins/media-action/crop/src/Extension/Crop.php index 8308cbd6c0fc3..cc437042281ca 100644 --- a/plugins/media-action/crop/src/Extension/Crop.php +++ b/plugins/media-action/crop/src/Extension/Crop.php @@ -68,7 +68,6 @@ public function onContentPrepareForm(Form $form, $data): void } // If no custom ratios configured, the form will use the hard-coded values from form/crop.xml // This is intentional - allows fallback to defaults - } } From 3c83c9a20fa9df5031e9daa1f377204dca07b981 Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Sun, 9 Nov 2025 21:23:43 +0100 Subject: [PATCH 11/19] Joomla.sanitizeHtml() for security --- build/media_source/plg_media-action_crop/js/calculator.es6.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/media_source/plg_media-action_crop/js/calculator.es6.js b/build/media_source/plg_media-action_crop/js/calculator.es6.js index 0d51158858773..040588ed2afe8 100644 --- a/build/media_source/plg_media-action_crop/js/calculator.es6.js +++ b/build/media_source/plg_media-action_crop/js/calculator.es6.js @@ -40,7 +40,7 @@ const copiedIcon = copyBtn.getAttribute('data-copied-icon'); const copiedText = copyBtn.getAttribute('data-copied-text'); - copyBtn.innerHTML = copiedIcon + copiedText; + copyBtn.innerHTML = Joomla.sanitizeHtml(copiedIcon + copiedText); copyBtn.disabled = true; setTimeout(() => { From db05b8270217808e775cf00515a83f674d2572fb Mon Sep 17 00:00:00 2001 From: Hans Kuijpers Date: Wed, 19 Nov 2025 16:27:30 +0100 Subject: [PATCH 12/19] removal obsolete title attribute --- plugins/media-action/crop/layouts/field/calculator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/media-action/crop/layouts/field/calculator.php b/plugins/media-action/crop/layouts/field/calculator.php index 7ff74b6eb2f41..7794cc16629d5 100644 --- a/plugins/media-action/crop/layouts/field/calculator.php +++ b/plugins/media-action/crop/layouts/field/calculator.php @@ -64,7 +64,6 @@ 1.7777777777777777