SSbits - Home page
Site by Carbon Crayon
Submit a Post >

Tutorials - Big bits of code to help you do more

Custom Login Form with Group Based Redirection

In this tutorial we are going to create a system that allows the Admin to decide which (front-end) page a group of users are redirected to after login. This will be defined via a TreeDropdown for each group in the security section, and can also be set to send them to the admin area via a checkbox.

Preparation

We will be using 2 files in this tutorial: CustomLoginForm.php and GroupDecorator.php. Create both of these files in your mysite/code directory and fill them will the following:

mysite/code/GroupDecorator.php

<?php
class GroupDecorator extends DataObjectDecorator {
}

Mysite/code/CustomLoginForm.php

<?php
class CustomLoginForm extends MemberLoginForm {
}

Decorating Group

In order to be able to define a page to redirect to on each group, we need to add a CMS field and corresponding database field. However because the Group class is part of the Core, we can't just add it directly. Instead we use a DataObjectDecorator, which allows us to add fields, relationships and functions without going near the origonal class! First lets add our extra database fields via the extraStatics() function:

mysite/code/GroupDecorator.php

class GroupDecorator extends DataObjectDecorator {
	
	public function extraStatics(){
		
		return array(
			'db' => array(
				"GoToAdmin" => "Boolean"
			),
			'has_one' => array(
				"LinkedPage" => "SiteTree"
			),
		);
	}
}	

Here we are defining a Boolean to use when deciding if we want our group to go strait to the admin area. We also define a new has_one relationship so that we can link a SiteTree page to each group.

Next we want to add our CMS fields to the Group object so that we can select our page from a nice dropdown of the Site Tree or check a checkbox to send them to the admin area. We use a TreeDropdownField and a CheckboxField for these respectively.

mysite/code/GroupDecorator.php

class GroupDecorator extends DataObjectDecorator {
.
.
.
	public function updateCMSFields(FieldSet &$fields) {
	   $fields->addFieldToTab("Root.Members", new CheckboxField("GoToAdmin", " Go to Admin area"), 'Members');
	   $fields->addFieldToTab("Root.Members", new TreeDropdownField("LinkedPageID", "Or select a Page to redirect to", "SiteTree"), 'Members');
	}
}

Note. Even though our has_one relationship was called LinkedPage we call the TreeDropdown LinkedPageID. This is an inconsistency in SilverStripe where the field needs to be called the same thing as the column in the data-base, rather than the has_one relationship it self. Usually (for example on an ImageField) this would not be the case and you can leave off the 'ID', but when using a TreeDropdownField, or dynamic Dropdown which is representing a relationship, the extra 'ID' is needed.

We now need to tell silverstripe to extend the group object with our decorator. We do this by adding the following line to our _config.php file.

Object::add_extension('Group', 'GroupDecorator');

Now if you visit www.yoursite.com/dev/build the fields will be added to the database. You can now see in the CMS under security that each group has a checkbox and a tree dropdown. However they don’t do a whole lot just yet.

Creating a Custom Login action

Our new Login form is going to be an extension of the MemberLoginForm which holds all of the login actions. We need to overload (which means create a new version of) the dologin() function, as this is the function that normally does the redirecting.

This is what our overloaded function will look like:

Mysite/code/CustomLoginForm.php

class CustomLoginForm extends MemberLoginForm 
{
	public function dologin($data) {
		if($this->performLogin($data)) {
		        if(!$this->redirectByGroup($data))
					Director::redirect(Director::baseURL());
		} else {
			if($badLoginURL = Session::get("BadLoginURL")) {
				Director::redirect($badLoginURL);
			} else {
				Director::redirectBack();
			}
		}      
	}
}

First we try to perform the login by calling $this->performLogin($data), if we succeed then we move on, otherwise we check for a bad Login and if that returns false we just return the user back to where they were.

If our login was a success then we try to call our redirectByGroup() function (which we will write in a minute) from an if statement. If this returns false then we run the contents of the if and just direct to the base URL. Otherwise if it has returned true then it was run and we don't need to worry. In most cases (hopefully!) this is what will happen, but it's still good practice to make sure there is a fallback just in case.

Now let's add our function that will redirect the user based on their group.

Mysite/code/CustomLoginForm.php

class CustomLoginForm extends MemberLoginForm {
.
.
.
	public function redirectByGroup($data) 
	{ 	
		// gets the current member that is logging in
		$member = Member::currentUser();
		
		// gets all the groups.
		$Groups = DataObject::get("Group");
		
		//cycle through each group	
		foreach($Groups as $Group)
		{
			//if the member is in the group and that group has GoToAdmin checked
			if($member && $member->inGroup($Group->ID) && $Group->GoToAdmin) 
			{	
				//redirect to the admin page
	 			return Director::redirect(Director::baseURL() . 'admin' );
			}
			//otherwise if the member is in the group and that group has a page linked
			elseif($member && $member->inGroup($Group->ID)  && $Page = $Group->LinkedPage()) 
			{	
				//direct to that page
				return Director::redirect(Director::baseURL() . $Page->Link());				
			}
		}
		
		return false;
	}
}

