Skip to content
Snippets Groups Projects
Commit 37e278b4 authored by Jess's avatar Jess
Browse files

Issue #2429443 by vijaycs85, rteijeiro, penyaskito, gloob, xjm, nod_, geertvd,...

Issue #2429443 by vijaycs85, rteijeiro, penyaskito, gloob, xjm, nod_, geertvd, Wim Leers, Gábor Hojtsy, Fabianx, pjonckiere, tim.plunkett: Date format form is unusable
parent 44e8ffd5
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
Showing
with 195 additions and 77 deletions
...@@ -192,6 +192,33 @@ public function formatInterval($interval, $granularity = 2, $langcode = NULL) { ...@@ -192,6 +192,33 @@ public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
return $output ? $output : $this->t('0 sec', array(), array('langcode' => $langcode)); return $output ? $output : $this->t('0 sec', array(), array('langcode' => $langcode));
} }
/**
* Provides values for all date formatting characters for a given timestamp.
*
* @param string|null $langcode
* (optional) Language code of the date format, if different from the site
* default language.
* @param int|null $timestamp
* (optional) The Unix timestamp to format, defaults to the request time.
* @param string|null $timezone
* (optional) The timezone to use, if different from the site's default
* timezone.
*
* @return array
* An array of formatted date values, indexed by the date format character.
*
* @see date()
*/
public function getSampleDateFormats($langcode = NULL, $timestamp = NULL, $timezone = NULL) {
$timestamp = $timestamp ?: (int) $_SERVER['REQUEST_TIME'];
// All date format characters for the PHP date() function.
$date_chars = str_split('dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU');
$date_elements = array_combine($date_chars, $date_chars);
return array_map(function($character) use ($timestamp, $timezone, $langcode) {
return $this->format($timestamp, 'custom', $character, $timezone, $langcode);
}, $date_elements);
}
/** /**
* Loads the given format pattern for the given langcode. * Loads the given format pattern for the given langcode.
* *
...@@ -200,8 +227,9 @@ public function formatInterval($interval, $granularity = 2, $langcode = NULL) { ...@@ -200,8 +227,9 @@ public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
* @param string $langcode * @param string $langcode
* The langcode of the language to use. * The langcode of the language to use.
* *
* @return string * @return string|null
* The pattern for the date format in the given language. * The pattern for the date format in the given language for non-custom
* formats, NULL otherwise.
*/ */
protected function dateFormat($format, $langcode) { protected function dateFormat($format, $langcode) {
if (!isset($this->dateFormats[$format][$langcode])) { if (!isset($this->dateFormats[$format][$langcode])) {
......
...@@ -176,6 +176,9 @@ public function buildForm(array $form, FormStateInterface $form_state, Request $ ...@@ -176,6 +176,9 @@ public function buildForm(array $form, FormStateInterface $form_state, Request $
if ($form_element = $this->createFormElement($schema)) { if ($form_element = $this->createFormElement($schema)) {
$parents = array('config_names', $name); $parents = array('config_names', $name);
$form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents); $form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents);
if ($attributes = $form_element->getFormAttributes()) {
$form = array_merge_recursive($form, $attributes);
}
} }
} }
......
...@@ -22,51 +22,29 @@ class DateFormat extends FormElementBase { ...@@ -22,51 +22,29 @@ class DateFormat extends FormElementBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) { public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
/** @var \Drupal\Core\Datetime\DateFormatter $date_formatter */
$date_formatter = \Drupal::service('date.formatter');
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')); $description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php'));
$format = $this->t('Displayed as %date_format', array('%date_format' => \Drupal::service('date.formatter')->format(REQUEST_TIME, 'custom', $translation_config))); $format = $this->t('Displayed as %date_format', array('%date_format' => $date_formatter->format((int) $_SERVER['REQUEST_TIME'], 'custom', $translation_config)));
return array( return [
'#type' => 'textfield', '#type' => 'textfield',
'#description' => $description, '#description' => $description,
'#field_suffix' => ' <div class="edit-date-format-suffix"><small id="edit-date-format-suffix">' . $format . '</small></div>', '#field_suffix' => ' <small data-drupal-date-formatter="preview">' . $format . '</small>',
'#ajax' => array( '#attributes' => [
'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample', 'data-drupal-date-formatter' => 'source',
'event' => 'keyup', ],
'progress' => array('type' => 'throbber', 'message' => NULL), '#attached' => [
), 'drupalSettings' => array('dateFormats' => $date_formatter->getSampleDateFormats($translation_language->getId())),
) + parent::getTranslationElement($translation_language, $source_config, $translation_config); ],
] + parent::getTranslationElement($translation_language, $source_config, $translation_config);
} }
/** /**
* Ajax callback to render a sample of the input date format. * {@inheritdoc}
*
* @param array $form
* Form API array structure.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state information.
*
* @return AjaxResponse
* Ajax response with the rendered sample date using the given format. If
* the given format cannot be identified or was empty, the response will
* be empty as well.
*/ */
public static function ajaxSample(array $form, FormStateInterface $form_state) { public function getFormAttributes() {
$response = new AjaxResponse(); return ['#attached' => ['library' => ['system/drupal.system.date']]];
$format_value = NestedArray::getValue($form_state->getValues(), $form_state->getTriggeringElement()['#array_parents']);
if (!empty($format_value)) {
// Format the date with a custom date format with the given pattern.
// The object is not instantiated in an Ajax context, so $this->t()
// cannot be used here.
$format = t('Displayed as %date_format', array('%date_format' => \Drupal::service('date.formatter')->format(REQUEST_TIME, 'custom', $format_value)));
// Return a command instead of a string, since the Ajax framework
// automatically prepends an additional empty DIV element for a string,
// which breaks the layout.
$response->addCommand(new ReplaceCommand('#edit-date-format-suffix', '<small id="edit-date-format-suffix">' . $format . '</small>'));
}
return $response;
} }
} }
...@@ -70,4 +70,12 @@ public function getTranslationBuild(LanguageInterface $source_language, Language ...@@ -70,4 +70,12 @@ public function getTranslationBuild(LanguageInterface $source_language, Language
*/ */
public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL); public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL);
/**
* Allows to provide form attributes for the configuration form.
*
* @return array
* An array of form attributes.
*/
public function getFormAttributes();
} }
...@@ -74,6 +74,13 @@ public function getTranslationBuild(LanguageInterface $source_language, Language ...@@ -74,6 +74,13 @@ public function getTranslationBuild(LanguageInterface $source_language, Language
return $build; return $build;
} }
/**
* {@inheritdoc}
*/
public function getFormAttributes() {
return [];
}
/** /**
* Returns the source element for a given configuration definition. * Returns the source element for a given configuration definition.
* *
......
...@@ -96,6 +96,23 @@ public function setConfig(Config $base_config, LanguageConfigOverride $config_tr ...@@ -96,6 +96,23 @@ public function setConfig(Config $base_config, LanguageConfigOverride $config_tr
} }
} }
/**
* {@inheritdoc}
*/
public function getFormAttributes() {
$attributes = [];
foreach ($this->element as $key => $element) {
if ($form_element = ConfigTranslationFormBase::createFormElement($element)) {
$form_attributes = $form_element->getFormAttributes();
if (empty($form_attributes)) {
continue;
}
$attributes += $form_attributes;
}
}
return $attributes;
}
/** /**
* Returns the title for the 'details' element of a group of schema elements. * Returns the title for the 'details' element of a group of schema elements.
* *
......
...@@ -446,6 +446,9 @@ public function testDateFormatTranslation() { ...@@ -446,6 +446,9 @@ public function testDateFormatTranslation() {
$this->drupalGet($translation_page_url); $this->drupalGet($translation_page_url);
$this->assertText($label); $this->assertText($label);
// Make sure that the date library is added.
$this->assertRaw('core/modules/system/js/system.date.js');
// Update translatable fields. // Update translatable fields.
$edit = array( $edit = array(
'translation[config_names][core.date_format.' . $id . '][label]' => $id . ' - FR', 'translation[config_names][core.date_format.' . $id . '][label]' => $id . ' - FR',
......
(function ($, Drupal, drupalSettings) {
"use strict";
var dateFormats = drupalSettings.dateFormats;
/**
* Display the preview for date format entered.
*/
Drupal.behaviors.dateFormat = {
attach: function (context) {
var $context = $(context);
var $source = $context.find('[data-drupal-date-formatter="source"]').once('dateFormat');
var $target = $context.find('[data-drupal-date-formatter="preview"]').once('dateFormat');
var $preview = $target.find('em');
// All elements have to exist.
if (!$source.length || !$target.length) {
return;
}
/**
* Event handler that replaces date characters with value.
*
* @param {object} e
*/
function dateFormatHandler(e) {
var baseValue = $(e.target).val() || '';
var dateString = baseValue.replace(/\\?(.?)/gi, function (key, value) {
return dateFormats[key] ? dateFormats[key] : value;
});
$preview.html(dateString);
$target.toggleClass('js-hide', !dateString.length);
}
/**
* On given event triggers the date character replacement.
*/
$source.on('keyup.dateFormat change.dateFormat input.dateFormat', dateFormatHandler)
// Initialize preview.
.trigger('keyup');
}
};
})(jQuery, Drupal, drupalSettings);
File moved
...@@ -21,7 +21,7 @@ public function form(array $form, FormStateInterface $form_state) { ...@@ -21,7 +21,7 @@ public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state); $form = parent::form($form, $form_state);
$now = t('Displayed as %date', array('%date' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id()))); $now = t('Displayed as %date', array('%date' => $this->dateFormatter->format(REQUEST_TIME, $this->entity->id())));
$form['date_format_pattern']['#field_suffix'] = ' <small id="edit-date-format-suffix">' . $now . '</small>'; $form['date_format_pattern']['#field_suffix'] = ' <small data-drupal-date-formatter="preview">' . $now . '</small>';
$form['date_format_pattern']['#default_value'] = $this->entity->getPattern(); $form['date_format_pattern']['#default_value'] = $this->entity->getPattern();
return $form; return $form;
......
...@@ -79,30 +79,6 @@ public function exists($entity_id, array $element) { ...@@ -79,30 +79,6 @@ public function exists($entity_id, array $element) {
->execute(); ->execute();
} }
/**
* Returns the date for a given format string.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An AJAX Response to update the date-time value of the date format.
*/
public static function dateTimeLookup(array $form, FormStateInterface $form_state) {
$format = '';
if (!$form_state->isValueEmpty('date_format_pattern')) {
$format = t('Displayed as %date_format', array('%date_format' => \Drupal::service('date.formatter')->format(REQUEST_TIME, 'custom', $form_state->getValue('date_format_pattern'))));
}
// Return a command instead of a string, since the Ajax framework
// automatically prepends an additional empty DIV element for a string, which
// breaks the layout.
$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand('#edit-date-format-suffix', '<small id="edit-date-format-suffix">' . $format . '</small>'));
return $response;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -126,20 +102,16 @@ public function form(array $form, FormStateInterface $form_state) { ...@@ -126,20 +102,16 @@ public function form(array $form, FormStateInterface $form_state) {
'error' => $this->t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'), 'error' => $this->t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'),
), ),
); );
$form['date_format_pattern'] = array( $form['date_format_pattern'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Format string'), '#title' => t('Format string'),
'#maxlength' => 100, '#maxlength' => 100,
'#description' => $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')), '#description' => $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
'#default_value' => '',
'#field_suffix' => ' <small id="edit-date-format-suffix"></small>',
'#ajax' => array(
'callback' => '::dateTimeLookup',
'event' => 'keyup',
'progress' => array('type' => 'throbber', 'message' => NULL),
),
'#required' => TRUE, '#required' => TRUE,
'#attributes' => [
'data-drupal-date-formatter' => 'source',
],
'#field_suffix' => ' <small class="js-hide" data-drupal-date-formatter="preview">' . $this->t('Displayed as %date_format', ['%date_format' => '']) . '</small>',
); );
$form['langcode'] = array( $form['langcode'] = array(
...@@ -148,7 +120,8 @@ public function form(array $form, FormStateInterface $form_state) { ...@@ -148,7 +120,8 @@ public function form(array $form, FormStateInterface $form_state) {
'#languages' => LanguageInterface::STATE_ALL, '#languages' => LanguageInterface::STATE_ALL,
'#default_value' => $this->entity->language()->getId(), '#default_value' => $this->entity->language()->getId(),
); );
$form['#attached']['drupalSettings']['dateFormats'] = $this->dateFormatter->getSampleDateFormats();
$form['#attached']['library'][] = 'system/drupal.system.date';
return parent::form($form, $form_state); return parent::form($form, $form_state);
} }
......
...@@ -29,7 +29,7 @@ maintenance: ...@@ -29,7 +29,7 @@ maintenance:
drupal.system: drupal.system:
version: VERSION version: VERSION
js: js:
system.js: {} js/system.js: {}
dependencies: dependencies:
- core/jquery - core/jquery
- core/drupal - core/drupal
...@@ -39,7 +39,7 @@ drupal.system: ...@@ -39,7 +39,7 @@ drupal.system:
drupal.system.modules: drupal.system.modules:
version: VERSION version: VERSION
js: js:
system.modules.js: {} js/system.modules.js: {}
dependencies: dependencies:
- core/jquery - core/jquery
- core/drupal - core/drupal
...@@ -50,3 +50,14 @@ diff: ...@@ -50,3 +50,14 @@ diff:
css: css:
component: component:
css/system.diff.css: {} css/system.diff.css: {}
drupal.system.date:
version: VERSION
js:
js/system.date.js: {}
dependencies:
- core/jquery
- core/drupal
- core/drupalSettings
- core/jquery.once
- core/drupal.form
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
* Contains \Drupal\Tests\Core\Datetime\DateTest. * Contains \Drupal\Tests\Core\Datetime\DateTest.
*/ */
namespace Drupal\Tests\Core\Datetime; namespace Drupal\Tests\Core\Datetime {
use Drupal\Core\Datetime\DateFormatter; use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
/** /**
...@@ -45,10 +46,21 @@ class DateTest extends UnitTestCase { ...@@ -45,10 +46,21 @@ class DateTest extends UnitTestCase {
protected $dateFormatter; protected $dateFormatter;
protected function setUp() { protected function setUp() {
parent::setUp();
$entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$this->entityManager->expects($this->once())->method('getStorage')->with('date_format')->willReturn($entity_storage);
$this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
$config_factory = $this->getConfigFactoryStub(['system.date' => ['country' => ['default' => 'GB']]]);
$container = new ContainerBuilder();
$container->set('config.factory', $config_factory);
\Drupal::setContainer($container);
$this->dateFormatter = new DateFormatter($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub()); $this->dateFormatter = new DateFormatter($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub());
} }
...@@ -57,7 +69,7 @@ protected function setUp() { ...@@ -57,7 +69,7 @@ protected function setUp() {
* *
* @dataProvider providerTestFormatInterval * @dataProvider providerTestFormatInterval
* *
* @see \Drupal\Core\Datetime\DateFormatter::formatInterval() * @covers \Drupal\Core\Datetime\DateFormatter::formatInterval
*/ */
public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) { public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) {
// Mocks a simple formatPlural implementation. // Mocks a simple formatPlural implementation.
...@@ -128,4 +140,36 @@ public function testFormatIntervalZeroSecond() { ...@@ -128,4 +140,36 @@ public function testFormatIntervalZeroSecond() {
$this->assertEquals('0 sec', $result); $this->assertEquals('0 sec', $result);
} }
/**
* Tests the getSampleDateFormats method.
*
* @covers \Drupal\Core\Datetime\DateFormatter::getSampleDateFormats
*/
public function testGetSampleDateFormats() {
include_once $this->root . '/core/includes/common.inc';
$timestamp = strtotime('2015-03-22 14:23:00');
$expected = $this->dateFormatter->getSampleDateFormats('en', $timestamp, 'Europe/London');
// Removed characters related to timezone 'e' and 'T', as test does not have
// timezone set.
$date_characters = 'dDjlNSwzWFmMntLoYyaABgGhHisuIOPZcrU';
$date_chars = str_split($date_characters);
foreach ($date_chars as $val) {
$this->assertEquals($expected[$val], date($val, $timestamp));
}
}
}
}
namespace {
use Drupal\Component\Utility\String;
if (!function_exists('t')) {
function t($string, array $args = []) {
return String::format($string, $args);
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment