From 2ba5ca92c356c115008f45ff98944325f00f9783 Mon Sep 17 00:00:00 2001
From: j <j@mailb.org>
Date: Fri, 3 Mar 2017 15:13:22 +0100
Subject: [PATCH] Issue #2786577 Improve the Views integration for DateRange
 fields

---
 core/modules/datetime/datetime.views.inc           |  37 ++-
 .../src/Tests/Views/DateTimeHandlerTestBase.php    |   9 +-
 core/modules/datetime_range/datetime_range.install | 109 +++++++
 .../datetime_range/datetime_range.views.inc        |  21 ++
 .../Tests/Update/DatetimeRangeViewUpdateTest.php   |  52 ++++
 .../src/Tests/Views/DateRangeHandlerTestBase.php   |  24 ++
 .../src/Tests/Views/FilterDateTest.php             | 136 +++++++++
 .../update/datetime_range-filter-values.php        | 322 +++++++++++++++++++++
 8 files changed, 701 insertions(+), 9 deletions(-)
 create mode 100644 core/modules/datetime_range/datetime_range.install
 create mode 100644 core/modules/datetime_range/datetime_range.views.inc
 create mode 100644 core/modules/datetime_range/src/Tests/Update/DatetimeRangeViewUpdateTest.php
 create mode 100644 core/modules/datetime_range/src/Tests/Views/DateRangeHandlerTestBase.php
 create mode 100644 core/modules/datetime_range/src/Tests/Views/FilterDateTest.php
 create mode 100644 core/modules/datetime_range/tests/fixtures/update/datetime_range-filter-values.php

