2

Problem

I'm trying to combine two forms. A custom form and the user register form. I want the user to be able to fill in both and submit them together. How would I do this?

CONTEXT: The custom form creates a node, but you have to be logged in to do it. To lower the barrier I want users to be able to create the node and register on the same screen.

(Ideally the user register form won't appear if they're logged in.)

So Far

I tried calling two forms on the page but they have different submit buttons.

function postajob_page_callback() {
    $build = array();

    $build['form_one'] = drupal_get_form('postajob_form');
    $build['form_two'] = drupal_get_form('user_register_form');
  return $build;
}

So I called the user register validate and submit handlers in the custom form validate and submit calls.

function postajob_form_validate($form, &$form_state) {
     user_register_validate($form, $form_state);
     ...
 }

function postajob_form_submitform, &$form_state) {
     user_register_submit($form, $form_state);
     ...
 }

This calls the function but because the form doesn't contain any of the fields (i.e. username, email, password etc.) it throws a horrible error:

Notice: Undefined index: administer_users in user_register_submit() (line 3824 of C:\XAMPP\htdocs\drupal5\modules\user\user.module).
Notice: Undefined index: pass in user_register_submit() (line 3827 of C:\XAMPP\htdocs\drupal5\modules\user\user.module).
Notice: Undefined index: mail in user_register_submit() (line 3838 of C:\XAMPP\htdocs\drupal5\modules\user\user.module).
Notice: Undefined index: #user in user_register_submit() (line 3840 of C:\XAMPP\htdocs\drupal5\modules\user\user.module).
Warning: Creating default object from empty value in entity_form_submit_build_entity() (line 8009 of C:\XAMPP\htdocs\drupal5\includes\common.inc).
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '' for key 'name': INSERT INTO {users} (uid, created, data) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2); Array ( [:db_insert_placeholder_0] => 165 [:db_insert_placeholder_1] => 1379868880 [:db_insert_placeholder_2] => a:6:{s:16:"ckeditor_default";s:1:"t";s:20:"ckeditor_show_toggle";s:1:"t";s:14:"ckeditor_width";s:4:"100%";s:13:"ckeditor_lang";s:2:"en";s:18:"ckeditor_auto_lang";s:1:"t";s:17:"mimemail_textonly";i:0;} ) in drupal_write_record() (line 7166 of C:\XAMPP\htdocs\drupal5\includes\common.inc).

So I tried to merge the forms using info from here and I added the following two lines of code just before the submit button in the form.

function postajob_form($form, &$form_state) {
      ....

     $extra = user_register_form($form, $form_state);
     $form = array_merge($form, $extra); 

     $form['submit'] = array(
       '#type' => 'submit',
       '#value' => t('Submit'),
      );

    return $form;
}

This adds the form but seems to break the submit button and both submit buttons call the user_register submit not the custom form submit. Any ideas?

1

Instead of using custom form you can add fields.
Go to
Administration » Configuration » People » Account settings

And In the manage fields tab u can add fields you want.

You can check the commerce_auto_product_display module tar files which has used similar functionality of adding nodes to the already built form

  • 1
    Unfortunately that won't work, the custom form is creating a node not just adding extra fields to the user register. Context: The user register is a separate process that I want to tag along to creating a job advert node. i.e. they can create a node and register all on one screen for the best user experience. Am I making any sense? – Dominic Woodman Sep 22 '13 at 19:38
  • I will study the code from commerce_auto_product_display and provide with some inputs. – harshal Sep 23 '13 at 7:53
  • So I solved it in the end by manually inserting the elements into my custom form. I'm not sure it's particularly slick but I've posted the code below. – Dominic Woodman Sep 23 '13 at 11:18
1

Ok so I've managed this in a little bit of a clumsy way. I didn't end up using page_callback at all.

Calling the user_register_submit and validate from inside the custom form functions works, they just don't have the values, so I added the form elements manually to my form.

Because it was a custom form I was able to copy and paste the respective form elements from user_register_form. That form calls user_account_form and I ended up having to paste both of them into my custom form.

That's the general answer. Here's the specifics I ended up using.