So first thing we do is get the currently logged in user (i.e. the one that just logged in) followed by all the groups that exist.

We then cycle through each group checking whether the user is in any of them. If they are and that group has GoToAdmin checked then we go straight to the admin page. Otherwise if they are in the group and that group has a page relationship defined we get that page and link to it. If the user is not in any other the groups (very unlikely if not impossible), or the group they are in does not have a pageLink defined then we return false and the doLogin() function takes back control.

Finally we just need to tell SilverStripe to use our custom login class instead of the usual one. Again this is done in the _config.php file.

Object::useCustomClass('MemberLoginForm', 'CustomLoginForm');

That's it! You can now create user groups and choose which page they link to on login.

Limitations

If a user is in multiple groups the login will just redirect them to the first group it comes across with that member in. This will usually be the one with the lowest ID. You can manipulate this by reordering the $Groups array before cycling through it in the redirectByGroup() function.

 

Aram Balakjian avatar

Aram Balakjian

Aram is a web developer running London based agency Aab Web. He has a strong passion for developing attractive, usable sites around the SilverStripe CMS.

  • Thomas Apfelbacher
    14/10/2010 10:52am (4 years ago)

    Hi Aram, thanks for the nice tut.The backend-part of the tut works well. All db-thingies (GoToAdmin, LinkedPage ) were created. But i've got a problem (SS2.4.2) with the redirection of the groupmember after submitting the login credentials. The redirected site is blank and the url is domain.com/Security/LoginForm. I've suspected a url like domain.com/admin (adminlogin, adminlogin in backend is set, all credentials are right). Where do i have to set the BadLoginURL? And how do i build a costum login form (page)?

    Thank and greetings from Germany, Thomas

  • dendeffe
    14/10/2010 5:16pm (4 years ago)

    I found a small issue with this. If your user doesn’t use English, addFieldToTab("Root.Members" doesn’t work.

    You can change it to the right div. But it would be better to have it change depending on the users language.

  • Thomas Apfelbacher
    20/10/2010 4:37pm (4 years ago)

    Hi dendeffe

    i've tried to switch the Users (Interface)Language. Neither us english nor uk english works. According to the (_config.php) the users default language is german.

    i don't get the sense "you can change it to the right div". what do you mean?

    Any other ideas on this redirection issue (blank page/wrong url)?

    Thanks again, Thomas

  • Thomas Apfelbacher
    01/11/2010 4:58pm (4 years ago)

    Any Ideas? I stuck on this...

    Thomas

  • Aram Balakjian
    01/11/2010 5:20pm (4 years ago)

    Hi Thomas, is there problem there when you are just using english as the default? Sorry I am not entirely sure what the situation your describing is...

  • nimeso
    03/11/2010 12:37am (4 years ago)

    Hi all please help.. have no idea what I'm doing wrong

    I have standard login redirect and it works great! if I log in via http://guardian.nimeso/Security/login BUT...

    When I put $LoginForm (eg:in homepage.ss) anywhere in the template it doesn't seem to do ANYTHING in ExtendedLoginForm and redirects to http://guardian.nimeso/home/LoginForm and dosn't log me in?

    Please someone help? I've wasted 7 hours tryin to sort.

    Ta heaps
    Jamie

  • Aram Balakjian
    07/11/2010 7:01pm (4 years ago)

    Hi Jamie,

    try using $MemberLoginForm in your template instead

    Aram

  • Jonas
    28/01/2011 12:34am (4 years ago)

    Hi,
    I get an error when putting this into the _config.php
    Object::add_extension('Group', 'GroupDecorator');

    when doing a dev/build it says :
    Fatal error: Object::add_extension() - Can't find extension class for "GroupDecorator" in ......./sapphire/core/Object.php on line 526

    any ideas? typos?

  • Anirath
    01/02/2011 1:07am (4 years ago)

    I'm having the same problem as Jonas.

  • Thomas Apfelbacher
    15/03/2011 8:06pm (4 years ago)

    Hi Aram,

    Sorry for the late response. I did some testing on this issue on a current project. It seems there is no effect, if the userlanguage ist set to default or not.Additional i've tried to change the site wide language by using i18n::set_locale('de_DE');. No effect!

    Unlike the project in october the admin redirection works fine. But the redirection of the usergroup ends in a servererror. Because the baseURL is missing on the retrurned URL (looks like http://service/downloads/costumer1/).

    It seems this line of Code wont work: return Director::redirect(Director::baseURL() . $Page->Link()); . Any Idea?

  • Bart van Irsel
    20/03/2011 11:35am (4 years ago)

    Discovered today that putting the following in _config.php is also very handy for redirecting all users to a different website part when logging in.

    Security::set_default_login_dest('urlsegment');

  • mierla
    13/04/2011 8:03pm (3 years ago)

    @Thomas -

    I'm new at this, so this may not be the best way to do it, but I was having the same problem, and got it to work with this:

    <code>
    // direct to that page
    Director::redirect(Director::baseURL().$Page->URLSegment);
    </code>

  • ryeze
    26/07/2011 3:17am (3 years ago)

    hi Aram

    I've tried your custom login, but when trying something strange happened. I create a user group, I want to redirect them to a page about us. but what happened instead they link to http://localhost/slhdsilver//slhdsilver/about-us/, when they should link to http://localhost/slhdsilver/about-us/. please me. thanks.

  • nofelapen
    26/07/2011 5:52am (3 years ago)

    Hi Aram...

    I have the same problem with ryeze, I tried to set up a user when he logs in directly accessing the about-us. I want to: localhost / domains / about-us but the result is: localhost / domain / / domain / about-us

    Please Help, Thanks Before...

    nofelapen

  • gav
    11/01/2012 2:33am (3 years ago)

    I had the same problem with the double redirects & redirects not working, so i've modifed the code. i've only tested it on mamp but it works for admin login, custom page login and incorrect logins. I haven't tested it if the custom page login is more than one level deep. E.g. mysite.com.au/members/membershome/

    <code>
    class CustomLoginForm extends MemberLoginForm {
    public function dologin($data) {
    if($this->performLogin($data)) {

    $url = $this->redirectByGroup($data);
    if ($url) {
    Director::redirect($url);
    } else {
    if($badLoginURL = Session::get("BadLoginURL")) {
    Director::redirect($badLoginURL);
    } else {
    Director::redirectBack();
    }
    }
    } else {
    Director::redirectBack();
    }
    }

    public function redirectByGroup($data) {
    $member = Member::currentUser();
    $Groups = DataObject::get("Group");

    foreach($Groups as $Group) {
    if($member && $member->inGroup($Group->ID) && $Group->GoToAdmin) {
    return Director::baseURL() . 'admin' ;

    } elseif($member && $member->inGroup($Group->ID) && $Page = $Group->LinkedPage()) {
    return $Page->Link();

    }
    }

    return false;
    }
    }
    </code>

  • flosen
    17/01/2012 11:25pm (3 years ago)

    Hello all,
    i also had the "already redirected" error. i modified the redirectByGroup function a little bit. that works for me - just made sure the function really returns true/false and used $Page->URLSegment in the end. maybe it will help someone as well:

    public function redirectByGroup($data) {
    // gets the current member that is logging in
    $retValue = false;
    $member = Member::currentUser();

    // gets all the groups.
    $Groups = DataObject::get("Group");

    //cycle through each group
    foreach($Groups as $Group)
    {
    //if the member is in the group and that group has GoToAdmin checked
    if($member && $member->inGroup($Group->ID) && $Group->GoToAdmin)
    {
    //redirect to the admin page
    //print_r(Director::baseURL() . 'admin' );
    //exit();
    Director::redirect(Director::baseURL() . 'admin' );
    $retValue = true;
    }
    //otherwise if the member is in the group and that group has a page linked
    elseif($member && $member->inGroup($Group->ID) && $Page = $Group->LinkedPage())
    {
    //direct to that page
    //print_r(Director::baseURL() . $Page->Link());
    //exit();
    Director::redirect(Director::baseURL() . $Page->URLSegment);
    $retValue = true;
    }
    }
    return $retValue;
    }


    cheers, florian

  • the-tech-guy
    03/04/2014 7:00am (6 months ago)

    I have changed the code in GroupDecorator.php to enable this module to work in SS3,

    Here is the code I have written, with the help of Willr it has been changed from Decorators to DataExtension:

    <?php
    class GroupDecorator extends DataExtension {

    private static $db = array(
    'GoToAdmin' => 'Boolean'
    );

    private static $has_one = array(
    'LinkedPage' => 'SiteTree'
    );


    public function updateCMSFields(FieldSet &$fields) {
    $fields->addFieldToTab("Root.Members", new CheckboxField("GoToAdmin", " Go to Admin area"), 'Members');
    $fields->addFieldToTab("Root.Members", new TreeDropdownField("LinkedPageID", "Or select a Page to redirect to", "SiteTree"), 'Members');
    }
    }

    But I am having an issue, it trys to redirect from
    mysite.com//index.php/Security/login
    to
    mysite.com//index.php/Security/LoginForm

    Which is just a blank page, any ideas??

Post a comment ...

You cannot post comments until you have logged in. Login Here.

Advertisement

Site of the Month

Find SSbits on

Top Contributers

Rank Avatar Name
1 article image Aram Balakjian
2 article image Daniel Hensby
3 article image Marcus Dalgren
4 article image Hamish Campbell
5 article image Ty Barho
6 article image Martijn van Nieuwenhoven
7 article image Darren-Lee
8 article image Roman Schmid
9 article image Matt Clegg
10 article image dalesaurus

View full leaderboard


Advertisement