Commit 4c87cc0f authored by catch's avatar catch
Browse files

Issue #3267870 by heddn, larowlan, catch, Fabianx, Alina Basarabeanu, xjm,...

Issue #3267870 by heddn, larowlan, catch, Fabianx, Alina Basarabeanu, xjm, Graber: Order image mappings by breakpoint ID and numeric multiplier
parent 64ea0bed
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -132,8 +132,9 @@ public function processDefinition(&$definition, $plugin_id) {
    if (!in_array('1x', $definition['multipliers'])) {
      $definition['multipliers'][] = '1x';
    }
    // Ensure that multipliers are sorted correctly.
    sort($definition['multipliers']);
    // Ensure that multipliers are sorted numerically so 1x, 1.5x and 2x
    // come out in that order instead of 1.5x, 1x, 2x.
    sort($definition['multipliers'], SORT_NUMERIC);
  }

  /**
+16 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@
 * Post update functions for Responsive Image.
 */

use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\responsive_image\ResponsiveImageConfigUpdater;
use Drupal\responsive_image\ResponsiveImageStyleInterface;

/**
 * Implements hook_removed_post_updates().
 */
@@ -13,3 +17,15 @@ function responsive_image_removed_post_updates() {
    'responsive_image_post_update_recreate_dependencies' => '9.0.0',
  ];
}

/**
 * Re-order mappings by breakpoint ID and descending numeric multiplier order.
 */
function responsive_image_post_update_order_multiplier_numerically(array &$sandbox = NULL): void {
  $responsive_image_config_updater = \Drupal::classResolver(ResponsiveImageConfigUpdater::class);
  assert($responsive_image_config_updater instanceof ResponsiveImageConfigUpdater);
  $responsive_image_config_updater->setDeprecationsEnabled(FALSE);
  \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'responsive_image_style', function (ResponsiveImageStyleInterface $responsive_image_style) use ($responsive_image_config_updater): bool {
    return $responsive_image_config_updater->orderMultipliersNumerically($responsive_image_style);
  });
}
+35 −6
Original line number Diff line number Diff line
@@ -3,7 +3,9 @@
namespace Drupal\responsive_image\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\responsive_image\ResponsiveImageConfigUpdater;
use Drupal\responsive_image\ResponsiveImageStyleInterface;

/**
@@ -110,6 +112,15 @@ public function __construct(array $values, $entity_type_id = 'responsive_image_s
    parent::__construct($values, $entity_type_id);
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);
    $config_updater = \Drupal::classResolver(ResponsiveImageConfigUpdater::class);
    $config_updater->orderMultipliersNumerically($this);
  }

  /**
   * {@inheritdoc}
   */
@@ -117,22 +128,40 @@ public function addImageStyleMapping($breakpoint_id, $multiplier, array $image_s
    // If there is an existing mapping, overwrite it.
    foreach ($this->image_style_mappings as &$mapping) {
      if ($mapping['breakpoint_id'] === $breakpoint_id && $mapping['multiplier'] === $multiplier) {
        $mapping = [
        $mapping = $image_style_mapping + [
          'breakpoint_id' => $breakpoint_id,
          'multiplier' => $multiplier,
        ] + $image_style_mapping;
        $this->keyedImageStyleMappings = NULL;
        ];
        $this->sortMappings();
        return $this;
      }
    }
    $this->image_style_mappings[] = [
    $this->image_style_mappings[] = $image_style_mapping + [
      'breakpoint_id' => $breakpoint_id,
      'multiplier' => $multiplier,
    ] + $image_style_mapping;
    $this->keyedImageStyleMappings = NULL;
    ];
    $this->sortMappings();
    return $this;
  }

  /**
   * Sort mappings by breakpoint ID and multiplier.
   */
  protected function sortMappings(): void {
    $this->keyedImageStyleMappings = NULL;
    $breakpoints = \Drupal::service('breakpoint.manager')->getBreakpointsByGroup($this->getBreakpointGroup());
    if (empty($breakpoints)) {
      return;
    }
    usort($this->image_style_mappings, static function (array $a, array $b) use ($breakpoints): int {
      $breakpoint_a = $breakpoints[$a['breakpoint_id']] ?? NULL;
      $breakpoint_b = $breakpoints[$b['breakpoint_id']] ?? NULL;
      $first = ((float) mb_substr($a['multiplier'], 0, -1)) * 100;
      $second = ((float) mb_substr($b['multiplier'], 0, -1)) * 100;
      return [$breakpoint_b ? $breakpoint_b->getWeight() : 0, $first] <=> [$breakpoint_a ? $breakpoint_a->getWeight() : 0, $second];
    });
  }

  /**
   * {@inheritdoc}
   */
