When the Select List widget is used for date fields, it's possible to enter a value like February 31. This causes DateTimePlus to throw an exception in the datetime value callback, even before validation callbacks are run.

Full trace below.

The website encountered an unexpected error. Please try again later.

Exception: The array contains invalid values. in Drupal\Component\Datetime\DateTimePlus::createFromArray() (line 146 of core/lib/Drupal/Component/Datetime/DateTimePlus.php).
Drupal\Core\Datetime\Element\Datelist::valueCallback(Array, Array, Object)
call_user_func_array(Array, Array) (Line: 1241)
Drupal\Core\Form\FormBuilder->handleInputElement('node_page_form', Array, Object) (Line: 977)
Drupal\Core\Form\FormBuilder->doBuildForm('node_page_form', Array, Object) (Line: 1047)
Drupal\Core\Form\FormBuilder->doBuildForm('node_page_form', Array, Object) (Line: 1047)
Drupal\Core\Form\FormBuilder->doBuildForm('node_page_form', Array, Object) (Line: 1047)
Drupal\Core\Form\FormBuilder->doBuildForm('node_page_form', Array, Object) (Line: 1047)
Drupal\Core\Form\FormBuilder->doBuildForm('node_page_form', Array, Object) (Line: 560)
Drupal\Core\Form\FormBuilder->processForm('node_page_form', Array, Object) (Line: 319)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 53)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object) (Line: 118)
Drupal\node\Controller\NodeController->add(Object)
call_user_func_array(Array, Array) (Line: 128)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 577)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 129)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 102)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 139)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 62)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 62)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 53)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 103)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 55)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 637)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

delzhand created an issue. See original summary.

delzhand’s picture

Issue summary: View changes
swentel’s picture

Component: field system » datetime.module
dpovshed’s picture

The attached patch fixed this. Tested manually on 8.0.x and 8.2.x versions.

mpdonadio’s picture

Version: 8.0.5 » 8.1.x-dev
Status: Needs review » Needs work
Issue tags: +Needs tests

Thanks for the report and the fix.

This is a bug, and we can get this into Drupal 8.1, probably in a point release after 8.1.0.

We need a test to demonstrate the problem, and that the patch address it. Also not 100% sure the raw exception message is the most appropriate to use in this case; "The array contains invalid values" doesn't made sense to show a user when they have invalid form input. Open to suggestions here.

dpovshed’s picture

Status: Needs work » Needs review
FileSize
4.72 KB

Please find new patch which:
- have automated test, when content type with datetime selector is created and used;
- error message changed to "Selected combination of day and month is not valid." since this seems to be the only reason of datetime cannot be created. Other problems like missing year/month etc already handled outside of issue scope.

mpdonadio’s picture

@dpovshed, again, thanks for helping out with this.

Typically, with bugs that introduce new tests, we post a test-only patch that shows the bug (ie, a failing test), and then the patch that fixes the big (and then comes up green). As long as you attach the test-only patch first, the status is OK.

Also, we try to post interdiffs with new patches, to make it easier to see what has changed.

Status: Needs review » Needs work

The last submitted patch, 7: 2696353_bad_dates_in_list_unhandled_exception_test_only.patch, failed testing.

dpovshed’s picture

@mpdonadio, thanks for cooperation and your help to tune up my work to comply Drupal standards.

