Commit 4e86ea5a authored by Damien McKenna's avatar Damien McKenna
Browse files

Issue #2914998 by JeroenT, DamienMcKenna, gngn, ytsurk: Add the mask icon "color" attribute.

parent 073b3913
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ Metatag 8.x-1.x-dev, xxxx-xx-xx
  meta tag lengths.
#3247678 by esolitos, DamienMcKenna: Support migration with multiple "d7_*"
  sources.
#2914998 by JeroenT, DamienMcKenna, gngn, ytsurk: Add the mask icon "color"
  attribute.


Metatag 8.x-1.16, 2021-03-15
+131 −0
Original line number Diff line number Diff line
@@ -74,3 +74,134 @@ function metatag_update_8107() {
function metatag_update_8108() {
  return (string) new TranslatableMarkup("The sites's caches will need to be rebuild to ensure Metatag works as intended.");
}

/**
 * Update mask_icon values to the new structure.
 */
function metatag_update_8109(&$sandbox) {
  // This whole top section only needs to be done the first time.
  if (!isset($sandbox['records_processed'])) {
    $sandbox['records_processed'] = 0;
    $sandbox['total_records'] = 0;
    $sandbox['current_field'] = 0;
    $sandbox['current_record'] = 0;

    // Counter to enumerate the fields so we can access them in the array
    // by number rather than name.
    $field_counter = 0;

    // Get all of the field storage entities of type metatag.
    $field_storage_configs = \Drupal::entityTypeManager()
      ->getStorage('field_storage_config')
      ->loadByProperties(['type' => 'metatag']);

    foreach ($field_storage_configs as $field_storage) {
      $field_name = $field_storage->getName();

      // Get the individual fields (field instances) associated with bundles.
      $fields = \Drupal::entityTypeManager()
        ->getStorage('field_config')
        ->loadByProperties(['field_name' => $field_name]);

      foreach ($fields as $field) {
        // Get the bundle this field is attached to.
        $bundle = $field->getTargetBundle();

        // Determine the table and "value" field names.
        $field_table = "node__" . $field_name;
        $field_value_field = $field_name . "_value";

        // Get all records where the field data does not match the default.
        $query = \Drupal::database()->select($field_table);
        $query->addField($field_table, 'entity_id');
        $query->addField($field_table, 'revision_id');
        $query->addField($field_table, 'langcode');
        $query->addField($field_table, $field_value_field);
        $query->condition('bundle', $bundle, '=');
        $result = $query->execute();
        $records = $result->fetchAll();

        // Fill in all the sandbox information so we can batch the individual
        // record comparing and updating.
        $sandbox['fields'][$field_counter]['field_table'] = $field_table;
        $sandbox['fields'][$field_counter]['field_value_field'] = $field_value_field;
        $sandbox['fields'][$field_counter]['records'] = $records;

        $sandbox['total_records'] += count($sandbox['fields'][$field_counter]['records'] = $records);
        $field_counter++;
      }
    }
  }

  if ($sandbox['total_records'] == 0) {
    // No partially overridden fields so we can skip the whole batch process.
    $sandbox['#finished'] = 1;
  }
  else {
    // Begin the batch processing of individual field records.
    $max_per_batch = 10;
    $counter = 1;

    $current_field = $sandbox['current_field'];
    $current_field_records = $sandbox['fields'][$current_field]['records'];
    $current_record = $sandbox['current_record'];

    $field_table = $sandbox['fields'][$current_field]['field_table'];
    $field_value_field = $sandbox['fields'][$current_field]['field_value_field'];

    // Loop through the field(s) and update the mask_icon values if necessary.
    while ($counter <= $max_per_batch && $record = $current_field_records[$current_record]) {
      // Strip any empty tags or ones matching the field's defaults and leave
      // only the overridden tags in $new_tags.
      $tags = unserialize($record->$field_value_field);
      if (isset($tags['mask-icon'])) {
        $tags['mask_icon'] = [
          'href' => $tags['mask-icon'],
        ];

        $tags_string = serialize($tags);
        \Drupal::database()->update($field_table)
          ->fields([
            $field_value_field => $tags_string,
          ])
          ->condition('entity_id', $record->entity_id)
          ->condition('revision_id', $record->revision_id)
          ->condition('langcode', $record->langcode)
          ->execute();
      }

      $counter++;
      $current_record++;
    }

    // We ran out of records for the field so start the next batch out with the
    // next field.
    if (!isset($current_field_records[$current_record])) {
      $current_field++;
      $current_record = 0;
    }

    // We have finished all the fields. All done.
    if (!isset($sandbox['fields'][$current_field])) {
      $sandbox['records_processed'] += $counter - 1;
      $sandbox['#finished'] = 1;
    }
    // Update the sandbox values to prepare for the next round.
    else {
      $sandbox['current_field'] = $current_field;
      $sandbox['current_record'] = $current_record;
      $sandbox['records_processed'] += $counter - 1;
      $sandbox['#finished'] = $sandbox['records_processed'] / $sandbox['total_records'];
    }
  }

  if ($sandbox['total_records'] > 0) {
    return (string) t('Processed @processed of @total overridden Metatag records.', [
      '@processed' => $sandbox['records_processed'],
      '@total' => $sandbox['total_records'],
    ]);
  }
  else {
    return (string) t("There were no overridden Metatag records.");
  }
}
+28 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Post update functions for Metatag.
 */

use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\metatag\Entity\MetatagDefaults;

/**
 * Convert mask-icon to array values.
 */
function metatag_post_update_convert_mask_icon_to_array_values(&$sandbox) {
  $config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class);
  $config_entity_updater->update($sandbox, 'metatag_defaults', function (MetatagDefaults $metatag_defaults) {
    if ($metatag_defaults->hasTag('mask-icon')) {
      $tags = $metatag_defaults->get('tags');
      $tags['mask_icon'] = [
        'href' => $metatag_defaults->getTag('mask-icon'),
      ];
      unset($tags['mask-icon']);
      $metatag_defaults->set('tags', $tags);
      return TRUE;
    }
    return FALSE;
  });
}
+9 −2
Original line number Diff line number Diff line
@@ -5,9 +5,16 @@
metatag.metatag_tag.shortcut_icon:
  type: label
  label: 'Default shortcut icon'
