You are here

trait EntityListTrait in Formatter Suite 8

Formats multiple fields as a list.

For multi-value fields, Drupal invokes the formatter with the list of field values and each one is formatted on its own. They are then added to the page and separated only by spaces.

This formatter presents a list of values as a group and formats them as one of:

  • Single line list using <div> around the group, and <span> for each value.
  • Numbered list using <ol> around the group, and <li> for each value.
  • Bulleted list using <ul> around the group, and <li> for each value.
  • Non-bulleted list using <div> around the group, and <div> for each value.

Optional list separator text is added between each value, such as a comma.

Hierarchy

Related topics

File

src/Plugin/Field/FieldFormatter/EntityListTrait.php, line 30

Namespace

Drupal\formatter_suite\Plugin\Field\FieldFormatter
View source
trait EntityListTrait {

  /*---------------------------------------------------------------------
   *
   * Configuration.
   *
   *---------------------------------------------------------------------*/

  /**
   * Returns an array of list styles.
   *
   * @return string[]
   *   Returns an associative array with internal names as keys and
   *   human-readable translated names as values.
   */
  protected static function getListStyles() {
    return [
      'span' => t('Single line list'),
      'ol' => t('Numbered list'),
      'ul' => t('Bulleted list'),
      'div' => t('Non-bulleted list'),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return array_merge([
      'listStyle' => 'span',
      'listSeparator' => ',',
    ], parent::defaultSettings());
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {
    $summary = parent::settingsSummary();
    $this
      ->sanitizeListSettings();
    $listStyles = $this
      ->getListStyles();
    $listStyle = $this
      ->getSetting('listStyle');
    $listSeparator = $this
      ->getSetting('listSeparator');

    // Summarize.
    $text = $listStyles[$listStyle];
    if ($listStyle === 'span' && empty($listSeparator) === FALSE) {
      $text .= $this
        ->t(', with separator');
    }
    $summary[] = $text;
    return $summary;
  }

  /*---------------------------------------------------------------------
   *
   * Settings form.
   *
   *---------------------------------------------------------------------*/

  /**
   * Returns a brief description of the formatter.
   *
   * @return string
   *   Returns a brief translated description of the formatter.
   */
  protected function getDescription() {
    return $this
      ->t('Format multi-value fields as a list.');
  }

  /**
   * Post-processes the settings form after it has been built.
   *
   * This may be used by formatters that use this trait to adjust the
   * form after it has been updated with list features.
   *
   * @param array $elements
   *   The form's elements.
   */
  protected function postProcessSettingsForm(array $elements) {
    return $elements;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $formState) {

    // Get the parent's form.
    $this
      ->sanitizeListSettings();
    $elements = parent::settingsForm($form, $formState);

    // Below, some checkboxes and select choices show/hide other form
    // elements. We use Drupal's obscure 'states' feature, which adds
    // Javascript to elements to auto show/hide based upon a set of
    // simple conditions.
    //
    // Those conditions need to reference the form elements to check
    // (e.g. a checkbox), but the element ID and name are automatically
    // generated by the parent form. We cannot set them, or predict them,
    // so we cannot use them. We could use a class, but this form may be
    // shown multiple times on the same page, so a simple class would not be
    // unique. Instead, we create classes for this form only by adding a
    // random number marker to the end of the class name.
    $marker = rand();

    // Add branding.
    $elements = Branding::addFieldFormatterBranding($elements);
    $elements['#attached']['library'][] = 'formatter_suite/formatter_suite.fieldformatter';

    // Add description.
    //
    // Use a large negative weight to insure it comes first.
    $elements['description'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#value' => $this
        ->getDescription(),
      '#weight' => -1000,
      '#attributes' => [
        'class' => [
          'formatter_suite-settings-description',
        ],
      ],
    ];
    $weight = 100;

    // Prompt for each setting.
    $elements['sectionBreak'] = [
      '#markup' => '<div class="formatter_suite-section-break"></div>',
      '#weight' => $weight++,
    ];
    $elements['listStyle'] = [
      '#title' => $this
        ->t('List style'),
      '#type' => 'select',
      '#options' => $this
        ->getListStyles(),
      '#default_value' => $this
        ->getSetting('listStyle'),
      '#weight' => $weight++,
      '#wrapper_attributes' => [
        'class' => [
          'formatter_suite-list-style',
        ],
      ],
      '#attributes' => [
        'class' => [
          'listStyle-' . $marker,
        ],
      ],
    ];
    $elements['listSeparator'] = [
      '#title' => $this
        ->t('Separator'),
      '#type' => 'textfield',
      '#size' => 10,
      '#default_value' => $this
        ->getSetting('listSeparator'),
      '#weight' => $weight++,
      '#attributes' => [
        'autocomplete' => 'off',
        'autocapitalize' => 'none',
        'spellcheck' => 'false',
        'autocorrect' => 'off',
      ],
      '#wrapper_attributes' => [
        'class' => [
          'formatter_suite-list-separator',
        ],
      ],
      '#states' => [
        'visible' => [
          '.listStyle-' . $marker => [
            'value' => 'span',
          ],
        ],
      ],
    ];
    return $this
      ->postProcessSettingsForm($elements);
  }

  /**
   * Check current settings and insure they make sense.
   */
  protected function sanitizeListSettings() {
    $listStyle = $this
      ->getSetting('listStyle');
    $listStyles = $this
      ->getListStyles();
    $defaults = $this
      ->defaultSettings();
    if (empty($listStyle) === TRUE || isset($listStyles[$listStyle]) === FALSE) {
      $listStyle = $defaults['listStyle'];
      $this
        ->setSetting('listStyle', $listStyle);
    }
  }

  /*---------------------------------------------------------------------
   *
   * View.
   *
   *---------------------------------------------------------------------*/

  /**
   * {@inheritdoc}
   */
  public function view(FieldItemListInterface $items, $langcode = NULL) {
    if ($items
      ->isEmpty() === TRUE) {
      return [];
    }

    // Let the parent class set up a render array for all items.
    $this
      ->sanitizeListSettings();
    $build = parent::view($items, $langcode);
    if (empty($build) === TRUE) {
      return [];
    }

    // Replace the 'field' theme with ours, which supports lists.
    $build['#theme'] = 'formatter_suite_field_list';

    // Set the list style.
    $build['#list_style'] = $this
      ->getSetting('listStyle');

    // Set the list separator.
    //
    // Security: The list separator is entered by an administrator.
    // It may legitimately include HTML entities and minor HTML, but
    // it should not include dangerous HTML.
    //
    // Because it may include HTML, we cannot pass it directly to t()
    // or let a TWIG template use {{ }}, both of which will process
    // the text and corrupt any entered HTML or HTML entities.
    //
    // We therefore use an Xss filter to remove any egreggious HTML
    // (such as scripts), and then FormattableMarkup() to mark the
    // resulting text as safe.
    $listSeparator = Xss::filterAdmin($this
      ->getSetting('listSeparator'));
    $build['#list_separator'] = new FormattableMarkup($listSeparator, []);
    return $build;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
EntityListTrait::defaultSettings public static function
EntityListTrait::getDescription protected function Returns a brief description of the formatter. 6
EntityListTrait::getListStyles protected static function Returns an array of list styles.
EntityListTrait::postProcessSettingsForm protected function Post-processes the settings form after it has been built. 3
EntityListTrait::sanitizeListSettings protected function Check current settings and insure they make sense.
EntityListTrait::settingsForm public function
EntityListTrait::settingsSummary public function
EntityListTrait::view public function