I have created a completely customised login / registration page in a template file called "page--login.tpl.php". This loads fine when I navigate to /login, however it is giving a 404 http response code and the title reads 'Page not found' (presumably because there is no node behind called 'login').

I have suppressed the error code and change the title through the use of drupal_set_title and drupal_add_http_header, however the page not found errors still show up in the Drupal message log.

Is there a way I can prevent Drupal from throwing a page not found error in this scenario?

Comments

kamleshpatidar’s picture

Hope this can help you for 404 Error
http://highrockmedia.com/blog/customizing-user-login-page-drupal-7

Kamlesh Patidar

okiosteve’s picture

Hi Kamlesh,

Thanks for the link.

I think this would work, except that I need to override the entire page (not just the content region output), as my login page does not have any other regions on it.

My understanding was you could effectively create a custom PHP page in a drupal site by using a custom page--[page_url].tpl.php approach, however this is then leading to the 404 issues.

Jaypan’s picture

Please show us your page--login.tpl.php file.

okiosteve’s picture

<?php drupal_set_title(t('Please login to continue | Quanta Device')); ?>
<?php drupal_add_http_header('status', '200 OK'); ?>

<link type="text/css" rel="stylesheet" href="http://fast.fonts.net/cssapi/44775bb4-54da-466c-be2a-0ed838409516.css"/>

<script type="text/javascript">
	$(document).ready(function(){
		$('body').addClass('login-body');
	});
</script>

<div class="container-fluid login-container">
	<header class="login">
		<?php print '<img src="'. $base_path . path_to_theme() . '/images/logo.png' .'" alt="Quanta Device" width="" height="" />'?>
	</header>

	
	<div class="login-page">

		<?php print $messages; ?>
		  <?php if (!empty($tabs)): ?>
			<?php print render($tabs); ?>
		  <?php endif; ?>

		  <?php if (!empty($page['help'])): ?>
			<div class="well"><?php print render($page['help']); ?></div>
		  <?php endif; ?>

		  <?php if (!empty($action_links)): ?>
			<ul class="action-links"><?php print render($action_links); ?></ul>
		  <?php endif; ?>



		<div class="login-intro">
			<h2>Please log in to enter website</h2>
		</div>
		<?php 
		$elements = drupal_get_form("user_login"); 
		$form = drupal_render($elements);
		echo $form;
		?>
		<?php 
		$elements = drupal_get_form("user_register_form"); 
		$form = drupal_render($elements);
		echo $form;
		?>
		<?php //print render($page['content']); ?>

	</div>


</div> 
nevets’s picture

templates are for theming/laying out pages, not creating/defining them.

okiosteve’s picture

Hi Nevets,

I had followed a stack exchange suggestion to someone else's query that said creating a custom page.tpl file was a way of creating a page - and to be fair it does seem to work in that the page does load the content in the page.tpl file (just with the "page not found" title).

If this is not the case and it's not the right way to do it, can you suggest an alternative way of creating a completely custom login page that doesn't use any of the other elements in the main page.tpl file?

nevets’s picture

I would either use panels or a custom module.

cfox612’s picture

You do not need to use a custom template. Within admin/config/people/accounts/fields you can configure the registration/login form to your hearts content. Then to assign a specific URL for the login (meaning something outside the "standard" website.com/user), try the module Rename admin paths.

okiosteve’s picture

I've added the custom fields I need, but the problem I am having is that the login / registration page is fundamentally different to every other page on the site - for example it has no header, navigation, footer etc. - It's just a title and login form. That's what I'm struggling to get my head around how I create it.

Jaypan’s picture

The problem is as you originally speculated, that an underlying page doesn't exist (though you were speaking of a node, but a node isn't really necessary).

I think you can add the following code to your template.php in your theme and everything should work:

/**
 * Implementation of hook_menu()
 *
 * In this function we will create the system path: /login
 */
