Workbench Moderation assumes that all revisionable content entities have bundles.

If there is a contrib module that defines a revisionable content entity without bundles the following error is thrown:

Drupal\Component\Plugin\Exception\PluginNotFoundException: The "" entity type does not exist. in Drupal\Core\Entity\EntityTypeManager->getDefinition() (line 130 of /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/EntityTypeManager.php).

An example of this is the Multiversion module which introduces Workspaces, Workspaces are revisionable but don't have bundles. I was going to add a single default bundle to the Workspace entity until I discovered that because Multiversion module makes the User entity also revisionable, it causes the same issue there.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

timmillwood created an issue. See original summary.

timmillwood’s picture

After speaking with @Crell it looks like the fix here is to make WBM only work on bundleable entities.

  • Crell committed 4179e67 on 2656742-bundles
    refs #2656742 Only apply moderation to bundleable entities.
    
Crell’s picture

I'm actually a bit unclear why this is breaking at all. The code that determines what entities to even try to moderate is:

  public function selectRevisionableEntityTypes(array $entity_types) {
    return array_filter($entity_types, function (EntityTypeInterface $type) use ($entity_types) {
      return ($type instanceof ConfigEntityTypeInterface)
      && ($bundle_of = $type->get('bundle_of'))
      && $entity_types[$bundle_of]->isRevisionable();
    });
  }

That is, it's selecting only those config entities that are the bundle definition of something, and that something is revisionable. So if Users are not bundleable, then there wouldn't even be such an object, so why is it passing at all? It should get filtered already. It should just be ignoring it.

Tim, can you post a backtrace?

timmillwood’s picture

I looks as though the issue is coming from \Drupal\workbench_moderation\Plugin\Field\FieldWidget\ModerationStateWidget::formElement, this is added in \Drupal\workbench_moderation\BaseFieldInfo::entityBaseFieldInfo. The attached patch should fix the issue.

#0 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/EntityTypeManager.php(230): Drupal\Core\Entity\EntityTypeManager->getDefinition(NULL)
#1 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/EntityTypeManager.php(166): Drupal\Core\Entity\EntityTypeManager->getHandler(NULL, 'storage')
#2 /home/timmillwood/Code/drupal1/modules/workbench_moderation/src/Plugin/Field/FieldWidget/ModerationStateWidget.php(145): Drupal\Core\Entity\EntityTypeManager->getStorage(NULL)
#3 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Field/WidgetBase.php(329): Drupal\workbench_moderation\Plugin\Field\FieldWidget\ModerationStateWidget->formElement(Object(Drupal\Core\Field\EntityReferenceFieldItemList), 0, Array, Array, Object(Drupal\Core\Form\FormState))
#4 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Field/WidgetBase.php(194): Drupal\Core\Field\WidgetBase->formSingleElement(Object(Drupal\Core\Field\EntityReferenceFieldItemList), 0, Array, Array, Object(Drupal\Core\Form\FormState))
#5 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Field/WidgetBase.php(109): Drupal\Core\Field\WidgetBase->formMultipleElements(Object(Drupal\Core\Field\EntityReferenceFieldItemList), Array, Object(Drupal\Core\Form\FormState))
#6 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php(173): Drupal\Core\Field\WidgetBase->form(Object(Drupal\Core\Field\EntityReferenceFieldItemList), Array, Object(Drupal\Core\Form\FormState))
#7 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/ContentEntityForm.php(59): Drupal\Core\Entity\Entity\EntityFormDisplay->buildForm(Object(Drupal\user\Entity\User), Array, Object(Drupal\Core\Form\FormState))
#8 /home/timmillwood/Code/drupal1/core/modules/user/src/AccountForm.php(274): Drupal\Core\Entity\ContentEntityForm->form(Array, Object(Drupal\Core\Form\FormState), Object(Drupal\user\Entity\User))
#9 /home/timmillwood/Code/drupal1/core/modules/user/src/RegisterForm.php(53): Drupal\user\AccountForm->form(Array, Object(Drupal\Core\Form\FormState), Object(Drupal\user\Entity\User))
#10 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/EntityForm.php(120): Drupal\user\RegisterForm->form(Array, Object(Drupal\Core\Form\FormState))
#11 [internal function]: Drupal\Core\Entity\EntityForm->buildForm(Array, Object(Drupal\Core\Form\FormState))
#12 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Form/FormBuilder.php(517): call_user_func_array(Array, Array)
#13 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Form/FormBuilder.php(276): Drupal\Core\Form\FormBuilder->retrieveForm('user_register_f...', Object(Drupal\Core\Form\FormState))
#14 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Controller/FormController.php(79): Drupal\Core\Form\FormBuilder->buildForm(Object(Drupal\user\RegisterForm), Object(Drupal\Core\Form\FormState))
#15 [internal function]: Drupal\Core\Controller\FormController->getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
#16 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(128): call_user_func_array(Array, Array)
#17 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Render/Renderer.php(577): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#18 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(129): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#19 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(102): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#20 [internal function]: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#21 /home/timmillwood/Code/drupal1/vendor/symfony/http-kernel/HttpKernel.php(139): call_user_func_array(Object(Closure), Array)
#22 /home/timmillwood/Code/drupal1/vendor/symfony/http-kernel/HttpKernel.php(62): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#23 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/StackMiddleware/Session.php(62): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#24 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(53): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#25 /home/timmillwood/Code/drupal1/core/modules/page_cache/src/StackMiddleware/PageCache.php(103): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#26 /home/timmillwood/Code/drupal1/core/modules/page_cache/src/StackMiddleware/PageCache.php(82): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#27 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(51): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#28 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(55): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#29 /home/timmillwood/Code/drupal1/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#30 /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/DrupalKernel.php(632): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#31 /home/timmillwood/Code/drupal1/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#32 {main}
timmillwood’s picture

