I've already customized the default user_register form with hook_form_register_form_alter().
That form is using complex multistep form and connects with a web service for validating etc.

Right now I have a new task and have to create a new registration form that will use the default Drupal form, but when I try to get the register form on a custom page, unfortunately it displays the customized form. Yes, that makes sense, but is it possible to create a new register form that will act as a normal default Drupal form and won't have any common with the customized form?

Let's say this is my code:


/*
 * * Implements hook_menu().
 */

function mymodule_menu() {
    $items = array();
	$items['registration'] = array(
		'title' => 'Registration',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('mymodule_registration_page'),
		'access arguments' => array('access content'),
	);
         return $items;
}

function mymodule_registration_page($form, &$form_state) {
    $form['name'] = array(
        '#title' => 'username',
        '#description' => 'choose a username',
        '#type' => 'textfield',
        '#required' => TRUE,
    );
    $form['mail'] = array(
        '#title' => 'email',
        '#description' => 'enter a valid email address',
        '#type' => 'textfield',
        '#required' => TRUE,
    );
     $form['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Save'),
    );
    return $form;
}

What functions do I need to register a user in the database?

Comments

WorldFallz’s picture

Why not just adjust the hook_form_register_form_alter to modify the form based on whatever conditions determine which registration form should be shown?

satvision83’s picture

Yes, that's a good idea. Thank you.
Meanwhile I've found another way how to register user on separate form and I'm posting the code here:

function mymodule_menu() {
	$items = array();
	$items['registration'] = array(
		'title' => 'Registration',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('mymodule_registration_page'),
		'access arguments' => array('access content'),
	);
	
	return $items;	
}

function mymodule_registration_page($form, &$form_state) {
	$form['name'] = array(
		'#type' => 'textfield',
		'#title' => 'Username',
		'#attributes' => array('class' => array('hidevaluetext')),
	);
	$form['pass'] = array(
		'#type' => 'password',
		'#title' => 'Password',
		'#attributes' => array('class' => array('hidevaluetext')),
	);	
	$form['mail'] = array(
		'#type' => 'textfield',
		'#title' => t('E-mail'),
	);
	
	$form['submit'] = array(
		'#type' => 'submit',
		'#value' => t('Create new account'),
	);
	
	return $form;

}
function mymodule_registration_page_submit($form, &$form_state) {
	$pass = user_password();
	$newuser = array(
	  'name' => $form_state['values']['name'],
	  'pass' => $pass,
	  'mail' => $form_state['values']['mail'],
	  'init' => $form_state['values']['mail'],
	  'status' => 1,
	);         
	$user = user_save('', $newuser);
	$user->password = $pass; // Add plain text password into user account to generate mail tokens.
	_user_mail_notify('register_no_approval_required', $user);
	
	drupal_set_message('DONE!');

} 
satvision83’s picture

I've got some new situation here.
I have Services+REST installed and on the custom page "/registration" I cannot register a user with JSON. I've already tested with Poster Firefox add-on.
If I turn off the custom module that has multistep form and connects with the webservice, the JSON registering works perfectly.

So, I guess the idea with the custom /registration page has failed in this situation.

WorldFallz, please can you help me with the conditions which registration should be shown?

My module starts like this:

require_once ('sites/all/SOAP/lib/nusoap.php');
/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_user_register_form_alter(&$form, &$form_state) {
    //the code here with the multistep form.
}
//and the code continues with the form, validating and submitting....

How do I make a condition when to use the custom form and when to use the default?

jaypan’s picture

Why are you trying to use the same path for registering using services, and registering through a browser?


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

Hi, Jaypan.

I think I'm not trying to use the same path.

With my custom module first I'm altering the register form on this default path "user/register". The users has to use the multistep form that connects with the external web service and validating.

On my other part of the code I've created a new menu with path "/registration". On that page callback I'm using $form = drupal_get_form('user_register_form'); to get the form and altering the elements like using unset($form['actions']['name_of_the_element_from_the_custom_form']); and setting #access = TRUE to the elements that I've previously hidden on the altered form.

On the services module settings I've set the user alias to "registration", but when I test with the Poster add-on on that page url/endpoint_path/registration, the results show the fields from the custom form that uses different elements, not the form from the "registration" path.

Jaypan, do you have some idea?

jaypan’s picture

Yes, I understand your problem.

You can take care of this using hook_forms(). This hook allows you to create aliases for forms, so that the same form can be used multiple times. You want an alias of the registration form. So you can do something like this:

function MODULENAME_forms($form_id, $args)
{
  $forms = array();

  // Alias the user_register_form() form
  if($form_id == 'MODULENAME_user_register_form')
  {
    $forms['MODULENAME_user_register_form'] = array
    (
      'callback' => 'user_register_form',
    );
  }

  return $forms;
}

