Skip to content
FieldPluginBase.php 62.5 KiB
Newer Older
Earl Miles's avatar
Earl Miles committed
<?php

namespace Drupal\views\Plugin\views\field;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\UrlHelper;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Render\ViewsRenderPipelineMarkup;
Earl Miles's avatar
Earl Miles committed
/**
 * @defgroup views_field_handlers Views field handler plugins
Earl Miles's avatar
Earl Miles committed
 * @{
 * Field handlers handle both querying and display of fields in views.
 *
 * Field handler plugins extend
 * \Drupal\views\Plugin\views\field\FieldPluginBase. They must be
 * annotated with \Drupal\views\Annotation\ViewsField annotation, and they
 * must be in namespace directory Plugin\views\field.
 *
 * The following items can go into a hook_views_data() implementation in a
 * field section to affect how the field handler will behave:
 * - additional fields: An array of fields that should be added to the query.
 *   The array is in one of these forms:
 *   // Simple form, for fields within the same table.
 *   array('identifier' => fieldname)
 *   // Form for fields in a different table.
 *   array('identifier' => array('table' => tablename, 'field' => fieldname))
 *   @endcode
 *   As many fields as are necessary may be in this array.
 * - click sortable: If TRUE (default), this field may be click sorted.
 *
 * @ingroup views_plugins
 * @see plugin_api

/**
 * Base class for views fields.
 *
 * @ingroup views_field_handlers
 */
abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterface {
  /**
   * Indicator of the renderText() method for rendering a single item.
   * (If no render_item() is present).
   */
  const RENDER_TEXT_PHASE_SINGLE_ITEM = 0;

  /**
   * Indicator of the renderText() method for rendering the whole element.
   * (if no render_item() method is available).
   */
  const RENDER_TEXT_PHASE_COMPLETELY = 1;

  /**
   * Indicator of the renderText() method for rendering the empty text.
   */
  const RENDER_TEXT_PHASE_EMPTY = 2;

  public $field_alias = 'unknown';
  /**
   * The field value prior to any rewriting.
   *
   * @var mixed
   */
  public $original_value = NULL;

Earl Miles's avatar
Earl Miles committed
  /**
   * Stores additional fields which get added to the query.
   *
Earl Miles's avatar
Earl Miles committed
   * The generated aliases are stored in $aliases.
  /**
   * The link generator.
   *
   * @var \Drupal\Core\Utility\LinkGeneratorInterface
   */
  protected $linkGenerator;

   * @var \Drupal\Core\Render\RendererInterface
  /**
   * Keeps track of the last render index.
   *
   * @var int|NULL
   */
  protected $lastRenderIndex;

  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
    parent::init($view, $display, $options);
Earl Miles's avatar
Earl Miles committed
    if (!empty($this->definition['additional fields'])) {
      $this->additional_fields = $this->definition['additional fields'];
    }

    if (!isset($this->options['exclude'])) {
      $this->options['exclude'] = '';
    }
  }

  /**
   * Determine if this field can allow advanced rendering.
   *
   * Fields can set this to FALSE if they do not wish to allow
   * token based rewriting or link-making.
   */
  protected function allowAdvancedRender() {
Earl Miles's avatar
Earl Miles committed
    return TRUE;
  }

  /**
   * Called to add the field to a query.
   */
Earl Miles's avatar
Earl Miles committed
    // Add the field.
    $params = $this->options['group_type'] != 'group' ? ['function' => $this->options['group_type']] : [];
    $this->field_alias = $this->query->addField($this->tableAlias, $this->realField, NULL, $params);
    $this->addAdditionalFields();