metatag.metatag_tag.mask-icon:
  type: label
metatag.metatag_tag.mask_icon:
  type: mapping
  label: 'Icon: SVG'
  mapping:
    href:
      type: label
      label: 'href'
    color:
      type: label
      label: 'Color'
metatag.metatag_tag.icon_16x16:
  type: label
  label: 'Icon: 16px x 16px'
+63 −3
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@ use Drupal\metatag\Plugin\metatag\Tag\LinkRelBase;
 * The Favicons "mask-icon" meta tag.
 *
 * @MetatagTag(
 *   id = "mask-icon",
 *   label = @Translation("Icon: SVG"),
 *   id = "mask_icon",
 *   label = @Translation("Mask icon (SVG)"),
 *   description = @Translation("A grayscale scalable vector graphic (SVG) file."),
 *   name = "mask-icon",
 *   group = "favicons",
@@ -20,5 +20,65 @@ use Drupal\metatag\Plugin\metatag\Tag\LinkRelBase;
 * )
 */
class MaskIcon extends LinkRelBase {
  // Nothing here yet. Just a placeholder class for a plugin.

  /**
   * {@inheritdoc}
   */
  public function form(array $element = []) {
    $form['#container'] = TRUE;
    $form['#tree'] = TRUE;

    // Backwards compatibility.
    $defaults = $this->value();
    if (is_string($defaults)) {
      $defaults = [
        'href' => $defaults,
        'color' => '',
      ];
    }

    // The main icon value.
    $form['href'] = [
      '#type' => 'textfield',
      '#title' => $this->label(),
      '#default_value' => isset($defaults['href']) ? $defaults['href'] : '',
      '#maxlength' => 255,
      '#required' => isset($element['#required']) ? $element['#required'] : FALSE,
      '#description' => $this->description(),
      '#element_validate' => [[get_class($this), 'validateTag']],
    ];

    // New form element for color.
    $form['color'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Mask icon color'),
      '#default_value' => isset($defaults['color']) ? $defaults['color'] : '',
      '#required' => FALSE,
      '#description' => $this->t("Color attribute for SVG (mask) icon in hexadecimal format, e.g. '#0000ff'. Setting it will break HTML validation. If not set macOS Safari ignores the Mask Icon entirely, making the Icon: SVG completely useless."),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function output() {
    $values = $this->value;

    // Build the output.
    $element['#tag'] = 'link';
    $element['#attributes'] = [
      'rel' => $this->name(),
      'href' => $this->tidy($values['href']),
    ];

    // Add the 'color' element.
    if (!empty($values['color'])) {
      $element['#attributes']['color'] = $this->tidy($values['color']);
    }

    return $element;
  }

}
Loading