Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
1
web/core/modules/text/config/install/text.settings.yml
Normal file
1
web/core/modules/text/config/install/text.settings.yml
Normal file
|
@ -0,0 +1 @@
|
|||
default_summary_length: 600
|
133
web/core/modules/text/config/schema/text.schema.yml
Normal file
133
web/core/modules/text/config/schema/text.schema.yml
Normal file
|
@ -0,0 +1,133 @@
|
|||
# Schema for the configuration files of the text module.
|
||||
|
||||
text.settings:
|
||||
type: config_object
|
||||
label: 'Text settings'
|
||||
mapping:
|
||||
default_summary_length:
|
||||
type: integer
|
||||
label: 'Default summary length'
|
||||
|
||||
field.storage_settings.text:
|
||||
type: mapping
|
||||
label: 'Text (formatted) settings'
|
||||
mapping:
|
||||
max_length:
|
||||
type: integer
|
||||
label: 'Maximum length'
|
||||
|
||||
field.field_settings.text:
|
||||
type: mapping
|
||||
label: 'Text (formatted) settings'
|
||||
|
||||
field.value.text:
|
||||
type: mapping
|
||||
label: 'Default value'
|
||||
mapping:
|
||||
value:
|
||||
type: label
|
||||
label: 'Value'
|
||||
format:
|
||||
type: string
|
||||
label: 'Text format'
|
||||
|
||||
field.storage_settings.text_long:
|
||||
label: 'Text (formatted, long) settings'
|
||||
type: mapping
|
||||
|
||||
field.field_settings.text_long:
|
||||
label: 'Text (formatted, long) settings'
|
||||
type: mapping
|
||||
|
||||
field.value.text_long:
|
||||
type: mapping
|
||||
label: 'Default value'
|
||||
mapping:
|
||||
value:
|
||||
type: text
|
||||
label: 'Value'
|
||||
format:
|
||||
type: string
|
||||
label: 'Text format'
|
||||
|
||||
field.storage_settings.text_with_summary:
|
||||
label: 'Text (formatted, long, with summary) settings'
|
||||
type: mapping
|
||||
|
||||
field.field_settings.text_with_summary:
|
||||
type: mapping
|
||||
label: 'Text (formatted, long, with summary) settings'
|
||||
mapping:
|
||||
display_summary:
|
||||
type: boolean
|
||||
label: 'Summary input'
|
||||
|
||||
field.value.text_with_summary:
|
||||
type: mapping
|
||||
label: 'Default value'
|
||||
mapping:
|
||||
value:
|
||||
type: text
|
||||
label: 'Body'
|
||||
summary:
|
||||
type: string
|
||||
label: 'Summary'
|
||||
format:
|
||||
type: string
|
||||
label: 'Text format'
|
||||
|
||||
field.formatter.settings.text_default:
|
||||
type: mapping
|
||||
label: 'Formatted text default display format settings'
|
||||
|
||||
field.formatter.settings.text_summary_or_trimmed:
|
||||
type: mapping
|
||||
label: 'Summary or trimmed formatted text display format settings'
|
||||
mapping:
|
||||
trim_length:
|
||||
type: integer
|
||||
label: 'Trim length'
|
||||
|
||||
field.formatter.settings.text_trimmed:
|
||||
type: mapping
|
||||
label: 'Trimmed text display format settings'
|
||||
mapping:
|
||||
trim_length:
|
||||
type: integer
|
||||
label: 'Trim length'
|
||||
|
||||
field.widget.settings.text_textarea:
|
||||
type: mapping
|
||||
label: 'Text area (multiple rows) display format settings'
|
||||
mapping:
|
||||
rows:
|
||||
type: integer
|
||||
label: 'Rows'
|
||||
placeholder:
|
||||
type: label
|
||||
label: 'Placeholder'
|
||||
|
||||
field.widget.settings.text_textarea_with_summary:
|
||||
type: mapping
|
||||
label: 'Text area with a summary display format settings'
|
||||
mapping:
|
||||
rows:
|
||||
type: integer
|
||||
label: 'Rows'
|
||||
summary_rows:
|
||||
type: integer
|
||||
label: 'Number of summary rows'
|
||||
placeholder:
|
||||
type: label
|
||||
label: 'Placeholder'
|
||||
|
||||
field.widget.settings.text_textfield:
|
||||
type: mapping
|
||||
label: 'Text field display format settings'
|
||||
mapping:
|
||||
size:
|
||||
type: integer
|
||||
label: 'Size of textfield'
|
||||
placeholder:
|
||||
type: label
|
||||
label: 'Placeholder'
|
14
web/core/modules/text/migration_templates/text_settings.yml
Normal file
14
web/core/modules/text/migration_templates/text_settings.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
id: text_settings
|
||||
label: Drupal teaser length configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- teaser_length
|
||||
process:
|
||||
default_summary_length: teaser_length
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: text.settings
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_default' formatter.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "text_default",
|
||||
* label = @Translation("Default"),
|
||||
* field_types = {
|
||||
* "text",
|
||||
* "text_long",
|
||||
* "text_with_summary",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TextDefaultFormatter extends FormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = array();
|
||||
|
||||
// The ProcessedText element already handles cache context & tag bubbling.
|
||||
// @see \Drupal\filter\Element\ProcessedText::preRenderText()
|
||||
foreach ($items as $delta => $item) {
|
||||
$elements[$delta] = array(
|
||||
'#type' => 'processed_text',
|
||||
'#text' => $item->value,
|
||||
'#format' => $item->format,
|
||||
'#langcode' => $item->getLangcode(),
|
||||
);
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldFormatter;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_summary_or_trimmed' formatter.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "text_summary_or_trimmed",
|
||||
* label = @Translation("Summary or trimmed"),
|
||||
* field_types = {
|
||||
* "text_with_summary"
|
||||
* },
|
||||
* quickedit = {
|
||||
* "editor" = "form"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TextSummaryOrTrimmedFormatter extends TextTrimmedFormatter { }
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_trimmed' formatter.
|
||||
*
|
||||
* Note: This class also contains the implementations used by the
|
||||
* 'text_summary_or_trimmed' formatter.
|
||||
*
|
||||
* @see \Drupal\text\Field\Formatter\TextSummaryOrTrimmedFormatter
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "text_trimmed",
|
||||
* label = @Translation("Trimmed"),
|
||||
* field_types = {
|
||||
* "text",
|
||||
* "text_long",
|
||||
* "text_with_summary"
|
||||
* },
|
||||
* quickedit = {
|
||||
* "editor" = "form"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TextTrimmedFormatter extends FormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return array(
|
||||
'trim_length' => '600',
|
||||
) + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$element['trim_length'] = array(
|
||||
'#title' => t('Trimmed limit'),
|
||||
'#type' => 'number',
|
||||
'#field_suffix' => t('characters'),
|
||||
'#default_value' => $this->getSetting('trim_length'),
|
||||
'#description' => t('If the summary is not set, the trimmed %label field will end at the last full sentence before this character limit.', array('%label' => $this->fieldDefinition->getLabel())),
|
||||
'#min' => 1,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = array();
|
||||
$summary[] = t('Trimmed limit: @trim_length characters', array('@trim_length' => $this->getSetting('trim_length')));
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = array();
|
||||
|
||||
$render_as_summary = function (&$element) {
|
||||
// Make sure any default #pre_render callbacks are set on the element,
|
||||
// because text_pre_render_summary() must run last.
|
||||
$element += \Drupal::service('element_info')->getInfo($element['#type']);
|
||||
// Add the #pre_render callback that renders the text into a summary.
|
||||
$element['#pre_render'][] = [TextTrimmedFormatter::class, 'preRenderSummary'];
|
||||
// Pass on the trim length to the #pre_render callback via a property.
|
||||
$element['#text_summary_trim_length'] = $this->getSetting('trim_length');
|
||||
};
|
||||
|
||||
// The ProcessedText element already handles cache context & tag bubbling.
|
||||
// @see \Drupal\filter\Element\ProcessedText::preRenderText()
|
||||
foreach ($items as $delta => $item) {
|
||||
$elements[$delta] = array(
|
||||
'#type' => 'processed_text',
|
||||
'#text' => NULL,
|
||||
'#format' => $item->format,
|
||||
'#langcode' => $item->getLangcode(),
|
||||
);
|
||||
|
||||
if ($this->getPluginId() == 'text_summary_or_trimmed' && !empty($item->summary)) {
|
||||
$elements[$delta]['#text'] = $item->summary;
|
||||
}
|
||||
else {
|
||||
$elements[$delta]['#text'] = $item->value;
|
||||
$render_as_summary($elements[$delta]);
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-render callback: Renders a processed text element's #markup as a summary.
|
||||
*
|
||||
* @param array $element
|
||||
* A structured array with the following key-value pairs:
|
||||
* - #markup: the filtered text (as filtered by filter_pre_render_text())
|
||||
* - #format: containing the machine name of the filter format to be used to
|
||||
* filter the text. Defaults to the fallback format. See
|
||||
* filter_fallback_format().
|
||||
* - #text_summary_trim_length: the desired character length of the summary
|
||||
* (used by text_summary())
|
||||
*
|
||||
* @return array
|
||||
* The passed-in element with the filtered text in '#markup' trimmed.
|
||||
*
|
||||
* @see filter_pre_render_text()
|
||||
* @see text_summary()
|
||||
*/
|
||||
public static function preRenderSummary(array $element) {
|
||||
$element['#markup'] = text_summary($element['#markup'], $element['#format'], $element['#text_summary_trim_length']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "text",
|
||||
* label = @Translation("Text (formatted)"),
|
||||
* description = @Translation("This field stores a text with a text format."),
|
||||
* category = @Translation("Text"),
|
||||
* default_widget = "text_textfield",
|
||||
* default_formatter = "text_default"
|
||||
* )
|
||||
*/
|
||||
class TextItem extends TextItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultStorageSettings() {
|
||||
return array(
|
||||
'max_length' => 255,
|
||||
) + parent::defaultStorageSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => $field_definition->getSetting('max_length'),
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'format' => array('format'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
|
||||
$constraints = parent::getConstraints();
|
||||
|
||||
if ($max_length = $this->getSetting('max_length')) {
|
||||
$constraints[] = $constraint_manager->create('ComplexData', array(
|
||||
'value' => array(
|
||||
'Length' => array(
|
||||
'max' => $max_length,
|
||||
'maxMessage' => t('%name: the text may not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length)),
|
||||
)
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
|
||||
$element = array();
|
||||
|
||||
$element['max_length'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => t('Maximum length'),
|
||||
'#default_value' => $this->getSetting('max_length'),
|
||||
'#required' => TRUE,
|
||||
'#description' => t('The maximum length of the field in characters.'),
|
||||
'#min' => 1,
|
||||
'#disabled' => $has_data,
|
||||
);
|
||||
$element += parent::storageSettingsForm($form, $form_state, $has_data);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Component\Utility\Random;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Base class for 'text' configurable field types.
|
||||
*/
|
||||
abstract class TextItemBase extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Text'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['format'] = DataDefinition::create('filter_format')
|
||||
->setLabel(t('Text format'));
|
||||
|
||||
$properties['processed'] = DataDefinition::create('string')
|
||||
->setLabel(t('Processed text'))
|
||||
->setDescription(t('The text with the text format applied.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\text\TextProcessed')
|
||||
->setSetting('text source', 'value');
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyDefaultValue($notify = TRUE) {
|
||||
// @todo: Add in the filter default format here.
|
||||
$this->setValue(array('format' => NULL), $notify);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
$value = $this->get('value')->getValue();
|
||||
return $value === NULL || $value === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onChange($property_name, $notify = TRUE) {
|
||||
// Unset processed properties that are affected by the change.
|
||||
foreach ($this->definition->getPropertyDefinitions() as $property => $definition) {
|
||||
if ($definition->getClass() == '\Drupal\text\TextProcessed') {
|
||||
if ($property_name == 'format' || ($definition->getSetting('text source') == $property_name)) {
|
||||
$this->writePropertyValue($property, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
parent::onChange($property_name, $notify);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
|
||||
$random = new Random();
|
||||
$settings = $field_definition->getSettings();
|
||||
|
||||
if (empty($settings['max_length'])) {
|
||||
// Textarea handling
|
||||
$value = $random->paragraphs();
|
||||
}
|
||||
else {
|
||||
// Textfield handling.
|
||||
$value = substr($random->sentences(mt_rand(1, $settings['max_length'] / 3), FALSE), 0, $settings['max_length']);
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'value' => $value,
|
||||
'summary' => $value,
|
||||
'format' => filter_fallback_format(),
|
||||
);
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_long' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "text_long",
|
||||
* label = @Translation("Text (formatted, long)"),
|
||||
* description = @Translation("This field stores a long text with a text format."),
|
||||
* category = @Translation("Text"),
|
||||
* default_widget = "text_textarea",
|
||||
* default_formatter = "text_default"
|
||||
* )
|
||||
*/
|
||||
class TextLongItem extends TextItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'format' => array('format'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_with_summary' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "text_with_summary",
|
||||
* label = @Translation("Text (formatted, long, with summary)"),
|
||||
* description = @Translation("This field stores long text with a format and an optional summary."),
|
||||
* category = @Translation("Text"),
|
||||
* default_widget = "text_textarea_with_summary",
|
||||
* default_formatter = "text_default"
|
||||
* )
|
||||
*/
|
||||
class TextWithSummaryItem extends TextItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultFieldSettings() {
|
||||
return array(
|
||||
'display_summary' => 0,
|
||||
) + parent::defaultFieldSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties = parent::propertyDefinitions($field_definition);
|
||||
|
||||
$properties['summary'] = DataDefinition::create('string')
|
||||
->setLabel(t('Summary'));
|
||||
|
||||
$properties['summary_processed'] = DataDefinition::create('string')
|
||||
->setLabel(t('Processed summary'))
|
||||
->setDescription(t('The summary text with the text format applied.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\text\TextProcessed')
|
||||
->setSetting('text source', 'summary');
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
'summary' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'format' => array('format'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
$value = $this->get('summary')->getValue();
|
||||
return parent::isEmpty() && ($value === NULL || $value === '');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
|
||||
$element = array();
|
||||
$settings = $this->getSettings();
|
||||
|
||||
$element['display_summary'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Summary input'),
|
||||
'#default_value' => $settings['display_summary'],
|
||||
'#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display type.'),
|
||||
);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextareaWidget;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_textarea' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "text_textarea",
|
||||
* label = @Translation("Text area (multiple rows)"),
|
||||
* field_types = {
|
||||
* "text_long"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TextareaWidget extends StringTextareaWidget {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$main_widget = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
|
||||
$element = $main_widget['value'];
|
||||
$element['#type'] = 'text_format';
|
||||
$element['#format'] = $items[$delta]->format;
|
||||
$element['#base_type'] = $main_widget['value']['#type'];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
|
||||
if ($violation->arrayPropertyPath == array('format') && isset($element['format']['#access']) && !$element['format']['#access']) {
|
||||
// Ignore validation errors for formats if formats may not be changed,
|
||||
// i.e. when existing formats become invalid. See filter_process_format().
|
||||
return FALSE;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_textarea_with_summary' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "text_textarea_with_summary",
|
||||
* label = @Translation("Text area with a summary"),
|
||||
* field_types = {
|
||||
* "text_with_summary"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TextareaWithSummaryWidget extends TextareaWidget {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return array(
|
||||
'rows' => '9',
|
||||
'summary_rows' => '3',
|
||||
'placeholder' => '',
|
||||
) + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$element = parent::settingsForm($form, $form_state);
|
||||
$element['summary_rows'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => t('Summary rows'),
|
||||
'#default_value' => $this->getSetting('summary_rows'),
|
||||
'#required' => TRUE,
|
||||
'#min' => 1,
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
$summary[] = t('Number of summary rows: @rows', array('@rows' => $this->getSetting('summary_rows')));
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
|
||||
$display_summary = $items[$delta]->summary || $this->getFieldSetting('display_summary');
|
||||
$element['summary'] = array(
|
||||
'#type' => $display_summary ? 'textarea' : 'value',
|
||||
'#default_value' => $items[$delta]->summary,
|
||||
'#title' => t('Summary'),
|
||||
'#rows' => $this->getSetting('summary_rows'),
|
||||
'#description' => t('Leave blank to use trimmed value of full text as the summary.'),
|
||||
'#attached' => array(
|
||||
'library' => array('text/drupal.text'),
|
||||
),
|
||||
'#attributes' => array('class' => array('js-text-summary', 'text-summary')),
|
||||
'#prefix' => '<div class="js-text-summary-wrapper text-summary-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
|
||||
$element = parent::errorElement($element, $violation, $form, $form_state);
|
||||
if ($element === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
elseif (isset($violation->arrayPropertyPath[0])) {
|
||||
return $element[$violation->arrayPropertyPath[0]];
|
||||
}
|
||||
else {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_textfield' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "text_textfield",
|
||||
* label = @Translation("Text field"),
|
||||
* field_types = {
|
||||
* "text"
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class TextfieldWidget extends StringTextfieldWidget {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$main_widget = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
|
||||
$element = $main_widget['value'];
|
||||
$element['#type'] = 'text_format';
|
||||
$element['#format'] = isset($items[$delta]->format) ? $items[$delta]->format : NULL;
|
||||
$element['#base_type'] = $main_widget['value']['#type'];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
|
||||
if ($violation->arrayPropertyPath == array('format') && isset($element['format']['#access']) && !$element['format']['#access']) {
|
||||
// Ignore validation errors for formats if formats may not be changed,
|
||||
// i.e. when existing formats become invalid. See filter_process_format().
|
||||
return FALSE;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
127
web/core/modules/text/src/Plugin/migrate/cckfield/TextField.php
Normal file
127
web/core/modules/text/src/Plugin/migrate/cckfield/TextField.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Plugin\migrate\cckfield;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateCckField(
|
||||
* id = "text",
|
||||
* type_map = {
|
||||
* "text" = "text",
|
||||
* "text_long" = "text_long",
|
||||
* "text_with_summary" = "text_with_summary"
|
||||
* },
|
||||
* core = {6,7}
|
||||
* )
|
||||
*/
|
||||
class TextField extends CckFieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
return [
|
||||
'text_textfield' => 'text_textfield',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [
|
||||
'default' => 'text_default',
|
||||
'trimmed' => 'text_trimmed',
|
||||
'plain' => 'basic_string',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processCckFieldValues(MigrationInterface $migration, $field_name, $field_info) {
|
||||
if ($field_info['widget_type'] == 'optionwidgets_onoff') {
|
||||
$process = [
|
||||
'value' => [
|
||||
'plugin' => 'static_map',
|
||||
'source' => 'value',
|
||||
'default_value' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$checked_value = explode("\n", $field_info['global_settings']['allowed_values'])[1];
|
||||
if (strpos($checked_value, '|') !== FALSE) {
|
||||
$checked_value = substr($checked_value, 0, strpos($checked_value, '|'));
|
||||
}
|
||||
$process['value']['map'][$checked_value] = 1;
|
||||
}
|
||||
else {
|
||||
// See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(),
|
||||
// signature_format for an example of the YAML that represents this
|
||||
// process array.
|
||||
$process = [
|
||||
'value' => 'value',
|
||||
'format' => [
|
||||
[
|
||||
'plugin' => 'static_map',
|
||||
'bypass' => TRUE,
|
||||
'source' => 'format',
|
||||
'map' => [0 => NULL],
|
||||
],
|
||||
[
|
||||
'plugin' => 'skip_on_empty',
|
||||
'method' => 'process',
|
||||
],
|
||||
[
|
||||
'plugin' => 'migration',
|
||||
'migration' => [
|
||||
'd6_filter_format',
|
||||
'd7_filter_format',
|
||||
],
|
||||
'source' => 'format',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$process = array(
|
||||
'plugin' => 'iterator',
|
||||
'source' => $field_name,
|
||||
'process' => $process,
|
||||
);
|
||||
$migration->setProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldType(Row $row) {
|
||||
$widget_type = $row->getSourceProperty('widget_type');
|
||||
|
||||
if ($widget_type == 'text_textfield') {
|
||||
$settings = $row->getSourceProperty('global_settings');
|
||||
$field_type = $settings['text_processing'] ? 'text' : 'string';
|
||||
if (empty($settings['max_length']) || $settings['max_length'] > 255) {
|
||||
$field_type .= '_long';
|
||||
}
|
||||
return $field_type;
|
||||
}
|
||||
else {
|
||||
switch ($widget_type) {
|
||||
case 'optionwidgets_buttons':
|
||||
case 'optionwidgets_select':
|
||||
return 'list_string';
|
||||
case 'optionwidgets_onoff':
|
||||
return 'boolean';
|
||||
case 'text_textarea':
|
||||
return 'text_long';
|
||||
default:
|
||||
return parent::getFieldType($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
245
web/core/modules/text/src/Tests/TextFieldTest.php
Normal file
245
web/core/modules/text/src/Tests/TextFieldTest.php
Normal file
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Tests\String\StringFieldTest;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Tests the creation of text fields.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class TextFieldTest extends StringFieldTest {
|
||||
|
||||
/**
|
||||
* A user with relevant administrative privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer filters'));
|
||||
}
|
||||
|
||||
// Test fields.
|
||||
|
||||
/**
|
||||
* Test text field validation.
|
||||
*/
|
||||
function testTextFieldValidation() {
|
||||
// Create a field with settings to validate.
|
||||
$max_length = 3;
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'text',
|
||||
'settings' => array(
|
||||
'max_length' => $max_length,
|
||||
)
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
// Test validation with valid and invalid values.
|
||||
$entity = EntityTest::create();
|
||||
for ($i = 0; $i <= $max_length + 2; $i++) {
|
||||
$entity->{$field_name}->value = str_repeat('x', $i);
|
||||
$violations = $entity->{$field_name}->validate();
|
||||
if ($i <= $max_length) {
|
||||
$this->assertEqual(count($violations), 0, "Length $i does not cause validation error when max_length is $max_length");
|
||||
}
|
||||
else {
|
||||
$this->assertEqual(count($violations), 1, "Length $i causes validation error when max_length is $max_length");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test required long text with file upload.
|
||||
*/
|
||||
function testRequiredLongTextWithFileUpload() {
|
||||
// Create a text field.
|
||||
$text_field_name = 'text_long';
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $text_field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'text_with_summary',
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'required' => TRUE,
|
||||
])->save();
|
||||
|
||||
// Create a file field.
|
||||
$file_field_name = 'file_field';
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $file_field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'file'
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($text_field_name, array(
|
||||
'type' => 'text_textarea_with_summary',
|
||||
))
|
||||
->setComponent($file_field_name, array(
|
||||
'type' => 'file_generic',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($text_field_name)
|
||||
->setComponent($file_field_name)
|
||||
->save();
|
||||
|
||||
$test_file = current($this->drupalGetTestFiles('text'));
|
||||
$edit['files[file_field_0]'] = drupal_realpath($test_file->uri);
|
||||
$this->drupalPostForm('entity_test/add', $edit, 'Upload');
|
||||
$this->assertResponse(200);
|
||||
$edit = array(
|
||||
'text_long[0][value]' => 'Long text'
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet('entity_test/1');
|
||||
$this->assertText('Long text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test widgets.
|
||||
*/
|
||||
function testTextfieldWidgets() {
|
||||
$this->_testTextfieldWidgets('text', 'text_textfield');
|
||||
$this->_testTextfieldWidgets('text_long', 'text_textarea');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test widgets + 'formatted_text' setting.
|
||||
*/
|
||||
function testTextfieldWidgetsFormatted() {
|
||||
$this->_testTextfieldWidgetsFormatted('text', 'text_textfield');
|
||||
$this->_testTextfieldWidgetsFormatted('text_long', 'text_textarea');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for testTextfieldWidgetsFormatted().
|
||||
*/
|
||||
function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
// Create a field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => $field_type
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => $widget_type,
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Disable all text formats besides the plain text fallback format.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
foreach (filter_formats() as $format) {
|
||||
if (!$format->isFallbackFormat()) {
|
||||
$this->drupalPostForm('admin/config/content/formats/manage/' . $format->id() . '/disable', array(), t('Disable'));
|
||||
}
|
||||
}
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Display the creation form. Since the user only has access to one format,
|
||||
// no format selector will be displayed.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertNoFieldByName("{$field_name}[0][format]", '', 'Format selector is not displayed');
|
||||
|
||||
// Submit with data that should be filtered.
|
||||
$value = '<em>' . $this->randomMachineName() . '</em>';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Display the entity.
|
||||
$entity = EntityTest::load($id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent($renderer->renderRoot($content));
|
||||
$this->assertNoRaw($value, 'HTML tags are not displayed.');
|
||||
$this->assertEscaped($value, 'Escaped HTML is displayed correctly.');
|
||||
|
||||
// Create a new text format that does not escape HTML, and grant the user
|
||||
// access to it.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array(
|
||||
'format' => Unicode::strtolower($this->randomMachineName()),
|
||||
'name' => $this->randomMachineName(),
|
||||
);
|
||||
$this->drupalPostForm('admin/config/content/formats/add', $edit, t('Save configuration'));
|
||||
filter_formats_reset();
|
||||
$format = FilterFormat::load($edit['format']);
|
||||
$format_id = $format->id();
|
||||
$permission = $format->getPermissionName();
|
||||
$roles = $this->webUser->getRoles();
|
||||
$rid = $roles[0];
|
||||
user_role_grant_permissions($rid, array($permission));
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Display edition form.
|
||||
// We should now have a 'text format' selector.
|
||||
$this->drupalGet('entity_test/manage/' . $id . '/edit');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", NULL, 'Widget is displayed');
|
||||
$this->assertFieldByName("{$field_name}[0][format]", NULL, 'Format selector is displayed');
|
||||
|
||||
// Edit and change the text format to the new one that was created.
|
||||
$edit = array(
|
||||
"{$field_name}[0][format]" => $format_id,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
|
||||
// Display the entity.
|
||||
$this->container->get('entity.manager')->getStorage('entity_test')->resetCache(array($id));
|
||||
$entity = EntityTest::load($id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent($renderer->renderRoot($content));
|
||||
$this->assertRaw($value, 'Value is displayed unfiltered');
|
||||
}
|
||||
|
||||
}
|
67
web/core/modules/text/src/TextProcessed.php
Normal file
67
web/core/modules/text/src/TextProcessed.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\text;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
|
||||
/**
|
||||
* A computed property for processing text with a format.
|
||||
*
|
||||
* Required settings (below the definition's 'settings' key) are:
|
||||
* - text source: The text property containing the to be processed text.
|
||||
*/
|
||||
class TextProcessed extends TypedData {
|
||||
|
||||
/**
|
||||
* Cached processed text.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $processed = NULL;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
|
||||
if ($definition->getSetting('text source') === NULL) {
|
||||
throw new \InvalidArgumentException("The definition's 'text source' key has to specify the name of the text property to be processed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
if ($this->processed !== NULL) {
|
||||
return $this->processed;
|
||||
}
|
||||
|
||||
$item = $this->getParent();
|
||||
$text = $item->{($this->definition->getSetting('text source'))};
|
||||
|
||||
// Avoid running check_markup() on empty strings.
|
||||
if (!isset($text) || $text === '') {
|
||||
$this->processed = '';
|
||||
}
|
||||
else {
|
||||
$this->processed = check_markup($text, $item->format, $item->getLangcode());
|
||||
}
|
||||
return $this->processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($value, $notify = TRUE) {
|
||||
$this->processed = $value;
|
||||
// Notify the parent of any changes.
|
||||
if ($notify && isset($this->parent)) {
|
||||
$this->parent->onChange($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\text\Kernel\Migrate;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Upgrade variables to text.settings.yml.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateTextConfigsTest extends MigrateDrupal6TestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('text_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of text variables to text.settings.yml.
|
||||
*/
|
||||
public function testTextSettings() {
|
||||
$config = $this->config('text.settings');
|
||||
$this->assertIdentical(456, $config->get('default_summary_length'));
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'text.settings', $config->get());
|
||||
}
|
||||
|
||||
}
|
98
web/core/modules/text/tests/src/Kernel/TextFormatterTest.php
Normal file
98
web/core/modules/text/tests/src/Kernel/TextFormatterTest.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\text\Kernel;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the text formatters functionality.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class TextFormatterTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('text');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
FilterFormat::create(array(
|
||||
'format' => 'my_text_format',
|
||||
'name' => 'My text format',
|
||||
'filters' => array(
|
||||
'filter_autop' => array(
|
||||
'module' => 'filter',
|
||||
'status' => TRUE,
|
||||
),
|
||||
),
|
||||
))->save();
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'formatted_text',
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'text',
|
||||
'settings' => array(),
|
||||
))->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => $this->entityType,
|
||||
'bundle' => $this->bundle,
|
||||
'field_name' => 'formatted_text',
|
||||
'label' => 'Filtered text',
|
||||
])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all text field formatters.
|
||||
*/
|
||||
public function testFormatters() {
|
||||
$formatters = array(
|
||||
'text_default',
|
||||
'text_trimmed',
|
||||
'text_summary_or_trimmed',
|
||||
);
|
||||
|
||||
// Create the entity to be referenced.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => $this->randomMachineName()));
|
||||
$entity->formatted_text = array(
|
||||
'value' => 'Hello, world!',
|
||||
'format' => 'my_text_format',
|
||||
);
|
||||
$entity->save();
|
||||
|
||||
foreach ($formatters as $formatter) {
|
||||
// Verify the text field formatter's render array.
|
||||
$build = $entity->get('formatted_text')->view(array('type' => $formatter));
|
||||
\Drupal::service('renderer')->renderRoot($build[0]);
|
||||
$this->assertEqual($build[0]['#markup'], "<p>Hello, world!</p>\n");
|
||||
$this->assertEqual($build[0]['#cache']['tags'], FilterFormat::load('my_text_format')->getCacheTags(), format_string('The @formatter formatter has the expected cache tags when formatting a formatted text field.', array('@formatter' => $formatter)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
216
web/core/modules/text/tests/src/Kernel/TextSummaryTest.php
Normal file
216
web/core/modules/text/tests/src/Kernel/TextSummaryTest.php
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\text\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Tests text_summary() with different strings and lengths.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class TextSummaryTest extends KernelTestBase {
|
||||
|
||||
public static $modules = array('system', 'user', 'filter', 'text');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(array('text'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an edge case where the first sentence is a question and
|
||||
* subsequent sentences are not. This edge case is documented at
|
||||
* https://www.drupal.org/node/180425.
|
||||
*/
|
||||
function testFirstSentenceQuestion() {
|
||||
$text = 'A question? A sentence. Another sentence.';
|
||||
$expected = 'A question? A sentence.';
|
||||
$this->assertTextSummary($text, $expected, NULL, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test summary with long example.
|
||||
*/
|
||||
function testLongSentence() {
|
||||
$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . // 125
|
||||
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' . // 108
|
||||
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' . // 103
|
||||
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; // 110
|
||||
$expected = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' .
|
||||
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' .
|
||||
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.';
|
||||
// First three sentences add up to: 336, so add one for space and then 3 to get half-way into next word.
|
||||
$this->assertTextSummary($text, $expected, NULL, 340);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test various summary length edge cases.
|
||||
*/
|
||||
function testLength() {
|
||||
FilterFormat::create(array(
|
||||
'format' => 'autop',
|
||||
'filters' => array(
|
||||
'filter_autop' => array(
|
||||
'status' => 1,
|
||||
),
|
||||
),
|
||||
))->save();
|
||||
FilterFormat::create(array(
|
||||
'format' => 'autop_correct',
|
||||
'filters' => array(
|
||||
'filter_autop' => array(
|
||||
'status' => 1,
|
||||
),
|
||||
'filter_htmlcorrector' => array(
|
||||
'status' => 1,
|
||||
),
|
||||
),
|
||||
))->save();
|
||||
|
||||
// This string tests a number of edge cases.
|
||||
$text = "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>";
|
||||
|
||||
// The summaries we expect text_summary() to return when $size is the index
|
||||
// of each array item.
|
||||
// Using no text format:
|
||||
$format = NULL;
|
||||
$i = 0;
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\n", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nH", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n<", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
|
||||
// Using a text format with filter_autop enabled.
|
||||
$format = 'autop';
|
||||
$i = 0;
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
|
||||
// Using a text format with filter_autop and filter_htmlcorrector enabled.
|
||||
$format = 'autop_correct';
|
||||
$i = 0;
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p></p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p></p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p></p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p></p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p></p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
$this->assertTextSummary($text, "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>", $format, $i++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls text_summary() and asserts that the expected teaser is returned.
|
||||
*/
|
||||
function assertTextSummary($text, $expected, $format = NULL, $size = NULL) {
|
||||
$summary = text_summary($text, $format, $size);
|
||||
$this->assertIdentical($summary, $expected, format_string('<pre style="white-space: pre-wrap">@actual</pre> is identical to <pre style="white-space: pre-wrap">@expected</pre>', array(
|
||||
'@actual' => $summary,
|
||||
'@expected' => $expected,
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\text\Kernel;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the text summary field type.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class TextWithSummaryItemTest extends FieldKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('filter');
|
||||
|
||||
/**
|
||||
* Field storage entity.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig.
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* Field entity.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
// Create the necessary formats.
|
||||
$this->installConfig(array('filter'));
|
||||
FilterFormat::create(array(
|
||||
'format' => 'no_filters',
|
||||
'filters' => array(),
|
||||
))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests processed properties.
|
||||
*/
|
||||
public function testCrudAndUpdate() {
|
||||
$entity_type = 'entity_test';
|
||||
$this->createField($entity_type);
|
||||
|
||||
// Create an entity with a summary and no text format.
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$entity = $storage->create();
|
||||
$entity->summary_field->value = $value = $this->randomMachineName();
|
||||
$entity->summary_field->summary = $summary = $this->randomMachineName();
|
||||
$entity->summary_field->format = NULL;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
$entity = $storage->load($entity->id());
|
||||
$this->assertTrue($entity->summary_field instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->summary_field[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->summary_field->value, $value);
|
||||
$this->assertEqual($entity->summary_field->summary, $summary);
|
||||
$this->assertNull($entity->summary_field->format);
|
||||
// Even if no format is given, if text processing is enabled, the default
|
||||
// format is used.
|
||||
$this->assertEqual($entity->summary_field->processed, "<p>$value</p>\n");
|
||||
$this->assertEqual($entity->summary_field->summary_processed, "<p>$summary</p>\n");
|
||||
|
||||
// Change the format, this should update the processed properties.
|
||||
$entity->summary_field->format = 'no_filters';
|
||||
$this->assertEqual($entity->summary_field->processed, $value);
|
||||
$this->assertEqual($entity->summary_field->summary_processed, $summary);
|
||||
|
||||
// Test the generateSampleValue() method.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create();
|
||||
$entity->summary_field->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text_with_summary field storage and field.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* Entity type for which the field should be created.
|
||||
*/
|
||||
protected function createField($entity_type) {
|
||||
// Create a field .
|
||||
$this->fieldStorage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'summary_field',
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'text_with_summary',
|
||||
'settings' => array(
|
||||
'max_length' => 10,
|
||||
)
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create([
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => $entity_type,
|
||||
]);
|
||||
$this->field->save();
|
||||
}
|
||||
|
||||
}
|
165
web/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php
Normal file
165
web/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\text\Unit\Migrate;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\text\Plugin\migrate\cckfield\TextField;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\text\Plugin\migrate\cckfield\TextField
|
||||
* @group text
|
||||
*/
|
||||
class TextFieldTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->plugin = new TextField([], 'text', []);
|
||||
|
||||
$migration = $this->prophesize(MigrationInterface::class);
|
||||
|
||||
// The plugin's processCckFieldValues() method will call
|
||||
// setProcessOfProperty() and return nothing. So, in order to examine the
|
||||
// process pipeline created by the plugin, we need to ensure that
|
||||
// getProcess() always returns the last input to setProcessOfProperty().
|
||||
$migration->setProcessOfProperty(Argument::type('string'), Argument::type('array'))
|
||||
->will(function($arguments) use ($migration) {
|
||||
$migration->getProcess()->willReturn($arguments[1]);
|
||||
});
|
||||
|
||||
$this->migration = $migration->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::processCckFieldValues
|
||||
*/
|
||||
public function testProcessFilteredTextFieldValues() {
|
||||
$field_info = [
|
||||
'widget_type' => 'text_textfield',
|
||||
];
|
||||
$this->plugin->processCckFieldValues($this->migration, 'body', $field_info);
|
||||
|
||||
$process = $this->migration->getProcess();
|
||||
$this->assertSame('iterator', $process['plugin']);
|
||||
$this->assertSame('body', $process['source']);
|
||||
$this->assertSame('value', $process['process']['value']);
|
||||
|
||||
// Ensure that filter format IDs will be looked up in the filter format
|
||||
// migrations.
|
||||
$lookup = $process['process']['format'][2];
|
||||
$this->assertSame('migration', $lookup['plugin']);
|
||||
$this->assertContains('d6_filter_format', $lookup['migration']);
|
||||
$this->assertContains('d7_filter_format', $lookup['migration']);
|
||||
$this->assertSame('format', $lookup['source']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::processCckFieldValues
|
||||
*/
|
||||
public function testProcessBooleanTextImplicitValues() {
|
||||
$info = array(
|
||||
'widget_type' => 'optionwidgets_onoff',
|
||||
'global_settings' => array(
|
||||
'allowed_values' => "foo\nbar",
|
||||
)
|
||||
);
|
||||
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
|
||||
|
||||
$expected = [
|
||||
'value' => [
|
||||
'plugin' => 'static_map',
|
||||
'source' => 'value',
|
||||
'default_value' => 0,
|
||||
'map' => [
|
||||
'bar' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->migration->getProcess()['process']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::processCckFieldValues
|
||||
*/
|
||||
public function testProcessBooleanTextExplicitValues() {
|
||||
$info = array(
|
||||
'widget_type' => 'optionwidgets_onoff',
|
||||
'global_settings' => array(
|
||||
'allowed_values' => "foo|Foo\nbaz|Baz",
|
||||
)
|
||||
);
|
||||
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
|
||||
|
||||
$expected = [
|
||||
'value' => [
|
||||
'plugin' => 'static_map',
|
||||
'source' => 'value',
|
||||
'default_value' => 0,
|
||||
'map' => [
|
||||
'baz' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->migration->getProcess()['process']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetFieldType().
|
||||
*/
|
||||
public function getFieldTypeProvider() {
|
||||
return array(
|
||||
array('string_long', 'text_textfield', array(
|
||||
'text_processing' => FALSE,
|
||||
)),
|
||||
array('string', 'text_textfield', array(
|
||||
'text_processing' => FALSE,
|
||||
'max_length' => 128,
|
||||
)),
|
||||
array('string_long', 'text_textfield', array(
|
||||
'text_processing' => FALSE,
|
||||
'max_length' => 4096,
|
||||
)),
|
||||
array('text_long', 'text_textfield', array(
|
||||
'text_processing' => TRUE,
|
||||
)),
|
||||
array('text', 'text_textfield', array(
|
||||
'text_processing' => TRUE,
|
||||
'max_length' => 128,
|
||||
)),
|
||||
array('text_long', 'text_textfield', array(
|
||||
'text_processing' => TRUE,
|
||||
'max_length' => 4096,
|
||||
)),
|
||||
array('list_string', 'optionwidgets_buttons'),
|
||||
array('list_string', 'optionwidgets_select'),
|
||||
array('boolean', 'optionwidgets_onoff'),
|
||||
array('text_long', 'text_textarea'),
|
||||
array(NULL, 'undefined'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getFieldType
|
||||
* @dataProvider getFieldTypeProvider
|
||||
*/
|
||||
public function testGetFieldType($expected_type, $widget_type, array $settings = array()) {
|
||||
$row = new Row(array('widget_type' => $widget_type), array('widget_type' => array()));
|
||||
$row->setSourceProperty('global_settings', $settings);
|
||||
$this->assertSame($expected_type, $this->plugin->getFieldType($row));
|
||||
}
|
||||
|
||||
}
|
9
web/core/modules/text/text.info.yml
Normal file
9
web/core/modules/text/text.info.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: Text
|
||||
type: module
|
||||
description: 'Defines simple text field types.'
|
||||
package: Field types
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- field
|
||||
- filter
|
61
web/core/modules/text/text.js
Normal file
61
web/core/modules/text/text.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* @file
|
||||
* Text behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Auto-hide summary textarea if empty and show hide and unhide links.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches auto-hide behavior on `text-summary` events.
|
||||
*/
|
||||
Drupal.behaviors.textSummary = {
|
||||
attach: function (context, settings) {
|
||||
$(context).find('.js-text-summary').once('text-summary').each(function () {
|
||||
var $widget = $(this).closest('.js-text-format-wrapper');
|
||||
|
||||
var $summary = $widget.find('.js-text-summary-wrapper');
|
||||
var $summaryLabel = $summary.find('label').eq(0);
|
||||
var $full = $widget.find('.js-text-full').closest('.js-form-item');
|
||||
var $fullLabel = $full.find('label').eq(0);
|
||||
|
||||
// Create a placeholder label when the field cardinality is greater
|
||||
// than 1.
|
||||
if ($fullLabel.length === 0) {
|
||||
$fullLabel = $('<label></label>').prependTo($full);
|
||||
}
|
||||
|
||||
// Set up the edit/hide summary link.
|
||||
var $link = $('<span class="field-edit-link"> (<button type="button" class="link link-edit-summary">' + Drupal.t('Hide summary') + '</button>)</span>');
|
||||
var $button = $link.find('button');
|
||||
var toggleClick = true;
|
||||
$link.on('click', function (e) {
|
||||
if (toggleClick) {
|
||||
$summary.hide();
|
||||
$button.html(Drupal.t('Edit summary'));
|
||||
$link.appendTo($fullLabel);
|
||||
}
|
||||
else {
|
||||
$summary.show();
|
||||
$button.html(Drupal.t('Hide summary'));
|
||||
$link.appendTo($summaryLabel);
|
||||
}
|
||||
e.preventDefault();
|
||||
toggleClick = !toggleClick;
|
||||
}).appendTo($summaryLabel);
|
||||
|
||||
// If no summary is set, hide the summary field.
|
||||
if ($widget.find('.js-text-summary').val() === '') {
|
||||
$link.trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
8
web/core/modules/text/text.libraries.yml
Normal file
8
web/core/modules/text/text.libraries.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
drupal.text:
|
||||
version: VERSION
|
||||
js:
|
||||
text.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/jquery.once
|
||||
- core/drupal
|
160
web/core/modules/text/text.module
Normal file
160
web/core/modules/text/text.module
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines simple text field types.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function text_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.text':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Text module allows you to create short and long text fields with optional summaries. See the <a href=":field">Field module help</a> and the <a href=":field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href=":text_documentation">online documentation for the Text module</a>.', array(':field' => \Drupal::url('help.page', array('name' => 'field')), ':field_ui' => (\Drupal::moduleHandler()->moduleExists('field_ui')) ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#', ':text_documentation' => 'https://www.drupal.org/documentation/modules/text')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Managing and displaying text fields') . '</dt>';
|
||||
$output .= '<dd>' . t('The <em>settings</em> and <em>display</em> of the text field can be configured separately. See the <a href=":field_ui">Field UI help</a> for more information on how to manage fields and their display.', array(':field_ui' => (\Drupal::moduleHandler()->moduleExists('field_ui')) ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#')) . '</dd>';
|
||||
$output .= '<dt>' . t('Creating short text fields') . '</dt>';
|
||||
$output .= '<dd>' . t('If you choose <em>Text (plain)</em> or <em>Text (formatted)</em> as the field type on the <em>Manage fields</em> page, then a field with a single row is displayed. You can change the maximum text length in the <em>Field settings</em> when you set up the field.') . '</dd>';
|
||||
$output .= '<dt>' . t('Creating long text fields') . '</dt>';
|
||||
$output .= '<dd>' . t('If you choose <em>Text (plain, long)</em>, <em>Text (formatted, long)</em>, or <em>Text (formatted, long, with summary)</em> on the <em>Manage fields</em> page, then users can insert text of unlimited length. On the <em>Manage form display</em> page, you can set the number of rows that are displayed to users.') . '</dd>';
|
||||
$output .= '<dt>' . t('Trimming the text length') . '</dt>';
|
||||
$output .= '<dd>' . t('On the <em>Manage display</em> page you can choose to display a trimmed version of the text, and if so, where to cut off the text.') . '</dd>';
|
||||
$output .= '<dt>' . t('Displaying summaries instead of trimmed text') . '</dt>';
|
||||
$output .= '<dd>' . t('As an alternative to using a trimmed version of the text, you can enter a separate summary by choosing the <em>Text (formatted, long, with summary)</em> field type on the <em>Manage fields</em> page. Even when <em>Summary input</em> is enabled, and summaries are provided, you can display <em>trimmed</em> text nonetheless by choosing the appropriate format on the <em>Manage display</em> page.') . '</dd>';
|
||||
$output .= '<dt>' . t('Using text formats and editors') . '</dt>';
|
||||
$output .= '<dd>' . t('If you choose <em>Text (plain)</em> or <em>Text (plain, long)</em> you restrict the input to <em>Plain text</em> only. If you choose <em>Text (formatted)</em>, <em>Text (formatted, long)</em>, or <em>Text (formatted, long with summary)</em> you allow users to write formatted text. Which options are available to individual users depends on the settings on the <a href=":formats">Text formats and editors page</a>.', array(':formats' => \Drupal::url('filter.admin_overview'))) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a trimmed, formatted version of a text field value.
|
||||
*
|
||||
* If the end of the summary is not indicated using the <!--break--> delimiter
|
||||
* then we generate the summary automatically, trying to end it at a sensible
|
||||
* place such as the end of a paragraph, a line break, or the end of a sentence
|
||||
* (in that order of preference).
|
||||
*
|
||||
* @param $text
|
||||
* The content for which a summary will be generated.
|
||||
* @param $format
|
||||
* The format of the content. If the line break filter is present then we
|
||||
* treat newlines embedded in $text as line breaks. If the htmlcorrector
|
||||
* filter is present, it will be run on the generated summary (if different
|
||||
* from the incoming $text).
|
||||
* @param $size
|
||||
* The desired character length of the summary. If omitted, the default value
|
||||
* will be used. Ignored if the special delimiter is present in $text.
|
||||
*
|
||||
* @return
|
||||
* The generated summary.
|
||||
*/
|
||||
function text_summary($text, $format = NULL, $size = NULL) {
|
||||
|
||||
if (!isset($size)) {
|
||||
$size = \Drupal::config('text.settings')->get('default_summary_length');
|
||||
}
|
||||
|
||||
// Find where the delimiter is in the body
|
||||
$delimiter = strpos($text, '<!--break-->');
|
||||
|
||||
// If the size is zero, and there is no delimiter, the entire body is the summary.
|
||||
if ($size == 0 && $delimiter === FALSE) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
// If a valid delimiter has been specified, use it to chop off the summary.
|
||||
if ($delimiter !== FALSE) {
|
||||
return substr($text, 0, $delimiter);
|
||||
}
|
||||
|
||||
// Retrieve the filters of the specified text format, if any.
|
||||
if (isset($format)) {
|
||||
$filters = FilterFormat::load($format)->filters();
|
||||
// If the specified format does not exist, return nothing. $text is already
|
||||
// filtered text, but the remainder of this function will not be able to
|
||||
// ensure a sane and secure summary.
|
||||
if (!$filters) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a short body, the entire body is the summary.
|
||||
if (Unicode::strlen($text) <= $size) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
// If the delimiter has not been specified, try to split at paragraph or
|
||||
// sentence boundaries.
|
||||
|
||||
// The summary may not be longer than maximum length specified. Initial slice.
|
||||
$summary = Unicode::truncate($text, $size);
|
||||
|
||||
// Store the actual length of the UTF8 string -- which might not be the same
|
||||
// as $size.
|
||||
$max_rpos = strlen($summary);
|
||||
|
||||
// How much to cut off the end of the summary so that it doesn't end in the
|
||||
// middle of a paragraph, sentence, or word.
|
||||
// Initialize it to maximum in order to find the minimum.
|
||||
$min_rpos = $max_rpos;
|
||||
|
||||
// Store the reverse of the summary. We use strpos on the reversed needle and
|
||||
// haystack for speed and convenience.
|
||||
$reversed = strrev($summary);
|
||||
|
||||
// Build an array of arrays of break points grouped by preference.
|
||||
$break_points = array();
|
||||
|
||||
// A paragraph near the end of sliced summary is most preferable.
|
||||
$break_points[] = array('</p>' => 0);
|
||||
|
||||
// If no complete paragraph then treat line breaks as paragraphs.
|
||||
$line_breaks = array('<br />' => 6, '<br>' => 4);
|
||||
// Newline only indicates a line break if line break converter
|
||||
// filter is present.
|
||||
if (isset($format) && $filters->has('filter_autop') && $filters->get('filter_autop')->status) {
|
||||
$line_breaks["\n"] = 1;
|
||||
}
|
||||
$break_points[] = $line_breaks;
|
||||
|
||||
// If the first paragraph is too long, split at the end of a sentence.
|
||||
$break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
|
||||
|
||||
// Iterate over the groups of break points until a break point is found.
|
||||
foreach ($break_points as $points) {
|
||||
// Look for each break point, starting at the end of the summary.
|
||||
foreach ($points as $point => $offset) {
|
||||
// The summary is already reversed, but the break point isn't.
|
||||
$rpos = strpos($reversed, strrev($point));
|
||||
if ($rpos !== FALSE) {
|
||||
$min_rpos = min($rpos + $offset, $min_rpos);
|
||||
}
|
||||
}
|
||||
|
||||
// If a break point was found in this group, slice and stop searching.
|
||||
if ($min_rpos !== $max_rpos) {
|
||||
// Don't slice with length 0. Length must be <0 to slice from RHS.
|
||||
$summary = ($min_rpos === 0) ? $summary : substr($summary, 0, 0 - $min_rpos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the htmlcorrector filter is present, apply it to the generated summary.
|
||||
if (isset($format) && $filters->has('filter_htmlcorrector') && $filters->get('filter_htmlcorrector')->status) {
|
||||
$summary = Html::normalize($summary);
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
Reference in a new issue