function THEME_NAME_menu()
{
  // We set the path
  $menu['login'] = array
  (
    'title' => 'Please login to continue | Quanta Device',
    'page callaback' => 'login_register_page',
    'access callback' => 'user_is_anonymous',
  );

  return $menu;
}

// The page callback we registered above
function login_register_page()
{
  // We don't need to return anything, since everything is created in the page--login.tpl.php file
  // But we do need to return something, so that the system picks up the path /login as a page.
  return '';
}

Now, that said, even if the above does work, it's not the best way to be doing things. Template files should contain only contain very simple PHP logic: if/else conditionals, switches, and loops. More complex logic should be done in the original page callback, or preprocess functions. If I were building your page, I would do it like this:

function THEME_NAME_menu()
{
  // We set the path
  $menu['login'] = array
  (
    'title' => 'Please login to continue | Quanta Device',
    'page callaback' => 'THEME_NAME_login_register_page',
    // We only want to show the page if the user is not signed in
    'access callback' => 'user_is_anonymous',
  );

  return $menu;
}

// The page callback we registered above
function THEME_NAME_login_register_page()
{
  // Create a render array containing the page information:
  $page = array
  (
    'login_form' => drupal_get_form('user_login'),
    'registration_form' => drupal_get_form('user_register_form'),
    '#attached' => array
    (
      'js' => array
      (
        array
        (
          'type' => 'file',
          'data' => drupal_get_path('theme', THEME_NAME) . '/js/THEME_NAME.js',
        ),
      ),
      'css' => array
      (
        array
        (
          'type' => 'external',
          'data' => 'http://fast.fonts.net/cssapi/44775bb4-54da-466c-be2a-0ed838409516.css',
        ),
      ),
    ),
  );

  return $page;
}

The above creates our two forms, and attaches a JS file, as well as your external CSS sheet from fonts.net. Note that THEME_NAME needs to be replaced with the name of your theme.

Next, THEME_NAME.js, which will contain the Drupal version of $(document).ready() that you used in the original code:

(function($, Drupal)
{
  Drupal.behaviors.THEMENAME = {
    attach:function()
    {
      // Your onload code goes in here
      $('body').addClass('login-body');
    }
  };
}(jQuery, Drupal));

See Managing JavaScript in Drupal 7: Using jQuery for more information on what is happening in the above code.

Finally, this will have set up the page content, so page--login.tpl.php will look like this:

<div class="container-fluid login-container">
  <header class="login">
    <?php print '<img src="'. $base_path . path_to_theme() . '/images/logo.png' .'" alt="Quanta Device" width="" height="" />'?>
  </header>

  <div class="login-page">
    <?php print $messages; ?>
    <?php if (!empty($tabs)): ?>
      <?php print render($tabs); ?>
    <?php endif; ?>

    <?php if (!empty($page['help'])): ?>
      <div class="well"><?php print render($page['help']); ?></div>
    <?php endif; ?>

    <?php if (!empty($action_links)): ?>
      <ul class="action-links"><?php print render($action_links); ?></ul>
    <?php endif; ?>

    <div class="login-intro">
      <h2>Please log in to enter website</h2>
    </div>

    <?php
      // $page['content'] will contain the forms that were
      // created in the page callback.
      print render($page['content']);
    ?>
  </div>
</div> 
okiosteve’s picture

Firstly, thanks for taking the time to write such a great reply - it's very much appreciated!

I've attempted to implement your suggested solution, however Drupal is not picking up the hook_menu() function (I have renamed where required and double checked, it is picking up other hooks in my template file so I don't think naming is the issue).

Having checked the documentation page for hook_menu(), (https://api.drupal.org/api/drupal/modules%21system%21system.api.php/func...) I appears as though this hook is designed to be used in modules - it is possible so use modules hooks in a teme template file as well?

Jaypan’s picture

Show us your hook_menu() implementation.

It's possible hook_menu() isn't picked up in a theme, and that you may need to put it in a module. But first, lets see your code to see if it looks right.