As I see now we do have failed test only (#7) and test with a fix (#6). Just order is a different if compare to ideal Drupal process. If you agree with proposed message to the user - maybe you can move forward with #6 manually?

Please advice!

mpdonadio’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

Yup, out of order patches happens all the time. I will look at #6 closer tonight (prefer final review applied, so I can see everything in context), but I think

+++ b/core/lib/Drupal/Core/Datetime/Element/Datelist.php
@@ -66,7 +66,12 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
+        try {
+          $date = DrupalDateTime::createFromArray($input, $timezone);
+        }
+        catch (\Exception $e) {
+          $element['#errors'][] = t("Selected combination of day and month is not valid.");
+        }
         if ($date instanceof DrupalDateTime && !$date->hasErrors()) {

message looks good, but think it should be static::t() to avoid using the global (nearly sure that trait should be on that class).

dpovshed’s picture

@mpdonadio, I just looked more carefully for translation in the class. There are a several more usages of t() across the file - for examlpes, see the processDatelist(), validateDatelist() functions.

So I propose to accept patch from #6 if there are no other issues, and to create separate task to migrate t() to static::t() . Surely, this may be simple enough for you to do so right in the code by yourself. I just want to stress that if we go for replacement t() with static::t() withing current issue scope this could be kind a mixing the subjects.

Let me know what you think!

mpdonadio’s picture

Typically, when we introduce new code, we try to follow best practices even if other places in the code don't do this. So, in this case we would do the static::t() in this patch in the usage you introduce, and then do a followup to replace t() with static::t().

I had a last minute deploy last night, so I didn't get to look at this closely. I want to be double sure the test coverage is OK so prevent future regressions. I will try to do so tonight.

dpovshed’s picture

@mpdonadio, OK, take your time to test that, I will wait for your feedback.

mpdonadio’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Datetime/Element/Datelist.php
    @@ -66,7 +66,12 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
    +          $element['#errors'][] = t("Selected combination of day and month is not valid.");
    +        }
    

    Let's change this to static::t()

  2. +++ b/core/modules/system/src/Tests/System/DateTimeTest.php
    @@ -161,4 +168,66 @@ function testDateFormatConfiguration() {
    +    $this->drupalPostForm('node/add/page_with_date', $edit, t('Save'));
    +    $this->drupalGet("node/1");
    +    $this->assertResponse(404, "Node not created because the date is invalid.");
    +
    

    I don't think this test is valid; something else could potentially cause the fail here unrelated to this patch and end up with the node not being created. I would skip the $this->drupalGet("node/1"); and just check for the error message.

  3. This is a nit, but as much as possible try to use single quoted strings unless you need double-quoted ones.

    Can we also add coverage for a datetime field configured for date-only?

dpovshed’s picture

Thanks for the review!

I'd appreciate hints for the following:

1) could you please point me to correct migration from t() to static::t() ? By simple replacement or by trying to mimic approach from Views.php (the only file I found usage of static::t), I had no success ended up with some Exceptions.
2) for some reason I was unable to detect error message text with assertText() - probably it is outputted via AJAX or some other way. Any ideas?

mpdonadio’s picture

OK, let's bail on the static:t(). Views is defining its own. My bad.

The test is failing because that string is coming from a %placeholder, so it is wrapped in an em (inspect element and look at it). However, it also looks like your specific error message is being ignored, and the generic one is used instead. I think you need to update Datelist::validateDatelist() to check the #errors, and use it if set (maybe join('
')?; not much precedence in core that I can see). Then in your test you can check for that specific error message.

mpdonadio’s picture

Status: Needs work » Needs review
FileSize
5.33 KB
4.09 KB

Let's try this.

dpovshed’s picture

Status: Needs review » Reviewed & tested by the community

@mpdonadio, I reviewed your version and tested it locally both manually and via running the tests.

Thanks for finding time to fine tune the stuff!

I like what I see so I am voting for going forward with #17.

mpdonadio’s picture

Status: Reviewed & tested by the community » Needs review

Unfortunately, with the review policy for patches, we have both had our hands in this patch now, so it wouldn't be appropriate for either of us to set RTBC. I will see if @jhedstrom can take a look.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

This looks good. I've also manually tested this patch to verify the bug and fix.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 69d9901 and pushed to 8.1.x and 8.2.x. Thanks!

  • alexpott committed 79f17dc on 8.2.x
    Issue #2696353 by mpdonadio, dpovshed: Bad dates in Select List widget...

  • alexpott committed 69d9901 on 8.1.x
    Issue #2696353 by mpdonadio, dpovshed: Bad dates in Select List widget...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.