Wrong bundle form is displayed if user has permissions to create only one of the available bundles

Created on 4 May 2023, over 1 year ago
Updated 23 January 2024, 11 months ago

Problem/Motivation

If a field supports creating multiple bundles of given entity type (e.g. a media reference field that allows referencing Video and Image), users are normally presented a dropdown list to select which bundle they want to create. If the user only has privileges to create one of the available bundles, no dropdown is selected. This makes sense. However, when they click the button to add a new entity, the wrong bundle form can be presented, allowing them to create an entity of a bundle type they don't have access to create.

This happens because of this code in the form widget:

        // Let the user select the bundle, if multiple are available.
        if ($create_bundles_count > 1) {
          $bundles = [];
          foreach ($this->entityTypeBundleInfo->getBundleInfo($target_type) as $bundle_name => $bundle_info) {
            if (in_array($bundle_name, $create_bundles)) {
              $bundles[$bundle_name] = $bundle_info['label'];
            }
          }
          asort($bundles);

          $element['actions']['bundle'] = [
            '#type' => 'select',
            '#options' => $bundles,
          ];
        }
        else {
          $element['actions']['bundle'] = [
            '#type' => 'value',
            '#value' => reset($create_bundles),
          ];
        }

The else statement is hit in this scenario. It sets the bundle to a fixed value form element. However, this value is NOT sent in the Ajax request that's sent when the button to add a new item is pressed. So the logic in determineBundle doesn't have a value to use from form state, so it defaults to instead use the first item from the list of bundles that can be referenced. Since "image" appears first in this list, it's used:

  protected function determineBundle(FormStateInterface $form_state) {
    $ief_settings = $form_state->get(['inline_entity_form', $this->getIefId()]);
    if (!empty($ief_settings['form settings']['bundle'])) {
      return $ief_settings['form settings']['bundle'];
    }
    elseif (!empty($ief_settings['bundle'])) {
      return $ief_settings['bundle'];
    }
    else {
      $target_bundles = $this->getTargetBundles();
      return reset($target_bundles);
    }
  }

The last else statement is hit here.

Steps to reproduce

  1. Fresh Drupal install using Standard profile with Inline Entity Form (I was using rc15) and Media installed
  2. Add a Media reference field on Basic Page, allowing both Image and Video to be referenced
  3. Configure the form widget to be Inline Entity Form - Complex, all default settings
  4. Modify the permissions for the Content Editor role to allow it to create Video media (nothing else)
  5. As an admin, Visit Basic Page node form, observe you have a dropdown to select which bundle to create
  6. As a content editor, Visit Basic Page node form, observe you don't have a dropdown (this is good, you only have perms to create Video, so no sense in showing dropdown). Observe that when clicking the button "Add new media item" you are presented the form for creating an Image instead of Video

Proposed resolution

I think updating the last else statement in determineBundle to pull from the list of bundles the user is allowed to create is the solution here, rather than the list the field is allowed to reference.

I'm not sure it's needed at all to set the fixed bundle value in the form. Not sure this is doing anything at all since it's not sent with the AJAX request.

Remaining tasks

User interface changes

API changes

Data model changes

πŸ› Bug report
Status

Fixed

Version

3.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States bkosborne New Jersey, USA

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024