Note: Replace MODULENAME with the machine name of your module.

Then, instead of:

$form = drupal_get_form('user_register_form');

You use:

$form = drupal_get_form('MODULENAME_user_register_form');

By doing this, the forms will have different a $form_id in hook_form_alter(), so you can alter each form accordingly.


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

.....

jaypan’s picture

You are calling drupal_get_form() twice, once in your hook_menu() definition, then again in the function definition that is called for your path. You need to change the callback for the menu definition, then make your alterations in hook_form_later():

function custom_register_menu() {
    $items = array();
        //The second form for register users with Services+REST+JSON
    $items['registration'] = array(
        'title' => 'Registration',
        'page callback' => 'drupal_get_form',
        'page arguments' => array('custom_register_user_register_form'),
        'access arguments' => array('access content'),
    );
    
    return $items;    
}

function custom_register_form_alter(&$form, &$form_state, $form_id)
{
  if($form_id == 'custom_register_user_register_form')
  {
    $form['name']['#attributes']['class'][] = 'hidevaluetext';
    ...
  }
}

Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

Sorry about the delay.
I must say I'm pretty confused.

Here's the things I've tried till now:

The module name is "custom_register".
First I've altered the default register form with this:
function custom_register_form_user_register_form_alter(&$form, &$form_state) {}
Inside that function I've created the complex multistep form, connects with an external web service, getting the results etc. It's working perfectly.

Now I've implemented hook_menu() and here's the code:

function custom_register_menu() {
	$items = array();
	$items['registration'] = array(
		'title' => 'Registration',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('custom_register_user_register_form'),
		'access arguments' => array('access content'),
	);
	return $items;	
}

So the path "/registration" will be the url of the page and "custom_register_user_register_form" will be the second form.

I've implemented hook_forms():

function custom_register_forms($form_id, $args) {
  $forms = array();

  // Alias the user_register_form() form
  if($form_id == 'custom_register_user_register_form') {
    $forms['custom_register_user_register_form'] = array (
      'callback' => 'user_register_form',
    );
  }

  return $forms;
}

Now what should I put inside the
function custom_register_user_register_form($form, &$form_state) {} ?

I've tried to call with drupal_get_form('user_register_form');,
its displaying nothing. I've put new fields here, but when I've test with the Poster on the /registration page with the webservice, it's delaying and after 15 seconds it shows the errors from the custom form, I mean It shows the validating details about the values that relates with the altered form, not the default.

I'll mention this again. When I disable the custom module, the Poster registers a new users with JSON without any problems.

jaypan’s picture

Now what should I put inside the function custom_register_user_register_form($form, &$form_state) {} ?

Nothing. You shouldn't even create this function. I'm assuming you have created this function, which is likely where your problem lies.

When you call drupal_get_form(FORM_NAME), the system does the following:
1) Looks for the function FORM_NAME(). If it does not find this function it goes to #2:
2) Invokes hook_forms(), to see if any aliases exist with that name. If an alias if found, the system uses the function returned as 'callback' in hook_forms(). In your case, this means that when you drupal_get_form('custom_register_user_register_form'), the system returns the form defined in user_register_form().

Therefore, by creating the function custom_register_user_registration_form(), the system is not moving on to #2, and is trying to use the function you have defined, instead of returning the form defined in user_register_form().

Look at this code:

function custom_register_forms($form_id, $args) {
  $forms = array();

  // Alias the user_register_form() form
  if($form_id == 'custom_register_user_register_form') {
    $forms['custom_register_user_register_form'] = array (
      'callback' => 'user_register_form',
    );
  }

  return $forms;
}

This function has created an alias of user_register_form(), called custom_register_user_register_form(). What this means is that any time you call drupal_get_form('custom_user_user_register_form'), as you are doing in your hook_menu() definition, the system will return the form defined in the function user_register_form(). This is the default registration form.

By creating the alias, none of the multistep alterations you have created in custom_register_form_user_register_form_alter() will be implemented, as this function will not be called, because the form key is 'custom_register_user_register_form' and not 'user_register_form'. Therefore you will get the unaltered user registration form. If you want to alter this form for the path /registration, you will implement the function custom_register_form_custom_register_user_register_form_alter().

I know this is all a little convoluted, hopefully my explanation helps you understand it all.


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

OK, now it's clear to me why the second wasn't showing on this page "/registration" with this page arguments ''custom_register_user_register_form".

I've already made alias that's called "custom_register_user_register_form" with hook_forms.
The code is same like you already show to me.