Earl Miles's avatar
Earl Miles committed
  }

  /**
   * Add 'additional' fields to the query.
   *
   * @param $fields
   *   An array of fields. The key is an identifier used to later find the
   *   field alias used. The value is either a string in which case it's
   *   assumed to be a field on this handler's table; or it's an array in the
   *   form of
   *   @code array('table' => $tablename, 'field' => $fieldname) @endcode
  protected function addAdditionalFields($fields = NULL) {
Earl Miles's avatar
Earl Miles committed
    if (!isset($fields)) {
      // notice check
      if (empty($this->additional_fields)) {
        return;
      }
      $fields = $this->additional_fields;
    }

Earl Miles's avatar
Earl Miles committed
    if ($this->options['group_type'] != 'group') {
Earl Miles's avatar
Earl Miles committed
        'function' => $this->options['group_type'],
Earl Miles's avatar
Earl Miles committed
    }

    if (!empty($fields) && is_array($fields)) {
      foreach ($fields as $identifier => $info) {
        if (is_array($info)) {
          if (isset($info['table'])) {
            $table_alias = $this->query->ensureTable($info['table'], $this->relationship);
Earl Miles's avatar
Earl Miles committed
          }
          else {
            $table_alias = $this->tableAlias;
Earl Miles's avatar
Earl Miles committed
          }

          if (empty($table_alias)) {
            debug(t('Handler @handler tried to add additional_field @identifier but @table could not be added!', ['@handler' => $this->definition['id'], '@identifier' => $identifier, '@table' => $info['table']]));
Earl Miles's avatar
Earl Miles committed
            $this->aliases[$identifier] = 'broken';
            continue;
          }

Earl Miles's avatar
Earl Miles committed
          if (!empty($info['params'])) {
            $params = $info['params'];
          }

          $params += $group_params;
          $this->aliases[$identifier] = $this->query->addField($table_alias, $info['field'], NULL, $params);
Earl Miles's avatar
Earl Miles committed
        }
        else {
          $this->aliases[$info] = $this->query->addField($this->tableAlias, $info, NULL, $group_params);
Earl Miles's avatar
Earl Miles committed
    if (isset($this->field_alias)) {
      // Since fields should always have themselves already added, just
      // add a sort on the field.
      $params = $this->options['group_type'] != 'group' ? ['function' => $this->options['group_type']] : [];
      $this->query->addOrderBy(NULL, NULL, $order, $this->field_alias, $params);
    return isset($this->definition['click sortable']) ? $this->definition['click sortable'] : TRUE;
Earl Miles's avatar
Earl Miles committed
    if (!isset($this->options['label'])) {
      return '';
    }
    return $this->options['label'];
  }

  /**
  public function elementType($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) {
Earl Miles's avatar
Earl Miles committed
    if ($none_supported) {
      if ($this->options['element_type'] === '0') {
        return '';
      }
    }
    if ($this->options['element_type']) {
      return $this->options['element_type'];
Earl Miles's avatar
Earl Miles committed
    }

    if ($default_empty) {
      return '';
    }

    if ($inline) {
      return 'span';
    }

    if (isset($this->definition['element type'])) {
      return $this->definition['element type'];
    }

    return 'span';
  }

  /**
  public function elementLabelType($none_supported = FALSE, $default_empty = FALSE) {
Earl Miles's avatar
Earl Miles committed
    if ($none_supported) {
      if ($this->options['element_label_type'] === '0') {
        return '';
      }
    }
    if ($this->options['element_label_type']) {
      return $this->options['element_label_type'];
Earl Miles's avatar
Earl Miles committed
    }

    if ($default_empty) {
      return '';
    }

    return 'span';
  }

  /**
  public function elementWrapperType($none_supported = FALSE, $default_empty = FALSE) {
Earl Miles's avatar
Earl Miles committed
    if ($none_supported) {
      if ($this->options['element_wrapper_type'] === '0') {
        return 0;
      }
    }
    if ($this->options['element_wrapper_type']) {
      return $this->options['element_wrapper_type'];
Earl Miles's avatar
Earl Miles committed
    }

    if ($default_empty) {
      return '';
    }

    return 'div';
  }

  /**
Earl Miles's avatar
Earl Miles committed
    static $elements = NULL;
    if (!isset($elements)) {
      // @todo Add possible html5 elements.
        '' => $this->t('- Use default -'),
      $elements += \Drupal::config('views.settings')->get('field_rewrite_elements');
Earl Miles's avatar
Earl Miles committed
    }

    return $elements;
  }

  /**
  public function elementClasses($row_index = NULL) {
    $classes = $this->tokenizeValue($this->options['element_class'], $row_index);
    $classes = explode(' ', $classes);
Earl Miles's avatar
Earl Miles committed
    foreach ($classes as &$class) {
      $class = Html::cleanCssIdentifier($class);
Earl Miles's avatar
Earl Miles committed
    }
    return implode(' ', $classes);
  }

  /**
  public function tokenizeValue($value, $row_index = NULL) {
    if (strpos($value, '{{') !== FALSE) {
Earl Miles's avatar
Earl Miles committed
        'alter_text' => TRUE,
        'text' => $value,
Earl Miles's avatar
Earl Miles committed

      // Use isset() because empty() will trigger on 0 and 0 is
      // the first row.
      if (isset($row_index) && isset($this->view->style_plugin->render_tokens[$row_index])) {
        $tokens = $this->view->style_plugin->render_tokens[$row_index];
      }
      else {
        // Get tokens from the last field.
        $last_field = end($this->view->field);
        if (isset($last_field->last_tokens)) {
          $tokens = $last_field->last_tokens;
        }
        else {
          $tokens = $last_field->getRenderTokens($fake_item);
      $value = strip_tags($this->renderAltered($fake_item, $tokens));
Earl Miles's avatar
Earl Miles committed
      if (!empty($this->options['alter']['trim_whitespace'])) {
        $value = trim($value);
      }
    }

    return $value;
  }

  /**
  public function elementLabelClasses($row_index = NULL) {
    $classes = $this->tokenizeValue($this->options['element_label_class'], $row_index);
    $classes = explode(' ', $classes);
Earl Miles's avatar
Earl Miles committed
    foreach ($classes as &$class) {
      $class = Html::cleanCssIdentifier($class);
Earl Miles's avatar
Earl Miles committed
    }
    return implode(' ', $classes);
  }

  /**
  public function elementWrapperClasses($row_index = NULL) {
    $classes = $this->tokenizeValue($this->options['element_wrapper_class'], $row_index);
    $classes = explode(' ', $classes);
Earl Miles's avatar
Earl Miles committed
    foreach ($classes as &$class) {
      $class = Html::cleanCssIdentifier($class);
Earl Miles's avatar
Earl Miles committed
    }
    return implode(' ', $classes);
  }

  public function getEntity(ResultRow $values) {
    $relationship_id = $this->options['relationship'];
    if ($relationship_id == 'none') {
      return $values->_entity;
    }
    elseif (isset($values->_relationship_entities[$relationship_id])) {
  public function getValue(ResultRow $values, $field = NULL) {
Earl Miles's avatar
Earl Miles committed
    $alias = isset($field) ? $this->aliases[$field] : $this->field_alias;
    if (isset($values->{$alias})) {
      return $values->{$alias};
    }
  }

  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['label'] = ['default' => ''];
    // Some styles (for example table) should have labels enabled by default.
    $style = $this->view->getStyle();
    if (isset($style) && $style->defaultFieldLabels()) {
      $options['label']['default'] = $this->definition['title'];
    }

    $options['exclude'] = ['default' => FALSE];
    $options['alter'] = [
      'contains' => [
        'alter_text' => ['default' => FALSE],
        'text' => ['default' => ''],
        'make_link' => ['default' => FALSE],
        'path' => ['default' => ''],
        'absolute' => ['default' => FALSE],
        'external' => ['default' => FALSE],
        'replace_spaces' => ['default' => FALSE],
        'path_case' => ['default' => 'none'],
        'trim_whitespace' => ['default' => FALSE],
        'alt' => ['default' => ''],
        'rel' => ['default' => ''],
        'link_class' => ['default' => ''],
        'prefix' => ['default' => ''],
        'suffix' => ['default' => ''],
        'target' => ['default' => ''],
        'nl2br' => ['default' => FALSE],
        'max_length' => ['default' => 0],
        'word_boundary' => ['default' => TRUE],
        'ellipsis' => ['default' => TRUE],
        'more_link' => ['default' => FALSE],
        'more_link_text' => ['default' => ''],
        'more_link_path' => ['default' => ''],
        'strip_tags' => ['default' => FALSE],
        'trim' => ['default' => FALSE],
        'preserve_tags' => ['default' => ''],
        'html' => ['default' => FALSE],
      ],
    ];
    $options['element_type'] = ['default' => ''];
    $options['element_class'] = ['default' => ''];

    $options['element_label_type'] = ['default' => ''];
    $options['element_label_class'] = ['default' => ''];
    $options['element_label_colon'] = ['default' => TRUE];

    $options['element_wrapper_type'] = ['default' => ''];
    $options['element_wrapper_class'] = ['default' => ''];

    $options['element_default_classes'] = ['default' => TRUE];

    $options['empty'] = ['default' => ''];
    $options['hide_empty'] = ['default' => FALSE];
    $options['empty_zero'] = ['default' => FALSE];
    $options['hide_alter_empty'] = ['default' => TRUE];
Earl Miles's avatar
Earl Miles committed

    return $options;
  }

  /**
   * Performs some cleanup tasks on the options array before saving it.
   */
  public function submitOptionsForm(&$form, FormStateInterface $form_state) {
    $types = ['element_type', 'element_label_type', 'element_wrapper_type'];
    $classes = array_combine(['element_class', 'element_label_class', 'element_wrapper_class'], $types);
Earl Miles's avatar
Earl Miles committed

    foreach ($types as $type) {
      if (!$options[$type . '_enable']) {
        $options[$type] = '';
      }
    }

    foreach ($classes as $class => $type) {
      if (!$options[$class . '_enable'] || !$options[$type . '_enable']) {
        $options[$class] = '';
      }
    }

    if (empty($options['custom_label'])) {
      $options['label'] = '';
      $options['element_label_colon'] = FALSE;
    }
  }

  /**
   * Default options form that provides the label widget that all fields
   * should have.
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);
Earl Miles's avatar
Earl Miles committed

    $label = $this->label();
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Create a label'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => $label !== '',
      '#weight' => -103,
Earl Miles's avatar
Earl Miles committed
      '#type' => 'textfield',
Earl Miles's avatar
Earl Miles committed
      '#default_value' => $label,
      '#states' => [
        'visible' => [
          ':input[name="options[custom_label]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#weight' => -102,
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Place a colon after the label'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => $this->options['element_label_colon'],
      '#states' => [
        'visible' => [
          ':input[name="options[custom_label]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#weight' => -101,
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Exclude from display'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => $this->options['exclude'],
      '#description' => $this->t('Enable to load this field as hidden. Often used to group fields, or to use as token in another field.'),
Earl Miles's avatar
Earl Miles committed
      '#weight' => -100,
      '#title' => $this->t('Style settings'),
Earl Miles's avatar
Earl Miles committed
      '#weight' => 99,
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Customize field HTML'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_type']) || (string) $this->options['element_type'] == '0' || !empty($this->options['element_class']) || (string) $this->options['element_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('HTML element'),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'select',
      '#default_value' => $this->options['element_type'],
      '#description' => $this->t('Choose the HTML element to wrap around this field, e.g. H1, H2, etc.'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Create a CSS class'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_class']) || (string) $this->options['element_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('CSS class'),
      '#description' => $this->t('You may use token substitutions from the rewriting section in this class.'),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'textfield',
      '#default_value' => $this->options['element_class'],
      '#states' => [
        'visible' => [
          ':input[name="options[element_type_enable]"]' => ['checked' => TRUE],
          ':input[name="options[element_class_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
    $form['element_label_type_enable'] = [
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Customize label HTML'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_label_type']) || (string) $this->options['element_label_type'] == '0' || !empty($this->options['element_label_class']) || (string) $this->options['element_label_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('Label HTML element'),
      '#options' => $this->getElements(FALSE),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'select',
      '#default_value' => $this->options['element_label_type'],
      '#description' => $this->t('Choose the HTML element to wrap around this label, e.g. H1, H2, etc.'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_label_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
    ];
    $form['element_label_class_enable'] = [
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Create a CSS class'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_label_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_label_class']) || (string) $this->options['element_label_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('CSS class'),
      '#description' => $this->t('You may use token substitutions from the rewriting section in this class.'),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'textfield',
      '#default_value' => $this->options['element_label_class'],
      '#states' => [
        'visible' => [
          ':input[name="options[element_label_type_enable]"]' => ['checked' => TRUE],
          ':input[name="options[element_label_class_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
    $form['element_wrapper_type_enable'] = [
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Customize field and label wrapper HTML'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_wrapper_type']) || (string) $this->options['element_wrapper_type'] == '0' || !empty($this->options['element_wrapper_class']) || (string) $this->options['element_wrapper_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('Wrapper HTML element'),
      '#options' => $this->getElements(FALSE),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'select',
      '#default_value' => $this->options['element_wrapper_type'],
      '#description' => $this->t('Choose the HTML element to wrap around this field and label, e.g. H1, H2, etc. This may not be used if the field and label are not rendered together, such as with a table.'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_wrapper_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
    $form['element_wrapper_class_enable'] = [
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Create a CSS class'),
      '#states' => [
        'visible' => [
          ':input[name="options[element_wrapper_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#default_value' => !empty($this->options['element_wrapper_class']) || (string) $this->options['element_wrapper_class'] == '0',
      '#fieldset' => 'style_settings',
      '#title' => $this->t('CSS class'),
      '#description' => $this->t('You may use token substitutions from the rewriting section in this class.'),
Earl Miles's avatar
Earl Miles committed
      '#type' => 'textfield',
      '#default_value' => $this->options['element_wrapper_class'],
      '#states' => [
        'visible' => [
          ':input[name="options[element_wrapper_class_enable]"]' => ['checked' => TRUE],
          ':input[name="options[element_wrapper_type_enable]"]' => ['checked' => TRUE],
        ],
      ],
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
    $form['element_default_classes'] = [
Earl Miles's avatar
Earl Miles committed
      '#type' => 'checkbox',
      '#title' => $this->t('Add default classes'),
Earl Miles's avatar
Earl Miles committed
      '#default_value' => $this->options['element_default_classes'],
      '#description' => $this->t('Use default Views classes to identify the field, field label and field content.'),
Earl Miles's avatar
Earl Miles committed
      '#fieldset' => 'style_settings',
      '#title' => $this->t('Rewrite results'),
Earl Miles's avatar
Earl Miles committed
      '#weight' => 100,
    if ($this->allowAdvancedRender()) {
Earl Miles's avatar
Earl Miles committed
      $form['alter']['#tree'] = TRUE;
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Override the output of this field with custom text'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['alter_text'],
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textarea',
        '#default_value' => $this->options['alter']['text'],
        '#description' => $this->t('The text to display for this field. You may include HTML or <a href=":url">Twig</a>. You may enter data from this view as per the "Replacement patterns" below.', [':url' => CoreUrl::fromUri('http://twig.sensiolabs.org/documentation')->toString()]),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][alter_text]"]' => ['checked' => TRUE],
          ],
        ],
      ];

      $form['alter']['make_link'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Output this field as a custom link'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['make_link'],
        '#title' => $this->t('Link path'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['path'],
        '#description' => $this->t('The Drupal path or absolute URL for this link. You may enter data from this view as per the "Replacement patterns" below.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
Earl Miles's avatar
Earl Miles committed
        '#maxlength' => 255,
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Use absolute path'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['absolute'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['replace_spaces'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Replace spaces with dashes'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['replace_spaces'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['external'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('External server URL'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['external'],
        '#description' => $this->t("Links to an external server using a full URL: e.g. 'http://www.example.com' or 'www.example.com'."),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['path_case'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'select',
        '#title' => $this->t('Transform the case'),
        '#description' => $this->t('When printing URL paths, how to transform the case of the filter value.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
          'none' => $this->t('No transform'),
          'upper' => $this->t('Upper case'),
          'lower' => $this->t('Lower case'),
          'ucfirst' => $this->t('Capitalize first letter'),
          'ucwords' => $this->t('Capitalize each word'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['path_case'],
        '#title' => $this->t('Link class'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['link_class'],
        '#description' => $this->t('The CSS class to apply to the link.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['alt'] = [
        '#title' => $this->t('Title text'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['alt'],
        '#description' => $this->t('Text to place as "title" text which most browsers display as a tooltip when hovering over the link.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['rel'] = [
        '#title' => $this->t('Rel Text'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['rel'],
        '#description' => $this->t('Include Rel attribute for use in lightbox2 or other javascript utility.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['prefix'] = [
        '#title' => $this->t('Prefix text'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['prefix'],
        '#description' => $this->t('Any text to display before this link. You may include HTML.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['suffix'] = [
        '#title' => $this->t('Suffix text'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['suffix'],
        '#description' => $this->t('Any text to display after this link. You may include HTML.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['target'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['target'],
        '#description' => $this->t("Target of the link, such as _blank, _parent or an iframe's name. This field is rarely used."),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
Earl Miles's avatar
Earl Miles committed

      // Get a list of the available fields and arguments for token replacement.

      // Setup the tokens for fields.
      $previous = $this->getPreviousFieldLabels();
      $optgroup_arguments = (string) t('Arguments');
      $optgroup_fields = (string) t('Fields');
        $options[$optgroup_fields]["{{ $id }}"] = substr(strrchr($label, ":"), 2);
      $options[$optgroup_fields]["{{ {$this->options['id']} }}"] = substr(strrchr($this->adminLabel(), ":"), 2);
      foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
        $options[$optgroup_arguments]["{{ arguments.$arg }}"] = $this->t('@argument title', ['@argument' => $handler->adminLabel()]);
        $options[$optgroup_arguments]["{{ raw_arguments.$arg }}"] = $this->t('@argument input', ['@argument' => $handler->adminLabel()]);
      $this->documentSelfTokens($options[$optgroup_fields]);
Earl Miles's avatar
Earl Miles committed

      // Default text.

      $output = [];
      $output[] = [
        '#markup' => '<p>' . $this->t('You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.') . '</p>',
      ];
Earl Miles's avatar
Earl Miles committed
      // We have some options, so make a list.
      if (!empty($options)) {
        $output[] = [
          '#markup' => '<p>' . $this->t("The following replacement tokens are available for this field. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.") . '</p>',
        ];
Earl Miles's avatar
Earl Miles committed
        foreach (array_keys($options) as $type) {
          if (!empty($options[$type])) {
Earl Miles's avatar
Earl Miles committed
            foreach ($options[$type] as $key => $value) {
              $items[] = $key . ' == ' . $value;
            }
              '#theme' => 'item_list',
              '#items' => $items,
Earl Miles's avatar
Earl Miles committed
          }
        }
      }
      // This construct uses 'hidden' and not markup because process doesn't
      // run. It also has an extra div because the dependency wants to hide
      // the parent in situations like this, so we need a second div to
      // make this work.
        '#title' => $this->t('Replacement patterns'),
        '#states' => [
          'visible' => [
            [
              ':input[name="options[alter][make_link]"]' => ['checked' => TRUE],
            ],
            [
              ':input[name="options[alter][alter_text]"]' => ['checked' => TRUE],
            ],
            [
              ':input[name="options[alter][more_link]"]' => ['checked' => TRUE],
            ],
          ],
        ],
      ];

      $form['alter']['trim'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Trim this field to a maximum number of characters'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['trim'],
        '#title' => $this->t('Maximum number of characters'),
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#default_value' => $this->options['alter']['max_length'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
          ],
        ],
      ];

      $form['alter']['word_boundary'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Trim only on a word boundary'),
        '#description' => $this->t('If checked, this field be trimmed only on a word boundary. This is guaranteed to be the maximum characters stated or less. If there are no word boundaries this could trim a field to nothing.'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['word_boundary'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
          ],
        ],
      ];

      $form['alter']['ellipsis'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Add "…" at the end of trimmed text'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['ellipsis'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
          ],
        ],
      ];

      $form['alter']['more_link'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'checkbox',
        '#title' => $this->t('Add a read-more link if output is trimmed'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['more_link'],
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
          ],
        ],
      ];

      $form['alter']['more_link_text'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#title' => $this->t('More link label'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['more_link_text'],
        '#description' => $this->t('You may use the "Replacement patterns" above.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
            ':input[name="options[alter][more_link]"]' => ['checked' => TRUE],
          ],
        ],
      ];
      $form['alter']['more_link_path'] = [
Earl Miles's avatar
Earl Miles committed
        '#type' => 'textfield',
        '#title' => $this->t('More link path'),
Earl Miles's avatar
Earl Miles committed
        '#default_value' => $this->options['alter']['more_link_path'],
        '#description' => $this->t('This can be an internal Drupal path such as node/add or an external URL such as "https://www.drupal.org". You may use the "Replacement patterns" above.'),
        '#states' => [
          'visible' => [
            ':input[name="options[alter][trim]"]' => ['checked' => TRUE],
            ':input[name="options[alter][more_link]"]' => ['checked' => TRUE],
          ],
        ],