You are here

function inline_entity_form_field_widget_form in Inline Entity Form 7

Implements hook_field_widget_form().

File

./inline_entity_form.module, line 372
Provides a widget for inline management (creation, modification, removal) of referenced entities. The primary use case is the parent -> children one (for example, order -> line items), where the child entities are never managed outside the…

Code

function inline_entity_form_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $widget = $instance['widget'];
  $settings = inline_entity_form_settings($field, $instance);
  $entity_info = entity_get_info($settings['entity_type']);
  $controller = inline_entity_form_get_controller($instance);

  // The current entity type is not supported, execution can't continue.
  if (!$controller) {
    return array();
  }

  // Get the entity type labels for the UI strings.
  $labels = $controller
    ->labels();

  // Build a parents array for this element's values in the form.
  $parents = array_merge($element['#field_parents'], array(
    $element['#field_name'],
    $element['#language'],
  ));

  // Get the langcode of the parent entity.
  $parent_langcode = entity_language($element['#entity_type'], $element['#entity']);

  // Assign a unique identifier to each IEF widget.
  // Since $parents can get quite long, sha1() ensures that every id has
  // a consistent and relatively short length while maintaining uniqueness.
  $ief_id = sha1(implode('-', $parents));

  // Determine the wrapper ID for the entire element.
  $wrapper = 'inline-entity-form-' . $ief_id;
  $element = array(
    '#type' => 'fieldset',
    '#tree' => TRUE,
    '#description' => filter_xss_admin($instance['description']),
    '#prefix' => '<div id="' . $wrapper . '">',
    '#suffix' => '</div>',
    '#attached' => array(
      'css' => array(),
    ),
    '#ief_id' => $ief_id,
    '#ief_root' => TRUE,
  ) + $element;
  if (module_exists('file')) {

    // file.js triggers uploads when the main Submit button is clicked.
    $element['#attached']['js'] = array(
      drupal_get_path('module', 'file') . '/file.js',
      drupal_get_path('module', 'inline_entity_form') . '/inline_entity_form.js',
    );
  }
  $base_css = array(
    'base' => drupal_get_path('module', 'inline_entity_form') . '/theme/inline_entity_form.css',
    'seven' => drupal_get_path('module', 'inline_entity_form') . '/theme/inline_entity_form.seven.css',
  );

  // Add the base module CSS.
  _inline_entity_form_attach_css($base_css, $element['#attached']['css']);

  // Add entity type specific CSS.
  _inline_entity_form_attach_css($controller
    ->css(), $element['#attached']['css']);

  // Initialize the IEF array in form state.
  if (empty($form_state['inline_entity_form'][$ief_id])) {
    $form_state['inline_entity_form'][$ief_id] = array(
      'form' => NULL,
      'settings' => $settings,
      'instance' => $instance,
    );

    // Load the entities from the $items array and store them in the form
    // state for further manipulation.
    $form_state['inline_entity_form'][$ief_id]['entities'] = array();
    $entity_ids = array();
    foreach ($items as $item) {
      $entity_ids[] = $item[$settings['column']];
    }
    $delta = 0;
    foreach (entity_load($settings['entity_type'], $entity_ids) as $entity) {
      $form_state['inline_entity_form'][$ief_id]['entities'][$delta] = array(
        'entity' => $entity,
        'weight' => $delta,
        'form' => NULL,
        'needs_save' => FALSE,
      );
      $delta++;
    }
  }

  // Prepare cardinality information.
  $cardinality = $field['cardinality'];
  $entity_count = count($form_state['inline_entity_form'][$ief_id]['entities']);
  $cardinality_reached = $cardinality > 0 && $entity_count == $cardinality;

  // Build the appropriate widget.
  // The "Single value" widget assumes it is operating on a required single
  // value reference field with 1 allowed bundle.
  if ($widget['type'] == 'inline_entity_form_single') {

    // Intentionally not using $settings['create_bundles'] here because this
    // widget doesn't care about permissions because of its use case.
    $bundle = reset($settings['bundles']);

    // Uh oh, the parent entity type and bundle are the same as the inline
    // entity type and bundle. We have recursion. Abort.
    if ($element['#entity_type'] == $settings['entity_type'] && $element['#bundle'] == $bundle) {
      return array();
    }
    $form_state['inline_entity_form'][$ief_id]['form settings'] = array(
      'bundle' => $bundle,
    );
    $element['form'] = array(
      '#type' => 'container',
      '#op' => 'add',
      // Used by Field API and controller methods to find the relevant
      // values in $form_state.
      '#parents' => array_merge($parents, array(
        'form',
      )),
      // Pass the current entity type.
      '#entity_type' => $settings['entity_type'],
      // Pass the langcode of the parent entity,
      '#parent_language' => $parent_langcode,
      // Identifies the IEF widget to which the form belongs.
      '#ief_id' => $ief_id,
    );
    if (!empty($form_state['inline_entity_form'][$ief_id]['entities'])) {
      $element['form']['#op'] = 'edit';
      $element['form']['#entity'] = $form_state['inline_entity_form'][$ief_id]['entities'][0]['entity'];
      $element['form']['#ief_row_delta'] = 0;
    }
    $element['form'] = inline_entity_form_entity_form($controller, $element['form'], $form_state);

    // Hide all actions, the widget form behaves like a part of the main form.
    $element['form']['actions']['#access'] = FALSE;
  }
  else {

    // Build the "Multiple value" widget.
    $element['#element_validate'] = array(
      'inline_entity_form_update_row_weights',
    );

    // Add the required element marker & validation.
    if ($element['#required']) {
      $element['#title'] .= ' ' . theme('form_required_marker', array(
        'element' => $element,
      ));
      $element['#element_validate'][] = 'inline_entity_form_required_field';
    }
    $element['entities'] = array(
      '#tree' => TRUE,
      '#theme' => 'inline_entity_form_entity_table',
      '#entity_type' => $settings['entity_type'],
      '#cardinality' => (int) $cardinality,
    );

    // Get the fields that should be displayed in the table.
    $fields = $controller
      ->tableFields($settings['bundles']);
    $context = array(
      'parent_entity_type' => $instance['entity_type'],
      'parent_bundle' => $instance['bundle'],
      'field_name' => $instance['field_name'],
      'entity_type' => $settings['entity_type'],
      'allowed_bundles' => $settings['bundles'],
    );
    drupal_alter('inline_entity_form_table_fields', $fields, $context);
    $element['entities']['#table_fields'] = $fields;
    $weight_delta = max(ceil(count($form_state['inline_entity_form'][$ief_id]['entities']) * 1.2), 50);
    foreach ($form_state['inline_entity_form'][$ief_id]['entities'] as $key => $value) {

      // Data used by theme_inline_entity_form_entity_table().
      $element['entities'][$key]['#entity'] = $entity = $value['entity'];
      $element['entities'][$key]['#needs_save'] = $value['needs_save'];

      // Handle row weights.
      $element['entities'][$key]['#weight'] = $value['weight'];

      // First check to see if this entity should be displayed as a form.
      if (!empty($value['form'])) {
        $element['entities'][$key]['delta'] = array(
          '#type' => 'value',
          '#value' => $value['weight'],
        );
        $element['entities'][$key]['form'] = array(
          '#type' => 'container',
          '#attributes' => array(
            'class' => array(
              'ief-form',
              'ief-form-row',
            ),
          ),
          '#op' => $value['form'],
          // Used by Field API and controller methods to find the relevant
          // values in $form_state.
          '#parents' => array_merge($parents, array(
            'entities',
            $key,
            'form',
          )),
          // Store the entity on the form, later modified in the controller.
          '#entity' => $entity,
          '#entity_type' => $settings['entity_type'],
          // Pass the langcode of the parent entity,
          '#parent_language' => $parent_langcode,
          // Identifies the IEF widget to which the form belongs.
          '#ief_id' => $ief_id,
          // Identifies the table row to which the form belongs.
          '#ief_row_delta' => $key,
        );

        // Prepare data for the form callbacks.
        $form =& $element['entities'][$key]['form'];

        // Add the appropriate form.
        if ($value['form'] == 'edit') {
          $form += inline_entity_form_entity_form($controller, $form, $form_state);
        }
        elseif ($value['form'] == 'remove') {
          $form += inline_entity_form_remove_form($controller, $form, $form_state);
        }
      }
      else {
        $row =& $element['entities'][$key];
        $row['delta'] = array(
          '#type' => 'weight',
          '#delta' => $weight_delta,
          '#default_value' => $value['weight'],
          '#attributes' => array(
            'class' => array(
              'ief-entity-delta',
            ),
          ),
        );

        // Add an actions container with edit and delete buttons for the entity.
        $row['actions'] = array(
          '#type' => 'container',
          '#attributes' => array(
            'class' => array(
              'ief-entity-operations',
            ),
          ),
        );

        // Make sure entity_access is not checked for unsaved entities.
        list($entity_id) = entity_extract_ids($controller
          ->entityType(), $entity);
        if (empty($entity_id) || entity_access('update', $controller
          ->entityType(), $entity)) {
          $row['actions']['ief_entity_edit'] = array(
            '#type' => 'submit',
            '#value' => t('Edit'),
            '#name' => 'ief-' . $ief_id . '-entity-edit-' . $key,
            '#limit_validation_errors' => array(),
            '#ajax' => array(
              'callback' => 'inline_entity_form_get_element',
              'wrapper' => $wrapper,
            ),
            '#submit' => array(
              'inline_entity_form_open_row_form',
            ),
            '#ief_row_delta' => $key,
            '#ief_row_form' => 'edit',
          );
        }

        // Add the clone button, if allowed.
        // The clone form follows the same semantics as the create form, so
        // it's opened below the table.
        if ($controller
          ->getSetting('allow_clone') && !$cardinality_reached && entity_access('create', $controller
          ->entityType(), $entity)) {
          $row['actions']['ief_entity_clone'] = array(
            '#type' => 'submit',
            '#value' => t('Clone'),
            '#name' => 'ief-' . $ief_id . '-entity-clone-' . $key,
            '#limit_validation_errors' => array(
              array_merge($parents, array(
                'actions',
              )),
            ),
            '#ajax' => array(
              'callback' => 'inline_entity_form_get_element',
              'wrapper' => $wrapper,
            ),
            '#submit' => array(
              'inline_entity_form_open_form',
            ),
            '#ief_row_delta' => $key,
            '#ief_form' => 'clone',
          );
        }

        // If 'allow_existing' is on, the default removal operation is unlink
        // and the access check for deleting happens inside the controller
        // removeForm() method.
        if (empty($entity_id) || $controller
          ->getSetting('allow_existing') || entity_access('delete', $controller
          ->entityType(), $entity)) {
          $row['actions']['ief_entity_remove'] = array(
            '#type' => 'submit',
            '#value' => t('Remove'),
            '#name' => 'ief-' . $ief_id . '-entity-remove-' . $key,
            '#limit_validation_errors' => array(),
            '#ajax' => array(
              'callback' => 'inline_entity_form_get_element',
              'wrapper' => $wrapper,
            ),
            '#submit' => array(
              'inline_entity_form_open_row_form',
            ),
            '#ief_row_delta' => $key,
            '#ief_row_form' => 'remove',
          );
        }
      }
    }
    if ($cardinality > 1) {

      // Add a visual cue of cardinality count.
      $message = t('You have added @entities_count out of @cardinality_count allowed @label.', array(
        '@entities_count' => $entity_count,
        '@cardinality_count' => $cardinality,
        '@label' => $labels['plural'],
      ));
      $element['cardinality_count'] = array(
        '#markup' => '<div class="ief-cardinality-count">' . $message . '</div>',
      );
    }

    // Do not return the rest of the form if cardinality count has been reached.
    if ($cardinality_reached) {
      return $element;
    }
    $hide_cancel = FALSE;

    // If the field is required and empty try to open one of the forms.
    if (empty($form_state['inline_entity_form'][$ief_id]['entities']) && $instance['required']) {
      if ($controller
        ->getSetting('allow_existing') && !$controller
        ->getSetting('allow_new')) {
        $form_state['inline_entity_form'][$ief_id]['form'] = 'ief_add_existing';
        $hide_cancel = TRUE;
      }
      elseif (count($settings['create_bundles']) == 1 && $controller
        ->getSetting('allow_new') && !$controller
        ->getSetting('allow_existing')) {
        $bundle = reset($settings['create_bundles']);

        // The parent entity type and bundle must not be the same as the inline
        // entity type and bundle, to prevent recursion.
        if ($element['#entity_type'] != $settings['entity_type'] || $element['#bundle'] != $bundle) {
          $form_state['inline_entity_form'][$ief_id]['form'] = 'add';
          $form_state['inline_entity_form'][$ief_id]['form settings'] = array(
            'bundle' => $bundle,
          );
          $hide_cancel = TRUE;
        }
      }
    }

    // If no form is open, show buttons that open one.
    if (empty($form_state['inline_entity_form'][$ief_id]['form'])) {
      $element['actions'] = array(
        '#attributes' => array(
          'class' => array(
            'container-inline',
          ),
        ),
        '#type' => 'container',
        '#weight' => 100,
      );

      // The user is allowed to create an entity of at least one bundle.
      if (count($settings['create_bundles'])) {

        // Let the user select the bundle, if multiple are available.
        if (count($settings['create_bundles']) > 1) {
          $bundles = array();
          foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
            if (in_array($bundle_name, $settings['create_bundles'])) {
              $bundles[$bundle_name] = $bundle_info['label'];
            }
          }
          asort($bundles);
          $element['actions']['bundle'] = array(
            '#type' => 'select',
            '#options' => $bundles,
          );
        }
        else {
          $element['actions']['bundle'] = array(
            '#type' => 'value',
            '#value' => reset($settings['create_bundles']),
          );
        }
        if ($controller
          ->getSetting('allow_new')) {
          $element['actions']['ief_add'] = array(
            '#type' => 'submit',
            '#value' => t('Add new @type_singular', array(
              '@type_singular' => $labels['singular'],
            )),
            '#name' => 'ief-' . $ief_id . '-add',
            '#limit_validation_errors' => array(
              array_merge($parents, array(
                'actions',
              )),
            ),
            '#ajax' => array(
              'callback' => 'inline_entity_form_get_element',
              'wrapper' => $wrapper,
            ),
            '#submit' => array(
              'inline_entity_form_open_form',
            ),
            '#ief_form' => 'add',
          );
        }
      }
      if ($controller
        ->getSetting('allow_existing')) {
        $element['actions']['ief_add_existing'] = array(
          '#type' => 'submit',
          '#value' => t('Add existing @type_singular', array(
            '@type_singular' => $labels['singular'],
          )),
          '#name' => 'ief-' . $ief_id . '-add-existing',
          '#limit_validation_errors' => array(
            array_merge($parents, array(
              'actions',
            )),
          ),
          '#ajax' => array(
            'callback' => 'inline_entity_form_get_element',
            'wrapper' => $wrapper,
          ),
          '#submit' => array(
            'inline_entity_form_open_form',
          ),
          '#ief_form' => 'ief_add_existing',
        );
      }
    }
    else {

      // There's a form open, show it.
      $element['form'] = array(
        '#type' => 'fieldset',
        '#attributes' => array(
          'class' => array(
            'ief-form',
            'ief-form-bottom',
          ),
        ),
        // Identifies the IEF widget to which the form belongs.
        '#ief_id' => $ief_id,
        // Used by Field API and controller methods to find the relevant
        // values in $form_state.
        '#parents' => array_merge($parents, array(
          'form',
        )),
        // Pass the current entity type.
        '#entity_type' => $settings['entity_type'],
        // Pass the langcode of the parent entity,
        '#parent_language' => $parent_langcode,
      );
      if ($form_state['inline_entity_form'][$ief_id]['form'] == 'add') {
        $element['form']['#op'] = 'add';
        $element['form'] += inline_entity_form_entity_form($controller, $element['form'], $form_state);
      }
      elseif ($form_state['inline_entity_form'][$ief_id]['form'] == 'ief_add_existing') {
        $element['form'] += inline_entity_form_reference_form($controller, $element['form'], $form_state);
      }
      elseif ($form_state['inline_entity_form'][$ief_id]['form'] == 'clone') {
        $element['form']['#op'] = 'clone';
        $element['form'] += inline_entity_form_entity_form($controller, $element['form'], $form_state);
      }

      // Pre-opened forms can't be closed in order to force the user to
      // add / reference an entity.
      if ($hide_cancel) {
        if (isset($element['form']['actions']['ief_add_cancel'])) {
          $element['form']['actions']['ief_add_cancel']['#access'] = FALSE;
        }
        elseif (isset($element['form']['actions']['ief_reference_cancel'])) {
          $element['form']['actions']['ief_reference_cancel']['#access'] = FALSE;
        }
      }

      // No entities have been added. Remove the outer fieldset to reduce
      // visual noise caused by having two titles.
      if (empty($form_state['inline_entity_form'][$ief_id]['entities'])) {
        $element['#type'] = 'container';
      }
    }
  }
  return $element;
}