class TextFormat

Same name in this branch
  1. 11.x core/modules/config_translation/src/FormElement/TextFormat.php \Drupal\config_translation\FormElement\TextFormat
Same name and namespace in other branches
  1. 9 core/modules/config_translation/src/FormElement/TextFormat.php \Drupal\config_translation\FormElement\TextFormat
  2. 9 core/modules/filter/src/Element/TextFormat.php \Drupal\filter\Element\TextFormat
  3. 8.9.x core/modules/config_translation/src/FormElement/TextFormat.php \Drupal\config_translation\FormElement\TextFormat
  4. 8.9.x core/modules/filter/src/Element/TextFormat.php \Drupal\filter\Element\TextFormat
  5. 10 core/modules/config_translation/src/FormElement/TextFormat.php \Drupal\config_translation\FormElement\TextFormat
  6. 10 core/modules/filter/src/Element/TextFormat.php \Drupal\filter\Element\TextFormat

Provides a text format render element.

Properties:

  • #base_type: The form element #type to use for the 'value' element. 'textarea' by default.
  • #format: (optional) The text format ID to preselect. If omitted, the default format for the current user will be used.
  • #allowed_formats: (optional) An array of text format IDs that are available for this element. If omitted, all text formats that the current user has access to will be allowed.

Usage Example:

$form['body'] = [
    '#type' => 'text_format',
    '#title' => 'Body',
    '#format' => 'full_html',
    '#default_value' => '<p>The quick brown fox jumped over the lazy dog.</p>',
];

Hierarchy

Expanded class hierarchy of TextFormat

See also

\Drupal\Core\Render\Element\Textarea

4 string references to 'TextFormat'
ClaroPreRender::trustedCallbacks in core/themes/claro/src/ClaroPreRender.php
Lists the trusted callbacks provided by the implementing class.
claro_element_info_alter in core/themes/claro/claro.theme
Implements hook_element_info_alter().
OliveroPreRender::trustedCallbacks in core/themes/olivero/src/OliveroPreRender.php
Lists the trusted callbacks provided by the implementing class.
olivero_element_info_alter in core/themes/olivero/olivero.theme
Implements hook_element_info_alter().

File

core/modules/filter/src/Element/TextFormat.php, line 35

Namespace

Drupal\filter\Element
View source
class TextFormat extends RenderElementBase {
    
    /**
     * {@inheritdoc}
     */
    public function getInfo() {
        $class = static::class;
        return [
            '#process' => [
                [
                    $class,
                    'processFormat',
                ],
            ],
            '#base_type' => 'textarea',
            '#theme_wrappers' => [
                'text_format_wrapper',
            ],
        ];
    }
    
