HowTo: Create a custom user login bar

In this how-to, I will show you how to create a user bar, that:

  • Displays a compact, nice user login form that can fit in one line.
  • Once user is logged in, It will display a welcome message and a couple of helpful links.

This is the final result we will be trying to achieve:

 the login form

 the welcome message after a successful login

As you can see, this how-to is written for Garland theme, the default theme for Drupal 5. But I'm pretty sure it can be easily used in any other theme.

Decide on the approach..

Drupal is very powerful, it's so flexible that you have many ways to do things. That's why a 5 minutes of thinking is very recommended before we get our hands dirty.

The solution I thought of will work like this:

  • We will write a separate function, will call it garland_user_bar(). It decide, and return HTML for the login form, or the welcome message. It will contain the necessary code to check which one it should return.
  • In page.tpl.php, we will place a call to this function somewhere where it gets displayed in the top.
  • Back to the function, If it should show a login form, then it will just borrow the default login form, we will not try to create our own. Why? Because likely, the default form, while it looks rectangular in shape and fits in many lines, by checking view-source I could tell that we can style it using CSS just like in the screenshots without changing anything in the form itself.
  • If the user is already logged in, the function will instead return a nice welcome message, and some helpful links.
  • Now we have the logic, after that we will write some CSS to style it, to change how it looks.

Oh, let's place the function in page.tpl.php first

