You are here

class TextWithExpandCollapseButtonsFormatter in Formatter Suite 8

Formats text with expand/collapse buttons to show more/less.

Long text fields may have long text. When presented along with other content on the same page, the text can be overwhelming and make it hard to find the other content. This formatter temporarily shortens the long text to a specified height and adds an "Expand" button. Clicking the button expands the text display to full size, and adds a "Collapse" button. Clicking that button shortes the text again.

Plugin annotation


@FieldFormatter(
  id = "formatter_suite_text_with_expand_collapse_buttons",
  label = @Translation("Formatter Suite - Text with expand/collapse buttons"),
  weight = 1000,
  field_types = {
    "text",
    "text_long",
    "text_with_summary",
    "string_long"
  }
)

Hierarchy

Expanded class hierarchy of TextWithExpandCollapseButtonsFormatter

File

src/Plugin/Field/FieldFormatter/TextWithExpandCollapseButtonsFormatter.php, line 35

Namespace

Drupal\formatter_suite\Plugin\Field\FieldFormatter
View source
class TextWithExpandCollapseButtonsFormatter extends FormatterBase {

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

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return [
      'collapsedHeight' => '8em',
      'expandButtonLabel' => t('Expand...'),
      'collapseButtonLabel' => t('Collapse...'),
      'animationDuration' => 500,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() {

    // Get current settings.
    $collapsedHeight = $this
      ->getSetting('collapsedHeight');
    $animationDuration = $this
      ->getSetting('animationDuration');

    // Security: The animation duration is entered by an administrator.
    // It should be a simple integer, with no other characters, HTML, or
    // HTML entities.
    //
    // By parsing it as an integer, we ignore anything else and remove
    // any security issues.
    $animationDuration = intval($animationDuration);

    // Security: The collapse height is entered by an administrator.
    // It should be a number followed by CSS units, such as "px", "pt",
    // or "em". It should not contain HTML or HTML entities.
    //
    // If integer parsing of the string yields a zero, then the string
    // is assumed to be empty or invalid and collapsing is disabled.
    // Otherwise the string is santized using an Html escape filter
    // that escapes all HTML and HTML entities. If the admin enters these,
    // the resulting string is not likely to work as a collapse height
    // and the Javascript will not get a meaningful result, but it will
    // still be safe.
    $collapsedHeight = Html::escape($collapsedHeight);
    $hasCollapseHeight = TRUE;
    if (empty($collapsedHeight) === TRUE || $collapsedHeight === "0" || (int) $collapsedHeight === 0) {
      $hasCollapseHeight = FALSE;
    }

    // Present.
    $summary = parent::settingsSummary();
    if ($hasCollapseHeight === FALSE) {
      $summary[] = $this
        ->t('Disabled because no collapsed height set.');
    }
    else {
      $summary[] = $this
        ->t('Shorten long text areas to @collapsedHeight.', [
        '@collapsedHeight' => $collapsedHeight,
      ]);
      if ($animationDuration > 0) {
        $summary[] = $this
          ->t('Animate over @animationDuration milliseconds.', [
          '@animationDuration' => $animationDuration,
        ]);
      }
    }
    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('Present long text in a shortened area and include links to expand the text to full height, and collapse it back again.');
  }

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

    //
    // Start with the parent form.
    $elements = parent::settingsForm($form, $formState);
    $elements['#attached'] = [
      'library' => [
        'formatter_suite/formatter_suite.settings',
      ],
    ];

    // Add branding.
    $elements = [];
    $elements = Branding::addFieldFormatterBranding($elements);
    $elements['#attached']['library'][] = 'formatter_suite/formatter_suite.fieldformatter';
    $elements['description'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#value' => $this
        ->getDescription(),
      '#weight' => -1000,
      '#attributes' => [
        'class' => [
          'formatter_suite-settings-description',
        ],
      ],
    ];

    // Add each of the values.
    $elements['collapsedHeight'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Collapsed height'),
      '#size' => 10,
      '#default_value' => $this
        ->getSetting('collapsedHeight'),
      '#description' => $this
        ->t("Text height when collapsed. Use CSS units (e.g. '200px', '40pt', '8em'). Empty or zero value disables."),
      '#attributes' => [
        'autocomplete' => 'off',
        'autocapitalize' => 'none',
        'spellcheck' => 'false',
        'autocorrect' => 'off',
      ],
    ];
    $elements['collapseButtonLabel'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Collapse link label'),
      '#size' => 10,
      '#maxlength' => 128,
      '#default_value' => $this
        ->getSetting('collapseButtonLabel'),
    ];
    $elements['expandButtonLabel'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Expand link label'),
      '#size' => 10,
      '#maxlength' => 128,
      '#default_value' => $this
        ->getSetting('expandButtonLabel'),
    ];
    $elements['animationDuration'] = [
      '#type' => 'number',
      '#title' => $this
        ->t('Animation duration'),
      '#size' => 10,
      '#default_value' => $this
        ->getSetting('animationDuration'),
      '#description' => $this
        ->t('Animation time in milliseconds (e.g. 500 = 1/2 second). Empty or zero value disables animation.'),
    ];
    return $elements;
  }

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

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langCode) {

    //
    // The $items array has a list of items to format. We need to return
    // an array with identical indexing and corresponding render elements
    // for those items.
    if ($items
      ->isEmpty() === TRUE) {
      return [];
    }

    // Get current settings.
    $collapsedHeight = $this
      ->getSetting('collapsedHeight');
    $animationDuration = $this
      ->getSetting('animationDuration');
    $collapseButtonLabel = $this
      ->getSetting('collapseButtonLabel');
    $expandButtonLabel = $this
      ->getSetting('expandButtonLabel');

    // Security: The button labels are entered by an administrator.
    // They may legitimately include HTML entities and minor HTML, but
    // they should not include dangerous HTML. For instance, it is
    // legitimate to include <span class="blah"> to style the label,
    // or to include <img src="blah"> to add an icon image. However,
    // it is not legitimate to add <style> or <script>.
    //
    // So, Xss::filterAdmin() is used here to get rid of the most
    // dangerous HTML, like <style> and <script>.
    //
    // We'd like the admin-entered text to be translated, if a site
    // is using that Drupal feature. However, simply calling t() will
    // not quite work. Drupal.org builds translatable text tables by
    // scanning the source code for calls to t() with literal strings.
    // Since the admin-entered button label text here is not a literal
    // string, it will not be found by that scan. This means it will
    // not be in automatically-generated translation tables and t()
    // will not necessarily do anything.
    //
    // Calling t() anyway will still do the translation table lookup.
    // If a site has MANUALLY entered the text into their own translation
    // tables, then translation will take place. Otherwise the text will
    // be used as-is.
    //
    // We'd like to call $this->t() here to do the translation, but the
    // various Drupal style checkers complain, even though this is a
    // legitimate use. To avoid those complaints, we get the string
    // translator from the StringTranslationTrait included in the
    // FormatterBase parent class. Calling the translate() method
    // directly dodges the style checkers.
    $translator = $this
      ->getStringTranslation();
    $collapseButtonLabel = $translator
      ->translate(Xss::filterAdmin($collapseButtonLabel));
    $expandButtonLabel = $translator
      ->translate(Xss::filterAdmin($expandButtonLabel));

    // Security: The animation duration is entered by an administrator.
    // It should be a simple integer, with no other characters, HTML, or
    // HTML entities.
    //
    // By parsing it as an integer, we ignore anything else and remove
    // any security issues.
    $animationDuration = intval($animationDuration);

    // Security: The collapse height is entered by an administrator.
    // It should be a number followed by CSS units, such as "px", "pt",
    // or "em". It should not contain HTML or HTML entities.
    //
    // If integer parsing of the string yields a zero, then the string
    // is assumed to be empty or invalid and collapsing is disabled.
    // Otherwise the string is santized using an Html escape filter
    // that escapes all HTML and HTML entities. If the admin enters these,
    // the resulting string is not likely to work as a collapse height
    // and the Javascript will not get a meaningful result, but it will
    // still be safe.
    $collapsedHeight = Html::escape($collapsedHeight);
    $hasCollapsedHeight = TRUE;
    if (empty($collapsedHeight) === TRUE || $collapsedHeight === "0" || (int) $collapsedHeight === 0) {
      $hasCollapsedHeight = FALSE;
    }

    // If there is no collapsed height, show text full height.
    $build = [];
    if ($hasCollapsedHeight === FALSE) {
      foreach ($items as $delta => $item) {
        $build[$delta] = [
          '#type' => 'processed_text',
          '#text' => $item->value,
          '#format' => $item->format,
          '#langcode' => $item
            ->getLangcode(),
        ];
      }
      return $build;
    }

    // Nest the text, add buttons, and add a behavior script.
    foreach ($items as $delta => $item) {
      $build[$delta] = [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'formatter_suite-text-with-expand-collapse-buttons',
          ],
        ],
        '#attached' => [
          'library' => [
            'formatter_suite/formatter_suite.usage',
            'formatter_suite/formatter_suite.text_with_expand_collapse_buttons',
          ],
        ],
        'text' => [
          '#type' => 'container',
          '#attributes' => [
            'class' => [
              'formatter_suite-text',
            ],
            'data-formatter_suite-collapsed-height' => $collapsedHeight,
            'data-formatter_suite-animation-duration' => $animationDuration,
          ],
          'processedtext' => [
            '#type' => 'processed_text',
            '#text' => $item->value,
            '#format' => $item->format,
            '#langcode' => $item
              ->getLangcode(),
          ],
        ],
        'collapse' => [
          '#type' => 'html_tag',
          '#tag' => 'div',
          '#value' => '<a href="#">' . $collapseButtonLabel . '</a>',
          '#attributes' => [
            'class' => [
              'formatter_suite-text-collapse-button',
            ],
            'style' => 'display: none',
          ],
        ],
        'expand' => [
          '#type' => 'html_tag',
          '#tag' => 'div',
          '#value' => '<a href="#">' . $expandButtonLabel . '</a>',
          '#attributes' => [
            'class' => [
              'formatter_suite-text-expand-button',
            ],
            'style' => 'display: none',
          ],
        ],
      ];
    }
    return $build;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormatterBase::$fieldDefinition protected property The field definition.
FormatterBase::$label protected property The label display setting.
FormatterBase::$settings protected property The formatter settings. Overrides PluginSettingsBase::$settings
FormatterBase::$viewMode protected property The view mode.
FormatterBase::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 11
FormatterBase::getFieldSetting protected function Returns the value of a field setting.
FormatterBase::getFieldSettings protected function Returns the array of field settings.
FormatterBase::isApplicable public static function Returns if the formatter can be used for the provided field. Overrides FormatterInterface::isApplicable 14
FormatterBase::prepareView public function Allows formatters to load information for field values being displayed. Overrides FormatterInterface::prepareView 2
FormatterBase::view public function Builds a renderable array for a fully themed field. Overrides FormatterInterface::view 1
FormatterBase::__construct public function Constructs a FormatterBase object. Overrides PluginBase::__construct 11
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginSettingsBase::$defaultSettingsMerged protected property Whether default settings have been merged into the current $settings.
PluginSettingsBase::$thirdPartySettings protected property The plugin settings injected by third party modules.
PluginSettingsBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies 6
PluginSettingsBase::getSetting public function Returns the value of a setting, or its default value if absent. Overrides PluginSettingsInterface::getSetting
PluginSettingsBase::getSettings public function Returns the array of settings, including defaults for missing settings. Overrides PluginSettingsInterface::getSettings
PluginSettingsBase::getThirdPartyProviders public function Gets the list of third parties that store information. Overrides ThirdPartySettingsInterface::getThirdPartyProviders
PluginSettingsBase::getThirdPartySetting public function Gets the value of a third-party setting. Overrides ThirdPartySettingsInterface::getThirdPartySetting
PluginSettingsBase::getThirdPartySettings public function Gets all third-party settings of a given module. Overrides ThirdPartySettingsInterface::getThirdPartySettings
PluginSettingsBase::mergeDefaults protected function Merges default settings values into $settings.
PluginSettingsBase::onDependencyRemoval public function Informs the plugin that some configuration it depends on will be deleted. Overrides PluginSettingsInterface::onDependencyRemoval 3
PluginSettingsBase::setSetting public function Sets the value of a setting for the plugin. Overrides PluginSettingsInterface::setSetting
PluginSettingsBase::setSettings public function Sets the settings for the plugin. Overrides PluginSettingsInterface::setSettings
PluginSettingsBase::setThirdPartySetting public function Sets the value of a third-party setting. Overrides ThirdPartySettingsInterface::setThirdPartySetting
PluginSettingsBase::unsetThirdPartySetting public function Unsets a third-party setting. Overrides ThirdPartySettingsInterface::unsetThirdPartySetting
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TextWithExpandCollapseButtonsFormatter::defaultSettings public static function Defines the default settings for this plugin. Overrides PluginSettingsBase::defaultSettings
TextWithExpandCollapseButtonsFormatter::getDescription protected function Returns a brief description of the formatter.
TextWithExpandCollapseButtonsFormatter::settingsForm public function Returns a form to configure settings for the formatter. Overrides FormatterBase::settingsForm
TextWithExpandCollapseButtonsFormatter::settingsSummary public function Returns a short summary for the current formatter settings. Overrides FormatterBase::settingsSummary
TextWithExpandCollapseButtonsFormatter::viewElements public function Builds a renderable array for a field value. Overrides FormatterInterface::viewElements