Right now with hook_menu() I want to create the "/registration" path with page argument "custom_register_second_form", so function custom_register_second_form($form, $form_state) will be the name of the new form.

How do I call the default form here? I've tried with drupal_get_form('custom_register_user_register_form'), but nothing shows.

With dpm() I can see the fields from the first altered form, but they're not displayed.

NEW EDIT: Here's my code:

/*
 * Implements hook_menu().
*/

function custom_register_menu() {
	$items = array();
	$items['registration'] = array(
		'title' => 'Registration',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('user_register_form_second'),
		'access arguments' => array('access content'),
	);
	return $items;	
}
/*
 * Implements hook_forms().
 */
function custom_register_forms($form_id, $args) {
  $forms = array();

  // Alias the user_register_form() form
  if($form_id == 'user_register_form_second') {
    $forms['user_register_form_second'] = array (
      'callback' => 'user_register_form',
    );
  }

  return $forms;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function custom_register_form_user_register_form_second_alter(&$form, &$form_state) {

	return $form;
	
}

It still shows the altered form here. Even I unset the validations from the custom fields the Poster still see them so it doesn't work.

I've changed the title of the new form, 'cause it was confusing.

Yes, its convoluted indeed.

jaypan’s picture

Ok, I just did a bit of testing, and I found the problem.

I don't actually use hook_form_FORMID_alter() myself, I always use hook_form_alter(), and pass the form_id. Example:

function custom_register_form_alter(&$form, &$form_state, $form_id)
{
  if($form_id == 'user_register_form')
  {
    // Alter user register form here
  }
}

With the above method, any alterations to user_register_form() will not be applied to 'user_register_form_second'. As such, I assumed that custom_register_form_user_register_form_alter() would not be called, as the form ID is different. My assumption was incorrect, both custom_register_form_user_register_form_alter() and custom_register_form_user_register_form_second_alter() are called.

To fix this, you need to use hook_form_alter() instead of hook_form_FORMID_alter(). So you will move all your code in custom_register_user_register_form_alter() into the hook_form_alter() implementation I showed above (where it says 'Alter user register form here'). If you need to make any alterations to user_register_form_second(), you will also make these in hook_form_alter().


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

I've changed the hook_form_FORM_ID_alter() with hook_form_alter() and passed the form_id.
This is the first project that I've used hook_form_FORM_ID_alter(), every other was with hook_form_alter() :)

Now there's no sign of the altered form in the second form. That's great, but when I tested with the Poster it still validates from the first altered form.

I don't know if you have been working with services module and REST, but it's easy to setup. In "server" settings it has to be checked both options for json and in "Resources" I'm using alias for the user called "registration".
In my case the path endpoint is "app", so it will be called "website_url/app/registration".
Even with this setup the Poster calls the altered form, not the second form and it's not working.

Now I've tried with different approach:
I've created an alias for the multistep form and it's on "/registration" path and the default form stays on "user/register" path, I've deleted the alias on the REST setting and when the Poster calls "website_url/app/user" it register the user successfully!

The problem is most of spam bots knows this path "user/register" and will be register automatically. The point is to use some other path like "/registration", so it will be used only for JSON and mobile app that will be synchronized with the website, REST server, SOAP external web service and the external database.

Yeah, it becomes very complicated, I know.

jaypan’s picture

EDIT: Before doing the following, can you list your goals? I think there may be an easier method to do achieve your goals than the method I've listed below. What paths do you want? What should happen at these paths? What are you trying to prevent from happening, and what are you trying to make happen?

----------------------------

I do use services with a REST server (in fact it's literally what I'm working on today). I can see the current problem you have. In the function _user_resource_create() (in services/resources/user_resource.inc), the service is submitting the submitted data to the user_register_form() here:

  drupal_form_submit('user_register_form', $form_state);

Of course the problem for you is that you have overridden user_register_form() as a multistep form.

The way I see it, you have two options:

1) Create your own resource with a CREATE method
2) Override the callback for the user resource using hook_services_resources_alter()

Then in whichever method you choose, you would copy the existing callback, but change user_register_form to user_register_form_second.

I would do #2 myself. To do so, you would do the following:

function custom_register_services_resources_alter(&$resources)
{
  $resources['file']['operations']['create']['file']['module'] = 'custom_register';
  $resources['file']['operations']['create']['file']['name'] = 'custom_register.resource';
  $resources['file']['operations']['create']['callback'] = '_custom_register_user_resource_create';
}

The above tells Services to use the function _custom_register_user_resource_create() located in the file custom_register/custom_register.resource.inc. You will then copy the contents of _user_resource_create() to _custom_register_user_resource_create(), changing this line:

drupal_form_submit('user_register_form', $form_state);

