Problem/Motivation

Machine names need to be unique, normally MachineName::validateMachineName takes care of this and sets form errors where required.

When creating a duplicate machine name for a MediaType entity the site crashes on form submit and throws the following error:

Uncaught PHP Exception Drupal\\Core\\Entity\\EntityStorageException: "'media_type' entity with ID 'image' already exists." at /.../core/lib/Drupal/Core/Entity/EntityStorageBase.php line 425, referer: http://example.com/admin/structure/media/add

Steps to reproduce:

1) Enable the media module, then go to /admin/structure/media/add
2) Create a type called "File Foo", click "Save".
3) Go to the same page, create another type called "File Foo", click "Save"

Proposed resolution

Figure out why MachineName::validateMachineName isn't firing for MediaType entities.

Remaining tasks

As above

User interface changes

None

API changes

None

Data model changes

None

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Darvanen created an issue. See original summary.

marcoscano’s picture

Priority: Minor » Major
Issue summary: View changes
Issue tags: +Media Initiative

Thanks for reporting, this is a pretty nasty bug :)

Bumping to major because a "normal" user action on the UI shouldn't trigger a WSOD.

Added steps to reproduce to the IS.

marcoscano’s picture

After digging into this a bit, this is what I could find:

- MachineName::validateMachineName() is being called during the first AJAX callback (when the source select is triggered), and correctly calls $form_state->setError() due to the machine name not being unique.
- This call to $form_state->setError() does NOT set the error because at this point $form_state->getLimitValidationErrors() returns an empty array.
- The empty array comes from FormValidator::determineLimitValidationErrors(), which will set it to [] whenever the triggering element does not execute submit callbacks and there is no explicit '#limit_validation_errors' key on the element.

Setting the element to have '#executes_submit_callback' => TRUE, appears to solve the problem. Also added a specific submit for this element, to prevent the whole form from being submitted on the AJAX call.

I'm adding a test-only patch to demonstrate the bug.

Also, solving this has surfaced to the UI the notices reported in #2932222: Undefined index: source_configuration breaks AJAX:

Notice: Undefined index: source_configuration in Drupal\media\MediaTypeForm->validateForm() (line 289 of /var/www/html/core/modules/media/src/MediaTypeForm.php)

Because now it's on the UI, I'm including here the fix from that patch as well. If this is OK, please credit @Darvanen for the patch as well.

Status: Needs review » Needs work

The last submitted patch, 3: 2932226-3-FAIL-test-only.patch, failed testing. View results

marcoscano’s picture

Status: Needs work » Needs review
Berdir’s picture

Fine if you found a workaround for that specific form but this is a generic problem for all forms with ajax elements, we've had #2557299: Any AJAX call disregards machine name verification when AJAX is used and leads to a fatal error open for a long time about that to fix it in general but it's complicated IIRC.

darvanen’s picture

I think that's a solid duplication.

marcoscano’s picture

Status: Closed (duplicate) » Needs review

Thank you @Berdir and @Darvanen

This one has a workaround for a specific form that affects site builders in a much more evident way. Solving it generically might take some time, so IMHO I believe it is justified to have a solution for the Media type form in the short term as its own issue.

phenaproxima’s picture

Issue tags: +Needs followup

I think I'm OK with this workaround, as long as we open a follow-up issue to remove it once #2557299: Any AJAX call disregards machine name verification when AJAX is used and leads to a fatal error lands, and a comment in the patch which points to that follow-up.

marcoscano’s picture

Status: Needs review » Needs work

The last submitted patch, 10: 2932226-10.patch, failed testing. View results

marcoscano’s picture

Status: Needs work » Needs review
xjm’s picture

+++ b/core/modules/media/src/MediaTypeForm.php
@@ -131,6 +131,13 @@ public function form(array $form, FormStateInterface $form_state) {
+      // @TODO This was added as part of #2932226 and it should be removed once
+      // https://www.drupal.org/project/drupal/issues/2557299 solves it in a
+      // more generic way.

Minor formatting nitpick: @todo should be lowercase, and the subsequent comment lines should be indented by two spaces between the // and the text.

xjm’s picture

FWIW I am also okay with an @todo postponed on the followup, at least in principle.

phenaproxima’s picture

Status: Needs review » Needs work

Looks great! Just nits.

  1. +++ b/core/modules/media/src/MediaTypeForm.php
    @@ -131,6 +131,13 @@ public function form(array $form, FormStateInterface $form_state) {
    +      '#submit' => [[get_called_class(), 'rebuildSubmit']],
    

    We can use static::class instead of get_called_class().

  2. +++ b/core/modules/media/src/MediaTypeForm.php
    @@ -234,6 +241,13 @@ public function form(array $form, FormStateInterface $form_state) {
    +  public static function rebuildSubmit(array $form, FormStateInterface $form_state) {
    

    $form is passed by reference.

  3. +++ b/core/modules/media/src/MediaTypeForm.php
    @@ -234,6 +241,13 @@ public function form(array $form, FormStateInterface $form_state) {
    +    $form_state->setRebuild(TRUE);
    

    No need for TRUE here.

  4. +++ b/core/modules/media/src/MediaTypeForm.php
    @@ -273,7 +287,7 @@ protected function getSourceSubFormState(array $form, FormStateInterface $form_s
    +    if (array_key_exists('source_configuration', $form['source_dependent'])) {
    

    Let's use isset($form['source_dependent']['source_configuration']) instead.

  5. +++ b/core/modules/media/src/MediaTypeForm.php
    @@ -296,7 +310,7 @@ function ($item) {
    +    if (array_key_exists('source_configuration', $form['source_dependent'])) {
    

    Same here.

robpowell’s picture

Assigned: Unassigned » robpowell
Status: Needs work » Active
robpowell’s picture

+++ b/core/modules/media/src/MediaTypeForm.php
@@ -234,6 +241,13 @@ public function form(array $form, FormStateInterface $form_state) {
+   * Rebuilds the form on select submit.
+   */
+  public static function rebuildSubmit(array $form, FormStateInterface $form_state) {

This should be a full code block.

Attached patch includes #13, #15 and the above code block.

robpowell’s picture

Assigned: robpowell » Unassigned
Status: Active » Needs review
phenaproxima’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/src/MediaTypeForm.php
@@ -241,10 +241,13 @@
+   * Form Submission to rebuild the form on select submit.
+   *
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state

SUPERNIT! The description should begin with "Form submission handler...", and the two parameters need a description line ("The structured form array" and "The current form state" oughta do the trick).

robpowell’s picture

robpowell’s picture

Status: Needs work » Needs review
phenaproxima’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/src/MediaTypeForm.php
@@ -241,10 +241,12 @@
    * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *  Parent form state.

Aw man, sorry to do this, but...

1) The description needs to be indented by one more space.

2) The description should say "Current form state", not "parent".

robpowell’s picture

robpowell’s picture

Status: Needs work » Needs review
phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Ah yesh, this is good to go once Drupal CI passes it. Thanks, @robpowell!

xjm credited larowlan.

xjm’s picture

Status: Reviewed & tested by the community » Fixed

I checked with @larowlan and he also mentioned being comfortable with this workaround/hotfix for this specific issue.

Committed and pushed to 8.5.x. Thanks!

  • xjm committed ee7f7f2 on 8.5.x
    Issue #2932226 by robpowell, marcoscano, phenaproxima, Darvanen,...

Status: Fixed » Closed (fixed)

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