Let's do this first, let's place a call to garland_user_bar() (even if it doesn't exist yet), that's better so while we are working on garland_user_bar() we can hit 'refresh' in our browser to see the result any time we want to.

  1. Open themes/garland/page.tpl.php, at around line 16 you will find:
    <div id="navigation"></div>

    This <div> is the container for the top bar in Garland theme, so let's add our call to garland_user_bar() there..
    <div id="navigation"><?php print garland_user_bar() ?></div>

Write the necessary code, garland_user_bar()

Before giving you the whole piece of code, I will explain how we go for writing this..

  1. Open themes/garland/template.php, at the of the file, let's start an empty function.. we will add code inside it in the next steps..
    function garland_user_bar() {
    }
  2. We will need access to the global $user variable, which Drupal makes available for us, and it's going to tell us whether a user is logged in already or not. So first line we add inside the function is going to be:
    global $user;
  3. I will also define a new variable called $output, so I can keep adding whatever I want to that variable, and at the end, I will return its contents to be placed on where we placed the function itself in page.tpl.php. Alright, So that's extra line..
    $output = '';
  4. I want to check if the user is logged in or not. We do this by checking if $user->uid exists or not, because when Drupal logs a user in, it will automatically change $user->uid to reflect the user id, which is always > 0. That's what we need to add..
    if (!$user->uid) { // if user is NOT logged in..

    Notice the ! here, it translates to NOT, so this code means "if not $user->uid" which means if the user is NOT logged in already...
  5. Alright, if the user is NOT logged in.. do what? Give him/her a login form! Remember what we concluded in the deciding step? We said that the default login form is okay, it just needs a bit of CSS love, no need to write another form. So let's just use that form.. this is how..
    $output .= drupal_get_form('user_login_block');

    drupal_get_form() is a very handy function, you give it the name of a form, and it returns it to you, ready for user consumption.
  6. Okay, now we handles the case when user is not logged in, but what if they are already logged in?? let's continue our code to handle this case.. add:
    }
    else {

    This allows us to handle the other case.
  7. If user is logged in already, we want to display a welcome message, and a few helpful links. Let's see how we display the welcome message first..
    $output .= t('<p class="user-info">Hi !user, welcome back.</p>', array('!user' => theme('username', $user)));

    t() function encapsulates the whole message, so it can be translated. theme('username', $user) is what you should always use for displaying usernames.
  8. Well, now the links.. I want two links, "Your accont" and "Sign out". Drupal has a function called theme_item_list() that you can give a list of items, and it will return a HTML <ul> list out of them. "Your account" should link to user/<uid>, and "Sign out" should link to logout. These are standard URLs in Drupal. Alright, let's do this..
    $output .= theme('item_list', array(
      l(t('Your account'), 'user/'.$user->uid, array('title' => t('Edit your account'))),
      l(t('Sign out'), 'logout')));
    }

    Oh well, new function here, l(), this one is used to generate links. You simply pass it the title and the URL, and it will generate a sound and safe <a href="..." title="..">....</a>.
  9. Now one more last thing before we exit and return the output, I want to encapsulate all this in a <div id="user-bar">...</div>. Why? because it will make it easier for us to write the CSS that will apply to this bar. How I do this? Easy, using the . (dot) operator. Like this:
    $output = '<div id="user-bar">' . $output . '</div>';
  10. Oh, now everything is ready, let's return the HTML we generated..
    return $output;

So what's the final overall function? this is it..

<?php
function garland_user_bar() {
  global
$user;                                                               
 
$output = '';

  if (!
$user->uid) {                                                          
   
$output .= drupal_get_form('user_login_block');                           
  }                                                                           
  else {                                                                      
   
$output .= t('<p class="user-info">Hi !user, welcome back.</p>', array('!user' => theme('username', $user)));
 
   
$output .= theme('item_list', array(
     
l(t('Your account'), 'user/'.$user->uid, array('title' => t('Edit your account'))),
     
l(t('Sign out'), 'logout')));
  }
   
 
$output = '<div id="user-bar">'.$output.'</div>';
     
  return
$output;
}
?>

Styling time!

Now that login form and welcome message are displayed correctly for users, we only need to make them look better. That's where CSS comes in.

  1. First, let's create a new file to place all our user bar CSS into, that way we keep organized. So create a new file themes/garland/user_bar.css.
  2. Now the file exists, but nobody knows about it, we should modify page.tpl.php to include it. How? Open themes/garland/page.tpl.php and AFTER line 11 add this:
    <style type="text/css" media="all">@import "<?php print base_path() . path_to_theme() ?>/user_bar.css";</style>

    That will include our user_bar.css file.
  3. Let's write the magical CSS inside user_bar.css
    Notice that it's not too big really. It's just the comments consuming too much space :p.
    /*
    this will expand the default garland bar, make it bigger so our form and message can fit in.
    */
    #navigation {
      height: 3em;
    }
     
    /*
    by default, the default form adds some surrounding space, this cancels it
    */
    #navigation div.form-item,
    #navigation div.content {
      margin: 0; padding: 0;
    }
     
    /*
    this adds some space in top and bottom, so anything inside can look vertically
    centered
    */
    #user-bar {
      padding: .65em 0;
    }

    /*
    by default, fields labels tries to reserve a whole line for itself, this
    cancels that and and sends it to the left.
    it also adds some space on the right and left of the label to look easy on
    the eye.
    */
    #user-bar label {
      float: left;
      margin-left: 10px;
      margin-right: 2px;
    }

    /*
    inputs too, they try to reserve a whole line for itself, this
    cancels that and sends it to the left
    */
    #user-bar input {
      float: left;
    }

    /*
    I don't like the required * (asterisks), so I hide them.
    */
    #user-bar span.form-required {
      display: none;
    }

    /*
    the form submit button, it's so tight so we expand it a bit, and give it some
    free space around.
    */
    #user-bar input.form-submit {
      margin-top: -1px;
      margin-left: 10px;
      padding: 0 .5em;
    }

    /*
    now this is for the links list, lists by default tries to reserve a whole line
    also they add space surrounding them. we cancel all that and send the list
    to the right
    */
    #user-bar div.item-list ul {
      float: right;
      margin: 0; padding: 0;
      margin-right: 10px;
    }

    /*
    remember, stylign above was for the whole list, now for each item,
    we all know each item in the list by default exists on a separate line, also
    has that bullet on the left. we cancel all that. and makes all items sit beside
    each other
    */
    #user-bar div.item-list ul li {
      float: left;
      background: none;
      margin: 0 5px;
      padding: 0 10px;
      border: 1px solid #b8d3e5;
    }

    /*
    this is the "Hi user, welcome back message".
    by default <p> tries to exist on a separate line, we cancel that.
    also by default <p> has some surrounding space, we cancel that too, and give it
    only space on the left.
    */
    #user-bar p.user-info {
      float: left;
      padding: 0;
      margin: 0 0 0 10px;
    }

The end..

That was it, A solution that works and doesn't override or modify any existing code. And since it doesn't have any custom hardcoded HTML for forms, that means it will rarely need to be maintained after new releases of Drupal.
Post any improvements you can think of in the comments, and I will add them here.

