4

I'm creating a custom field element type that contains other interactive elements. I've been following the Drupal core File module to try to simulate some of the functionality of it. One thing I've been having trouble with is submission and validation errors.

When you upload a file using the File module, only that part of the form seems to be submitted. In my module, the whole form gets submitted instead. I'd really like to have it act like the File module by only submitting my one element and its contained elements.

The other thing is validation errors. In the file module when you run into an error with an uploaded file, the validation error appears within the area where the file field appears instead of the top of the page. I also have the trouble of my validation for that field being checked when I submit the whole form, but I don't want this validation to happen for submitting the whole form. I only want validation when I hit the button within my custom element like file validation works.

Here's what I have so far. I apologize that this is regarding the same code as a previous question, but that was about the overall framework to use and this is specifically asking about issues with submission and validation for this custom element.

/**
 * Implements hook_element_info().
 */
function my_module_element_info() {
  $types['user_management'] = array(
      '#input' => TRUE,
      '#process' => array('my_module_user_management_process'),
      '#size' => 45,
  );
  return $types;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_my_module_node_form_alter(&$form, &$form_state) {
  $form['administrators_management'] = array(
      '#title' => t('Account Administrators Management'),
      '#type' => 'fieldset',
      '#description' => t('Administration for users who have administrative access to this account.'),
      '#weight' => 33,
  );
  // unset($form['my_module_administrators']);
  $form['administrators_management']['user_management'] = array(
      '#type' => 'user_management',
  );
}

/**
 * Process function to expand the user_management element type.
 */
function my_module_user_management_process($element, &$form_state, $complete_form) {

  global $user;

  $element['#tree'] = TRUE;

  // Normal FAPI field definitions, except that #value is defined.
  $element['user_email'] = array(
      '#type' => 'textfield',
      '#title' => t('Enter email address to add new administrator user'),
      '#size' => 45,
      '#maxlength' => 100,
      '#prefix' => '<span id="user_email">',
      '#suffix' => '</span>',
      '#element_validate' => array('my_module_user_management_validate'),
  );
  $element['add_button'] = array(
      '#type' => 'submit',
      '#value' => t('Add User'),
      '#submit' => array('my_module_user_management_submit'),
      '#validate' => array(),
      '#limit_validation_errors' => array($element['#parents']),
  );
  $element['administrators'] = array(
      '#type' => 'select',
      '#title' => t('Account Administrators'),
      '#default_value()' => array(),
      '#options' => field_view_field('node', $complete_form['#node'], 'my_module_administrators'),
      '#multiple' => TRUE,
  );

  return $element;
}

/**
 * An #element_validate callback for the user_management element.
 */
function my_module_user_management_validate(&$element, &$form_state) {

  if (!array_key_exists('user_management', $input = $form_state['input'])
      || !array_key_exists('user_email', $user_management = $input['user_management'])
      || !($email_address = $user_management['user_email'])) {

    form_set_error('user_management][user_email',
        t("An email address must be entered before pressing 'Add User'."));

  } else { // if $form_state['input']['user_management']['user_email'] has a value

    if ($email_address && !filter_var($email_address, FILTER_VALIDATE_EMAIL)) {
      form_set_error('user_management][user_email',
          t("User email must be a valid email address."));
    }
  }
}

/**
 * Form submission handler for add / remove buttons of user_management elements.
 */
function my_module_user_management_submit($form, &$form_state) {

  $email_address = $form_state['input']['user_management']['user_email'];



  $administrators_options[strval(count($administrators_options))] = $email_address;

  $form_state['rebuild'] = TRUE;
}

/**
 * Implements hook_node_submit().
 */
function my_module_node_submit($nodes, $form, &$form_state) {
  $administrators_options = $form['administrators_management']['user_management']['administrators']['#options'];

  foreach ($administrators_options as $email_address) {

    $new_user = user_load_by_mail($email_address);

    if (!$new_user) {
      $new_user = new stdClass();

      list($name) = explode('@', $email_address);
      for ($temp = $name, $count = 1; user_load_by_name($temp); $temp = $name . ++$count);
      $new_user->name = $temp;

      $new_user->mail = $email_address;
      $new_user->init = $email_address;
      $new_user->status = TRUE;
      $new_user->roles = array(
          DRUPAL_AUTHENTICATED_RID => TRUE,
          db_query("SELECT rid FROM {role} WHERE name = 'customer'")->fetchField() => TRUE,
          db_query("SELECT rid FROM {role} WHERE name = 'customer admin'")->fetchField() => TRUE,
      );
      $new_user = user_save($new_user);
    }
  }
}
4
  • The file module uses AJAX which is why the validation errors appear in its container element (it's probably the reason for the other thing you mentioned as well). I don't see #ajax on any of your elements so that would probably account for the behaviour you're getting, which would be that of a standard form
    – Clive
    Commented Aug 8, 2012 at 17:43
  • Thanks Clive. You're right, it says right in the docs for it: "This rebuilds the form element for a particular field item. As long as the form processing is properly encapsulated in the widget element the form should rebuild correctly using FAPI without the need for additional callbacks or processing." I was glancing over the ajax stuff before because I thought it was related to the progress bar, but there's a second part of it for the upload. I'll see what I can accomplish by working off that code. Hopefully it's not overly difficult.
    – tcm5025
    Commented Aug 8, 2012 at 18:19
  • The complication I'm running into is that File says it achieves its encapsulation by using a widget. So, I was about to start implementing a widget, but I guess that would mean I would need to implement a field to apply the widget to. If I have to go down that route, than so be it but I already have achieved the majority of the functionality I need just by altering the node form. I'm going to try working with the ajax callback stuff to see if there's another way I can isolate my validation and submission to this element, but File probably depends on the widget for encapsulation for a reason...
    – tcm5025
    Commented Aug 8, 2012 at 19:50
  • See how inline_entity_form implements the inline_entity_form_single widget, which is what you're basically reimplementing here. Commented Aug 18, 2013 at 17:03

1 Answer 1

1

The file module uses AJAX which is why the validation errors appear in its container element (it's probably the reason for the other thing you mentioned as well). There is no #ajax on any of your elements so that would probably account for the behaviour you're getting, which would be that of a standard form.

It says right in the docs for it:

This rebuilds the form element for a particular field item. As long as the form processing is properly encapsulated in the widget element the form should rebuild correctly using FAPI without the need for additional callbacks or processing.

See also how inline_entity_form implements the inline_entity_form_single widget, which is what you're basically reimplementing here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.