function mycustommodule_form($form, $form_state) {
... MY CUSTOM NODE FIELDS GO HERE....

global $user;

The first note here is I've wrapped all the user-register stuff in an if function so it will only run if the user is anonymous. If the user is logged in they'll never see this.

if (user_is_anonymous($user) || $user->uid == 1) {

  /*Theres a lot of stuff on the user form which only appears if you're an admin. That's not really important
   *because as an admin you won't be using it here, but i've left it in because I'm not sure entirely how
   *to take it our safely.*/
    $admin = user_access('administer users');

    // Pass access information to the submit handler. Running an access check
    // inside the submit function interferes with form processing and breaks
    // hook_form_alter().
    $form['administer_users'] = array(
       '#type' => 'value',
       '#value' => $admin,
    );

    // If we aren't admin but already logged on, go to the user page instead.

    $form['#user'] = drupal_anonymous_user();
    $form['#user_category'] = 'register';

    $form['#attached']['library'][] = array('system', 'jquery.cookie');
    $form['#attributes']['class'][] = 'user-info-from-cookie';

    // Start with the default user account fields.

Here in user_register_form it calls user_account_form.

user_account_form($form, $form_state);

This partly works. Unfortunately it doesn't ever display the role selector which appears in the normal register process. I also can't seem to set the role if I mimic the array structure. So instead I just pasted the relevant code from that function.

            $account = $form['#user'];
            $register = ($form['#user']->uid > 0 ? FALSE : TRUE);

            $form['#validate'][] = 'user_account_form_validate';

            // Account information.
            $form['account'] = array(
              '#type'   => 'container',
              '#weight' => -10,
            );
            // Only show name field on registration form or user can change own username.
            $form['account']['name'] = array(
              '#type' => 'textfield',
              '#title' => t('Username'),
              '#maxlength' => USERNAME_MAX_LENGTH,
              '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
              '#required' => TRUE,
              '#attributes' => array('class' => array('username')),
              '#default_value' => (!$register ? $account->name : ''),
              '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
              '#weight' => -10,
            );

            $form['account']['mail'] = array(
              '#type' => 'textfield',
              '#title' => t('E-mail address'),
              '#maxlength' => EMAIL_MAX_LENGTH,
              '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
              '#required' => TRUE,
              '#default_value' => (!$register ? $account->mail : ''),
            );


              $form['account']['pass'] = array(
                '#type' => 'password_confirm',
                '#size' => 25,
                '#description' => t('To change the current user password, enter the new password in both fields.'),
              );

              $form['account']['pass'] = array(
                '#type' => 'password_confirm',
                '#size' => 25,
                '#description' => t('Provide a password for the new account in both fields.'),
                '#required' => TRUE,
              );


            if ($admin) {
              $status = isset($account->status) ? $account->status : 1;
            }
            else {
              $status = $register ? variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS : $account->status;
            }
            $form['account']['status'] = array(
              '#type' => 'radios',
              '#title' => t('Status'),
              '#default_value' => $status,
              '#options' => array(t('Blocked'), t('Active')),
              '#access' => $admin,
            );

This is the part which works with roles. First the user is set to authenticated.

            $roles = array_map('check_plain', user_roles(TRUE));
            // The disabled checkbox sub-element for the 'authenticated user' role
            // must be generated separately and added to the checkboxes element,
            // because of a limitation in Form API not supporting a single disabled
            // checkbox within a set of checkboxes.
            // @todo This should be solved more elegantly. See issue #119038.
            $checkbox_authenticated = array(
              '#type' => 'checkbox',
              '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
              '#default_value' => TRUE,
              '#disabled' => TRUE,
            );

Then the role selector is displayed. Normally all the roles are shown at this stage. i.e. admin, other and my two custom roles. I only want my two custom ones so I unset the values I don't want people to select. i.e. admin and other. (authenticated is always unset).

            unset($roles[DRUPAL_AUTHENTICATED_RID]);
            unset($roles['3']);
            unset($roles['6']);
            dpm($roles);
            $form['account']['roles'] = array(
              '#type' => 'checkboxes',
              '#title' => t('Roles'),
              '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()),
              '#options' => $roles,
            );

Finally we have the end of the user_register_account function.

    // Attach field widgets, and hide the ones where the 'user_register_form'
    // setting is not on.
    $langcode = entity_language('user', $form['#user']);
    field_attach_form('user', $form['#user'], $form, $form_state, $langcode);
    foreach (field_info_instances('user', 'user') as $field_name => $instance) {
      if (empty($instance['settings']['user_register_form'])) {
        $form[$field_name]['#access'] = FALSE;
      }
    }
  }

I'm not sure this is a very slick way of doing it, so if anyone has any better ways of doing this, or improving what I've done I'd be really pleased to hear them.

  • where is the node used here its just custom form.? – harshal Sep 23 '13 at 11:34
  • I left it out because it's quite long and this post was already a bit of a monster. The fields with the new node information go at the top where it says my custom node fields go here (edited) and logic goes into the submit function. The submit code is an altered version of drupal.org/node/1388922. – Dominic Woodman Sep 23 '13 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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