Issue #2429443 by vijaycs85, rteijeiro, penyaskito, gloob, xjm, nod_, geertvd, pjonckiere, alexpott, Wim Leers, Gábor Hojtsy, Fabianx, tim.plunkett: Date format form is unusable
parent
ba05c7401d
commit
4e3e39cbf5
|
@ -385,42 +385,6 @@ function date_iso8601($date) {
|
|||
return date('c', $date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a formatted date string.
|
||||
*
|
||||
* Callback for preg_replace_callback() within format_date().
|
||||
*/
|
||||
function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
|
||||
// We cache translations to avoid redundant and rather costly calls to t().
|
||||
static $cache, $langcode;
|
||||
|
||||
if (!isset($matches)) {
|
||||
$langcode = $new_langcode;
|
||||
return;
|
||||
}
|
||||
|
||||
$code = $matches[1];
|
||||
$string = $matches[2];
|
||||
|
||||
if (!isset($cache[$langcode][$code][$string])) {
|
||||
$options = array(
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
|
||||
if ($code == 'F') {
|
||||
$options['context'] = 'Long month name';
|
||||
}
|
||||
|
||||
if ($code == '') {
|
||||
$cache[$langcode][$code][$string] = $string;
|
||||
}
|
||||
else {
|
||||
$cache[$langcode][$code][$string] = t($string, array(), $options);
|
||||
}
|
||||
}
|
||||
return $cache[$langcode][$code][$string];
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "defgroup format".
|
||||
*/
|
||||
|
|
|
@ -192,6 +192,33 @@ class DateFormatter {
|
|||
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 current 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 ?: 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.
|
||||
*
|
||||
|
@ -200,8 +227,9 @@ class DateFormatter {
|
|||
* @param string $langcode
|
||||
* The langcode of the language to use.
|
||||
*
|
||||
* @return string
|
||||
* The pattern for the date format in the given language.
|
||||
* @return string|null
|
||||
* The pattern for the date format in the given language for non-custom
|
||||
* formats, NULL otherwise.
|
||||
*/
|
||||
protected function dateFormat($format, $langcode) {
|
||||
if (!isset($this->dateFormats[$format][$langcode])) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Extends DateTimePlus().
|
||||
|
@ -23,6 +24,14 @@ use Drupal\Component\Datetime\DateTimePlus;
|
|||
*/
|
||||
class DrupalDateTime extends DateTimePlus {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Format string translation cache.
|
||||
*
|
||||
*/
|
||||
protected $formatTranslationCache;
|
||||
|
||||
/**
|
||||
* Constructs a date object.
|
||||
*
|
||||
|
@ -84,7 +93,8 @@ class DrupalDateTime extends DateTimePlus {
|
|||
* The formatted value of the date.
|
||||
*/
|
||||
public function format($format, $settings = array()) {
|
||||
$settings['langcode'] = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
|
||||
$langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
|
||||
$value = '';
|
||||
// Format the date and catch errors.
|
||||
try {
|
||||
// Encode markers that should be translated. 'A' becomes
|
||||
|
@ -98,15 +108,33 @@ class DrupalDateTime extends DateTimePlus {
|
|||
// Call date_format().
|
||||
$format = parent::format($format);
|
||||
|
||||
// Pass the langcode to _format_date_callback().
|
||||
_format_date_callback(NULL, $settings['langcode']);
|
||||
// Translates a formatted date string.
|
||||
$translation_callback = function($matches) use ($langcode) {
|
||||
$code = $matches[1];
|
||||
$string = $matches[2];
|
||||
if (!isset($this->formatTranslationCache[$langcode][$code][$string])) {
|
||||
$options = array('langcode' => $langcode);
|
||||
if ($code == 'F') {
|
||||
$options['context'] = 'Long month name';
|
||||
}
|
||||
|
||||
if ($code == '') {
|
||||
$this->formatTranslationCache[$langcode][$code][$string] = $string;
|
||||
}
|
||||
else {
|
||||
$this->formatTranslationCache[$langcode][$code][$string] = $this->t($string, array(), $options);
|
||||
}
|
||||
}
|
||||
return $this->formatTranslationCache[$langcode][$code][$string];
|
||||
};
|
||||
|
||||
// Translate the marked sequences.
|
||||
$value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', '_format_date_callback', $format);
|
||||
$value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', $translation_callback, $format);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->errors[] = $e->getMessage();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
|
@ -22,51 +18,23 @@ class DateFormat extends FormElementBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
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'));
|
||||
$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(REQUEST_TIME, 'custom', $translation_config)));
|
||||
|
||||
return array(
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#description' => $description,
|
||||
'#field_suffix' => ' <div class="edit-date-format-suffix"><small id="edit-date-format-suffix">' . $format . '</small></div>',
|
||||
'#ajax' => array(
|
||||
'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
|
||||
'event' => 'keyup',
|
||||
'progress' => array('type' => 'throbber', 'message' => NULL),
|
||||
),
|
||||
) + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback to render a sample of the input date format.
|
||||
*
|
||||
* @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) {
|
||||
$response = new AjaxResponse();
|
||||
|
||||
$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;
|
||||
'#field_suffix' => ' <small data-drupal-date-formatter="preview">' . $format . '</small>',
|
||||
'#attributes' => [
|
||||
'data-drupal-date-formatter' => 'source',
|
||||
],
|
||||
'#attached' => [
|
||||
'drupalSettings' => ['dateFormats' => $date_formatter->getSampleDateFormats($translation_language->getId())],
|
||||
'library' => ['system/drupal.system.date'],
|
||||
],
|
||||
] + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -446,6 +446,9 @@ class ConfigTranslationUiTest extends WebTestBase {
|
|||
$this->drupalGet($translation_page_url);
|
||||
$this->assertText($label);
|
||||
|
||||
// Make sure that the date library is added.
|
||||
$this->assertRaw('core/modules/system/js/system.date.js');
|
||||
|
||||
// Update translatable fields.
|
||||
$edit = array(
|
||||
'translation[config_names][core.date_format.' . $id . '][label]' => $id . ' - FR',
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
(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);
|
|
@ -21,7 +21,7 @@ class DateFormatEditForm extends DateFormatFormBase {
|
|||
$form = parent::form($form, $form_state);
|
||||
|
||||
$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();
|
||||
|
||||
return $form;
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
namespace Drupal\system\Form;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
@ -79,30 +77,6 @@ abstract class DateFormatFormBase extends EntityForm {
|
|||
->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}
|
||||
*/
|
||||
|
@ -126,20 +100,16 @@ abstract class DateFormatFormBase extends EntityForm {
|
|||
'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(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Format string'),
|
||||
'#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')),
|
||||
'#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,
|
||||
'#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(
|
||||
|
@ -148,7 +118,8 @@ abstract class DateFormatFormBase extends EntityForm {
|
|||
'#languages' => LanguageInterface::STATE_ALL,
|
||||
'#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);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ maintenance:
|
|||
drupal.system:
|
||||
version: VERSION
|
||||
js:
|
||||
system.js: {}
|
||||
js/system.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
|
@ -39,7 +39,7 @@ drupal.system:
|
|||
drupal.system.modules:
|
||||
version: VERSION
|
||||
js:
|
||||
system.modules.js: {}
|
||||
js/system.modules.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
|
@ -50,3 +50,14 @@ diff:
|
|||
css:
|
||||
component:
|
||||
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
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Tests\Core\Datetime;
|
||||
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
|
@ -45,10 +46,22 @@ class DateTest extends UnitTestCase {
|
|||
protected $dateFormatter;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
|
||||
$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->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);
|
||||
$container->set('string_translation', $this->getStringTranslationStub());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$this->dateFormatter = new DateFormatter($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub());
|
||||
}
|
||||
|
||||
|
@ -57,7 +70,7 @@ class DateTest extends UnitTestCase {
|
|||
*
|
||||
* @dataProvider providerTestFormatInterval
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatInterval()
|
||||
* @covers \Drupal\Core\Datetime\DateFormatter::formatInterval
|
||||
*/
|
||||
public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) {
|
||||
// Mocks a simple formatPlural implementation.
|
||||
|
@ -128,4 +141,23 @@ class DateTest extends UnitTestCase {
|
|||
$this->assertEquals('0 sec', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the getSampleDateFormats method.
|
||||
*
|
||||
* @covers \Drupal\Core\Datetime\DateFormatter::getSampleDateFormats
|
||||
*/
|
||||
public function testGetSampleDateFormats() {
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue