Now updated for concrete5.6!
Please note: the code we'll borrow from the Login and Register pages below is included only for example. We strongly recommend you use the latest code from your own core's login and register single pages, no matter what version of concrete5 you're using. The same concepts & approach should still apply.
You'll also likely want to spend some time stripping out any core styles from the HTML you recycle, and tweak it to suit your site's theme.
Overview
By default, the Login and Register pages are separate entities in the concrete5 core. If your site’s requirements specify that users should be able to either login or register from the same page, one easy option would be to create a new single page that submits its form information to the already existing login and register pages.
This how-to is a companion to Theming System Pages, where I explain how to apply your custom theme to single pages (like login and registration).
Remember, if you haven't already added this necessary validation code to your theme's view.php, now is a good time to do so! This will make sure empty & incorrectly filled-in form elements are caught by your individual login and register pages when we submit the form:
<?php Loader::element('system_errors', array('error' => $error)); ?>
The approach
Both Login and Register are single pages. While it’s possible to override either or both, this approach presents a few challenges involving specific functionality that’s tied to these pages. Making a new single page with forms that submit to the existing pages lets you customize the front end of your login process while leaving the complicated part of the login / registration process untouched. We’ll replicate the form fields we need on our new page and submit them to the existing login and registration pages.
Make the new single page
First, let’s create our new single page. Since it’s a custom page, we’ll place in in the single_pages override directory: (your_site_root)/single_pages/. Let’s call our new page sign_in.php.
Let’s think about how we’d like this combined login / register page to look. For the sake of simplicity, let’s use a side-by-side layout where our two forms live right next to each other, divided by a separator.
Bring in our borrowed form elements
Now we’ll take a look at the form elements on the Register page and re-create them in our new page. Open the core login.php page in /concrete/single_pages and let’s take a look what’s inside.
Here's the code I borrowed from the Login page. Note that the form still submits to /login, where we'll do our validation, etc. I added an ID to help style the div, but otherwise it's pretty much just the core login page code:
<?php defined('C5_EXECUTE') or die("Access Denied."); ?>
<?php Loader::library('authentication/open_id');?>
<?php $form = Loader::helper('form'); ?>
<script type="text/javascript">
$(function() {
$("input[name=uName]").focus();
});
</script>
<?php if (isset($intro_msg)) { ?>
<div class="alert-message block-message success"><p><?php echo $intro_msg?></p></div>
<?php } ?>
<div class="row">
<div class="span10 offset1">
<div class="page-header">
<h1><?php echo t('Sign in to %s', SITE)?></h1>
</div>
</div>
</div>
<?php if( $passwordChanged ){ ?>
<div class="block-message info alert-message"><p><?php echo t('Password changed. Please login to continue. ') ?></p></div>
<?php } ?>
<?php if($changePasswordForm){ ?>
<p><?php echo t('Enter your new password below.') ?></p>
<div class="ccm-form">
<form method="post" action="<?php echo $this->url( '/login', 'change_password', $uHash )?>">
<div class="control-group">
<label for="uPassword" class="control-label"><?php echo t('New Password')?></label>
<div class="controls">
<input type="password" name="uPassword" id="uPassword" class="ccm-input-text">
</div>
</div>
<div class="control-group">
<label for="uPasswordConfirm" class="control-label"><?php echo t('Confirm Password')?></label>
<div class="controls">
<input type="password" name="uPasswordConfirm" id="uPasswordConfirm" class="ccm-input-text">
</div>
</div>
<div class="actions">
<?php echo $form->submit('submit', t('Sign In') . ' >')?>
</div>
</form>
</div>
<?php }elseif($validated) { ?>
<h3><?php echo t('Email Address Verified')?></h3>
<div class="success alert-message block-message">
<p>
<?php echo t('The email address <b>%s</b> has been verified and you are now a fully validated member of this website.', $uEmail)?>
</p>
<div class="alert-actions"><a class="btn small" href="<?php echo $this->url('/')?>"><?php echo t('Continue to Site')?></a></div>
</div>
<?php } else if (isset($_SESSION['uOpenIDError']) && isset($_SESSION['uOpenIDRequested'])) { ?>
<div class="ccm-form">
<?php switch($_SESSION['uOpenIDError']) {
case OpenIDAuth::E_REGISTRATION_EMAIL_INCOMPLETE: ?>
<form method="post" action="<?php echo $this->url('/login', 'complete_openid_email')?>">
<p><?php echo t('To complete the signup process, you must provide a valid email address.')?></p>
<label for="uEmail"><?php echo t('Email Address')?></label><br/>
<?php echo $form->text('uEmail')?>
<div class="ccm-button">
<?php echo $form->submit('submit', t('Sign In') . ' >')?>
</div>
</form>
<?php break;
case OpenIDAuth::E_REGISTRATION_EMAIL_EXISTS:
$ui = UserInfo::getByID($_SESSION['uOpenIDExistingUser']);
?>
<form method="post" action="<?php echo $this->url('/login', 'do_login')?>">
<p><?php echo t('The OpenID account returned an email address already registered on this site. To join this OpenID to the existing user account, login below:')?></p>
<label for="uEmail"><?php echo t('Email Address')?></label><br/>
<div><strong><?php echo $ui->getUserEmail()?></strong></div>
<br/>
<div>
<label for="uName"><?php if (USER_REGISTRATION_WITH_EMAIL_ADDRESS == true) { ?>
<?php echo t('Email Address')?>
<?php } else { ?>
<?php echo t('Username')?>
<?php } ?></label><br/>
<input type="text" name="uName" id="uName" <?php echo (isset($uName)?'value="'.$uName.'"':'');?> class="ccm-input-text">
</div> <div>
<label for="uPassword"><?php echo t('Password')?></label><br/>
<input type="password" name="uPassword" id="uPassword" class="ccm-input-text">
</div>
<div class="ccm-button">
<?php echo $form->submit('submit', t('Sign In') . ' >')?>
</div>
</form>
<?php break;
}
?>
</div>
<?php } else if ($invalidRegistrationFields == true) { ?>
<div class="ccm-form">
<p><?php echo t('You must provide the following information before you may login.')?></p>
<form method="post" action="<?php echo $this->url('/login', 'do_login')?>">
<?php
$attribs = UserAttributeKey::getRegistrationList();
$af = Loader::helper('form/attribute');
$i = 0;
foreach($unfilledAttributes as $ak) {
if ($i > 0) {
print '<br/><br/>';
}
print $af->display($ak, $ak->isAttributeKeyRequiredOnRegister());
$i++;
}
?>
<?php echo $form->hidden('uName', Loader::helper('text')->entities($_POST['uName']))?>
<?php echo $form->hidden('uPassword', Loader::helper('text')->entities($_POST['uPassword']))?>
<?php echo $form->hidden('uOpenID', $uOpenID)?>
<?php echo $form->hidden('completePartialProfile', true)?>
<div class="ccm-button">
<?php echo $form->submit('submit', t('Sign In'))?>
<?php echo $form->hidden('rcID', $rcID); ?>
</div>
</form>
</div>
<?php } else { ?>
<form method="post" action="<?php echo $this->url('/login', 'do_login')?>" class="form-horizontal">
<div class="row">
<div class="span10 offset1">
<div class="row">
<div class="span5">
<fieldset>
<legend><?php echo t('User Account')?></legend>
<div class="control-group">
<label for="uName" class="control-label"><?php if (USER_REGISTRATION_WITH_EMAIL_ADDRESS == true) { ?>
<?php echo t('Email Address')?>
<?php } else { ?>
<?php echo t('Username')?>
<?php } ?></label>
<div class="controls">
<input type="text" name="uName" id="uName" <?php echo (isset($uName)?'value="'.$uName.'"':'');?> class="ccm-input-text">
</div>
</div>
<div class="control-group">
<label for="uPassword" class="control-label"><?php echo t('Password')?></label>
<div class="controls">
<input type="password" name="uPassword" id="uPassword" class="ccm-input-text" />
</div>
</div>
</fieldset>
<?php if (OpenIDAuth::isEnabled()) { ?>
<fieldset>
<legend><?php echo t('OpenID')?></legend>
<div class="control-group">
<label for="uOpenID" class="control-label"><?php echo t('Login with OpenID')?>:</label>
<div class="controls">
<input type="text" name="uOpenID" id="uOpenID" <?php echo (isset($uOpenID)?'value="'.$uOpenID.'"':'');?> class="ccm-input-openid">
</div>
</div>
</fieldset>
<?php } ?>
</div>
<div class="span4 offset1">
<fieldset>
<legend><?php echo t('Options')?></legend>
<?php if (isset($locales) && is_array($locales) && count($locales) > 0) { ?>
<div class="control-group">
<label for="USER_LOCALE" class="control-label"><?php echo t('Language')?></label>
<div class="controls"><?php echo $form->select('USER_LOCALE', $locales)?></div>
</div>
<?php } ?>
<div class="control-group">
<label class="checkbox"><?php echo $form->checkbox('uMaintainLogin', 1)?> <span><?php echo t('Remain logged in to website.')?></span></label>
</div>
<?php $rcID = isset($_REQUEST['rcID']) ? Loader::helper('text')->entities($_REQUEST['rcID']) : $rcID; ?>
<input type="hidden" name="rcID" value="<?php echo $rcID?>" />
</fieldset>
</div>
<div class="span10">
<div class="actions">
<?php echo $form->submit('submit', t('Sign In') . ' >', array('class' => 'primary'))?>
</div>
</div>
</div>
</div>
</div>
</form>
<a name="forgot_password"></a>
<form method="post" action="<?php echo $this->url('/login', 'forgot_password')?>" class="form-horizontal">
<div class="row">
<div class="span10 offset1">
<h3><?php echo t('Forgot Your Password?')?></h3>
<p><?php echo t("Enter your email address below. We will send you instructions to reset your password.")?></p>
<input type="hidden" name="rcID" value="<?php echo $rcID?>" />
<div class="control-group">
<label for="uEmail" class="control-label"><?php echo t('Email Address')?></label>
<div class="controls">
<input type="text" name="uEmail" value="" class="ccm-input-text" >
</div>
</div>
<div class="actions">
<?php echo $form->submit('submit', t('Reset and Email Password') . ' >')?>
</div>
</div>
</div>
</form>
<?php if (ENABLE_REGISTRATION == 1) { ?>
<div class="row">
<div class="span10 offset1">
<div class="control-group">
<h3><?php echo t('Not a Member')?></h3>
<p><?php echo t('Create a user account for use on this website.')?></p>
<div class="actions">
<a class="btn" href="<?php echo $this->url('/register')?>"><?php echo t('Register here!')?></a>
</div>
</div>
</div>
</div>
<?php } ?>
<?php } ?>
And here's what I'm using from the Register page:
<div class="row">
<div class="span10 offset1">
<div class="page-header">
<h1><?php echo t('Site Registration')?></h1>
</div>
</div>
</div>
<div class="ccm-form">
<?php
$attribs = UserAttributeKey::getRegistrationList();
if($success) { ?>
<div class="row">
<div class="span10 offset1">
<?php switch($success) {
case "registered":
?>
<p><strong><?php echo $successMsg ?></strong><br/><br/>
<a href="<?php echo $this->url('/')?>"><?php echo t('Return to Home')?></a></p>
<?php
break;
case "validate":
?>
<p><?php echo $successMsg[0] ?></p>
<p><?php echo $successMsg[1] ?></p>
<p><a href="<?php echo $this->url('/')?>"><?php echo t('Return to Home')?></a></p>
<?php
break;
case "pending":
?>
<p><?php echo $successMsg ?></p>
<p><a href="<?php echo $this->url('/')?>"><?php echo t('Return to Home')?></a></p>
<?php
break;
} ?>
</div>
</div>
<?php
} else { ?>
<form method="post" action="<?php echo $this->url('/register', 'do_register')?>" class="form-horizontal">
<div class="row">
<div class="<?php if (count($attribs) > 0) {?>span5<?php } else {?>span10<?php } ?> offset1">
<fieldset>
<legend><?php echo t('Your Details')?></legend>
<?php if ($displayUserName) { ?>
<div class="control-group">
<?php echo $form->label('uName',t('Username')); ?>
<div class="controls">
<?php echo $form->text('uName'); ?>
</div>
</div>
<?php } ?>
<div class="control-group">
<?php echo $form->label('uEmail',t('Email Address')); ?>
<div class="controls">
<?php echo $form->text('uEmail'); ?>
</div>
</div>
<div class="control-group">
<?php echo $form->label('uPassword',t('Password')); ?>
<div class="controls">
<?php echo $form->password('uPassword'); ?>
</div>
</div>
<div class="control-group">
<?php echo $form->label('uPasswordConfirm',t('Confirm Password')); ?>
<div class="controls">
<?php echo $form->password('uPasswordConfirm'); ?>
</div>
</div>
</fieldset>
</div>
<?php if (count($attribs) > 0) { ?>
<div class="span5">
<fieldset>
<legend><?php echo t('Options')?></legend>
<?php
$af = Loader::helper('form/attribute');
foreach($attribs as $ak) { ?>
<?php echo $af->display($ak, $ak->isAttributeKeyRequiredOnRegister()); ?>
<?php }?>
</fieldset>
</div>
<?php } ?>
<div class="span10 offset1 ">
<?php if (ENABLE_REGISTRATION_CAPTCHA) { ?>
<div class="control-group">
<?php $captcha = Loader::helper('validation/captcha'); ?>
<?php echo $captcha->label()?>
<div class="controls">
<?php
$captcha->showInput();
$captcha->display();
?>
</div>
</div>
<?php } ?>
</div>
<div class="span10 offset1">
<div class="actions">
<?php echo $form->hidden('rcID', $rcID); ?>
<?php echo $form->submit('register', t('Register') . ' >', array('class' => 'primary'))?>
</div>
</div>
</div>
</form>
<?php } ?>
</div>
Now let's clean up a couple of things. Since we don't need the "Register here!" link from the original core login page, let's cut it (and the conditional that surrounds it) out: Remove all this code:
<?php if (ENABLE_REGISTRATION == 1) { ?>
<div class="row">
<div class="span10 offset1">
<div class="control-group">
<h3><?php echo t('Not a Member')?></h3>
<p><?php echo t('Create a user account for use on this website.')?></p>
<div class="actions">
<a class="btn" href="<?php echo $this->url('/register')?>"><?php echo t('Register here!')?></a>
</div>
</div>
</div>
</div>
<?php } ?>
Now notice that the code we copied from the Registration page seems to be missing the Username field. That's because there's another now-unnecessary conditional wrapped around it. Comment this out, or remove the PHP tags that contain the if statement and its closing bracket:
<?php //if ($displayUserName) { ?>
<div class="control-group">
<?php echo $form->label('uName',t('Username')); ?>
<div class="controls">
<?php echo $form->text('uName'); ?>
</div>
</div>
<?php // } ?>
After we make a single page, we need to add it to our site from Dashboard > Pages and Themes > Single Pages. The new page will be available at index.php/sign_in.
Here's the end result:
If a form field needs to be corrected, users will land on the single page they’re submitting their form to—where they can correct their data and proceed. If you’ve already skinned your login and registration pages to match your site’s theme, correcting a form and re-submitting it should be a fairly seamless process for the end user.
Here's our login page, incomplete form submitted:
And if a user doesn't fill out the registration form correctly, our register page handles the error messages:
Conclusion
Now we can direct users to our new unified sign-in page as needed. We've left the core Login and Register pages intact, and themed / skinned them to match our theme to present a unified experience for our users.