A great way to further

ktonini - January 5, 2007 - 02:27

A great way to further customize each of the parts of the login form would be to change number 5 from

$output .= drupal_get_form('user_login_block');

to for example

$output .= drupal_get_form('custom_login_block');

and then in template.php add

function custom_login_block() {
  $form = array(
    '#action' => url($_GET['q'], drupal_get_destination()),
    '#id' => 'user-login-form',
    '#base' => 'user_login',
  );
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Username'),
    '#maxlength' => 60,
    '#size' => 15,
    '#required' => TRUE,
  );
  $form['pass'] = array('#type' => 'password',
    '#title' => t('Password'),
    '#maxlength' => 60,
    '#size' => 15,
    '#required' => TRUE,
  );
  $form['submit'] = array('#type' => 'submit',
    '#value' => t('Log in'),
  );
  $items = array();
  if (variable_get('user_register', 1)) {
    $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
  }
  $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
  $form['links'] = array('#value' => theme('item_list', $items));
  return $form;
}

The action does not supply error messages

JoepH - April 24, 2007 - 22:30

The form action is not correct. If an error occurs, for example omitted a required field, then the error message is not displayed.

'#action' => url($_GET['q'], drupal_get_destination()),

It is strange because the original drupal_get_form('user_login_block'); has the same action!
http://api.drupal.org/api/5/function/user_login_block

I haven't found out yet what the right action should be.

-----------------------------------------
CompuBase, websites and webdesign

use hook_form_alter()

3ejnt9sOwPgMAAdxCVdS - May 22, 2007 - 16:08

Creating a custom login block will not call the validation for the user_login in the correct order -- so the error messages are delayed until page refresh, regardless of the action.

Instead of creating a new custom block; adjust the form via hook_form_alter() in your module. This keeps the user_login_block functioning as intended.

To change the action / adjust the links:

<?php
function YOURMODULENAME_form_alter($form_id, &$form) {
    global
$form_values;
    switch (
$form_id) {
        case
'user_login_block':
           
$form['#action'] = url($_GET['q'], drupal_get_destination());
           
$items = array();
            if (
variable_get('user_register', 1)) {
               
$items[] = l(t('Register'), 'user/register', array('title' => t('Create a new user account.')));
            }
           
$items[] = l(t('Forgot Password?'), 'user/password', array('title' => t('Request new password via e-mail.')));
           
$form['links'] = array('#value' => theme('item_list', $items));
            break;
    }
}
?>

Also, it seems to work better when you create a block via the block administration than just having it in the template

Login_Destination Module

caramelson - December 6, 2007 - 17:32

If you're using the Login_Destination module, replace

$form['#action'] = url($_GET['q'], drupal_get_destination());

with

$form['#action'] = login_destination_get_destination();

--
Fredric

A little twist on this great

v1nce - September 30, 2007 - 00:36

A little twist on this great HOW-TO to get the best of both worlds. In order to get the error message to display if a user enters incorrect login info, add the call to the YOURTHEMENAME_user_bar() function in a block.

<?php
 
// output custom user bar login function defined in template.php file
 
print YOURTHEMENAME_user_bar();
?>

Then to display this above the header info add a region to your template file. In this example I'm adding the region "above_header"

<?php
function YOURTHEMENAME_regions() {
  return array(
   
'right' => t('right sidebar'),
   
'content' => t('content'),
   
'header' => t('header'),
   
'footer' => t('footer'),
   
'above_header' => t('above header'),
  );
}
?>

Finally, add this code in your page.tpl.php where you want to output the login form:

<div id="custom_user_login">
  <?php print $above_header; ?>
</div>

-----------------------------------------------------------------
Petafoo - We Love Animals

Hi V1nce, I tried

Fayna - January 18, 2008 - 05:49

Hi V1nce,

I tried implementing your idea for the horizontal user bar, and it works great when a user is logged in. However, when a user logs out, the login form is how it normally is, and aligned vertically. Is there something in the CSS I need to change? What have I done wrong?

Thank you for any help you can give!

Alternative for Drupal 5: login pwd block on header or sidebar

pushkar - February 1, 2007 - 23:06