To this:

drupal_form_submit('user_register_form_second', $form_state);

Note that because of this, you shouldn't even need your hook_form() implementation, since you will never be accessing the form at the path /register.


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

EDIT:
Jay, I've just edited "user_resource.inc" on line 326 with "user_register_form_second" and the poster is working great.
I think that will be right solution for this, 'cause I wont need REST for 'user_register_form' in future.
Thank you very very much to teach me about hook_forms() and lot's of ideas here.
If you have some new advice here, I'm pleased to read.
_________________________________________________________

The project is for a bank and right now I'm adding the last features. It's quite complex.
It will have a mobile application too, and that's why I'm doing this on the website, so the app can be synchronized when a special user registers there. These users are not regular like anyone, but they are clients.
There were many new features, so the mobile app will be able to get the whole data from the website.

So the task here is on the "/registration" page to be able to register with JSON and I'm passing the details to the other guy that works for the bank.
But as you see because of the altered multistep form, the Services module is trying to post the data on "user_register_form", not "user_register_form_second".
As I mention it before when I'm disabling the custom_register.module, posting data with JSON is working perfectly and it's registering a user with no problems.

OK, now I've did this, correct me If I do something wrong:

First, I'm using now hook_form_alter() with the form_id parameter, not hook_form_FORM_ID_alter().
That's for the multistep form.

Down bellow I've added this code:

/*
 * Implements hook_forms().
 */
function custom_register_forms($form_id, $args) {
  $forms = array();

  // Alias the user_register_form() form
  if($form_id == 'user_register_form_second') {
    $forms['user_register_form_second'] = array (
      'callback' => 'user_register_form',
    );
  }

  return $forms;
}
/*
 * Implements hook_menu().
*/

function custom_register_menu() {
	$items = array();
	$items['registration'] = array(
		'title' => 'Registration',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('user_register_form_second'),
		'access arguments' => array('access content'),
	);
	
	return $items;	
}
function custom_register_services_resources_alter(&$resources) {
  $resources['file']['operations']['create']['file']['module'] = 'custom_register';
  $resources['file']['operations']['create']['file']['name'] = 'custom_register.resource';
  $resources['file']['operations']['create']['callback'] = '_custom_register_user_resource_create';
}

Created an alias, so the "/registration" path is displaying the default Drupal form.
As you see I've added your code for altering the services.
After that I've created a new file called: custom_register.resource.inc.
Inside that file I've copied all the contents from "user_resource.inc" and on line 326 I've set
drupal_form_submit('user_register_form_second', $form_state);

I've tried again with poster, it doesn't work.

I'll write this again. I've tried to do opposite, to set the multistep form on the "/registration" path, so "user/register" will be displaying the default form. The poster registers a new user with no problem, but I'm afraid "user/register" is way too known path for the spam bots.
That's why I need not so regular path. Later I might change the "/registration" to something different, of course. After all that path will be only for registering the users with JSON, not for anyone.

WorldFallz’s picture

I'd never thought to do it this way-- brilliant!

jaypan’s picture

Thanks WF!

You can look at my Fancy Login module to see how I've used this technique to customize all three user/* forms and ajaxify them. I actually had to look at it to remember how I did it :)


Contact me to contract me for D7 -> D10/11 migrations.
Or anything really. I'm friendly, and open to work or conversation.
satvision83’s picture

Congrats for your great module!

tsramkumar’s picture

satvision83  

I see that you have done exactly what i am trying to do. Perhaps i could learn from you expereine. I am newbie with Drupal - we have api management tool from APIGEE that we are installing and it uses Drupal for the developer portal.

We are tying to customize the default user registration form to capture more fields and also call some webservcie to post the data to our internal app (similar to your validation service) before letting drupal to create the user.

I realize that i need to use the hook_form_register_form_alter() to customize it to add new fields. I am going through the documentation - The fact that i am learning druapl and PHP is slowing me down - is there anyway you can share template of your customized user registration form with the multi step and the web services calls for me to just get an idea?

anybody’s picture

Thank you for this helpful article. I recently wrote an article about decorating existing forms in Drupal 7, like for example the core user_register_form. This might be helpful for some of you, if you'd like to create a separate registration form containing most of the user_register_form fields and logic, keeping the default form unaltered as-is. You can find the blog post here: https://julian.pustkuchen.com/en/drupal-7-decorate-wrap-existing-form-se...

http://www.DROWL.de || Professionelle Drupal Lösungen aus Ostwestfalen-Lippe (OWL)
http://www.webks.de || webks: websolutions kept simple - Webbasierte Lösungen die einfach überzeugen!
http://www.drupal-theming.com || Individuelle Responsive Themes