Hello. How do I alter the structure of the html inside the form tags of the user login form block in Drupal 7? I already got block--user--login.tpl.php to work which outputs the whole login form with "print $content". How do I output each element so I can control/re-arrange it. Thanks.

Comments

Xomby’s picture

I'm in the same boat, but need to get this solved in the next day or two... so if I figure it out, I'll post back here.

[edit]: OK, after banging my head against the internet for a while, this is what I came up with:
*Note- I think this is primarily for php-template engine themes, but YMMV.

  1. Find the template.php file for the theme you're working on. Should be in the root of the theme folder for your theme
  2. Assuming the "yourthemename_hook" function does NOT exist, at the bottom of your template.php file insert the following code:
    function yourthemenamehere_theme(&$existing, $type, $theme, $path) {
       $hooks['user_login_block'] = array(
         'template' => 'templates/user-login-block',
         'render element' => 'form',
       );
       return $hooks;
     }
    function yourthemenamehere_preprocess_user_login_block(&$vars) {
      $vars['name'] = render($vars['form']['name']);
      $vars['pass'] = render($vars['form']['pass']);
      $vars['submit'] = render($vars['form']['actions']['submit']);
      $vars['rendered'] = drupal_render_children($vars['form']);
    }
    

    save and close!

  3. Now create a directory inside your theme's directory called "templates" (assuming it doesn't exist already)
  4. Then create a php file inside that folder called: "user-login-block.tpl.php"
  5. Inside this file, put the following code:
    <div id="user-login-block-container">
      <div id="user-login-block-form-fields">
        <?php print $name; // Display username field ?>
        <?php print $pass; // Display Password field ?>
        <?php print $submit; // Display submit button ?>
        <?php print $rendered; // Display hidden elements (required for successful login) ?> 
      </div>
      <div class="links">
        <a href="/user/register">Create an Account</a> | <a href="/user/password">Forgot Password</a>
      </div>
    </div>
    
  6. CRITICAL STEP as an admin on your website, navigate to "admin/config/development/performance" and click "clear all caches" -- if you don't, then these changes won't display on your website
  7. Modify the html markup inside "user-login-block.tpl.php" to your heart's content, and don't forget that you can also add those css classes to your main style.css file (AFAIK)


I realize that to many this may seem like a very trivial thing to accomplish, however I found that while many folks had a good idea of the solution, nobody had a 100% thorough walk-through for this. Hence this "compilation" of steps here.
I'd like to thank the authors of posts that WERE helpful in resolving this, especially "Kevin" and "Nikit" over at http://drupal.stackexchange.com/questions/9505/user-login-block-does-not... Minus the "clear caches" bit, definitely the most comprehensive and basic example of the solution here.

SandeepPatel’s picture

Just thought I should say,
yeah I've been looking all over and just as you said: "nobody had a 100% thorough walk-through for this"
your post was very helpful =]

agoradesign’s picture

Excellent tutorial! This is very useful if you want to restyle the login form block completely.

If you want to do some smaller changes like changing the size of the input field or want to change the way the links are displayed, it would be easier to use the hook_form_alter() function and adjust the values in the form variable. To change the sizes of both name and pass field and change the markup of the links, I would use this code:

function yourthemenamehere_form_user_login_block_alter(&$form, &$form_state, $form_id) {
  $form['name']['#size'] = 30;
  $form['pass']['#size'] = 30;
  $markup = l(t('Forgot your password?'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
  if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
    $markup .= ' ' . l(t('Sign up'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'), 'class' => 'register-link')));
  }
  $markup = '<div class="clearfix">' . $markup . '</div>';
  $form['links']['#markup'] = $markup;
}
Augusto182’s picture

Hello.

I put exactly you funcition in my template file (o course, with my theme name)
and nothing happen

I clear the cache, and nothing happen too

I try to do this via mymodule_form_alter(&$form, &$form_state, $form_id) ...
and nothing happen

What is wrong whit my code?, please... i have 4 hour in this issue...

HELP please

TelFiRE’s picture

Those instructions do not work in Drupal 7. The OP must have had some module or something to make it work and didn't realize it.

agoradesign’s picture

Hi,
sorry to Augusto182 that I have overseen his question to my post. I guess I always just looked at the bottom of the page...

There is definitely no other module involved in my solution. I've just tried it again in a complete different project and it still works for me. Here the steps I've done:

  1. went to admin/structure/block and made the user login block visible
  2. opened my theme's template.php file and created the hook function: as the theme's name is "humer", my function is called "humer_form_user_login_block_alter"
  3. as described above, I've modified the length of username and password field and modified the markup text below the input fields to 'xxx' like that:
      $form['name']['#size'] = 40;
      $form['pass']['#size'] = 30;
      $form['links']['#markup'] = 'xxx';
  4. Clear page cache!!
marian170876’s picture

Just thought I should say,
yeah I've been looking all over and just as you said: "nobody had a 100% thorough walk-through for this" Parchet

zkent’s picture

I, too, chose this method. You can also add to the existing fields by altering them, such as adding an attribute to the password field:

$form['pass']['#attributes']['autocomplete'] = 'off';
omrmankar’s picture

You can this code alter login page this to link are created automattically

$markup = l(t('Forgot your password?'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
  if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
    $markup .= ' ' . l(t('Sign up'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'), 'class' => 'register-link')));
  }
  $markup = '<div class="clearfix">' . $markup . '</div>';
  $form['links']['#markup'] = $markup;

Best regards,

Omprakash Mankar
Senior Drupal Developer

anenkov’s picture

Great tutorial.
Although I'm really a beginner I just wanted to add that instead of

$vars['submit'] = render($vars['form']['actions']['submit']); 

some might use

$vars['actions'] = render($vars['form']['actions']); 

and later in the tpl file print $actions; // Display actions div
I preferred that to keep the html structure. Don't know if it's because I use Omega but otherwise form-actions was rendering empty at the end.

bbinkovitz’s picture

This is a very useful walkthrough but I'm having trouble getting it to do anything. I'm using a subtheme of Omega and I'm wondering if I'm putting the .inc file in the wrong directory, or if there's some other code that needs to be included if it's in its own .inc file rather than pasted into a tpl file?

jienckebd’s picture

Thanks Xomby -- this was very helpful and saved me some time researching it on my own.

Jeff Burnz’s picture

Well this is one way of achieving an end result, but not quite the Drupal way.

This is a form, and the real way to deal with this is to modify the form, using hook_form_alter() and dealing directly with the structured data. What this does is actually flatten it into strings in preprocess which means any other module trying to alter the data structure AFTER preprocess can't do it.

So while this will work for some use cases, its going to break many others.

You can use this hook to modify the form:

/**
 * hook_form_FORM_ID_alter()
 * Modify the User Login Block Form
 */
function ThemeName_form_user_login_block_alter(&$form, &$form_state, $form_id) {
   dsm($form); // output in Krumo with the Devel module
}
Xomby’s picture

Seriously? If the "Drupal Way" is to be vague and unhelpful, I'd rather do it wrong.

Jeff Burnz’s picture

Just because you don't get it does not mean other users won't use some initiative and find out how. Its all out there, well documented, in the API, in many web pages and books. Many users only need push in the right direction, that is what I provided.

Please be civil, there is no need to be rude, we're trying to have fun here man :)

agoradesign’s picture

@Xomby:
Jeff's right. For one how's familiar with Drupal's hook system, his short hint is very helpful. If you don't have so much Drupal experience by now, such answers may be hard to understand. The beginning is always hard. But if you want to do some deeper customization, you won't get around to implement some hooks, especially theme, preprocess and alter hooks like that.

Two things may help you in this case: first, look at my comment above (http://drupal.org/node/1167712#comment-5492710) where I've implemented exactly this hook doing some modifications.

And second, install the Devel module (http://drupal.org/project/devel). The dsm() function Jeff has used in his reply is a function from Devel module. You can put dsm() or dpm() anywhere in the code to print out debugging info you need. If you use dpm($form); in this example, you get the $form parameter pretty-printed in the message section of your Drupal page. Devel is a MUST-HAVE for any Drupal coder out there.

TelFiRE’s picture

It really doesn't make a difference how long you've been using Drupal. Sometimes you have to look something up, no one memorizes every template file. When you respond to a topic like this and refuse to give the answer, instead only giving hints of how it might theoretically work, you are making it extremely hard for people to look up the proper way of doing something. There are a dozen other topics out there like this, with someone who gets all indignant because they think the OP didn't read documentation or try to Google it. Well let me tell you, I've been googling for 4 hours and all I can find are self-righteous posts about how they are being helpful and we should be grateful for anything they can give. There is NO documentation to be found on how to actually accomplish the goal. That is not helpful, it is just creating cyberjunk and obfuscating the path to the information. We owe you nothing.

Jeff Burnz’s picture

You didn't stage your goal. As far as this tread goes, all the docs exits, and they are very, very good. I bet I can find loads of tutorials on hook_form_alter() in five minutes, here, let me try... hmmm...

http://api.drupal.org/api/drupal/modules%21system%21system.api.php/funct...
http://websmiths.co/blog/very-introduction-drupals-hookformalter
http://drupal.org/node/651106
http://alligatorsneeze.com/drupal-alters-and-overrides-hookformalter

Not enough, hmmm, how about some video...

http://blip.tv/mustardseed-media-hd/mustardseed-hd-53-5900123
http://blip.tv/dmitrig01/drupal-dojo-hook_form_alter-hook-series-217170

You want an "answer" but someone has to write that answer, and much of the time people with the knowledge and time (rare) will produce very generic docs because that way we teach you how to fish - get it?

And no, I am not indignant, I am time pressured, and lets be frank, its replies like yours that would make me indignant, I mean hey, why bother, why not just write NO DOCS at all, never reply to any post because there is always someone who thinks you didn't do enough to satisfy them. Talk about self entitlement!

agoradesign’s picture

...just a few words about "self-righteous posts" in here. One thing that I hate really very much in any forum are these typical "use the search function" answers to any question. You can find these replies in nearly any tech-related forum. I really hate this because if the answerer already knows about that this question was already discussed in another thread, he still can refer to the search functionality in his answer in a polite way, but at least put the link to the existing thread in his answer as well.

It's very seldom that you find forums without such posts. Well, this place here is such an exception. I've NEVER seen this here. You always get at least small hints. And if you read this thread calmly, you will see that Jeff's first answer was everything but self-righteous. This was very useful information, which of course needs some background knowledge. The first impolite and unhelpful post was Xombi's reaction to Jeff's. If he'd have written something like "Sorry, I absolutely don't have a clue what do you with that function. Can you explain it to me further or post some helpful links for me?" I'm 100% sure that Jeff or any other person would have explained it more detailed. But just writing "Seriously? If the "Drupal Way" is to be vague and unhelpful, I'd rather do it wrong." isn't a proper reaction. But as you can see, you still get some help anyway...

So, not only the answerer, but also the asker has to carry some duties. Just criticizing around doesn't help anyone either. If something disturbs you, help making it better. If you feel something is undocumented or some questions are unanswered and you have found a solution - especially if you needed hours for that - then share your knowledge! Post an answer in the forum, post a comment to the function in the API doc,... This is how such a community works!

Jeff Burnz’s picture

I'm 100% sure that Jeff or any other person would have explained it more detailed.

You are 100% correct.

But as you can see, you still get some help anyway...

Again, correct, although my post echos my pissed-off-ness at being attacked, twice, I provided those links for others.

Thanks for your well reasoned post, all very good points, especially the very last line :)

agoradesign’s picture

Well-fitting to this "alter login form" topic there's a new tutorial on Drupal Watchdog on how to customize the login form: http://drupalwatchdog.com/2/1/dedrupalize-login-form

reign85’s picture

Hook_form_alter is the best and faster way, you can use #weight property to sort elements like this:

if($form_id == 'user_login_block') {
$form['name']['#weight']  = -10;
$form['pass']['#weight']  = -9;
}

add html with ['#suffix'] and ['#prefix'] and many many more

Jeff post is right

Sheldon Rampton’s picture

It might help to note that Drupal's User module creates both a user_login form and a user_login_block form, so altering the user_login_block form won't change the result you see on the user login page. So, when I tested this hook in a custom module on the /user page, it didn't work:

function mymodule_form_user_login_block_alter(&$form, &$form_state, $form_id) {
  drupal_set_message('hi');
}

but this (without the "_block") worked:

function mymodule_form_user_login_alter(&$form, &$form_state, $form_id) {
  drupal_set_message('hi');
}

Also, depending on how you have caching set up on your site, you may need to clear caches before you see the change.

----------------
Customer Support Engineer, Granicus
https://granicus.com

cartagena’s picture

Hi. This is in response to the very first comment with instructions for the preprocess function and user-login-block.tpl.php file.

Thanks for this, I did it and it works...except the necessary print $rendered also prints the Create an Account and Forgot Password links so they appear twice. How do I get around that print $rendered? Thank you.

Web Building and Design

arlingtonvoicellc’s picture

I would also be interested in knowing the solution to this problem. The original poster's solution works except that the $render variable spits out the register/password reset links. I'm sure you could target this with CSS display property. But I would rather it not print twice. Any ideas?

VirtualMitra’s picture

Your instructions were very helpful. I was able to implement them almost right away. The only minor thing I had to adjust was the template location in the user_login_block hook. I was customizing a theme we purchased and that theme has its templates in a folder named "tpl" instead of "templates". So, I had to change the hook to:

$hooks['user_login_block'] = array(
     'template' => 'tpl/user-login-block',
     'render element' => 'form',
);

A very minor thing, but thought it might help to point this out if it doesn't work right away.

Thanks so much for the concise and clear instruction!

~VM

devdesigner’s picture

I was looking for this for ages! Thanks!

d0tcom’s picture

I tried this but it hasn't worked out for me.. I keep getting an variable undefined error and the fields never show up.

kamescg’s picture

Thank you for posting your solution. It's always great to have go-to Drupal solutions available.

I noticed a "gotcha" with the Links though - the global setting/variable to decide whether people are allowed to register is skipped/ignored.

It would be probably considered best practice to include the links field in a hook_form_alter() function, so Administrators can control the settings from the appropriate settings page, instead of hard-coded in a template.tpl.php file.

<div id="user-login-block-container">
  <div id="user-login-block-form-fields">
    <?php print $name; // Display username field ?>
    <?php print $pass; // Display Password field ?>
    <?php print $submit; // Display submit button ?>
    <?php print $links; // Display submit button ?>
    <?php print $rendered; // Display hidden elements (required for successful login) ?> 
  </div>
</div>
function THEME_preprocess_user_login_block(&$vars) {
  $vars['name'] = render($vars['form']['name']);
  $vars['pass'] = render($vars['form']['pass']);
  $vars['links'] = render($vars['form']['links']);
  $vars['submit'] = render($vars['form']['actions']['submit']);
  $vars['rendered'] = drupal_render_children($vars['form']);
}

function THEME_form_user_login_block_alter(&$form, &$form_state, $form_id) {
  $items = array();
  if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
    $items[] = l(t('Create Account'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'))));
  }
  $items[] = l(t('Request Password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
  $form['links'] = array('#markup' => theme('item_list', array('items' => $items)));
  return $form;
}
hockey2112’s picture

Awesome, thanks!

yasinn’s picture

this help lot.

drupy_moe’s picture

Thank you so much. Out of the ten or so other tutes I read, yours was the one that explained it indepth enough for me to do it!!! In case it's not obvious I'm giving you major props.

Thanks again.
From Australia
Moe

Yusleidy’s picture

How can I include the containers div THML structure user-login block with firebug I can see the structure, but not to find the file that contains this html to modify it. Thank you for your help.

er.pushpinderrana’s picture

The easiest way to find all of the possibilities for naming template files is to put print_r($variables['theme_hook_suggestions']); at the end of your theme's template_preprocess_HOOK().

You can override block--user--login.tpl.php template here.

Pushpinder Rana #pushpinderdrupal
Acquia Certified Drupal Expert

drupal a11y’s picture

And another question from a non-php-themer: How to simply add a "?destination=URL" to the request new password link?

Currently I totally replace the markup within the user login box like this:

$newlinks = l(t('Forgot your password?'), 'user/password?destination=homepage', array('attributes' => array('title' => t('Request new password via e-mail.'))));
$form['links']['#markup'] = $newlinks;

Problem 1: Drupal endcodes the "?" and the "=", also when I enter already them encoded (user/password%253Fdestination%253Dhomepage) I always get "user/password%253Fdestination%253Dhomepage" in the browser URL when clicking the link.

Problem 2: The links also appears within a message when the user has entered a wrong password with an already attached parameter username (a href="/user/password?name=username").
How/Where do I change this? Is there a way to attach the URL-Parameter just in one place to make sure it´s everywhere where the link to the password reset form page is shown?

BTW: also a very cool tutorial: http://drupalwatchdog.com/volume-2/issue-1/de-drupalizing-login-form

------
SOLVED Problem 1:

$newlinks = l(t('Request new password'), 'user/password', array('query' => array('destination' => 'homepage', 'foo' => 'bar'), 'attributes' => array('title' => t('Request new password via e-mail.'))));

------
SOLVED Problem 2: via translations and adding "&destination=homepage" to the URL.

ak_23’s picture

hello, this tutorial is very helpful and awesome. I used it for changing the user login block. However I want to give a link for sign up. And when cursor hovers on the link I want the user log in block to show up. Can anyone help me?

Jaypan’s picture

Here's a tutorial I wrote based on that, though you'll have to create your own popup: http://www.jaypan.com/tutorial/drupal-7-ajaxify-form-popup

ak_23’s picture

thank you so much!!

klidifia’s picture

This is how I've set the destination query parameter for login and register links, in theme template.php:

function mytheme_menu_link(&$variables) {
  if (($element['#href'] === 'user/login' || $element['#href'] === 'user/register') && $_SERVER['REQUEST_URI'] != '/') {
    $element['#localized_options']['query'] = array('destination' => check_plain(substr($_SERVER['REQUEST_URI'], 1)));
  }
}