first commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime;
|
||||
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
|
||||
/**
|
||||
* A computed property for dates of date time field items.
|
||||
*
|
||||
* Required settings (below the definition's 'settings' key) are:
|
||||
* - date source: The date property containing the to be computed date.
|
||||
*/
|
||||
class DateTimeComputed extends TypedData {
|
||||
|
||||
/**
|
||||
* Cached computed date.
|
||||
*
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
protected $date = NULL;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
if (!$definition->getSetting('date source')) {
|
||||
throw new \InvalidArgumentException("The definition's 'date source' key has to specify the name of the date property to be computed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
if ($this->date !== NULL) {
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Field\FieldItemInterface $item */
|
||||
$item = $this->getParent();
|
||||
$value = $item->{($this->definition->getSetting('date source'))};
|
||||
|
||||
$datetime_type = $item->getFieldDefinition()->getSetting('datetime_type');
|
||||
$storage_format = $datetime_type === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
try {
|
||||
$date = DrupalDateTime::createFromFormat($storage_format, $value, DateTimeItemInterface::STORAGE_TIMEZONE);
|
||||
if ($date instanceof DrupalDateTime && !$date->hasErrors()) {
|
||||
$this->date = $date;
|
||||
// If the format did not include an explicit time portion, then the
|
||||
// time will be set from the current time instead. For consistency, we
|
||||
// set the time to 12:00:00 UTC for date-only fields. This is used so
|
||||
// that the local date portion is the same, across nearly all time
|
||||
// zones.
|
||||
// @see \Drupal\Component\Datetime\DateTimePlus::setDefaultDateTime()
|
||||
// @see http://php.net/manual/datetime.createfromformat.php
|
||||
if ($datetime_type === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
$this->date->setDefaultDateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// @todo Handle this.
|
||||
}
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($value, $notify = TRUE) {
|
||||
$this->date = $value;
|
||||
// Notify the parent of any changes.
|
||||
if ($notify && isset($this->parent)) {
|
||||
$this->parent->onChange($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'Custom' formatter for 'datetime' fields.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "datetime_custom",
|
||||
* label = @Translation("Custom"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
*)
|
||||
*/
|
||||
class DateTimeCustomFormatter extends DateTimeFormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'date_format' => DateTimeItemInterface::DATETIME_STORAGE_FORMAT,
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
// @todo Evaluate removing this method in
|
||||
// https://www.drupal.org/node/2793143 to determine if the behavior and
|
||||
// markup in the base class implementation can be used instead.
|
||||
$elements = [];
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
if (!empty($item->date)) {
|
||||
/** @var \Drupal\Core\Datetime\DrupalDateTime $date */
|
||||
$date = $item->date;
|
||||
|
||||
$elements[$delta] = $this->buildDate($date);
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function formatDate($date) {
|
||||
$format = $this->getSetting('date_format');
|
||||
$timezone = $this->getSetting('timezone_override') ?: $date->getTimezone()->getName();
|
||||
return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $timezone != '' ? $timezone : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::settingsForm($form, $form_state);
|
||||
|
||||
$form['date_format'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Date/time format'),
|
||||
'#description' => $this->t('See <a href="http://php.net/manual/function.date.php" target="_blank">the documentation for PHP date formats</a>.'),
|
||||
'#default_value' => $this->getSetting('date_format'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
$date = new DrupalDateTime();
|
||||
$this->setTimeZone($date);
|
||||
$summary[] = $date->format($this->getSetting('date_format'), $this->getFormatSettings());
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'Default' formatter for 'datetime' fields.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "datetime_default",
|
||||
* label = @Translation("Default"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DateTimeDefaultFormatter extends DateTimeFormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'format_type' => 'medium',
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function formatDate($date) {
|
||||
$format_type = $this->getSetting('format_type');
|
||||
$timezone = $this->getSetting('timezone_override') ?: $date->getTimezone()->getName();
|
||||
return $this->dateFormatter->format($date->getTimestamp(), $format_type, '', $timezone != '' ? $timezone : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::settingsForm($form, $form_state);
|
||||
|
||||
$time = new DrupalDateTime();
|
||||
$format_types = $this->dateFormatStorage->loadMultiple();
|
||||
$options = [];
|
||||
foreach ($format_types as $type => $type_info) {
|
||||
$format = $this->dateFormatter->format($time->getTimestamp(), $type);
|
||||
$options[$type] = $type_info->label() . ' (' . $format . ')';
|
||||
}
|
||||
|
||||
$form['format_type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Date format'),
|
||||
'#description' => t("Choose a format for displaying the date. Be sure to set a format appropriate for the field, i.e. omitting time for a field that only has a date."),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->getSetting('format_type'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
$date = new DrupalDateTime();
|
||||
$summary[] = t('Format: @display', ['@display' => $this->formatDate($date)]);
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base class for 'DateTime Field formatter' plugin implementations.
|
||||
*/
|
||||
abstract class DateTimeFormatterBase extends FormatterBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The date format entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $dateFormatStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new DateTimeDefaultFormatter.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the formatter.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The definition of the field to which the formatter is associated.
|
||||
* @param array $settings
|
||||
* The formatter settings.
|
||||
* @param string $label
|
||||
* The formatter label display setting.
|
||||
* @param string $view_mode
|
||||
* The view mode.
|
||||
* @param array $third_party_settings
|
||||
* Third party settings.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $date_format_storage
|
||||
* The date format entity storage.
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $date_formatter, EntityStorageInterface $date_format_storage) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
|
||||
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->dateFormatStorage = $date_format_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$configuration['field_definition'],
|
||||
$configuration['settings'],
|
||||
$configuration['label'],
|
||||
$configuration['view_mode'],
|
||||
$configuration['third_party_settings'],
|
||||
$container->get('date.formatter'),
|
||||
$container->get('entity_type.manager')->getStorage('date_format')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'timezone_override' => '',
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::settingsForm($form, $form_state);
|
||||
|
||||
$form['timezone_override'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Time zone override'),
|
||||
'#description' => $this->t('The time zone selected here will always be used'),
|
||||
'#options' => system_time_zones(TRUE, TRUE),
|
||||
'#default_value' => $this->getSetting('timezone_override'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
if ($override = $this->getSetting('timezone_override')) {
|
||||
$summary[] = $this->t('Time zone: @timezone', ['@timezone' => $override]);
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = [];
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
if ($item->date) {
|
||||
/** @var \Drupal\Core\Datetime\DrupalDateTime $date */
|
||||
$date = $item->date;
|
||||
$elements[$delta] = $this->buildDateWithIsoAttribute($date);
|
||||
|
||||
if (!empty($item->_attributes)) {
|
||||
$elements[$delta]['#attributes'] += $item->_attributes;
|
||||
// Unset field item attributes since they have been included in the
|
||||
// formatter output and should not be rendered in the field template.
|
||||
unset($item->_attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted date value as a string.
|
||||
*
|
||||
* @param object $date
|
||||
* A date object.
|
||||
*
|
||||
* @return string
|
||||
* A formatted date string using the chosen format.
|
||||
*/
|
||||
abstract protected function formatDate($date);
|
||||
|
||||
/**
|
||||
* Sets the proper time zone on a DrupalDateTime object for the current user.
|
||||
*
|
||||
* A DrupalDateTime object loaded from the database will have the UTC time
|
||||
* zone applied to it. This method will apply the time zone for the current
|
||||
* user, based on system and user settings.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime $date
|
||||
* A DrupalDateTime object.
|
||||
*/
|
||||
protected function setTimeZone(DrupalDateTime $date) {
|
||||
if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// A date without time has no timezone conversion.
|
||||
$timezone = DateTimeItemInterface::STORAGE_TIMEZONE;
|
||||
}
|
||||
else {
|
||||
$timezone = date_default_timezone_get();
|
||||
}
|
||||
$date->setTimeZone(timezone_open($timezone));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a settings array suitable for DrupalDateTime::format().
|
||||
*
|
||||
* @return array
|
||||
* The settings array that can be passed to DrupalDateTime::format().
|
||||
*/
|
||||
protected function getFormatSettings() {
|
||||
$settings = [];
|
||||
|
||||
if ($this->getSetting('timezone_override') != '') {
|
||||
$settings['timezone'] = $this->getSetting('timezone_override');
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a render array from a date object.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime $date
|
||||
* A date object.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function buildDate(DrupalDateTime $date) {
|
||||
$this->setTimeZone($date);
|
||||
|
||||
$build = [
|
||||
'#markup' => $this->formatDate($date),
|
||||
'#cache' => [
|
||||
'contexts' => [
|
||||
'timezone',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a render array from a date object with ISO date attribute.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime $date
|
||||
* A date object.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function buildDateWithIsoAttribute(DrupalDateTime $date) {
|
||||
// Create the ISO date in Universal Time.
|
||||
$iso_date = $date->format("Y-m-d\TH:i:s") . 'Z';
|
||||
|
||||
$this->setTimeZone($date);
|
||||
|
||||
$build = [
|
||||
'#theme' => 'time',
|
||||
'#text' => $this->formatDate($date),
|
||||
'#html' => FALSE,
|
||||
'#attributes' => [
|
||||
'datetime' => $iso_date,
|
||||
],
|
||||
'#cache' => [
|
||||
'contexts' => [
|
||||
'timezone',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'Plain' formatter for 'datetime' fields.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "datetime_plain",
|
||||
* label = @Translation("Plain"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
*)
|
||||
*/
|
||||
class DateTimePlainFormatter extends DateTimeFormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = [];
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
if (!empty($item->date)) {
|
||||
/** @var \Drupal\Core\Datetime\DrupalDateTime $date */
|
||||
$date = $item->date;
|
||||
|
||||
$elements[$delta] = $this->buildDate($date);
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function formatDate($date) {
|
||||
$format = $this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
$timezone = $this->getSetting('timezone_override') ?: $date->getTimezone()->getName();
|
||||
return $this->dateFormatter->format($date->getTimestamp(), 'custom', $format, $timezone != '' ? $timezone : NULL);
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'Time ago' formatter for 'datetime' fields.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "datetime_time_ago",
|
||||
* label = @Translation("Time ago"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DateTimeTimeAgoFormatter extends TimestampAgoFormatter {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = [];
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
$date = $item->date;
|
||||
$output = [];
|
||||
if (!empty($item->date)) {
|
||||
$output = $this->formatDate($date);
|
||||
}
|
||||
$elements[$delta] = $output;
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date/time as a time interval.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime|object $date
|
||||
* A date/time object.
|
||||
*
|
||||
* @return array
|
||||
* The formatted date/time string using the past or future format setting.
|
||||
*/
|
||||
protected function formatDate(DrupalDateTime $date) {
|
||||
return parent::formatTimestamp($date->getTimestamp());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemList;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Represents a configurable entity datetime field.
|
||||
*/
|
||||
class DateTimeFieldItemList extends FieldItemList {
|
||||
|
||||
/**
|
||||
* Defines the default value as now.
|
||||
*/
|
||||
const DEFAULT_VALUE_NOW = 'now';
|
||||
|
||||
/**
|
||||
* Defines the default value as relative.
|
||||
*/
|
||||
const DEFAULT_VALUE_CUSTOM = 'relative';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultValuesForm(array &$form, FormStateInterface $form_state) {
|
||||
if (empty($this->getFieldDefinition()->getDefaultValueCallback())) {
|
||||
$default_value = $this->getFieldDefinition()->getDefaultValueLiteral();
|
||||
|
||||
$element = [
|
||||
'#parents' => ['default_value_input'],
|
||||
'default_date_type' => [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default date'),
|
||||
'#description' => t('Set a default value for this date.'),
|
||||
'#default_value' => isset($default_value[0]['default_date_type']) ? $default_value[0]['default_date_type'] : '',
|
||||
'#options' => [
|
||||
static::DEFAULT_VALUE_NOW => t('Current date'),
|
||||
static::DEFAULT_VALUE_CUSTOM => t('Relative date'),
|
||||
],
|
||||
'#empty_value' => '',
|
||||
],
|
||||
'default_date' => [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Relative default value'),
|
||||
'#description' => t("Describe a time by reference to the current day, like '+90 days' (90 days from the day the field is created) or '+1 Saturday' (the next Saturday). See <a href=\"http://php.net/manual/function.strtotime.php\">strtotime</a> for more details."),
|
||||
'#default_value' => (isset($default_value[0]['default_date_type']) && $default_value[0]['default_date_type'] == static::DEFAULT_VALUE_CUSTOM) ? $default_value[0]['default_date'] : '',
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[id="edit-default-value-input-default-date-type"]' => ['value' => static::DEFAULT_VALUE_CUSTOM],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultValuesFormValidate(array $element, array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getValue(['default_value_input', 'default_date_type']) == static::DEFAULT_VALUE_CUSTOM) {
|
||||
$is_strtotime = @strtotime($form_state->getValue(['default_value_input', 'default_date']));
|
||||
if (!$is_strtotime) {
|
||||
$form_state->setErrorByName('default_value_input][default_date', t('The relative date value entered is invalid.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultValuesFormSubmit(array $element, array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getValue(['default_value_input', 'default_date_type'])) {
|
||||
if ($form_state->getValue(['default_value_input', 'default_date_type']) == static::DEFAULT_VALUE_NOW) {
|
||||
$form_state->setValueForElement($element['default_date'], static::DEFAULT_VALUE_NOW);
|
||||
}
|
||||
return [$form_state->getValue('default_value_input')];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function processDefaultValue($default_value, FieldableEntityInterface $entity, FieldDefinitionInterface $definition) {
|
||||
$default_value = parent::processDefaultValue($default_value, $entity, $definition);
|
||||
|
||||
if (isset($default_value[0]['default_date_type'])) {
|
||||
if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// A default date only value should be in the format used for date
|
||||
// storage but in the user's local timezone.
|
||||
$date = new DrupalDateTime($default_value[0]['default_date'], date_default_timezone_get());
|
||||
$format = DateTimeItemInterface::DATE_STORAGE_FORMAT;
|
||||
}
|
||||
else {
|
||||
// A default date+time value should be in the format and timezone used
|
||||
// for date storage.
|
||||
$date = new DrupalDateTime($default_value[0]['default_date'], DateTimeItemInterface::STORAGE_TIMEZONE);
|
||||
$format = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
}
|
||||
$value = $date->format($format);
|
||||
// We only provide a default value for the first item, as do all fields.
|
||||
// Otherwise, there is no way to clear out unwanted values on multiple value
|
||||
// fields.
|
||||
$default_value = [
|
||||
[
|
||||
'value' => $value,
|
||||
'date' => $date,
|
||||
],
|
||||
];
|
||||
}
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'datetime' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "datetime",
|
||||
* label = @Translation("Date"),
|
||||
* description = @Translation("Create and store date values."),
|
||||
* default_widget = "datetime_default",
|
||||
* default_formatter = "datetime_default",
|
||||
* list_class = "\Drupal\datetime\Plugin\Field\FieldType\DateTimeFieldItemList",
|
||||
* constraints = {"DateTimeFormat" = {}}
|
||||
* )
|
||||
*/
|
||||
class DateTimeItem extends FieldItemBase implements DateTimeItemInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultStorageSettings() {
|
||||
return [
|
||||
'datetime_type' => 'datetime',
|
||||
] + parent::defaultStorageSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Value for the 'datetime_type' setting: store only a date.
|
||||
*/
|
||||
const DATETIME_TYPE_DATE = 'date';
|
||||
|
||||
/**
|
||||
* Value for the 'datetime_type' setting: store a date and time.
|
||||
*/
|
||||
const DATETIME_TYPE_DATETIME = 'datetime';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('datetime_iso8601')
|
||||
->setLabel(t('Date value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['date'] = DataDefinition::create('any')
|
||||
->setLabel(t('Computed date'))
|
||||
->setDescription(t('The computed DateTime object.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\datetime\DateTimeComputed')
|
||||
->setSetting('date source', 'value');
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return [
|
||||
'columns' => [
|
||||
'value' => [
|
||||
'description' => 'The date value.',
|
||||
'type' => 'varchar',
|
||||
'length' => 20,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
'value' => ['value'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
|
||||
$element = [];
|
||||
|
||||
$element['datetime_type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Date type'),
|
||||
'#description' => t('Choose the type of date to create.'),
|
||||
'#default_value' => $this->getSetting('datetime_type'),
|
||||
'#options' => [
|
||||
static::DATETIME_TYPE_DATETIME => t('Date and time'),
|
||||
static::DATETIME_TYPE_DATE => t('Date only'),
|
||||
],
|
||||
'#disabled' => $has_data,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
|
||||
$type = $field_definition->getSetting('datetime_type');
|
||||
|
||||
// Just pick a date in the past year. No guidance is provided by this Field
|
||||
// type.
|
||||
$timestamp = REQUEST_TIME - mt_rand(0, 86400 * 365);
|
||||
if ($type == DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
$values['value'] = gmdate(static::DATE_STORAGE_FORMAT, $timestamp);
|
||||
}
|
||||
else {
|
||||
$values['value'] = gmdate(static::DATETIME_STORAGE_FORMAT, $timestamp);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
$value = $this->get('value')->getValue();
|
||||
return $value === NULL || $value === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onChange($property_name, $notify = TRUE) {
|
||||
// Enforce that the computed date is recalculated.
|
||||
if ($property_name == 'value') {
|
||||
$this->date = NULL;
|
||||
}
|
||||
parent::onChange($property_name, $notify);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldType;
|
||||
|
||||
/**
|
||||
* Interface definition for Datetime items.
|
||||
*/
|
||||
interface DateTimeItemInterface {
|
||||
|
||||
/**
|
||||
* Defines the timezone that dates should be stored in.
|
||||
*/
|
||||
const STORAGE_TIMEZONE = 'UTC';
|
||||
|
||||
/**
|
||||
* Defines the format that date and time should be stored in.
|
||||
*/
|
||||
const DATETIME_STORAGE_FORMAT = 'Y-m-d\TH:i:s';
|
||||
|
||||
/**
|
||||
* Defines the format that dates should be stored in.
|
||||
*/
|
||||
const DATE_STORAGE_FORMAT = 'Y-m-d';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'datetime_datelist' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "datetime_datelist",
|
||||
* label = @Translation("Select list"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DateTimeDatelistWidget extends DateTimeWidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'increment' => '15',
|
||||
'date_order' => 'YMD',
|
||||
'time_type' => '24',
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
|
||||
// Wrap all of the select elements with a fieldset.
|
||||
$element['#theme_wrappers'][] = 'fieldset';
|
||||
|
||||
$date_order = $this->getSetting('date_order');
|
||||
|
||||
if ($this->getFieldSetting('datetime_type') == 'datetime') {
|
||||
$time_type = $this->getSetting('time_type');
|
||||
$increment = $this->getSetting('increment');
|
||||
}
|
||||
else {
|
||||
$time_type = '';
|
||||
$increment = '';
|
||||
}
|
||||
|
||||
// Set up the date part order array.
|
||||
switch ($date_order) {
|
||||
case 'YMD':
|
||||
$date_part_order = ['year', 'month', 'day'];
|
||||
break;
|
||||
|
||||
case 'MDY':
|
||||
$date_part_order = ['month', 'day', 'year'];
|
||||
break;
|
||||
|
||||
case 'DMY':
|
||||
$date_part_order = ['day', 'month', 'year'];
|
||||
break;
|
||||
}
|
||||
switch ($time_type) {
|
||||
case '24':
|
||||
$date_part_order = array_merge($date_part_order, ['hour', 'minute']);
|
||||
break;
|
||||
|
||||
case '12':
|
||||
$date_part_order = array_merge($date_part_order, ['hour', 'minute', 'ampm']);
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
break;
|
||||
}
|
||||
|
||||
$element['value'] = [
|
||||
'#type' => 'datelist',
|
||||
'#date_increment' => $increment,
|
||||
'#date_part_order' => $date_part_order,
|
||||
] + $element['value'];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$element = parent::settingsForm($form, $form_state);
|
||||
|
||||
$element['date_order'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Date part order'),
|
||||
'#default_value' => $this->getSetting('date_order'),
|
||||
'#options' => ['MDY' => t('Month/Day/Year'), 'DMY' => t('Day/Month/Year'), 'YMD' => t('Year/Month/Day')],
|
||||
];
|
||||
|
||||
if ($this->getFieldSetting('datetime_type') == 'datetime') {
|
||||
$element['time_type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Time type'),
|
||||
'#default_value' => $this->getSetting('time_type'),
|
||||
'#options' => ['24' => t('24 hour time'), '12' => t('12 hour time')],
|
||||
];
|
||||
|
||||
$element['increment'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Time increments'),
|
||||
'#default_value' => $this->getSetting('increment'),
|
||||
'#options' => [
|
||||
1 => t('1 minute'),
|
||||
5 => t('5 minute'),
|
||||
10 => t('10 minute'),
|
||||
15 => t('15 minute'),
|
||||
30 => t('30 minute'),
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$element['time_type'] = [
|
||||
'#type' => 'hidden',
|
||||
'#value' => 'none',
|
||||
];
|
||||
|
||||
$element['increment'] = [
|
||||
'#type' => 'hidden',
|
||||
'#value' => $this->getSetting('increment'),
|
||||
];
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = [];
|
||||
|
||||
$summary[] = t('Date part order: @order', ['@order' => $this->getSetting('date_order')]);
|
||||
if ($this->getFieldSetting('datetime_type') == 'datetime') {
|
||||
$summary[] = t('Time type: @time_type', ['@time_type' => $this->getSetting('time_type')]);
|
||||
$summary[] = t('Time increments: @increment', ['@increment' => $this->getSetting('increment')]);
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'datetime_default' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "datetime_default",
|
||||
* label = @Translation("Date and time"),
|
||||
* field_types = {
|
||||
* "datetime"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DateTimeDefaultWidget extends DateTimeWidgetBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The date format storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $dateStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityStorageInterface $date_storage) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
|
||||
|
||||
$this->dateStorage = $date_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$configuration['field_definition'],
|
||||
$configuration['settings'],
|
||||
$configuration['third_party_settings'],
|
||||
$container->get('entity_type.manager')->getStorage('date_format')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
|
||||
// If the field is date-only, make sure the title is displayed. Otherwise,
|
||||
// wrap everything in a fieldset, and the title will be shown in the legend.
|
||||
if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
$element['value']['#title'] = $this->fieldDefinition->getLabel();
|
||||
$element['value']['#description'] = $this->fieldDefinition->getDescription();
|
||||
}
|
||||
else {
|
||||
$element['#theme_wrappers'][] = 'fieldset';
|
||||
}
|
||||
|
||||
// Identify the type of date and time elements to use.
|
||||
switch ($this->getFieldSetting('datetime_type')) {
|
||||
case DateTimeItem::DATETIME_TYPE_DATE:
|
||||
$date_type = 'date';
|
||||
$time_type = 'none';
|
||||
$date_format = $this->dateStorage->load('html_date')->getPattern();
|
||||
$time_format = '';
|
||||
break;
|
||||
|
||||
default:
|
||||
$date_type = 'date';
|
||||
$time_type = 'time';
|
||||
$date_format = $this->dateStorage->load('html_date')->getPattern();
|
||||
$time_format = $this->dateStorage->load('html_time')->getPattern();
|
||||
break;
|
||||
}
|
||||
|
||||
$element['value'] += [
|
||||
'#date_date_format' => $date_format,
|
||||
'#date_date_element' => $date_type,
|
||||
'#date_date_callbacks' => [],
|
||||
'#date_time_format' => $time_format,
|
||||
'#date_time_element' => $time_type,
|
||||
'#date_time_callbacks' => [],
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
|
||||
/**
|
||||
* Base class for the 'datetime_*' widgets.
|
||||
*/
|
||||
class DateTimeWidgetBase extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element['value'] = [
|
||||
'#type' => 'datetime',
|
||||
'#default_value' => NULL,
|
||||
'#date_increment' => 1,
|
||||
'#date_timezone' => date_default_timezone_get(),
|
||||
'#required' => $element['#required'],
|
||||
];
|
||||
|
||||
if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// A date-only field should have no timezone conversion performed, so
|
||||
// use the same timezone as for storage.
|
||||
$element['value']['#date_timezone'] = DateTimeItemInterface::STORAGE_TIMEZONE;
|
||||
}
|
||||
|
||||
if ($items[$delta]->date) {
|
||||
$date = $items[$delta]->date;
|
||||
// The date was created and verified during field_load(), so it is safe to
|
||||
// use without further inspection.
|
||||
$date->setTimezone(new \DateTimeZone($element['value']['#date_timezone']));
|
||||
$element['value']['#default_value'] = $this->createDefaultValue($date, $element['value']['#date_timezone']);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
|
||||
// The widget form element type has transformed the value to a
|
||||
// DrupalDateTime object at this point. We need to convert it back to the
|
||||
// storage timezone and format.
|
||||
|
||||
$datetime_type = $this->getFieldSetting('datetime_type');
|
||||
if ($datetime_type === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
$storage_format = DateTimeItemInterface::DATE_STORAGE_FORMAT;
|
||||
}
|
||||
else {
|
||||
$storage_format = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
}
|
||||
|
||||
$storage_timezone = new \DateTimezone(DateTimeItemInterface::STORAGE_TIMEZONE);
|
||||
|
||||
foreach ($values as &$item) {
|
||||
if (!empty($item['value']) && $item['value'] instanceof DrupalDateTime) {
|
||||
/** @var \Drupal\Core\Datetime\DrupalDateTime $date */
|
||||
$date = $item['value'];
|
||||
|
||||
// Adjust the date for storage.
|
||||
$item['value'] = $date->setTimezone($storage_timezone)->format($storage_format);
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a date object for use as a default value.
|
||||
*
|
||||
* This will take a default value, apply the proper timezone for display in
|
||||
* a widget, and set the default time for date-only fields.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime $date
|
||||
* The UTC default date.
|
||||
* @param string $timezone
|
||||
* The timezone to apply.
|
||||
*
|
||||
* @return \Drupal\Core\Datetime\DrupalDateTime
|
||||
* A date object for use as a default value in a field widget.
|
||||
*/
|
||||
protected function createDefaultValue($date, $timezone) {
|
||||
// The date was created and verified during field_load(), so it is safe to
|
||||
// use without further inspection.
|
||||
if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
$date->setDefaultDateTime();
|
||||
}
|
||||
$date->setTimezone(new \DateTimeZone($timezone));
|
||||
return $date;
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* Validation constraint for DateTime items to ensure the format is correct.
|
||||
*
|
||||
* @Constraint(
|
||||
* id = "DateTimeFormat",
|
||||
* label = @Translation("Datetime format valid for datetime type.", context = "Validation"),
|
||||
* )
|
||||
*/
|
||||
class DateTimeFormatConstraint extends Constraint {
|
||||
|
||||
/**
|
||||
* Message for when the value isn't a string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $badType = "The datetime value must be a string.";
|
||||
|
||||
/**
|
||||
* Message for when the value isn't in the proper format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $badFormat = "The datetime value '@value' is invalid for the format '@format'";
|
||||
|
||||
/**
|
||||
* Message for when the value did not parse properly.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $badValue = "The datetime value '@value' did not parse properly for the format '@format'";
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Constraint validator for DateTime items to ensure the format is correct.
|
||||
*/
|
||||
class DateTimeFormatConstraintValidator extends ConstraintValidator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($item, Constraint $constraint) {
|
||||
/* @var $item \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem */
|
||||
if (isset($item)) {
|
||||
$value = $item->getValue()['value'];
|
||||
if (!is_string($value)) {
|
||||
$this->context->addViolation($constraint->badType);
|
||||
}
|
||||
else {
|
||||
$datetime_type = $item->getFieldDefinition()->getSetting('datetime_type');
|
||||
$format = $datetime_type === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
$date = NULL;
|
||||
try {
|
||||
$date = DateTimePlus::createFromFormat($format, $value, new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE));
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->context->addViolation($constraint->badFormat, [
|
||||
'@value' => $value,
|
||||
'@format' => $format,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
catch (\UnexpectedValueException $e) {
|
||||
$this->context->addViolation($constraint->badValue, [
|
||||
'@value' => $value,
|
||||
'@format' => $format,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if ($date === NULL || $date->hasErrors()) {
|
||||
$this->context->addViolation($constraint->badFormat, [
|
||||
'@value' => $value,
|
||||
'@format' => $format,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\migrate\field;
|
||||
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
|
||||
|
||||
/**
|
||||
* Provides a field plugin for date and time fields.
|
||||
*
|
||||
* @MigrateField(
|
||||
* id = "datetime",
|
||||
* type_map = {
|
||||
* "date" = "datetime",
|
||||
* "datestamp" = "timestamp",
|
||||
* "datetime" = "datetime",
|
||||
* },
|
||||
* core = {6,7},
|
||||
* source_module = "date",
|
||||
* destination_module = "datetime"
|
||||
* )
|
||||
*/
|
||||
class DateField extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [
|
||||
'date_default' => 'datetime_default',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
return [
|
||||
'date' => 'datetime_default',
|
||||
'datetime' => 'datetime_default',
|
||||
'datestamp' => 'datetime_timestamp',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
$to_format = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
if (isset($data['field_definition']['data'])) {
|
||||
$field_data = unserialize($data['field_definition']['data']);
|
||||
if (isset($field_data['settings']['granularity'])) {
|
||||
$granularity = $field_data['settings']['granularity'];
|
||||
$collected_date_attributes = is_numeric(array_keys($granularity)[0])
|
||||
? $granularity
|
||||
: array_keys(array_filter($granularity));
|
||||
if (empty(array_intersect($collected_date_attributes, ['hour', 'minute', 'second']))) {
|
||||
$to_format = DateTimeItemInterface::DATE_STORAGE_FORMAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($data['type']) {
|
||||
case 'date':
|
||||
$from_format = 'Y-m-d\TH:i:s';
|
||||
break;
|
||||
|
||||
case 'datestamp':
|
||||
$from_format = 'U';
|
||||
$to_format = 'U';
|
||||
break;
|
||||
|
||||
case 'datetime':
|
||||
$from_format = 'Y-m-d H:i:s';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new MigrateException(sprintf('Field %s of type %s is an unknown date field type.', $field_name, var_export($data['type'], TRUE)));
|
||||
}
|
||||
$process = [
|
||||
'value' => [
|
||||
'plugin' => 'format_date',
|
||||
'from_format' => $from_format,
|
||||
'to_format' => $to_format,
|
||||
'source' => 'value',
|
||||
],
|
||||
];
|
||||
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => $process,
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\migrate\field\d6;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateField(
|
||||
* id = "date",
|
||||
* type_map = {
|
||||
* "date" = "datetime",
|
||||
* "datestamp" = "timestamp",
|
||||
* "datetime" = "datetime",
|
||||
* },
|
||||
* core = {6},
|
||||
* source_module = "date",
|
||||
* destination_module = "datetime",
|
||||
* weight = 9999999,
|
||||
* )
|
||||
*
|
||||
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\datetime\Plugin\migrate\field\DateField instead.
|
||||
*/
|
||||
class DateField extends FieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
@trigger_error('DateField is deprecated in Drupal 8.4.x and will be removed before Drupal 9.0.x. Use \Drupal\datetime\Plugin\migrate\field\DateField instead.', E_USER_DEPRECATED);
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
return [
|
||||
'date' => 'datetime_default',
|
||||
'datetime' => 'datetime_default',
|
||||
'datestamp' => 'datetime_timestamp',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
|
||||
switch ($data['type']) {
|
||||
case 'date':
|
||||
$from_format = 'Y-m-d\TH:i:s';
|
||||
$to_format = 'Y-m-d\TH:i:s';
|
||||
break;
|
||||
case 'datestamp':
|
||||
$from_format = 'U';
|
||||
$to_format = 'U';
|
||||
break;
|
||||
case 'datetime':
|
||||
$from_format = 'Y-m-d H:i:s';
|
||||
$to_format = 'Y-m-d\TH:i:s';
|
||||
break;
|
||||
default:
|
||||
throw new MigrateException(sprintf('Field %s of type %s is an unknown date field type.', $field_name, var_export($data['type'], TRUE)));
|
||||
}
|
||||
$process = [
|
||||
'value' => [
|
||||
'plugin' => 'format_date',
|
||||
'from_format' => $from_format,
|
||||
'to_format' => $to_format,
|
||||
'source' => 'value',
|
||||
],
|
||||
];
|
||||
|
||||
$process = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => $process,
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\views\FieldAPIHandlerTrait;
|
||||
use Drupal\views\Plugin\views\argument\Date as NumericDate;
|
||||
|
||||
/**
|
||||
* Abstract argument handler for dates.
|
||||
*
|
||||
* Adds an option to set a default argument based on the current date.
|
||||
*
|
||||
* Definitions terms:
|
||||
* - many to one: If true, the "many to one" helper will be used.
|
||||
* - invalid input: A string to give to the user for obviously invalid input.
|
||||
* This is deprecated in favor of argument validators.
|
||||
*
|
||||
* @see \Drupal\views\ManyTonOneHelper
|
||||
*
|
||||
* @ingroup views_argument_handlers
|
||||
*
|
||||
* @ViewsArgument("datetime")
|
||||
*/
|
||||
class Date extends NumericDate {
|
||||
|
||||
use FieldAPIHandlerTrait;
|
||||
|
||||
/**
|
||||
* Determines if the timezone offset is calculated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $calculateOffset = TRUE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match, DateFormatterInterface $date_formatter) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_match, $date_formatter);
|
||||
|
||||
$definition = $this->getFieldStorageDefinition();
|
||||
if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// Timezone offset calculation is not applicable to dates that are stored
|
||||
// as date-only.
|
||||
$this->calculateOffset = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDateField() {
|
||||
// Use string date storage/formatting since datetime fields are stored as
|
||||
// strings rather than UNIX timestamps.
|
||||
return $this->query->getDateField("$this->tableAlias.$this->realField", TRUE, $this->calculateOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDateFormat($format) {
|
||||
// Pass in the string-field option.
|
||||
return $this->query->getDateFormat($this->getDateField(), $format, TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a day.
|
||||
*
|
||||
* @ViewsArgument("datetime_day")
|
||||
*/
|
||||
class DayDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'd';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a full date (CCYYMMDD).
|
||||
*
|
||||
* @ViewsArgument("datetime_full_date")
|
||||
*/
|
||||
class FullDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'Ymd';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a month.
|
||||
*
|
||||
* @ViewsArgument("datetime_month")
|
||||
*/
|
||||
class MonthDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'm';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a week.
|
||||
*
|
||||
* @ViewsArgument("datetime_week")
|
||||
*/
|
||||
class WeekDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'W';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a year.
|
||||
*
|
||||
* @ViewsArgument("datetime_year")
|
||||
*/
|
||||
class YearDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'Y';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\argument;
|
||||
|
||||
/**
|
||||
* Argument handler for a year plus month (CCYYMM).
|
||||
*
|
||||
* @ViewsArgument("datetime_year_month")
|
||||
*/
|
||||
class YearMonthDate extends Date {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $argFormat = 'Ym';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\filter;
|
||||
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
use Drupal\views\FieldAPIHandlerTrait;
|
||||
use Drupal\views\Plugin\views\filter\Date as NumericDate;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Date/time views filter.
|
||||
*
|
||||
* Even thought dates are stored as strings, the numeric filter is extended
|
||||
* because it provides more sensible operators.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*
|
||||
* @ViewsFilter("datetime")
|
||||
*/
|
||||
class Date extends NumericDate implements ContainerFactoryPluginInterface {
|
||||
|
||||
use FieldAPIHandlerTrait;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* Date format for SQL conversion.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @see \Drupal\views\Plugin\views\query\Sql::getDateFormat()
|
||||
*/
|
||||
protected $dateFormat = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
|
||||
|
||||
/**
|
||||
* Determines if the timezone offset is calculated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $calculateOffset = TRUE;
|
||||
|
||||
/**
|
||||
* The request stack used to determine current time.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new Date handler.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack used to determine the current time.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, DateFormatterInterface $date_formatter, RequestStack $request_stack) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->requestStack = $request_stack;
|
||||
|
||||
$definition = $this->getFieldStorageDefinition();
|
||||
if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// Date format depends on field storage format.
|
||||
$this->dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
|
||||
// Timezone offset calculation is not applicable to dates that are stored
|
||||
// as date-only.
|
||||
$this->calculateOffset = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('date.formatter'),
|
||||
$container->get('request_stack')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent method, which deals with dates as integers.
|
||||
*/
|
||||
protected function opBetween($field) {
|
||||
$timezone = $this->getTimezone();
|
||||
$origin_offset = $this->getOffset($this->value['min'], $timezone);
|
||||
|
||||
// Although both 'min' and 'max' values are required, default empty 'min'
|
||||
// value as UNIX timestamp 0.
|
||||
$min = (!empty($this->value['min'])) ? $this->value['min'] : '@0';
|
||||
|
||||
// Convert to ISO format and format for query. UTC timezone is used since
|
||||
// dates are stored in UTC.
|
||||
$a = new DateTimePlus($min, new \DateTimeZone($timezone));
|
||||
$a = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($a->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
|
||||
$b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone));
|
||||
$b = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($b->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
|
||||
|
||||
// This is safe because we are manually scrubbing the values.
|
||||
$operator = strtoupper($this->operator);
|
||||
$field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
|
||||
$this->query->addWhereExpression($this->options['group'], "$field $operator $a AND $b");
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent method, which deals with dates as integers.
|
||||
*/
|
||||
protected function opSimple($field) {
|
||||
$timezone = $this->getTimezone();
|
||||
$origin_offset = $this->getOffset($this->value['value'], $timezone);
|
||||
|
||||
// Convert to ISO. UTC timezone is used since dates are stored in UTC.
|
||||
$value = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone));
|
||||
$value = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($value->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
|
||||
|
||||
// This is safe because we are manually scrubbing the value.
|
||||
$field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
|
||||
$this->query->addWhereExpression($this->options['group'], "$field $this->operator $value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the proper time zone to use in computations.
|
||||
*
|
||||
* Date-only fields do not have a time zone associated with them, so the
|
||||
* filter input needs to use UTC for reference. Otherwise, use the time zone
|
||||
* for the current user.
|
||||
*
|
||||
* @return string
|
||||
* The time zone name.
|
||||
*/
|
||||
protected function getTimezone() {
|
||||
return $this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT
|
||||
? DateTimeItemInterface::STORAGE_TIMEZONE
|
||||
: date_default_timezone_get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the proper offset from UTC to use in computations.
|
||||
*
|
||||
* @param string $time
|
||||
* A date/time string compatible with \DateTime. It is used as the
|
||||
* reference for computing the offset, which can vary based on the time
|
||||
* zone rules.
|
||||
* @param string $timezone
|
||||
* The time zone that $time is in.
|
||||
*
|
||||
* @return int
|
||||
* The computed offset in seconds.
|
||||
*/
|
||||
protected function getOffset($time, $timezone) {
|
||||
// Date-only fields do not have a time zone or offset from UTC associated
|
||||
// with them. For relative (i.e. 'offset') comparisons, we need to compute
|
||||
// the user's offset from UTC for use in the query.
|
||||
$origin_offset = 0;
|
||||
if ($this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT && $this->value['type'] === 'offset') {
|
||||
$origin_offset = $origin_offset + timezone_offset_get(new \DateTimeZone(date_default_timezone_get()), new \DateTime($time, new \DateTimeZone($timezone)));
|
||||
}
|
||||
|
||||
return $origin_offset;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Plugin\views\sort;
|
||||
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\views\FieldAPIHandlerTrait;
|
||||
use Drupal\views\Plugin\views\sort\Date as NumericDate;
|
||||
|
||||
/**
|
||||
* Basic sort handler for datetime fields.
|
||||
*
|
||||
* This handler enables granularity, which is the ability to make dates
|
||||
* equivalent based upon nearness.
|
||||
*
|
||||
* @ViewsSort("datetime")
|
||||
*/
|
||||
class Date extends NumericDate {
|
||||
|
||||
use FieldAPIHandlerTrait;
|
||||
|
||||
/**
|
||||
* Determines if the timezone offset is calculated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $calculateOffset = TRUE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$definition = $this->getFieldStorageDefinition();
|
||||
if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// Timezone offset calculation is not applicable to dates that are stored
|
||||
// as date-only.
|
||||
$this->calculateOffset = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Override to account for dates stored as strings.
|
||||
*/
|
||||
public function getDateField() {
|
||||
// Use string date storage/formatting since datetime fields are stored as
|
||||
// strings rather than UNIX timestamps.
|
||||
return $this->query->getDateField("$this->tableAlias.$this->realField", TRUE, $this->calculateOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Overridden in order to pass in the string date flag.
|
||||
*/
|
||||
public function getDateFormat($format) {
|
||||
return $this->query->getDateFormat($this->getDateField(), $format, TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Tests;
|
||||
|
||||
@trigger_error('\Drupal\datetime\Tests\DateTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\BrowserTestBase instead. See https://www.drupal.org/node/2780063.', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Provides a base class for testing Datetime field functionality.
|
||||
*
|
||||
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0.
|
||||
* Use \Drupal\Tests\BrowserTestBase instead.
|
||||
*/
|
||||
abstract class DateTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'entity_test', 'datetime', 'field_ui'];
|
||||
|
||||
/**
|
||||
* An array of display options.
|
||||
*
|
||||
* An array of display options to pass to
|
||||
* EntityDisplayRepositoryInterface::getViewDisplay()
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityDisplayRepositoryInterface::getViewDisplay()
|
||||
*/
|
||||
protected $displayOptions;
|
||||
|
||||
/**
|
||||
* A field storage to use in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field used in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* An array of timezone extremes to test.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $timezones = [
|
||||
// UTC-12, no DST.
|
||||
'Pacific/Kwajalein',
|
||||
// UTC-11, no DST
|
||||
'Pacific/Midway',
|
||||
// UTC-7, no DST.
|
||||
'America/Phoenix',
|
||||
// UTC.
|
||||
'UTC',
|
||||
// UTC+5:30, no DST.
|
||||
'Asia/Kolkata',
|
||||
// UTC+12, no DST
|
||||
'Pacific/Funafuti',
|
||||
// UTC+13, no DST.
|
||||
'Pacific/Tongatapu',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the type of field to be tested.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getTestFieldType();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer entity_test form display',
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create a field with settings to validate.
|
||||
$this->createField();
|
||||
|
||||
$this->dateFormatter = $this->container->get('date.formatter');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a date test field.
|
||||
*/
|
||||
protected function createField() {
|
||||
$field_name = mb_strtolower($this->randomMachineName());
|
||||
$type = $this->getTestFieldType();
|
||||
$widget_type = $formatter_type = $type . '_default';
|
||||
|
||||
$this->fieldStorage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => $type,
|
||||
'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATE],
|
||||
]);
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create([
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => 'entity_test',
|
||||
'description' => 'Description for ' . $field_name,
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$this->field->save();
|
||||
|
||||
EntityFormDisplay::load('entity_test.entity_test.default')
|
||||
->setComponent($field_name, ['type' => $widget_type])
|
||||
->save();
|
||||
|
||||
$this->displayOptions = [
|
||||
'type' => $formatter_type,
|
||||
'label' => 'hidden',
|
||||
'settings' => ['format_type' => 'medium'] + $this->defaultSettings,
|
||||
];
|
||||
EntityViewDisplay::create([
|
||||
'targetEntityType' => $this->field->getTargetEntityTypeId(),
|
||||
'bundle' => $this->field->getTargetBundle(),
|
||||
'mode' => 'full',
|
||||
'status' => TRUE,
|
||||
])->setComponent($field_name, $this->displayOptions)
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a entity_test and sets the output in the internal browser.
|
||||
*
|
||||
* @param int $id
|
||||
* The entity_test ID to render.
|
||||
* @param string $view_mode
|
||||
* (optional) The view mode to use for rendering. Defaults to 'full'.
|
||||
* @param bool $reset
|
||||
* (optional) Whether to reset the entity_test controller cache. Defaults to
|
||||
* TRUE to simplify testing.
|
||||
*/
|
||||
protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
|
||||
if ($reset) {
|
||||
$this->container->get('entity_type.manager')->getStorage('entity_test')->resetCache([$id]);
|
||||
}
|
||||
$entity = EntityTest::load($id);
|
||||
$display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
|
||||
$build = $display->build($entity);
|
||||
$output = $this->container->get('renderer')->renderRoot($build);
|
||||
$this->setRawContent($output);
|
||||
$this->verbose($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site timezone to a given timezone.
|
||||
*
|
||||
* @param string $timezone
|
||||
* The timezone identifier to set.
|
||||
*/
|
||||
protected function setSiteTimezone($timezone) {
|
||||
// Set an explicit site timezone, and disallow per-user timezones.
|
||||
$this->config('system.date')
|
||||
->set('timezone.user.configurable', 0)
|
||||
->set('timezone.default', $timezone)
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Massages test date values.
|
||||
*
|
||||
* If a date object is generated directly by a test, then it needs to be
|
||||
* adjusted to behave like the computed date from the item.
|
||||
*
|
||||
* @param \Drupal\Core\Datetime\DrupalDateTime $date
|
||||
* A date object directly generated by the test.
|
||||
*/
|
||||
protected function massageTestDate($date) {
|
||||
if ($this->field->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
|
||||
// Set the default time for date-only items.
|
||||
$date->setDefaultDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\datetime\Tests\Views;
|
||||
|
||||
@trigger_error('\Drupal\datetime\Tests\Views\DateTimeHandlerTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\BrowserTestBase', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\views\Tests\Handler\HandlerTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Base class for testing datetime handlers.
|
||||
*
|
||||
* @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0.
|
||||
* Use \Drupal\Tests\BrowserTestBase.
|
||||
*/
|
||||
abstract class DateTimeHandlerTestBase extends HandlerTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['datetime_test', 'node', 'datetime'];
|
||||
|
||||
/**
|
||||
* Name of the field.
|
||||
*
|
||||
* Note, this is used in the default test view.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $field_name = 'field_date';
|
||||
|
||||
/**
|
||||
* Nodes to test.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
// Add a date field to page nodes.
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'page',
|
||||
'name' => 'page',
|
||||
]);
|
||||
$node_type->save();
|
||||
$fieldStorage = FieldStorageConfig::create([
|
||||
'field_name' => static::$field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'datetime',
|
||||
'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
|
||||
]);
|
||||
$fieldStorage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $fieldStorage,
|
||||
'bundle' => 'page',
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Views needs to be aware of the new field.
|
||||
$this->container->get('views.views_data')->clear();
|
||||
|
||||
// Set column map.
|
||||
$this->map = [
|
||||
'nid' => 'nid',
|
||||
];
|
||||
|
||||
// Load test views.
|
||||
ViewTestData::createTestViews(get_class($this), ['datetime_test']);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user