I wanted to add a login / pwd link on the header and found an awesome link for login blocks - that might allow a user to place the login blocks or refer to the login blocks more cleanly.

http://nossdutytask.com/node/228

I dont want to 'copy' everything but here is a quick snapshot from that link - create a block with these.

<?php global $user; ?>

<?php if ($user->uid) : ?>

Welcome: <?php print l($user->name,'user/'.$user->uid); ?> |

<?php print l("logout","logout"); ?>

<?php else : ?>

For full access and to post messages and comments please Login/Register: <?php print l("Login/Register","user/login"); ?>

<?php endif; ?>

or if you wanted to go all the way, do this:


<?php global $user; ?>
<?php if ($user->uid) : ?>
<span class="login_text">Welcome, </span> <b><?php print ($user->name); ?> </b> <br>
<?php print l("Your Account",'user/'.$user->uid); ?> |
<?php print l("Log-Out","logout"); ?>
<?php else : ?>
<form action="/drupal/?q=user&amp;<?php print drupal_get_destination() ?>" method="post" id="user-login">
Username:

<br>

<input type="text" maxlength="60" name="edit[name]" id="edit-name" size="15" value="" class="form-text required" />
<br>
Password:

<br>

<input type="password" maxlength="" name="edit[pass]" id="edit-pass" size="15" class="form-text required" />
<br>
<input type="submit" name="op" value="Login" class="form-submit" />
<br>
<a href="/drupal/?q=user/register" title="Create account">Create account</a>
<br>
<a href="/drupal/?q=user/password" title="Retrieve lost password"> Retrieve lost password</a>
<input type="hidden" name="edit[form_id]" id="edit-user-login" value="user_login" />

</form>

<?php endif; ?>

IN my block, i enclosed the php code in a class as described above
e.g. <div id="custom_login_bar"></div>

I put the block in the header, and in the stylesheet, i added

.custom_login_bar {
   float: right;
}

Drupal 5.1

jpolt - April 11, 2007 - 23:43

How can I get this to work on Drupal 5.1?

Can't see the line <div id="navigation"></div >in the file page.tpl.php.

use header-region instead

spazfox - April 14, 2007 - 03:28

For 5.1, do this:

CHANGE:
Line 17, <div id="header-region" class="clear-block"><?php print $header; ?></div>

TO:
<div id="header-region" class="clear-block"><?php print garland_user_bar() ?><?php print $header; ?></div>

To return the user to the same page after logging out

Bevan - May 3, 2007 - 10:01

To return the user to the same page after logging out, change

<?php

      l
(t('Sign out'), 'logout'),
?>

to
<?php
      l
(t('Sign out'), 'logout', array(), drupal_get_destination()),
?>

Add additional parentheses)

rp_praveen - September 28, 2007 - 20:26

I needed to two additional parentheses added to the code to get working. Thanks for everyone on this thread, worked out Great!

l(t('Sign out'), 'logout'),

to

l(t('Sign out'), 'logout', array(), drupal_get_destination())));

www.sligoyouth.com

Problems with 'create account' and 'request password' forms

brettsmith - October 2, 2007 - 00:46

This has been a great tutorial but upon testing I found an error when using the custom login bar with other user forms (i.e 'create new account' and 'request new password').

For example, when you click on the 'request new password' link from the custom login bar, the login bar still appears at the top of the password form/page but if you use the standard user login form you'll notice it disappears when you access the 'create account' or 'request password' forms.

It would be good if the custom login bar disappeared when you accessed the other user forms, but I can't think of a way to do this.

You could add your code

v1nce - October 31, 2007 - 02:58

You could add your code inside an if statement so the login bar does not show on these pages. For instance, to not show on the 'create new account' page add:

<?php
if (arg(0) != 'user' && arg(1) != 'register') {
 
// login bar code here
}
?>

-----------------------------------------------------------------
Petafoo - We Love Animals

Outdated

seaneffel - February 1, 2008 - 15:31

This article seems dated and should be updated to reflect the changes. Instruction #1 is incorrect since there is no longer a DIV named "navigation" in the page.tpl.php file.

I wonder if the template.php code portion is also out of date? When inserting this into my template.php file I get the wsod. I've tried various basic syntax changes just to see if I am in error but beyond that I don't understand why it doesn't work as described.

<?php
function garland_user_bar() {
  global $user;                                                               
  $output
= '';

  if (!$user->uid) {                                                          
    $output
.= drupal_get_form('user_login_block');                           
 
}                                                                           
 
else {                                                                      
    $output
.= t('<p class="user-info">Hi !user, welcome back.</p>', array('!user' => theme('username', $user)));
 
    $output
.= theme('item_list', array(
      l(t('Your account'), 'user/'.$user->uid, array('title' => t('Edit your account'))),
      l(t('Sign out'), 'logout', array(), drupal_get_destination())));
  }
   
  $output
= '<div id="user-bar">'.$output.'</div>';
     
 
return $output;
}
?>

More up-to-date method

wheelercreek - April 2, 2008 - 22:55

I had to modify this for it work. Added this function to the module code I was implementing:

<?php
function MODULENAMEHERE_form_alter($form_id, &$form) {
    global
$form_values;
    switch (
$form_id) {
        case
'user_login_block':
           
$form['#action'] = '/?q=user&' .drupal_get_destination();
           
$form['#method'] = 'post';
           
$form['form_id'] = array(
            
'#type' => 'hidden',
            
'#default_value' => 'user_login'
            
);
           
$items = array();
            if (
variable_get('user_register', 1)) {
               
$items[] = l(t('Register'), 'user/register', array('title' => t('Create a new user account.'))). ' | &nbsp;';
            }
           
$items[] = l(t('Forgot Password?'), 'user/password', array('title' => t('Request new password via e-mail.')));
                   
           
$form['links'] = array('#value' => theme('item_list', $items));
            break;
    }
}
?>

The login form needs a hidden field to be passed in as well (at least for Drupal 5.6), so I needed to add that. The action has also been updated. Then I dropped this function into the template.php:

<?php
function phptemplate_user_bar() {
    global
$user;                                                              
   
$output = '';

    if (!
$user->uid) {                                                         
   
$output .= drupal_get_form('user_login_block');                          
    }                                                                          
    else {                                                                     
   
$output .= t('<p class="user-info">Hi !user, welcome back.</p>', array('!user' => theme('username', $user)));

   
$output .= theme('item_list', array(
     
l(t('Your account'), 'user/'.$user->uid, array('title' => t('Edit your account'))),
     
l(t('Sign out'), 'logout')));
    }

   
$output = '<div id="user-login-form">'.$output.'</div>';
    
    return
$output;
}
?>

Also be aware that you'll need to make several changes to the above user_bar.css file. Most notably, the #user-bar should be changed to #user-login-form. I also made things smaller, brought the links underneath and aligned left, and spiced up the submit button a bit.

Here is mine:

/*
by default, the default form adds some surrounding space, this cancels it
*/
.content div.form-item,
.content div.content {
  margin: 0; padding: 0;
}

/*
this adds some space in top and bottom, so anything inside can look vertically
centered
*/
#user-login-form {
  padding: .65em 0;
  margin-top:30px;
  width:370px;
}

/*
by default, fields labels tries to reserve a whole line for itself, this
cancels that and and sends it to the left.
it also adds some space on the right and left of the label to look easy on
the eye.
*/
#user-login-form label {
  float: left;
  margin-left: 10px;
  margin-right: 2px;
}

/*
inputs too, they try to reserve a whole line for itself, this
cancels that and sends it to the left
*/
#user-login-form input {
  float: left;
}

/*
I don't like the required * (asterisks), so I hide them.
*/
#user-login-form span.form-required {
  display: none;
}

/*
the form submit button,
*/
#user-login-form input.form-submit {
  margin-top: -1px;
  margin-left: 10px;
  padding: 0em .5em .1em .5em;
  font-size:.9em;
  border:1px solid #999;
  background-color:#E3A332;
  color:white;
  cursor:pointer;
}
#edit-name, #edit-pass {
width: 70px;
font-size:.9em;
}


/*
now this is for the links list, lists by default tries to reserve a whole line
also they add space surrounding them. we cancel all that and send the list
to the right
*/
#user-login-form div.item-list ul {
  margin: 10px; padding: 0px;
  padding-top:5px;
  text-align:left;
  clear:both;
}