    /**
     * Expands an element into a base element with text format selector attached.
     *
     * The form element will be expanded into two separate form elements, one
     * holding the original element, and the other holding the text format
     * selector:
     * - value: Holds the original element, having its #type changed to the value
     *   of #base_type or 'textarea' by default.
     * - format: Holds the text format details and the text format selection,
     *   using the text format ID specified in #format or the user's default
     *   format by default, if NULL.
     *
     * The resulting value for the element will be an array holding the value and
     * the format. For example, the value for the body element will be:
     * @code
     *   $values = $form_state->getValue('body');
     *   $values['value'] = 'foo';
     *   $values['format'] = 'foo';
     * @endcode
     *
     * @param array $element
     *   The form element to process. See main class documentation for properties.
     * @param \Drupal\Core\Form\FormStateInterface $form_state
     *   The current state of the form.
     * @param array $complete_form
     *   The complete form structure.
     *
     * @return array
     *   The form element.
     */
    public static function processFormat(&$element, FormStateInterface $form_state, &$complete_form) {
        $user = static::currentUser();
        // Ensure that children appear as subkeys of this element.
        $element['#tree'] = TRUE;
        $keys_not_to_copy = [
            // Make \Drupal::formBuilder()->doBuildForm() regenerate child properties.
'#parents',
            '#id',
            '#name',
            // Do not copy this #process function to prevent
            // \Drupal::formBuilder()->doBuildForm() from recursing infinitely.
'#process',
            // Ensure #pre_render functions will be run.
'#pre_render',
            // Description is handled by theme_text_format_wrapper().
'#description',
            // Ensure proper ordering of children.
'#weight',
            // Properties already processed for the parent element.
'#prefix',
            '#suffix',
            '#attached',
            '#processed',
            '#theme_wrappers',
        ];
        // Move this element into sub-element 'value'.
        unset($element['value']);
        foreach (Element::properties($element) as $key) {
            if (!in_array($key, $keys_not_to_copy)) {
                $element['value'][$key] = $element[$key];
            }
        }
        $element['value']['#type'] = $element['#base_type'];
        $element['value'] += static::elementInfo()->getInfo($element['#base_type']);
        // Make sure the #default_value key is set, so we can use it below.
        $element['value'] += [
            '#default_value' => '',
        ];
        // Turn original element into a text format wrapper.
        $element['#attached']['library'][] = 'filter/drupal.filter';
        // Setup child container for the text format widget.
        $element['format'] = [
            '#type' => 'container',
            '#theme_wrappers' => [
                'container__text_format_filter_wrapper',
            ],
            '#attributes' => [
                'class' => [
                    'js-filter-wrapper',
                ],
            ],
        ];
        // Get a list of formats that the current user has access to.
        $formats = filter_formats($user);
        // Allow the list of formats to be restricted.
        if (isset($element['#allowed_formats'])) {
            // We do not add the fallback format here to allow the use-case of forcing
            // certain text formats to be used for certain text areas. In case the
            // fallback format is supposed to be allowed as well, it must be added to
            // $element['#allowed_formats'] explicitly.
            $formats = array_intersect_key($formats, array_flip($element['#allowed_formats']));
        }
        if (!isset($element['#format']) && !empty($formats)) {
            // If no text format was selected, use the allowed format with the highest
            // weight. This is equivalent to calling filter_default_format().
            $element['#format'] = reset($formats)->id();
        }
        // If #allowed_formats is set, the list of formats must not be modified in
        // any way. Otherwise, however, if all of the following conditions are true,
        // remove the fallback format from the list of formats:
        // 1. The 'always_show_fallback_choice' filter setting has not been
        //    activated.
        // 2. Multiple text formats are available.
        // 3. The fallback format is not the default format.
        // The 'always_show_fallback_choice' filter setting is a hidden setting that
        // has no UI. It defaults to FALSE.
        $config = static::configFactory()->get('filter.settings');
        if (!isset($element['#allowed_formats']) && !$config->get('always_show_fallback_choice')) {
            $fallback_format = $config->get('fallback_format');
            if ($element['#format'] !== $fallback_format && count($formats) > 1) {
                unset($formats[$fallback_format]);
            }
        }
        // If the value element has #states set, copy it to the format element.
        if (isset($element['value']['#states'])) {
            $element['format']['#states'] = $element['value']['#states'];
        }
        // Prepare text format guidelines.
        $element['format']['guidelines'] = [
            '#type' => 'container',
            '#attributes' => [
                'class' => [
                    'js-filter-guidelines',
                ],
            ],
            '#theme_wrappers' => [
                'container__text_format_filter_guidelines',
            ],
            '#weight' => 20,
        ];
        $options = [];
        foreach ($formats as $format) {
            $options[$format->id()] = $format->label();
            $element['format']['guidelines'][$format->id()] = [
                '#theme' => 'filter_guidelines',
                '#format' => $format,
            ];
        }
        $element['format']['format'] = [
            '#type' => 'select',
            '#title' => t('Text format'),
            '#options' => $options,
            '#default_value' => $element['#format'],
            '#access' => count($formats) > 1,
            '#weight' => 10,
            '#attributes' => [
                'class' => [
                    'js-filter-list',
                ],
            ],
            '#parents' => array_merge($element['#parents'], [
                'format',
            ]),
        ];
        $element['format']['help'] = [
            '#type' => 'container',
            '#theme_wrappers' => [
                'container__text_format_filter_help',
            ],
            'about' => [
                '#type' => 'link',
                '#title' => t('About text formats'),
                '#url' => new Url('filter.tips_all'),
                '#attributes' => [
                    'target' => '_blank',
                ],
            ],
            '#weight' => 0,
        ];
        $all_formats = filter_formats();
        $format_exists = isset($all_formats[$element['#format']]);
        $format_allowed = !isset($element['#allowed_formats']) || in_array($element['#format'], $element['#allowed_formats']);
        $user_has_access = isset($formats[$element['#format']]);
        $user_is_admin = $user->hasPermission('administer filters');
        // If the stored format does not exist or if it is not among the allowed
        // formats for this textarea, administrators have to assign a new format.
        if ((!$format_exists || !$format_allowed) && $user_is_admin) {
            $element['format']['format']['#required'] = TRUE;
            $element['format']['format']['#default_value'] = NULL;
            // Force access to the format selector (it may have been denied above if
            // the user only has access to a single format).
            $element['format']['format']['#access'] = TRUE;
        }
        elseif (!$user_has_access || !$format_exists) {
            // Overload default values into #value to make them unalterable.
            $element['value']['#value'] = $element['value']['#default_value'];
            $element['format']['format']['#value'] = $element['format']['format']['#default_value'];
            // Prepend #pre_render callback to replace field value with user notice
            // prior to rendering.
            $element['value'] += [
                '#pre_render' => [],
            ];
            array_unshift($element['value']['#pre_render'], [
                static::class,
                'accessDeniedCallback',
            ]);
            // Cosmetic adjustments.
            if (isset($element['value']['#rows'])) {
                $element['value']['#rows'] = 3;
            }
            $element['value']['#disabled'] = TRUE;
            $element['value']['#resizable'] = 'none';
            // Hide the text format selector and any other child element (such as text
            // field's summary).
            foreach (Element::children($element) as $key) {
                if ($key != 'value') {
                    $element[$key]['#access'] = FALSE;
                }
            }
        }
        return $element;
    }
    
    /**
     * Render API callback: Hides the field value of 'text_format' elements.
     *
     * To not break form processing and previews if a user does not have access to
     * a stored text format, the expanded form elements in
     * \Drupal\filter\Element\TextFormat::processFormat() are forced to take over
     * the stored #default_values for 'value' and 'format'. However, to prevent
     * the unfiltered, original #value from being displayed to the user, we
     * replace it with a friendly notice here.
     *
     * @param array $element
     *   The render array to add the access denied message to.
     *
     * @return array
     *   The updated render array.
     */
    public static function accessDeniedCallback(array $element) {
        $element['#value'] = t('This field has been disabled because you do not have sufficient permissions to edit it.');
        return $element;
    }
    
    /**
     * Wraps the current user.
     *
     * \Drupal\Core\Session\AccountInterface
     */
    protected static function currentUser() {
        return \Drupal::currentUser();
    }
    
    /**
     * Wraps the config factory.
     *
     * @return \Drupal\Core\Config\ConfigFactoryInterface
     */
    protected static function configFactory() {
        return \Drupal::configFactory();
    }
    
    /**
     * Wraps the element info service.
     *
     * @return \Drupal\Core\Render\ElementInfoManagerInterface
     */
    protected static function elementInfo() {
        return \Drupal::service('element_info');
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
PluginInspectionInterface::getPluginDefinition public function Gets the definition of the plugin implementation. 6
PluginInspectionInterface::getPluginId public function Gets the plugin_id of the plugin instance. 2
RenderElementBase::preRenderAjaxForm public static function Adds Ajax information about an element to communicate with JavaScript. 2
RenderElementBase::preRenderGroup public static function Adds members of this group as actual elements for rendering. 2
RenderElementBase::processAjaxForm public static function Form element processing handler for the #ajax form property. 3
RenderElementBase::processGroup public static function Arranges elements into groups. 2
RenderElementBase::setAttributes public static function Sets a form element&#039;s class attribute. Overrides ElementInterface::setAttributes 2
TextFormat::accessDeniedCallback public static function Render API callback: Hides the field value of &#039;text_format&#039; elements.
TextFormat::configFactory protected static function Wraps the config factory.
TextFormat::currentUser protected static function Wraps the current user.
TextFormat::elementInfo protected static function Wraps the element info service.
TextFormat::getInfo public function Returns the element properties for this element. Overrides ElementInterface::getInfo
TextFormat::processFormat public static function Expands an element into a base element with text format selector attached.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.