diff --git a/core/modules/datetime/datetime.views.inc b/core/modules/datetime/datetime.views.inc
index d3b0d18617..539f4bfadb 100644
--- a/core/modules/datetime/datetime.views.inc
+++ b/core/modules/datetime/datetime.views.inc
@@ -11,18 +11,38 @@
  * Implements hook_field_views_data().
  */
 function datetime_field_views_data(FieldStorageConfigInterface $field_storage) {
+  return datetime_type_field_views_data($field_storage, [], 'value');
+}
+
+/**
+ * Helper for datetime based fields.
+ *
+ * Override the default Views data for a datetime based fields,
+ * adding datetime views plugins.
+ *
+ * @param FieldStorageConfigInterface $field_storage
+ *   The field storage config entity.
+ * @param array $data
+ *   Field view data or views_field_default_views_data($field_storage) if empty.
+ * @param string $column_name
+ *   The schema column name with the datetime value.
+ *
+ * @return array
+ *   The array of field views data with the datetime plugin.
+ */
+function datetime_type_field_views_data(FieldStorageConfigInterface $field_storage, $data, $column_name) {
   // @todo This code only covers configurable fields, handle base table fields
   //   in https://www.drupal.org/node/2489476.
-  $data = views_field_default_views_data($field_storage);
+  $data = (empty($data)) ? views_field_default_views_data($field_storage) : $data;
   foreach ($data as $table_name => $table_data) {
     // Set the 'datetime' filter type.
-    $data[$table_name][$field_storage->getName() . '_value']['filter']['id'] = 'datetime';
+    $data[$table_name][$field_storage->getName() . '_' . $column_name]['filter']['id'] = 'datetime';
 
     // Set the 'datetime' argument type.
-    $data[$table_name][$field_storage->getName() . '_value']['argument']['id'] = 'datetime';
+    $data[$table_name][$field_storage->getName() . '_' . $column_name]['argument']['id'] = 'datetime';
 
     // Create year, month, and day arguments.
-    $group = $data[$table_name][$field_storage->getName() . '_value']['group'];
+    $group = $data[$table_name][$field_storage->getName() . '_' . $column_name]['group'];
     $arguments = [
       // Argument type => help text.
       'year' => t('Date in the form of YYYY.'),
@@ -33,11 +53,12 @@ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) {
       'full_date' => t('Date in the form of CCYYMMDD.'),
     ];
     foreach ($arguments as $argument_type => $help_text) {
-      $data[$table_name][$field_storage->getName() . '_value_' . $argument_type] = [
-        'title' => $field_storage->getLabel() . ' (' . $argument_type . ')',
+      $column_name_text = $column_name !== 'value' ? ':' . $column_name : '';
+      $data[$table_name][$field_storage->getName() . '_' . $column_name . '_' . $argument_type] = [
+        'title' => $field_storage->getLabel() . $column_name_text . ' (' . $argument_type . ')',
         'help' => $help_text,
         'argument' => [
-          'field' => $field_storage->getName() . '_value',
+          'field' => $field_storage->getName() . '_' . $column_name,
           'id' => 'datetime_' . $argument_type,
         ],
         'group' => $group,
@@ -45,7 +66,7 @@ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) {
     }
 
     // Set the 'datetime' sort handler.
-    $data[$table_name][$field_storage->getName() . '_value']['sort']['id'] = 'datetime';
+    $data[$table_name][$field_storage->getName() . '_' . $column_name]['sort']['id'] = 'datetime';
   }
 
   return $data;
diff --git a/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
index 42f309d6d7..6b889de00c 100644
--- a/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
+++ b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
@@ -29,6 +29,13 @@
   protected static $field_name = 'field_date';
 
   /**
+   * Type of the field.
+   *
+   * @var string
+   */
+  protected static $field_type = 'datetime';
+
+  /**
    * Nodes to test.
    *
    * @var \Drupal\node\NodeInterface[]
@@ -50,7 +57,7 @@ protected function setUp() {
     $fieldStorage = FieldStorageConfig::create([
       'field_name' => static::$field_name,
       'entity_type' => 'node',
-      'type' => 'datetime',
+      'type' => static::$field_type,
       'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
     ]);
     $fieldStorage->save();
diff --git a/core/modules/datetime_range/datetime_range.install b/core/modules/datetime_range/datetime_range.install
new file mode 100644
index 0000000000..df02d3857e
--- /dev/null
+++ b/core/modules/datetime_range/datetime_range.install
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains install and update functions for Datetime Range.
+ */
+
+/**
+ * Update existing views using datetime_range fields.
+ */
+function datetime_range_update_8001(&$sandbox) {
+  $config_factory = \Drupal::configFactory();
+  $message = NULL;
+  $ids = [];
+
+  foreach ($config_factory->listAll('views.view.') as $view_config_name) {
+    $view = $config_factory->getEditable($view_config_name);
+    $displays = $view->get('display');
+
+    foreach ($displays as $display_name => $display) {
+
+      // Update datetime_range filters.
+      if (isset($display['display_options']['filters'])) {
+        foreach ($display['display_options']['filters'] as $field_name => $filter) {
+          if ($filter['plugin_id'] == 'string') {
+
+            // Get field config.
+            $filter_views_data = \Drupal\views\Views::viewsData()->get($filter['table'])[$filter['field']]['filter'];
+            if (!isset($filter_views_data['entity_type']) || !isset($filter_views_data['field_name'])) {
+              continue;
+            }
+            $field_storage_name = 'field.storage.' . $filter_views_data['entity_type'] . '.' . $filter_views_data['field_name'];
+            $field_configuration = $config_factory->get($field_storage_name);
+
+            if ($field_configuration->get('type') == 'daterange') {
+              $ids[] = $view->get('id');
+
+              // Save off the base part of the config path we are updating.
+              $base = "display.$display_name.display_options.filters.$field_name";
+
+              // Set entity_type and field_name if missing.
+              if (!isset($filter['entity_type'])) {
+                $view->set($base . '.entity_type', $filter_views_data['entity_type']);
+              }
+
+              // Set datetime plugin_id.
+              $view->set($base . '.plugin_id', 'datetime');
+
+              // Set datetime value.
+              $datetime_value = [
+                'min' => '',
+                'max' => '',
+                'value' => $filter['value'],
+                'type' => 'date',
+              ];
+              $view->set($base . '.value', $datetime_value);
+
+              // Default to '=' operator.
+              $view->set($base . '.operator', '=');
+
+              // Store the changes.
+              $view->save(TRUE);
+            }
+          }
+        }
+      }
+
+      // Update datetime_range sort handlers.
+      if (isset($display['display_options']['sorts'])) {
+        foreach ($display['display_options']['sorts'] as $field_name => $sort) {
+          if ($sort['plugin_id'] == 'standard') {
+
+            // Get field config.
+            $sort_views_data = \Drupal\views\Views::viewsData()->get($sort['table'])[$sort['field']]['sort'];
+            if (!isset($sort_views_data['entity_type']) || !isset($sort_views_data['field_name'])) {
+              continue;
+            }
+            $field_storage_name = 'field.storage.' . $sort_views_data['entity_type'] . '.' . $sort_views_data['field_name'];
+            $field_configuration = $config_factory->get($field_storage_name);
+
+            if ($field_configuration->get('type') == 'daterange') {
+              $ids[] = $view->get('id');
+
+              // Save off the base part of the config path we are updating.
+              $base = "display.$display_name.display_options.sorts.$field_name";
+
+              // Set entity_type and field_name if missing.
+              if (!isset($sort['entity_type'])) {
+                $view->set($base . '.entity_type', $sort_views_data['entity_type']);
+              }
+
+              // Set datetime plugin_id.
+              $view->set($base . '.plugin_id', 'datetime');
+
+              // Store the changes.
+              $view->save(TRUE);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (!empty($ids)) {
+    $message = \Drupal::translation()->translate('Updated datetime_range filter/sort plugins for views: @ids', ['@ids' => implode(', ', array_unique($ids))]);
+  }
+
+  return $message;
+}
diff --git a/core/modules/datetime_range/datetime_range.views.inc b/core/modules/datetime_range/datetime_range.views.inc
new file mode 100644
index 0000000000..6d4407b0c7
--- /dev/null
+++ b/core/modules/datetime_range/datetime_range.views.inc
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Provides views data for the datetime_range module.
+ */
+
+use Drupal\field\FieldStorageConfigInterface;
+
+/**
+ * Implements hook_field_views_data().
+ */
+function datetime_range_field_views_data(FieldStorageConfigInterface $field_storage) {
+  \Drupal::moduleHandler()->loadInclude('datetime', 'inc', 'datetime.views');
+
+  // Get datetime field data for value and end_value
+  $data = datetime_type_field_views_data($field_storage, [], 'value');
+  $data = datetime_type_field_views_data($field_storage, $data, 'end_value');
+
+  return $data;
+}
diff --git a/core/modules/datetime_range/src/Tests/Update/DatetimeRangeViewUpdateTest.php b/core/modules/datetime_range/src/Tests/Update/DatetimeRangeViewUpdateTest.php
new file mode 100644
index 0000000000..fd21383458
--- /dev/null
+++ b/core/modules/datetime_range/src/Tests/Update/DatetimeRangeViewUpdateTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\datetime_range\Tests\Update;
+
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+use Drupal\views\Entity\View;
+use Drupal\Core\Database\Database;
+
+/**
+ * Test update of views with datetime_range filters.
+ *
+ * @see https://www.drupal.org/node/2786577
+ * @see datetime_range_update_8001()
+ *
+ * @group Update
+ */
+class DatetimeRangeViewUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+      __DIR__ . '/../../../tests/fixtures/update/datetime_range-filter-values.php',
+    ];
+  }
+
+  /**
+   * Tests that datetime_range filter values are updated properly.
+   */
+  public function testViewsPostUpdateDateRangeFilterValues() {
+
+    $this->runUpdates();
+
+    // Load and initialize our test view.
+    $field_name = 'field_range_value';
+    $view = View::load('test_datetime_range_filter_values');
+    $data = $view->toArray();
+
+    // Check filter values
+    $filter = $data['display']['default']['display_options']['filters'][$field_name];
+
+    $this->assertIdentical('datetime', $filter['plugin_id']);
+    $this->assertIdentical('2017', $filter['value']['value']);
+
+    // Check sort values
+    $sort = $data['display']['default']['display_options']['sorts'][$field_name];
+    $this->assertIdentical('datetime', $sort['plugin_id']);
+  }
+
+}
diff --git a/core/modules/datetime_range/src/Tests/Views/DateRangeHandlerTestBase.php b/core/modules/datetime_range/src/Tests/Views/DateRangeHandlerTestBase.php
new file mode 100644
index 0000000000..2dcf829d00
--- /dev/null
+++ b/core/modules/datetime_range/src/Tests/Views/DateRangeHandlerTestBase.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\datetime_range\Tests\Views;
+
+use Drupal\datetime\Tests\Views\DateTimeHandlerTestBase;
+
+/**
+ * Base class for testing datetime handlers.
+ */
+abstract class DateRangeHandlerTestBase extends DateTimeHandlerTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['datetime_test', 'node', 'datetime_range'];
+
+  /**
+   * Type of the field.
+   *
+   * @var string
+   */
+  protected static $field_type = 'daterange';
+
+}
diff --git a/core/modules/datetime_range/src/Tests/Views/FilterDateTest.php b/core/modules/datetime_range/src/Tests/Views/FilterDateTest.php
new file mode 100644
index 0000000000..b46f63be48
--- /dev/null
+++ b/core/modules/datetime_range/src/Tests/Views/FilterDateTest.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\datetime_range\Tests\Views;
+
+use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\views\Views;
+
+/**
+ * Tests date-only fields.
+ *
+ * @group datetime
+ */
+class FilterDateTest extends DateRangeHandlerTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $testViews = ['test_filter_datetime'];
+
+  /**
+   * For offset tests, set to the current time.
+   */
+  protected static $date;
+
+  /**
+   * {@inheritdoc}
+   *
+   * Create nodes with relative date range of:
+   * yesterday - today, today - today, and today - tomorrow.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Set to 'today'.
+    static::$date = REQUEST_TIME;
+
+    // Change field storage to date-only.
+    $storage = FieldStorageConfig::load('node.' . static::$field_name);
+    $storage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATE);
+    $storage->save();
+
+    $dates = [
+      // Tomorrow.
+      \Drupal::service('date.formatter')->format(static::$date + 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+      // Today.
+      \Drupal::service('date.formatter')->format(static::$date, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+      // Yesterday.
+      \Drupal::service('date.formatter')->format(static::$date - 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+    ];
+
+    // Node 0: Yesterday - Today.
+    $this->nodes[] = $this->drupalCreateNode([
+      'field_date' => [
+        'value' => $dates[2],
+        'end_value' => $dates[1],
+      ],
+    ]);
+    // Node 1: Today - Today.
+    $this->nodes[] = $this->drupalCreateNode([
+      'field_date' => [
+        'value' => $dates[1],
+        'end_value' => $dates[1],
+      ],
+    ]);
+    // Node 2: Today - Tomorrow.
+    $this->nodes[] = $this->drupalCreateNode([
+      'field_date' => [
+        'value' => $dates[1],
+        'end_value' => $dates[0],
+      ],
+    ]);
+
+    // Add end date filter to the test_filter_datetime view.
+    /** @var \Drupal\views\Entity\View $view */
+    $view = \Drupal::entityTypeManager()->getStorage('view')->load('test_filter_datetime');
+    $field_end = static::$field_name . '_end_value';
+    $display = $view->getDisplay('default');
+    $filter_end_date = $display['display_options']['filters'][static::$field_name . '_value'];
+    $filter_end_date['id'] = $field_end;
+    $filter_end_date['field'] = $field_end;
+
+    $view->getDisplay('default')['display_options']['filters'][$field_end] = $filter_end_date;
+    $view->save();
+  }
+
+  /**
+   * Test offsets with date-only fields.
+   */
+  public function testDateOffsets() {
+    $view = Views::getView('test_filter_datetime');
+    $field_start = static::$field_name . '_value';
+    $field_end = static::$field_name . '_end_value';
+
+    // Test simple operations.
+    $view->initHandlers();
+
+    // Search nodes with:
+    // - start date greater than or equal to 'yesterday'.
+    // - end date lower than or equal to 'today'.
+    // Expected results: nodes 0 and 1.
+    $view->filter[$field_start]->operator = '>=';
+    $view->filter[$field_start]->value['type'] = 'offset';
+    $view->filter[$field_start]->value['value'] = '-1 day';
+    $view->filter[$field_end]->operator = '<=';
+    $view->filter[$field_end]->value['type'] = 'offset';
+    $view->filter[$field_end]->value['value'] = 'now';
+    $view->setDisplay('default');
+    $this->executeView($view);
+    $expected_result = [
+      ['nid' => $this->nodes[0]->id()],
+      ['nid' => $this->nodes[1]->id()],
+    ];
+    $this->assertIdenticalResultset($view, $expected_result, $this->map);
+    $view->destroy();
+
+    // Search nodes with:
+    // - start date greater than or equal to 'yesterday'.
+    // - end date greater than 'today'.
+    // Expected results: node 2.
+    $view->initHandlers();
+    $view->filter[$field_start]->operator = '>=';
+    $view->filter[$field_start]->value['type'] = 'offset';
+    $view->filter[$field_start]->value['value'] = '-1 day';
+    $view->filter[$field_end]->operator = '>';
+    $view->filter[$field_end]->value['type'] = 'offset';
+    $view->filter[$field_end]->value['value'] = 'now';
+    $view->setDisplay('default');
+    $this->executeView($view);
+    $expected_result = [
+      ['nid' => $this->nodes[2]->id()],
+    ];
+    $this->assertIdenticalResultset($view, $expected_result, $this->map);
+  }
+
+}
diff --git a/core/modules/datetime_range/tests/fixtures/update/datetime_range-filter-values.php b/core/modules/datetime_range/tests/fixtures/update/datetime_range-filter-values.php
new file mode 100644
index 0000000000..869e60a12c
--- /dev/null
+++ b/core/modules/datetime_range/tests/fixtures/update/datetime_range-filter-values.php
@@ -0,0 +1,322 @@
+<?php
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ * Contains database additions to drupal-8.bare.standard.php.gz for testing the
+ * upgrade path of https://www.drupal.org/node/2786577.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+// Update core.entity_form_display.node.page.default
+$data = $connection->select('config')
+  ->fields('config', ['data'])
+  ->condition('collection', '')
+  ->condition('name', 'core.entity_form_display.node.page.default')
+  ->execute()
+  ->fetchField();
+
+$data = unserialize($data);
+$data['dependencies']['config'][] = 'field.field.node.page.field_range';
+$data['dependencies']['module'][] = 'datetime_range';
+$data['content']['field_range'] = array(
+    "weight"=> 27,
+    "settings" => array(),
+    "third_party_settings" => array(),
+    "type" => "daterange_default",
+    "region" => "content"
+);
+$connection->update('config')
+  ->fields([
+    'data' => serialize($data),
+  ])
+  ->condition('collection', '')
+  ->condition('name', 'core.entity_form_display.node.page.default')
+  ->execute();
+
+// Update core.entity_view_display.node.page.default
+$data = $connection->select('config')
+  ->fields('config', ['data'])
+  ->condition('collection', '')
+  ->condition('name', 'core.entity_view_display.node.page.default')
+  ->execute()
+  ->fetchField();
+
+$data = unserialize($data);
+$data['dependencies']['config'][] = 'field.field.node.page.field_range';
+$data['dependencies']['module'][] = 'datetime_range';
+$data['content']['field_range'] = array(
+    "weight"=> 102,
+    "label"=> "above",
+    "settings" => array("separator"=> "-", "format_type" => "medium", "timezone_override" => ""),
+    "third_party_settings" => array(),
+    "type" => "daterange_default",
+    "region" => "content"
+);
+$connection->update('config')
+  ->fields([
+    'data' => serialize($data),
+  ])
+  ->condition('collection', '')
+  ->condition('name', 'core.entity_view_display.node.page.default')
+  ->execute();
+
+$connection->insert('config')
+->fields(array(
+  'collection',
+  'name',
+  'data',
+))
+->values(array(
+  'collection' => '',
+  'name' => 'field.field.node.page.field_range',
+  'data' => 'a:16:{s:4:"uuid";s:36:"87dc4221-8d56-4112-8a7f-7a855ac35d08";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:2:{i:0;s:30:"field.storage.node.field_range";i:1;s:14:"node.type.page";}s:6:"module";a:1:{i:0;s:14:"datetime_range";}}s:2:"id";s:21:"node.page.field_range";s:10:"field_name";s:11:"field_range";s:11:"entity_type";s:4:"node";s:6:"bundle";s:4:"page";s:5:"label";s:5:"range";s:11:"description";s:0:"";s:8:"required";b:0;s:12:"translatable";b:0;s:13:"default_value";a:0:{}s:22:"default_value_callback";s:0:"";s:8:"settings";a:0:{}s:10:"field_type";s:9:"daterange";}',
+))
+->values(array(
+  'collection' => '',
+  'name' => 'field.storage.node.field_range',
+  'data' => 'a:16:{s:4:"uuid";s:36:"2190ad8c-39dd-4eb1-b189-1bfc0c244a40";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:1:{s:6:"module";a:2:{i:0;s:14:"datetime_range";i:1;s:4:"node";}}s:2:"id";s:16:"node.field_range";s:10:"field_name";s:11:"field_range";s:11:"entity_type";s:4:"node";s:4:"type";s:9:"daterange";s:8:"settings";a:1:{s:13:"datetime_type";s:8:"datetime";}s:6:"module";s:14:"datetime_range";s:6:"locked";b:0;s:11:"cardinality";i:1;s:12:"translatable";b:1;s:7:"indexes";a:0:{}s:22:"persist_with_no_fields";b:0;s:14:"custom_storage";b:0;}',
+))
+->values(array(
+  'collection' => '',
+  'name' => 'views.view.test_datetime_range_filter_values',
+  'data' => 'a:13:{s:4:"uuid";s:36:"d20760b6-7cc4-4844-ae04-96da7225a46f";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:1:{s:6:"module";a:2:{i:0;s:4:"node";i:1;s:4:"user";}}s:2:"id";s:33:"test_datetime_range_filter_values";s:5:"label";s:33:"test_datetime_range_filter_values";s:6:"module";s:5:"views";s:11:"description";s:0:"";s:3:"tag";s:0:"";s:10:"base_table";s:15:"node_field_data";s:10:"base_field";s:3:"nid";s:4:"core";s:3:"8.x";s:7:"display";a:1:{s:7:"default";a:6:{s:14:"display_plugin";s:7:"default";s:2:"id";s:7:"default";s:13:"display_title";s:6:"Master";s:8:"position";i:0;s:15:"display_options";a:17:{s:6:"access";a:2:{s:4:"type";s:4:"perm";s:7:"options";a:1:{s:4:"perm";s:14:"access content";}}s:5:"cache";a:2:{s:4:"type";s:3:"tag";s:7:"options";a:0:{}}s:5:"query";a:2:{s:4:"type";s:11:"views_query";s:7:"options";a:5:{s:19:"disable_sql_rewrite";b:0;s:8:"distinct";b:0;s:7:"replica";b:0;s:13:"query_comment";s:0:"";s:10:"query_tags";a:0:{}}}s:12:"exposed_form";a:2:{s:4:"type";s:5:"basic";s:7:"options";a:7:{s:13:"submit_button";s:5:"Apply";s:12:"reset_button";b:0;s:18:"reset_button_label";s:5:"Reset";s:19:"exposed_sorts_label";s:7:"Sort by";s:17:"expose_sort_order";b:1;s:14:"sort_asc_label";s:3:"Asc";s:15:"sort_desc_label";s:4:"Desc";}}s:5:"pager";a:2:{s:4:"type";s:4:"mini";s:7:"options";a:6:{s:14:"items_per_page";i:10;s:6:"offset";i:0;s:2:"id";i:0;s:11:"total_pages";N;s:6:"expose";a:7:{s:14:"items_per_page";b:0;s:20:"items_per_page_label";s:14:"Items per page";s:22:"items_per_page_options";s:13:"5, 10, 25, 50";s:26:"items_per_page_options_all";b:0;s:32:"items_per_page_options_all_label";s:7:"- All -";s:6:"offset";b:0;s:12:"offset_label";s:6:"Offset";}s:4:"tags";a:2:{s:8:"previous";s:6:"‹‹";s:4:"next";s:6:"››";}}}s:5:"style";a:2:{s:4:"type";s:7:"default";s:7:"options";a:4:{s:8:"grouping";a:0:{}s:9:"row_class";s:0:"";s:17:"default_row_class";b:1;s:11:"uses_fields";b:0;}}s:3:"row";a:2:{s:4:"type";s:6:"fields";s:7:"options";a:4:{s:6:"inline";a:0:{}s:9:"separator";s:0:"";s:10:"hide_empty";b:0;s:22:"default_field_elements";b:1;}}s:6:"fields";a:1:{s:5:"title";a:37:{s:2:"id";s:5:"title";s:5:"table";s:15:"node_field_data";s:5:"field";s:5:"title";s:11:"entity_type";s:4:"node";s:12:"entity_field";s:5:"title";s:5:"label";s:0:"";s:5:"alter";a:8:{s:10:"alter_text";b:0;s:9:"make_link";b:0;s:8:"absolute";b:0;s:4:"trim";b:0;s:13:"word_boundary";b:0;s:8:"ellipsis";b:0;s:10:"strip_tags";b:0;s:4:"html";b:0;}s:10:"hide_empty";b:0;s:10:"empty_zero";b:0;s:8:"settings";a:1:{s:14:"link_to_entity";b:1;}s:9:"plugin_id";s:5:"field";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:7:"exclude";b:0;s:12:"element_type";s:0:"";s:13:"element_class";s:0:"";s:18:"element_label_type";s:0:"";s:19:"element_label_class";s:0:"";s:19:"element_label_colon";b:1;s:20:"element_wrapper_type";s:0:"";s:21:"element_wrapper_class";s:0:"";s:23:"element_default_classes";b:1;s:5:"empty";s:0:"";s:16:"hide_alter_empty";b:1;s:17:"click_sort_column";s:5:"value";s:4:"type";s:6:"string";s:12:"group_column";s:5:"value";s:13:"group_columns";a:0:{}s:10:"group_rows";b:1;s:11:"delta_limit";i:0;s:12:"delta_offset";i:0;s:14:"delta_reversed";b:0;s:16:"delta_first_last";b:0;s:10:"multi_type";s:9:"separator";s:9:"separator";s:2:", ";s:17:"field_api_classes";b:0;}}s:7:"filters";a:1:{s:17:"field_range_value";a:14:{s:2:"id";s:17:"field_range_value";s:5:"table";s:17:"node__field_range";s:5:"field";s:17:"field_range_value";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:8:"contains";s:5:"value";s:4:"2017";s:5:"group";i:1;s:7:"exposed";b:0;s:6:"expose";a:10:{s:11:"operator_id";s:0:"";s:5:"label";s:0:"";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:0:"";s:10:"identifier";s:0:"";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:1:{s:13:"authenticated";s:13:"authenticated";}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:9:"plugin_id";s:6:"string";}}s:5:"sorts";a:1:{s:17:"field_range_value";a:10:{s:2:"id";s:17:"field_range_value";s:5:"table";s:17:"node__field_range";s:5:"field";s:17:"field_range_value";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"order";s:3:"ASC";s:7:"exposed";b:0;s:6:"expose";a:1:{s:5:"label";s:0:"";}s:9:"plugin_id";s:8:"standard";}}s:6:"header";a:0:{}s:6:"footer";a:0:{}s:5:"empty";a:0:{}s:13:"relationships";a:0:{}s:9:"arguments";a:0:{}s:17:"display_extenders";a:0:{}s:13:"filter_groups";a:2:{s:8:"operator";s:3:"AND";s:6:"groups";a:0:{}}}s:14:"cache_metadata";a:3:{s:7:"max-age";i:-1;s:8:"contexts";a:5:{i:0;s:26:"languages:language_content";i:1;s:28:"languages:language_interface";i:2;s:14:"url.query_args";i:3;s:21:"user.node_grants:view";i:4;s:16:"user.permissions";}s:4:"tags";a:0:{}}}}}',
+))
+->execute();
+
+// Update core.extension.
+$extensions = $connection->select('config')
+  ->fields('config', ['data'])
+  ->condition('collection', '')
+  ->condition('name', 'core.extension')
+  ->execute()
+  ->fetchField();
+$extensions = unserialize($extensions);
+$extensions['module']['datetime_range'] = 0;
+$connection->update('config')
+  ->fields([
+    'data' => serialize($extensions),
+  ])
+  ->condition('collection', '')
+  ->condition('name', 'core.extension')
+  ->execute();
+
+$connection->insert('key_value')
+->fields(array(
+  'collection',
+  'name',
+  'value',
+))
+->values(array(
+  'collection' => 'config.entity.key_store.field_config',
+  'name' => 'uuid:87dc4221-8d56-4112-8a7f-7a855ac35d08',
+  'value' => 'a:1:{i:0;s:33:"field.field.node.page.field_range";}',
+))
+->values(array(
+  'collection' => 'config.entity.key_store.field_storage_config',
+  'name' => 'uuid:2190ad8c-39dd-4eb1-b189-1bfc0c244a40',
+  'value' => 'a:1:{i:0;s:30:"field.storage.node.field_range";}',
+))
+->values(array(
+  'collection' => 'config.entity.key_store.view',
+  'name' => 'uuid:d20760b6-7cc4-4844-ae04-96da7225a46f',
+  'value' => 'a:1:{i:0;s:44:"views.view.test_datetime_range_filter_values";}',
+))
+->values(array(
+  'collection' => 'entity.storage_schema.sql',
+  'name' => 'node.field_schema_data.field_range',
+  'value' => 'a:2:{s:17:"node__field_range";a:4:{s:11:"description";s:40:"Data storage for node field field_range.";s:6:"fields";a:8:{s:6:"bundle";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:128;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:88:"The field instance bundle to which this row belongs, used when deleting a field instance";}s:7:"deleted";a:5:{s:4:"type";s:3:"int";s:4:"size";s:4:"tiny";s:8:"not null";b:1;s:7:"default";i:0;s:11:"description";s:60:"A boolean indicating whether this data item has been deleted";}s:9:"entity_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:38:"The entity id this data is attached to";}s:11:"revision_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:47:"The entity revision id this data is attached to";}s:8:"langcode";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:32;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:37:"The language code for this data item.";}s:5:"delta";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:67:"The sequence number for this data item, used for multi-value fields";}s:17:"field_range_value";a:4:{s:11:"description";s:21:"The start date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}s:21:"field_range_end_value";a:4:{s:11:"description";s:19:"The end date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}}s:11:"primary key";a:4:{i:0;s:9:"entity_id";i:1;s:7:"deleted";i:2;s:5:"delta";i:3;s:8:"langcode";}s:7:"indexes";a:4:{s:6:"bundle";a:1:{i:0;s:6:"bundle";}s:11:"revision_id";a:1:{i:0;s:11:"revision_id";}s:17:"field_range_value";a:1:{i:0;s:17:"field_range_value";}s:21:"field_range_end_value";a:1:{i:0;s:21:"field_range_end_value";}}}s:26:"node_revision__field_range";a:4:{s:11:"description";s:52:"Revision archive storage for node field field_range.";s:6:"fields";a:8:{s:6:"bundle";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:128;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:88:"The field instance bundle to which this row belongs, used when deleting a field instance";}s:7:"deleted";a:5:{s:4:"type";s:3:"int";s:4:"size";s:4:"tiny";s:8:"not null";b:1;s:7:"default";i:0;s:11:"description";s:60:"A boolean indicating whether this data item has been deleted";}s:9:"entity_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:38:"The entity id this data is attached to";}s:11:"revision_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:47:"The entity revision id this data is attached to";}s:8:"langcode";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:32;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:37:"The language code for this data item.";}s:5:"delta";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:67:"The sequence number for this data item, used for multi-value fields";}s:17:"field_range_value";a:4:{s:11:"description";s:21:"The start date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}s:21:"field_range_end_value";a:4:{s:11:"description";s:19:"The end date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}}s:11:"primary key";a:5:{i:0;s:9:"entity_id";i:1;s:11:"revision_id";i:2;s:7:"deleted";i:3;s:5:"delta";i:4;s:8:"langcode";}s:7:"indexes";a:4:{s:6:"bundle";a:1:{i:0;s:6:"bundle";}s:11:"revision_id";a:1:{i:0;s:11:"revision_id";}s:17:"field_range_value";a:1:{i:0;s:17:"field_range_value";}s:21:"field_range_end_value";a:1:{i:0;s:21:"field_range_end_value";}}}}',
+))
+->values(array(
+  'collection' => 'system.schema',
+  'name' => 'datetime_range',
+  'value' => 'i:8000;',
+))
+->execute();
+
+// Update entity.definitions.bundle_field_map
+$value = $connection->select('key_value')
+  ->fields('key_value', ['value'])
+  ->condition('collection', 'entity.definitions.bundle_field_map')
+  ->condition('name', 'node')
+  ->execute()
+  ->fetchField();
+
+$value = unserialize($value);
+$value["field_range"] = array("type" => "daterange", "bundles" => array("page" => "page"));
+
+$connection->update('key_value')
+  ->fields([
+    'value' => serialize($value),
+  ])
+  ->condition('collection', 'entity.definitions.bundle_field_map')
+  ->condition('name', 'node')
+  ->execute();
+
+// Update system.module.files
+$files = $connection->select('key_value')
+  ->fields('key_value', ['value'])
+  ->condition('collection', 'state')
+  ->condition('name', 'system.module.files')
+  ->execute()
+  ->fetchField();
+
+$files = unserialize($files);
+$files["datetime_range"] = "core/modules/datetime_range/datetime_range.info.yml";
+
+$connection->update('key_value')
+  ->fields([
+    'value' => serialize($files),
+  ])
+  ->condition('collection', 'state')
+  ->condition('name', 'system.module.files')
+  ->execute();
+
+$connection->schema()->createTable('node__field_range', array(
+  'fields' => array(
+    'bundle' => array(
+      'type' => 'varchar_ascii',
+      'not null' => TRUE,
+      'length' => '128',
+      'default' => '',
+    ),
+    'deleted' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+    ),
+    'entity_id' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'revision_id' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'langcode' => array(
+      'type' => 'varchar_ascii',
+      'not null' => TRUE,
+      'length' => '32',
+      'default' => '',
+    ),
+    'delta' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'field_range_value' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '20',
+    ),
+    'field_range_end_value' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '20',
+    ),
+  ),
+  'primary key' => array(
+    'entity_id',
+    'deleted',
+    'delta',
+    'langcode',
+  ),
+  'indexes' => array(
+    'bundle' => array(
+      'bundle',
+    ),
+    'revision_id' => array(
+      'revision_id',
+    ),
+    'field_range_value' => array(
+      'field_range_value',
+    ),
+    'field_range_end_value' => array(
+      'field_range_end_value',
+    ),
+  ),
+  'mysql_character_set' => 'utf8mb4',
+));
+
+$connection->schema()->createTable('node_revision__field_range', array(
+  'fields' => array(
+    'bundle' => array(
+      'type' => 'varchar_ascii',
+      'not null' => TRUE,
+      'length' => '128',
+      'default' => '',
+    ),
+    'deleted' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+    ),
+    'entity_id' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'revision_id' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'langcode' => array(
+      'type' => 'varchar_ascii',
+      'not null' => TRUE,
+      'length' => '32',
+      'default' => '',
+    ),
+    'delta' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ),
+    'field_range_value' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '20',
+    ),
+    'field_range_end_value' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '20',
+    ),
+  ),
+  'primary key' => array(
+    'entity_id',
+    'revision_id',
+    'deleted',
+    'delta',
+    'langcode',
+  ),
+  'indexes' => array(
+    'bundle' => array(
+      'bundle',
+    ),
+    'revision_id' => array(
+      'revision_id',
+    ),
+    'field_range_value' => array(
+      'field_range_value',
+    ),
+    'field_range_end_value' => array(
+      'field_range_end_value',
+    ),
+  ),
+  'mysql_character_set' => 'utf8mb4',
+));
+
-- 
2.11.0