+71 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\responsive_image;

/**
 * Provides a BC layer for modules providing old configurations.
 *
 * @internal
 *   This class is only meant to fix outdated responsive image configuration and
 *   its methods should not be invoked directly.
 */
final class ResponsiveImageConfigUpdater {

  /**
   * Flag determining whether deprecations should be triggered.
   *
   * @var bool
   */
  private $deprecationsEnabled = TRUE;

  /**
   * Stores which deprecations were triggered.
   *
   * @var bool
   */
  private $triggeredDeprecations = [];

  /**
   * Sets the deprecations enabling status.
   *
   * @param bool $enabled
   *   Whether deprecations should be enabled.
   */
  public function setDeprecationsEnabled(bool $enabled): void {
    $this->deprecationsEnabled = $enabled;
  }

  /**
   * Re-order mappings by breakpoint ID and descending numeric multiplier order.
   *
   * @param \Drupal\responsive_image\ResponsiveImageStyleInterface $responsive_image_style
   *   The responsive image style
   *
   * @return bool
   *   Whether the responsive image style was updated.
   *
   *   TODO: when removing this, evaluate if we need to keep it permanently
   *   to support an upgrade path (migration) from Drupal 7 picture module.
   */
  public function orderMultipliersNumerically(ResponsiveImageStyleInterface $responsive_image_style): bool {
    $changed = FALSE;

    $original_mapping_order = $responsive_image_style->getImageStyleMappings();
    $responsive_image_style->removeImageStyleMappings();
    foreach ($original_mapping_order as $mapping) {
      $responsive_image_style->addImageStyleMapping($mapping['breakpoint_id'], $mapping['multiplier'], $mapping);
    }
    if ($responsive_image_style->getImageStyleMappings() !== $original_mapping_order) {
      $changed = TRUE;
    }

    $deprecations_triggered = &$this->triggeredDeprecations['3267870'][$responsive_image_style->id()];
    if ($this->deprecationsEnabled && $changed && !$deprecations_triggered) {
      $deprecations_triggered = TRUE;
      @trigger_error(sprintf('The responsive image style multiplier re-ordering update for "%s" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Profile, module and theme provided Responsive Image configuration should be updated to accommodate the changes described at https://www.drupal.org/node/3274803.', $responsive_image_style->id()), E_USER_DEPRECATED);
    }

    return $changed;
  }

}
+71 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Test fixture for re-ordering responsive image style multipliers numerically.
 */

use Drupal\Core\Database\Database;

$connection = Database::getConnection();

// Add a responsive image style.
$styles = [];
$styles['langcode'] = 'en';
$styles['status'] = TRUE;
$styles['dependencies']['config'][] = 'image.style.large';
$styles['dependencies']['config'][] = 'image.style.medium';
$styles['dependencies']['config'][] = 'image.style.thumbnail';
$styles['id'] = 'responsive_image_style';
$styles['uuid'] = '46225242-eb4c-4b10-9a8c-966130b18630';
$styles['label'] = 'Responsive Image Style';
$styles['breakpoint_group'] = 'responsive_image';
$styles['fallback_image_style'] = 'medium';
$styles['image_style_mappings'] = [
  [
    'image_mapping_type' => 'sizes',
    'image_mapping' => [
      'sizes' => '75vw',
      'sizes_image_styles' => [
        'medium',
      ],
    ],
    'breakpoint_id' => 'responsive_image.viewport_sizing',
    'multiplier' => '1.5x',
  ],
  [
    'image_mapping_type' => 'sizes',
    'image_mapping' => [
      'sizes' => '100vw',
      'sizes_image_styles' => [
        'large',
      ],
    ],
    'breakpoint_id' => 'responsive_image.viewport_sizing',
    'multiplier' => '2x',
  ],
  [
    'image_mapping_type' => 'sizes',
    'image_mapping' => [
      'sizes' => '50vw',
      'sizes_image_styles' => [
        'thumbnail',
      ],
    ],
    'breakpoint_id' => 'responsive_image.viewport_sizing',
    'multiplier' => '1x',
  ],
];

$connection->insert('config')
  ->fields([
    'collection',
    'name',
    'data',
  ])
  ->values([
    'collection' => '',
    'name' => 'responsive_image.styles.responsive_image_style',
    'data' => serialize($styles),
  ])
  ->execute();
Loading