/*
remember, stylign above was for the whole list, now for each item,
we all know each item in the list by default exists on a separate line, also
has that bullet on the left. we cancel all that. and makes all items sit beside
each other
*/
#user-login-form div.item-list ul li {
  margin:0px;
  padding: 0px;
  margin-top: 6px;
  background: none;
  list-style:none;
  display:inline;
}

/*
this is the "Hi user, welcome back message".
by default <p> tries to exist on a separate line, we cancel that.
also by default <p> has some surrounding space, we cancel that too, and give it
only space on the left.
*/
#user-login-form p.user-info {
  float: left;
  padding: 0;
  margin: 0 0 0 10px;
}

Tom Wheeler
wheelercreek.com

Too bad this requires a

charlie_ - May 9, 2008 - 01:58

Too bad this requires a Module to use.
It would be great if there were a working version/revision of the original example.
As it is, when logging out you are presented with two login forms.
Oh well, I guess I should make a request to have this thread removed. So that others
won't be fooled into thinking that any of this actually works. Which will save many
people a lot of time and frustration.

--Charlie

But it does work

seaneffel - May 9, 2008 - 22:00

I've got an example of this working fine from the original post. I struggled adding this to a custom theme, but doing this properly in Garland showed me where I was making my mistakes.

If you are seeing two login screens when logged out then is it possible you have still got the login -block- still enabled?

Apparently this whole thing

charlie_ - May 9, 2008 - 22:16

Apparently this whole thing (the original posting) only works with the Garland theme. I made attempts
with 5 different themes, all with varying failed results. In all cases, I carefully examined the
theme/template/css for collisions/duplications. Yet there were none. But all attempts failed. I finally
Used the suggestion offered above.
But I still wasn't able to use the code this thread was started with. I ended up creating
my own, and all is good.

Perhaps the code that was originally posted at the top of this thread applies to an early version
of 5 and not 5-current (5-1-7) which is the one I'm using.

In any case. Onlookers should take all of this into consideration before embarking on an attempt
to use the original suggestion in their own Drupal install (YMMV).

--Charlie

not just garland

silverwing - May 11, 2008 - 00:54

I got it working on a theme that started life as a danger4k. I just copied the code, made the name changes, and it works.

~silverwing

_____________________________________________
Land of Midnight | MisguidedThoughts | showcaseCMS

I've come to the same

Tammamtu - April 8, 2008 - 00:45

I've come to the same problem of delayed messages ..
I found that the easiest solution is to add a block anywhere in your theme with the function you've made without adding "print"

<?php
YOURTHEMENAME_login_bar
();
?>

and everything will go fine ..

subscribing

drupalina - May 11, 2008 - 00:26

subscribing

Two questions on formatting the bar

domsisti - May 11, 2008 - 21:19

Hi- I have two questions: #1. As in one comment I saw above, my login bar looks great when i am NOT logged in. As soon as I log in, however, the links ( * Your account & * Sign out) shift vertically below the welcome message. I'd like to put the links alongside the welcome message. I can't figure out where to do this. And #2. I'd like the form/bar/welcome message to be aligned against the right margin of my page. If anyone can help with these two issues, I would be most appreciative. And thanks for all the helpful tips so far.

CSS

seaneffel - May 12, 2008 - 02:47

The formatting is all done in the CSS file. My advice is to use a tool like Firebug (a plug in for Firefox) so that you can load the page and tweak the CSS on the fly - and when you have the layout the way you like it then you can make the final edits to the CSS file manually.

To make it easy, I often wrap the whole custom_login_bar function in a DIV tag with a specific ID so I can handle alignment more easily.

One problem down, one to go.

domsisti - May 12, 2008 - 15:59

Thanks.

I managed to move the login bar over to the right using the user_bar.css with Firebug.
It looks perfect when I am logged out.

But I am still having trouble when I log in.

It shifts back over to the left and screws up the theme. I should mention I am working in a custom theme.

Firebug indicates that the welcome message and links are somehow defined by the theme css. I'd like to override this and just keep working in the user_bar.css if possible.

Thanks again.

Translate User Name?

MGParisi - June 25, 2008 - 02:57

If you translate User Name. You will add a large number of Users into the translation database, making that DB very large, and slowing everything down!

 
 

Drupal is a registered trademark of Dries Buytaert.