Problem/Motivation

Although datetime_extras hides the daterange_duration widget if duration_field isn't installed, there's no check for the right version. We require the 8.x-2.x series. The .info.yml file defines a test dependency, but we don't want a hard dependency enforced by core. However, if a site builder installs duration_field version 8.x-1.*, they get really confusing and obtuse error messages about it (if they know where to even look):

The website encountered an unexpected error. Please try again later.
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "duration_field.granularity.service" in Drupal\Component\DependencyInjection\Container->get() (line 153 of core/lib/Drupal/Component/DependencyInjection/Container.php). 

Proposed resolution

Inside datetime_extras_field_widget_info_alter() check for the right version of duration_field (perhaps by seeing if we have the required services) and hide the widget if we don't.

Perhaps generate a warning if we see an earlier version of duration_field? Not sure where that warning should go? Status report?

Remaining tasks

  1. Finalize scope of the fix
  2. Code
  3. Tests?
  4. Review
  5. Commit

User interface changes

TBD. Probably site builders never see the widget as an option unless the proper version of duration_field is installed. Perhaps a warning is generated if the module is installed but it's from the wrong minor version release series.

API changes

None.

Data model changes

None.

Release notes snippet

?

Original report by @MrPaulDriver

After installing the duration module I am unable to add the daterange offset widget to the form display. After selecting the widget I see that the blue throbber is stuck in a spin and I also see two console messages referring to jquery errors, as below. I am running Drupal 8.7.3

Console message

...
The website encountered an unexpected error. Please try again later.
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "duration_field.granularity.service" in Drupal\Component\DependencyInjection\Container->get() (line 153 of core/lib/Drupal/Component/DependencyInjection/Container.php). 
...

Comments

MrPaulDriver created an issue. See original summary.

mrpauldriver’s picture

Issue summary: View changes
dww’s picture

Title: Unable to add daterange offset widget to form display » Gracefully warn users if they don't have the right version of duration_field
Component: Code » daterange_duration widget
Category: Bug report » Task
Issue summary: View changes

There's an awful lot of noise in those console messages. :( Thanks Symfony + D8. :/

The relevant part is this:

Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "duration_field.granularity.service"

That's because you installed duration_field version 8.x-1.*, not the 8.x-2.* branch (as indicated in the README.md file as a requirement for this widget).

datetime_extras.info.yml says this:

test_dependencies:
  - duration_field:duration_field (>=8.x-2.0-rc2)

But we don't enforce that as a hard module-level requirement, since we don't *require* that you use this widget. There's code to check for the module being installed before the widget is visible, but that doesn't also verify the version. To be more humane to site builders, we should also check the installed version (and/or ensure the existence of the services we're relying on).

Updated the summary and title to be a task about making this more user-friendly.

Meanwhile, if you upgrade to duration_field 8.x-2.0-rc2 or later, your problems should go away.

Thanks/sorry,
-Derek

p.s. I also updated the text in the project page about this version requirement to help avoid confusion.

mrpauldriver’s picture

Thanks for that @dww

dww’s picture

Status: Active » Needs review
StatusFileSize
new952 bytes

@MrPaulDriver: you're welcome. Sorry for the hassles/confusion.

To get this started, here's a fairly simple patch. Tested locally and working well.

Still TODO:

- Automated tests? Not sure how to do this since we have to get the testbot to install 8.x-2.0-rc2 for the actual tests to work. I guess we could do something unholy like a hook_system_info_alter(), but I'm inclined not to care about trying to have automated tests for this.

- Do we warn users if they've installed duration_field but have an old/stale version? If so, where/how?

Anyone have any opinions / thoughts on these?

Thanks,
-Derek

dww’s picture

Also possible TODO: system_get_info() returns an empty array if the requested module isn't installed. So, we could refactor this function a bit to drop the \Drupal::moduleHandler()->moduleExists('duration_field') call and only use system_get_info().

dww’s picture

Automated tests are being weird and annoying. See #3062037: [PP-1] datetime_extras automated tests are confused about PHP versions for more on that.

Meanwhile, manual testing of this patch and feedback on the questions/TODOs in #5 would be welcome.

mrpauldriver’s picture

Just circling back round to this, sorry for the delay.

After applying the patch, I see that the widget is not available and therefore not capable of giving errors. This much better, but still a bit confusing.

But now that the requirement for 8.x-2.x is documented on the project page, I question if it is necessary to do more work on this?

Isn't the real problem that composer require drupal/duration_field fetches the 8.x-1.x branch even though 8.x-2.0-rc2 is available and is actually recommended by the project’s maintainer?

dww’s picture

Yeah, that's an unfortunate symptom of the duration_field maintainer not releasing 8.x-2.0 official. So long as it's an RC, even if d.o says that's the recommended branch, composer won't fetch it for you unless you say something like:

composer require 'drupal/duration_field:^2.0'

Anyway, yeah, I'm also not too motivated to spend much more time on this. Would love some feedback from the other datetime_extras maintainers before I simply commit and move on.

Thanks,
-Derek

  • dww committed 0881e23 on 8.x-1.x
    Issue #3061411 by dww: Update README.md with composer command for the...
dww’s picture

I updated both README.md and the project page with the specific composer require command to use to get the right duration_field.

Meanwhile, I suspected system_get_info() was on the way out, and lo: #2940189: Deprecate system_get_info()

So here's a new patch to use \Drupal::service('extension.list.module')->getExtensionInfo().
Also took out the @todo that the test bot is flagging as a CS failure.
RTBC?

Status: Needs review » Needs work

The last submitted patch, 11: 3061411-11.duration_field-version-check.patch, failed testing. View results

dww’s picture

Status: Needs work » Needs review
StatusFileSize
new1.28 KB
new1.1 KB

Whoops, \Drupal::service('extension.list.module')->getExtensionInfo() throws an exception if the module isn't enabled, not just returning an empty array (like system_get_info() did). This should be better.

mrpauldriver’s picture

Would you like this this testing Derek?

dww’s picture

@MrPaulDriver: If you like. ;) I've tested, but more testing doesn't hurt.