Status: Active » Needs review

I am now getting the following uncaught exception when I visit "/admin/structure/types".

Uncaught PHP Exception Symfony\\Component\\Routing\\Exception\\RouteNotFoundException: "Route "entity.node_type.moderation" does not exist." at /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Routing/RouteProvider.php line 191, referer: http://drupal1.dev/admin/structure
timmillwood’s picture

It looks as though commit 4179e67ef780f24c2b46bb4dd38d23cf85220662 in branch 2656742-bundles is causing the issue in #6.

So with the 8.x-1.x branch, and the patch from #5, multiversion works, until I try to save a node, then I get the error:

Uncaught PHP Exception Symfony\\Component\\Routing\\Exception\\RouteNotFoundException: "Route "entity.moderation_state.canonical" does not exist." at /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Routing/RouteProvider.php line 191, referer: http://drupal1.dev/node/add/article
timmillwood’s picture

It looks as though the issue in #7 is a definitely a conflict between WBM and Multiversion.

  • Crell committed 0defcec on 2656742-bundles
    refs #2656742 Collapse BaseFieldInfo into EntityTypeInfo, and tweak...
  • Crell committed 3569444 on 2656742-bundles
    refs #2656742 Code style cleanup.
    
Crell’s picture

Tim, please try the attached patch. It folds BaseFieldInfo into EntityTypeInfo, and alters the filter logic in it to only check for entities we've already marked as being moderatable. (That process already should filter for bundleable entities, as above.)

It passes PHPUnit tests for me, but I've not asked Simpletest for its opinion yet.

timmillwood’s picture

@Crell - This looks good, it produces the same results as my patch in #5, which is good.

I have two issues now with the WBM / Multiversion compatibility, which I still need to work out if they are WBM issues or Multiversion issues.

When saving a node (with moderation):

Uncaught PHP Exception Symfony\\Component\\Routing\\Exception\\RouteNotFoundException: "Route "entity.moderation_state.canonical" does not exist." at /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Routing/RouteProvider.php line 191, referer: http://drupal1.dev/node/add/article

When using the bundles branch of Multiversion, saving a workspace as draft etc works, but editing a workspace:

Recoverable fatal error: Argument 1 passed to Drupal\\workbench_moderation\\ModerationInformation::hasForwardRevision() must implement interface Drupal\\Core\\Entity\\ContentEntityInterface, null given, called in /home/timmillwood/Code/drupal1/modules/workbench_moderation/src/Access/LatestRevisionCheck.php on line 56 and defined in /home/timmillwood/Code/drupal1/modules/workbench_moderation/src/ModerationInformation.php on line 196, referer: http://drupal1.dev/admin/structure/workspaces
timmillwood’s picture

@Crell - I am also getting the following error when editing a user with WBM and Multiversion installed:

Uncaught PHP Exception Symfony\\Component\\Routing\\Exception\\RouteNotFoundException: "Route "entity.user.latest_version" does not exist." at /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Routing/RouteProvider.php line 191, referer: http://drupal1.dev/admin/structure/workspaces/1/edit?destination=/admin/structure/workspaces

I assume we need a check somewhere else. Is it trying to add the "Latest version" tab on the user edit page?

timmillwood’s picture

I have fixed the issue:

Recoverable fatal error: Argument 1 passed to Drupal\\workbench_moderation\\ModerationInformation::hasForwardRevision() must implement interface Drupal\\Core\\Entity\\ContentEntityInterface, null given, called in /home/timmillwood/Code/drupal1/modules/workbench_moderation/src/Access/LatestRevisionCheck.php on line 56 and defined in /home/timmillwood/Code/drupal1/modules/workbench_moderation/src/ModerationInformation.php on line 196, referer: http://drupal1.dev/admin/structure/workspaces

It was because we have not defined the links section of the Workspace entity annotation.

So just the two route issues remain.

timmillwood’s picture

How about a patch such as this to prevent against issues like the one in #13.

Status: Needs review » Needs work

The last submitted patch, 14: no-entity.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
timmillwood’s picture

To overcome the first issue stated in #11 I added the following to workbench_moderation.routing.yml:

entity.moderation_state.canonical:
  path: '/admin/structure/moderation-state/states/{moderation_state}'
  defaults:
    _entity_form: 'moderation_state.edit'
    _title: 'Edit Moderation state'
  requirements:
    _permission: 'administer moderation states'
  options:
    _admin_route: TRUE

However I now get the following issue when viewing the newly created node:

Uncaught PHP Exception Drupal\\Core\\Entity\\Query\\QueryException: "No revision table for user, invalid query." at /home/timmillwood/Code/drupal1/core/lib/Drupal/Core/Entity/Query/Sql/Query.php line 104
josephdpurcell’s picture

I've reviewed @Crell's patch 2656742-bundles.patch, and it looks good. I really like the refactoring to use a service instead instantiating a class in workbench_moderation_entity_base_field_info.

I do have two questions:

1. I see there are @todo's for testing. While this is out of scope for this issue, the process of writing the tests may uncover bugs and further refactoring. Is there a plan for those tests to be written at some point>

2. Why not use {@inheritdoc}?

+++ b/src/ModerationInformation.php
@@ -64,6 +64,19 @@ class ModerationInformation implements ModerationInformationInterface {
+   * Determines if an entity type has been marked as moderatable.
Crell’s picture

1) We should at some point. Most of those are left over from larowlan's original work. I didn't bother removing them as I went.

2) Because I originally wrote the method in the class then moved it to the interface, but forgot to fix the docblock afterward. :-) Fixed here.

  • Crell committed af93707 on 2656742-bundles
    refs #2656742 Docfix.
    

  • Crell committed 0defcec on 8.x-1.x
    refs #2656742 Collapse BaseFieldInfo into EntityTypeInfo, and tweak...
  • Crell committed 3569444 on 8.x-1.x
    refs #2656742 Code style cleanup.
    
  • josephdpurcell committed 9148d05 on 8.x-1.x
    Issue #2656742 by Crell, timmillwood: Moderation does not work on...
  • Crell committed af93707 on 8.x-1.x
    refs #2656742 Docfix.
    
josephdpurcell’s picture

Status: Needs review » Fixed

This has been committed to 8.x-1.x. Nice work!

Status: Fixed » Closed (fixed)

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

MrSchoolcraft’s picture

@crell I am using ECK in 8 for the UI to create my types and bundles. However, once diving in to edit and set up fields on my bundle, I was expecting to see the 'manage moderation' tab there buy do not see it.

Is there something I'm missing on being able to make ECK created entities/bundles moderated?