I think once #3064009: Fix test deprecation notices in datetime_extras for 8.8.x core is committed and the 8.8 tests are actually passing again, we could commit this as-is.

Perhaps it's worth adding test coverage that we hide the widget when duration_field isn't enabled at all?
Neither this nor #2845081: Provide a datetime_range widget to define end time via a duration offset added any.
Maybe a new test method inside tests/src/Functional/LoadTest.php?
Or even add more assertions to testLoad() so we don't have to reinstall?

  • dww committed 4552616 on 8.x-1.x
    Issue #3061411 by dww: Hide the daterange_duration widget if the right...
dww’s picture

Title: Gracefully warn users if they don't have the right version of duration_field » Hide the daterange_duration widget if the right version of duration_field isn't installed
Issue summary: View changes
Status: Needs review » Fixed

After resolving #3064009: Fix test deprecation notices in datetime_extras for 8.8.x core, tests are passing properly on both 8.7.x and 8.8.x core, locally and on the d.o testbot.

Hearing no objections in here, committed and pushed #13 to 8.x-1.x branch.

If someone wants to add a test for the disappearing widget, we can do that in a follow-up.

Cheers,
-Derek

heddn’s picture

+++ b/datetime_extras.module
@@ -23,10 +24,19 @@ function datetime_extras_help($route_name, RouteMatchInterface $route_match) {
+    $duration_field_info = \Drupal::service('extension.list.module')->getExtensionInfo('duration_field');

A bit late, but there's a exists method in extension list that would obviate the need for try/catch.

heddn’s picture

+++ b/datetime_extras.module
@@ -23,10 +24,19 @@ function datetime_extras_help($route_name, RouteMatchInterface $route_match) {
+    if (version_compare($duration_field_info['version'], '8.x-2.0-rc2', '<')) {

There's also a core version constraint and API. See https://www.drupal.org/node/2756875

dww’s picture

Status: Fixed » Needs work

@heddn: thanks for the review. Further clean-up patches welcome if anyone is inspired, or maybe I'll be able to circle back soon.

Cheers,
-Derek

dww’s picture

Assigned: Unassigned » dww

I'm gonna take a quick stab at this.

dww’s picture

Assigned: dww » Unassigned
Status: Needs work » Fixed

I'm bailing. The code in 8.x-1.x is working fine.

Re: #18:

A bit late, but there's a exists method in extension list that would obviate the need for try/catch.

Sadly, nope. :( the ExtensionList::exists() method returns TRUE for a module that's present in the file system but not enabled. :( We need to ensure the module is actually enabled. I see nothing in that service that tells us what we want in 1 pass. So try/catch it is, since it does exactly what we need.

Re: #19:

There's also a core version constraint and API. See https://www.drupal.org/node/2756875

The docs @ https://www.drupal.org/node/2756875 are pretty sad. The inline code comments make no sense. I tried using this:

    $dependency = Dependency::createFromString('duration_field:duration_field (>=8.x-2.0-rc2)');
    if (!$dependency->isCompatible($duration_field_info['version'])) {
      // If we don't have 8.x-2.0-rc2 or later, hide the widget.
      dvm("no good");
      unset($info['daterange_duration']);
    }
    else {
      dvm($duration_field_info['version'] . " is compatible.");
      dvm($dependency);
    }

I then kept messing with the version key in duration_field.info.yml. I couldn't get a version string that this code found incompatible. It claimed that all of these are compatible:

  • 8.x-2.0-rc1
  • 8.x-2.0-beta3
  • 8.x-1.3

...

WTF? I'm giving up. This dependency/constraint API is either thoroughly broken, or the docs are so unclear that it's unusable. Either way, I've gotta get back to real work.

Restoring the status.

Cheers,
-Derek

Status: Fixed » Closed (fixed)

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