mlanthaler:OpenID authentication works now. To use it simple create an user account without password and put your *complete* OpenID identifier in the email field, e.g. "http://markus-lanthaler.myopenid.com/".

You can get a free OpenID from http://www.myopenid.com  (merged from branches/gsoc)


git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@41785 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2007-09-14 19:10:18 +00:00
parent b0e9e89aad
commit fbc375a282
8 changed files with 956 additions and 415 deletions

View File

@ -7,9 +7,9 @@
* section of an application. * section of an application.
* *
* On its own, content controller does very little. Its constructor is passed a {@link DataObject} * On its own, content controller does very little. Its constructor is passed a {@link DataObject}
* which is stored in $this->dataRecord. Any unrecognised method calls, for example, Title() * which is stored in $this->dataRecord. Any unrecognised method calls, for example, Title()
* and Content(), will be passed along to the data record, * and Content(), will be passed along to the data record,
* *
* Subclasses of ContentController are generally instantiated by ModelAsController; this will create * Subclasses of ContentController are generally instantiated by ModelAsController; this will create
* a controller based on the URLSegment action variable, by looking in the SiteTree table. * a controller based on the URLSegment action variable, by looking in the SiteTree table.
*/ */
@ -26,7 +26,7 @@ class ContentController extends Controller {
parent::__construct(); parent::__construct();
} }
public function Link($action = null) { public function Link($action = null) {
return Director::baseURL() . $this->RelativeLink($action); return Director::baseURL() . $this->RelativeLink($action);
} }
@ -34,7 +34,7 @@ class ContentController extends Controller {
if($this->URLSegment){ if($this->URLSegment){
if($action == "index") $action = ""; if($action == "index") $action = "";
// '&' in a URL is apparently naughty // '&' in a URL is apparently naughty
$action = preg_replace('/&/', '&', $action); $action = preg_replace('/&/', '&', $action);
return $this->URLSegment . "/$action"; return $this->URLSegment . "/$action";
@ -42,10 +42,10 @@ class ContentController extends Controller {
user_error("ContentController::RelativeLink() No URLSegment given on a '$this->class' object. Perhaps you should overload it?", E_USER_WARNING); user_error("ContentController::RelativeLink() No URLSegment given on a '$this->class' object. Perhaps you should overload it?", E_USER_WARNING);
} }
} }
//----------------------------------------------------------------------------------// //----------------------------------------------------------------------------------//
// These flexible data methods remove the need for custom code to do simple stuff // These flexible data methods remove the need for custom code to do simple stuff
/* /*
* Return the children of the given page. * Return the children of the given page.
* $parentRef can be a page number or a URLSegment * $parentRef can be a page number or a URLSegment
@ -62,12 +62,12 @@ class ContentController extends Controller {
} }
} }
public function Page($url) { public function Page($url) {
$SQL_url = Convert::raw2sql($url); $SQL_url = Convert::raw2sql($url);
return DataObject::get_one('SiteTree', "URLSegment = '$SQL_url'"); return DataObject::get_one('SiteTree', "URLSegment = '$SQL_url'");
} }
public function init() { public function init() {
parent::init(); parent::init();
@ -84,7 +84,7 @@ class ContentController extends Controller {
singleton('SiteTree')->extend('contentcontrollerInit', $this); singleton('SiteTree')->extend('contentcontrollerInit', $this);
Director::set_site_mode('site'); Director::set_site_mode('site');
// Check permissions // Check permissions
if($this->dataRecord && !$this->dataRecord->can('View')) Security::permissionFailure($this); if($this->dataRecord && !$this->dataRecord->can('View')) Security::permissionFailure($this);
@ -92,7 +92,7 @@ class ContentController extends Controller {
Requirements::themedCSS("typography"); Requirements::themedCSS("typography");
Requirements::themedCSS("form"); Requirements::themedCSS("form");
} }
/** /**
* Get the project name * Get the project name
* *
@ -109,9 +109,9 @@ class ContentController extends Controller {
public function data() { public function data() {
return $this->dataRecord; return $this->dataRecord;
} }
/*--------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------*/
/** /**
* Returns a fixed navigation menu of the given level. * Returns a fixed navigation menu of the given level.
*/ */
@ -147,7 +147,7 @@ class ContentController extends Controller {
* Returns the page in the current page stack of the given level. * Returns the page in the current page stack of the given level.
* Level(1) will return the main menu item that we're currently inside, etc. * Level(1) will return the main menu item that we're currently inside, etc.
*/ */
public function Level($level) { public function Level($level) {
$parent = $this->data(); $parent = $this->data();
$stack = array($parent); $stack = array($parent);
@ -157,25 +157,28 @@ class ContentController extends Controller {
return isset($stack[$level-1]) ? $stack[$level-1] : null; return isset($stack[$level-1]) ? $stack[$level-1] : null;
} }
public function Menu($level) { public function Menu($level) {
return $this->getMenu($level); return $this->getMenu($level);
} }
public function Section2() { public function Section2() {
return $this->Level(2)->URLSegment; return $this->Level(2)->URLSegment;
} }
/** /**
* Returns the default log-in form. * Returns the default log-in form.
*/ *
* @todo Check if here should be returned just the default log-in form or
* all available log-in forms (also OpenID...)
*/
public function LoginForm() { public function LoginForm() {
return Object::create('LoginForm', $this, "LoginForm"); return MemberAuthenticator::getLoginForm($this);
} }
public function SilverStripeNavigator() { public function SilverStripeNavigator() {
$member = Member::currentUser(); $member = Member::currentUser();
if(Director::isDev() || ($member && $member->isCMSUser())) { if(Director::isDev() || ($member && $member->isCMSUser())) {
Requirements::css('sapphire/css/SilverStripeNavigator.css'); Requirements::css('sapphire/css/SilverStripeNavigator.css');
@ -188,8 +191,8 @@ class ContentController extends Controller {
var w = window.open(this.href,windowName(this.target)); var w = window.open(this.href,windowName(this.target));
w.focus(); w.focus();
return false; return false;
} }
} }
}); });
function windowName(suffix) { function windowName(suffix) {
@ -213,9 +216,9 @@ JS
$thisPage = $this->Link(); $thisPage = $this->Link();
$cmsLink = ''; $cmsLink = '';
} }
$archiveLink = ""; $archiveLink = "";
if($date = Versioned::current_archived_date()) { if($date = Versioned::current_archived_date()) {
$dateObj = Object::create('Datetime', $date, null); $dateObj = Object::create('Datetime', $date, null);
// $dateObj->setVal($date); // $dateObj->setVal($date);
@ -224,7 +227,7 @@ JS
$liveLink = "<a href=\"$thisPage?stage=Live\" target=\"site\" style=\"left : -3px;\">Published Site</a>"; $liveLink = "<a href=\"$thisPage?stage=Live\" target=\"site\" style=\"left : -3px;\">Published Site</a>";
$stageLink = "<a href=\"$thisPage?stage=Stage\" target=\"site\" style=\"left : -1px;\">Draft Site</a>"; $stageLink = "<a href=\"$thisPage?stage=Stage\" target=\"site\" style=\"left : -1px;\">Draft Site</a>";
$message = "<div id=\"SilverStripeNavigatorMessage\">Archived site from<br>" . $dateObj->Nice() . "</div>"; $message = "<div id=\"SilverStripeNavigatorMessage\">Archived site from<br>" . $dateObj->Nice() . "</div>";
} else if(Versioned::current_stage() == 'Stage') { } else if(Versioned::current_stage() == 'Stage') {
$stageLink = "<a class=\"current\">Draft Site</a>"; $stageLink = "<a class=\"current\">Draft Site</a>";
$liveLink = "<a href=\"$thisPage?stage=Live\" target=\"site\" style=\"left : -3px;\">Published Site</a>"; $liveLink = "<a href=\"$thisPage?stage=Live\" target=\"site\" style=\"left : -3px;\">Published Site</a>";
@ -235,7 +238,7 @@ JS
$stageLink = "<a href=\"$thisPage?stage=Stage\" target=\"site\" style=\"left : -1px;\">Draft Site</a>"; $stageLink = "<a href=\"$thisPage?stage=Stage\" target=\"site\" style=\"left : -1px;\">Draft Site</a>";
$message = "<div id=\"SilverStripeNavigatorMessage\">PUBLISHED SITE</div>"; $message = "<div id=\"SilverStripeNavigatorMessage\">PUBLISHED SITE</div>";
} }
if($member) { if($member) {
$firstname = Convert::raw2xml($member->FirstName); $firstname = Convert::raw2xml($member->FirstName);
$surname = Convert::raw2xml($member->Surame); $surname = Convert::raw2xml($member->Surame);
@ -254,7 +257,7 @@ JS
<div id="logInStatus"> <div id="logInStatus">
$logInMessage $logInMessage
</div> </div>
<div id="switchView" class="bottomTabs"> <div id="switchView" class="bottomTabs">
<div class="blank"> View page in: </div> <div class="blank"> View page in: </div>
$cmsLink $cmsLink
@ -291,15 +294,15 @@ HTML;
} }
} }
} }
/** /**
* Throw an error to test the error system * Throw an error to test the error system
*/ */
function throwerror() { function throwerror() {
user_error("This is a test of the error handler - nothing to worry about.", E_USER_ERROR); user_error("This is a test of the error handler - nothing to worry about.", E_USER_ERROR);
} }
/** /**
* Throw a warning to test the error system * Throw a warning to test the error system
*/ */
@ -339,13 +342,13 @@ HTML;
<div style="margin-left: auto; margin-right: auto; width: 50%;"><p><a href="home/deleteinstallfiles" style="text-align: center;">Click here to delete the install files.</a></p></div></div> <div style="margin-left: auto; margin-right: auto; width: 50%;"><p><a href="home/deleteinstallfiles" style="text-align: center;">Click here to delete the install files.</a></p></div></div>
HTML HTML
); );
return array( return array(
"Title" => $title, "Title" => $title,
"Content" => $content, "Content" => $content,
); );
} }
function deleteinstallfiles() { function deleteinstallfiles() {
$title = new Varchar("Title"); $title = new Varchar("Title");
$content = new HTMLText("Content"); $content = new HTMLText("Content");
@ -396,7 +399,7 @@ HTML
HTML HTML
; ;
$content->setValue($tempcontent); $content->setValue($tempcontent);
return array( return array(
"Title" => $title, "Title" => $title,
"Content" => $content, "Content" => $content,

View File

@ -1,5 +1,10 @@
<?php <?php
/**
* Authenticator base class
*/
/** /**
* Abstract base class for an authentication method * Abstract base class for an authentication method
@ -8,6 +13,8 @@
* methods like {@link MemberAuthenticator} or {@link OpenIDAuthenticator}. * methods like {@link MemberAuthenticator} or {@link OpenIDAuthenticator}.
* *
* @author Markus Lanthaler <markus@silverstripe.com> * @author Markus Lanthaler <markus@silverstripe.com>
*
* @todo Wouldn't be an interface be the better choice?
*/ */
abstract class Authenticator extends Object abstract class Authenticator extends Object
{ {
@ -15,19 +22,24 @@ abstract class Authenticator extends Object
* Method to authenticate an user * Method to authenticate an user
* *
* @param array $RAW_data Raw data to authenticate the user * @param array $RAW_data Raw data to authenticate the user
* @param Form $form Optional: If passed, better error messages can be
* produced by using
* {@link Form::sessionMessage()}
* @return bool|Member Returns FALSE if authentication fails, otherwise * @return bool|Member Returns FALSE if authentication fails, otherwise
* the member object * the member object
*/ */
public abstract function authenticate(array $RAW_data); public abstract function authenticate(array $RAW_data, Form $form = null);
/** /**
* Method that creates the login form for this authentication method * Method that creates the login form for this authentication method
* *
* @param Controller The parent controller, necessary to create the
* appropriate form action tag
* @return Form Returns the login form to use with this authentication * @return Form Returns the login form to use with this authentication
* method * method
*/ */
public abstract function getLoginForm(); public abstract static function getLoginForm(Controller $controller);
} }
?> ?>

View File

@ -6,20 +6,19 @@ class Member extends DataObject {
'Surname' => "Varchar", 'Surname' => "Varchar",
'Email' => "Varchar", 'Email' => "Varchar",
'Password' => "Varchar", 'Password' => "Varchar",
'RememberLoginToken' => "Varchar(50)",
'NumVisit' => "Int", 'NumVisit' => "Int",
'LastVisited' => 'Datetime', 'LastVisited' => 'Datetime',
'Bounced' => 'Boolean', 'Bounced' => 'Boolean',
'AutoLoginHash' => 'Varchar(10)', 'AutoLoginHash' => 'Varchar(10)',
'AutoLoginExpired' => 'Datetime', 'AutoLoginExpired' => 'Datetime',
'BlacklistedEmail' => 'Boolean', 'BlacklistedEmail' => 'Boolean',
); );
static $belongs_many_many = array( static $belongs_many_many = array(
"Groups" => "Group", "Groups" => "Group",
); );
static $has_one = array(
);
static $has_many = array( static $has_many = array(
'UnsubscribedRecords' => 'Member_UnsubscribeRecord' 'UnsubscribedRecords' => 'Member_UnsubscribeRecord'
); );
@ -30,23 +29,86 @@ class Member extends DataObject {
'Email' => true, 'Email' => true,
); );
/** /**
* Logs this member in. * Logs this member in
*
* @param bool $remember If set to TRUE, the member will be logged in
* automatically the next time.
*/ */
function logIn() { function logIn($remember = false) {
session_regenerate_id(true);
Session::set("loggedInAs", $this->ID); Session::set("loggedInAs", $this->ID);
$this->NumVisit++; $this->NumVisit++;
if($remember) {
$token = substr(md5(uniqid(rand(), true)), 0, 49 - strlen($this->ID));
$this->RememberLoginToken = $token;
Cookie::set('alc_enc', $this->ID . ':' . $token);
} else {
$this->RememberLoginToken = null;
Cookie::set('alc_enc', null);
Cookie::forceExpiry('alc_enc');
}
$this->write(); $this->write();
} }
/** /**
*Logs this member in. * Log the user in if the "remember login" cookie is set
*
* The <i>remember login token</i> will be changed on every successful
* auto-login.
*/ */
function logOut(){ static function autoLogin() {
Cookie::set('alc_enc',null); if(isset($_COOKIE['alc_enc']) && !Session::get("loggedInAs")) {
Session::clear("loggedInAs");
list($uid, $token) = explode(':', $_COOKIE['alc_enc'], 2);
$uid = Convert::raw2sql($uid);
$token = Convert::raw2sql($token);
$member = DataObject::get_one(
"Member", "Member.ID = $uid And RememberLoginToken = '$token'");
if($member) {
session_regenerate_id(true);
Session::set("loggedInAs", $member->ID);
$token = substr(md5(uniqid(rand(), true)),
0, 49 - strlen($member->ID));
$member->RememberLoginToken = $token;
Cookie::set('alc_enc', $member->ID . ':' . $token);
$member->NumVisit++;
$member->write();
}
}
} }
/**
* Logs this member out.
*/
function logOut() {
Session::clear("loggedInAs");
session_regenerate_id(true);
$this->RememberLoginToken = null;
Cookie::set('alc_enc', null);
Cookie::forceExpiry('alc_enc');
$this->write();
}
/**
* Generate an auto login hash
*
* @todo This is relative insecure, check if we should fix it
*/
function generateAutologinHash() { function generateAutologinHash() {
$linkHash = sprintf('%10d', time() ); $linkHash = sprintf('%10d', time() );
@ -59,31 +121,51 @@ class Member extends DataObject {
$this->write(); $this->write();
} }
/** /**
* Log a member in with an auto login hash link * Log a member in with an auto login hash link
*/ */
static function autoLoginHash( $RAW_hash ) { static function autoLoginHash($RAW_hash) {
$SQL_hash = Convert::raw2sql($RAW_hash);
$SQL_hash = Convert::raw2sql( $RAW_hash );
$member = DataObject::get_one('Member',"`AutoLoginHash`='$SQL_hash' AND `AutoLoginExpired` > NOW()"); $member = DataObject::get_one('Member',"`AutoLoginHash`='$SQL_hash' AND `AutoLoginExpired` > NOW()");
if( $member ) if($member)
$member->logIn(); $member->logIn();
return $member; return $member;
} }
function sendInfo($type = 'signup'){
/**
* Send signup, change password or forgot password informations to an user
*
* @param string $type Information type to send ("signup",
* "changePassword" or "forgotPassword")
*/
function sendInfo($type = 'signup') {
switch($type) { switch($type) {
case "signup": $e = new Member_SignupEmail(); break; case "signup":
case "changePassword": $e = new Member_ChangePasswordEmail(); break; $e = new Member_SignupEmail();
case "forgotPassword": $e = new Member_ForgotPasswordEmail(); break; break;
case "changePassword":
$e = new Member_ChangePasswordEmail();
break;
case "forgotPassword":
$e = new Member_ForgotPasswordEmail();
break;
} }
$e->populateTemplate($this); $e->populateTemplate($this);
$e->send(); $e->send();
} }
/**
* Returns the fields for the member form
*
* @return FieldSet Returns a {@link FieldSet} containing the fields for
* the member form.
*/
function getMemberFormFields() { function getMemberFormFields() {
return new FieldSet( return new FieldSet(
new TextField("FirstName", "First Name"), new TextField("FirstName", "First Name"),
@ -93,108 +175,84 @@ class Member extends DataObject {
); );
} }
/**
* Factory method for the member validator
*
* @return Member_Validator Returns an instance of a
* {@link Member_Validator} object.
*/
function getValidator() { function getValidator() {
return new Member_Validator(); return new Member_Validator();
} }
/** /**
* Returns the currenly logged in user * Returns the current logged in user
* @todo get_one() is a bit funky. *
* @return bool|Member Returns the member object of the current logged in
* user or FALSE.
*/ */
static function currentUser() { static function currentUser() {
self::autoLogin(); $id = Session::get("loggedInAs");
if(!$id) {
self::autoLogin();
$id = Session::get("loggedInAs");
}
// Return the details if($id) {
if($id = Session::get("loggedInAs")) {
return DataObject::get_one("Member", "Member.ID = $id"); return DataObject::get_one("Member", "Member.ID = $id");
} }
} }
static function autoLogin() {
// Auto-login
if(isset($_COOKIE['alc_enc']) && !Session::get("loggedInAs")) {
// Deliberately obscure...
list($data['Email'], $data['Password']) = explode(':',base64_decode($_COOKIE['alc_enc']),2);
$lf = new LoginForm(null, null, null, null, false);
$lf->performLogin($data);
}
}
/**
* Get the ID of the current logged in user
*
* @return int Returns the ID of the current logged in user or 0.
*/
static function currentUserID() { static function currentUserID() {
self::autoLogin();
$id = Session::get("loggedInAs"); $id = Session::get("loggedInAs");
if(!$id) {
self::autoLogin();
$id = Session::get("loggedInAs");
}
return is_numeric($id) ? $id : 0; return is_numeric($id) ? $id : 0;
} }
/** /**
* before the save of this member, the blacklisted email table is updated to ensure no * Add the members email address to the blacklist
* promotional material is sent to the member. (newsletters) *
* standard system messages are still sent such as receipts. * With this method the blacklisted email table is updated to ensure that
* no promotional material is sent to the member (newsletters).
* Standard system messages are still sent such as receipts.
*
* @param bool $val Set to TRUE if the address should be added to the
* blacklist, otherwise to FALSE.
* @return
* @todo Check for what the parameter $val is needed! (Markus)
*/ */
function setBlacklistedEmail($val){ function setBlacklistedEmail($val) {
if($val && $this->Email){ if($val && $this->Email) {
$blacklisting = new Email_BlackList(); $blacklisting = new Email_BlackList();
$blacklisting->BlockedEmail = $this->Email; $blacklisting->BlockedEmail = $this->Email;
$blacklisting->MemberID = $this->ID; $blacklisting->MemberID = $this->ID;
$blacklisting->write(); $blacklisting->write();
} }
return $this->setField("BlacklistedEmail",$val);
$this->setField("BlacklistedEmail", $val);
} }
function onBeforeWrite() {
// If an email's filled out
if($this->Email) {
// Look for a record with the same email
if($this->ID) $idClause = "AND `Member`.ID <> $this->ID";
else $idClause = "";
$existingRecord = DataObject::get_one("Member", "Email = '" . addslashes($this->Email) . "' $idClause");
// Debug::message("Found an existing member for email $this->Email");
// If found
if($existingRecord) {
// Update this record to merge with that member
$newID = $existingRecord->ID;
if($this->ID) {
DB::query("UPDATE Group_Members SET MemberID = $newID WHERE MemberID = $this->ID");
}
$this->ID = $newID;
// Merge existing data into the local record
foreach($existingRecord->getAllFields() as $k => $v) {
if(!isset($this->changed[$k]) || !$this->changed[$k]) $this->record[$k] = $v;
}
}
}
parent::onBeforeWrite();
}
/**
* Check if the member is in one of the given groups
*/
public function inGroups( $groups ) {
foreach( $this->Groups() as $group )
$memberGroups[] = $group->Title;
return count( array_intersect( $memberGroups, $groups ) ) > 0;
}
public function inGroup( $groupID ) {
foreach( $this->Groups() as $group )
if( $groupID == $group->ID )
return true;
return false;
}
/* /*
* Generate a random password * Generate a random password
* BDC - added randomiser to kick in if there's no words file on the filesystem. *
* BDC - added randomiser to kick in if there's no words file on the
* filesystem.
*
* @return string Returns a random password.
*/ */
static function createNewPassword() { static function createNewPassword() {
if(file_exists('/usr/share/silverstripe/wordlist.txt')) { if(file_exists('/usr/share/silverstripe/wordlist.txt')) {
@ -214,17 +272,105 @@ class Member extends DataObject {
return $output; return $output;
} }
} }
/**
* Event handler called before writing to the database
*
* If an email's filled out look for a record with the same email and if
* found update this record to merge with that member.
*/
function onBeforeWrite() {
if($this->Email) {
if($this->ID) {
$idClause = "AND `Member`.ID <> $this->ID";
} else {
$idClause = "";
}
$existingRecord = DataObject::get_one(
"Member", "Email = '" . addslashes($this->Email) . "' $idClause");
// Debug::message("Found an existing member for email $this->Email");
if($existingRecord) {
$newID = $existingRecord->ID;
if($this->ID) {
DB::query("UPDATE Group_Members SET MemberID = $newID WHERE MemberID = $this->ID");
}
$this->ID = $newID;
// Merge existing data into the local record
foreach($existingRecord->getAllFields() as $k => $v) {
if(!isset($this->changed[$k]) || !$this->changed[$k]) $this->record[$k] = $v;
}
}
}
parent::onBeforeWrite();
}
/**
* Check if the member is in one of the given groups
*
* @param array $groups Groups to check
* @return bool Returns TRUE if the member is in one of the given groups,
* otherwise FALSE.
*/
public function inGroups(array $groups) {
foreach($this->Groups() as $group)
$memberGroups[] = $group->Title;
return count(array_intersect($memberGroups, $groups)) > 0;
}
/**
* Check if the member is in the given group
*
* @param int $groupID ID of the group to check
* @return bool Returns TRUE if the member is in the given group,
* otherwise FALSE.
*/
public function inGroup($groupID) {
foreach($this->Groups() as $group) {
if($groupID == $group->ID)
return true;
}
return false;
}
/**
* Alias for {@link inGroup}
*
* @param int $groupID ID of the group to check
* @return bool Returns TRUE if the member is in the given group,
* otherwise FALSE.
* @see inGroup()
*/
public function isInGroup($groupID) {
return $this->inGroup($groupID);
}
/** /**
* Returns true if this user is an administrator. * Returns true if this user is an administrator.
* Administrators have access to everything. The lucky bastards! ;-) * Administrators have access to everything. The lucky bastards! ;-)
* *
* @return Returns TRUE if this user is an administrator.
* @todo Should this function really exists? Is not {@link isAdmin()} the * @todo Should this function really exists? Is not {@link isAdmin()} the
* only right name for this? * only right name for this?
* @todo Is {@link Group}::CanCMSAdmin not deprecated? * @todo Is {@link Group}::CanCMSAdmin not deprecated?
*/ */
function _isAdmin() { function _isAdmin() {
if($groups = $this->Groups()) { if($groups = $this->Groups()) {
foreach($groups as $group) if($group->CanCMSAdmin) return true; foreach($groups as $group) {
if($group->CanCMSAdmin)
return true;
}
} }
return Permission::check('ADMIN'); return Permission::check('ADMIN');
@ -245,41 +391,79 @@ class Member extends DataObject {
} }
function _isCMSUser() { function _isCMSUser() {
if($groups = $this->Groups()) { if($groups = $this->Groups()) {
foreach($groups as $group) if($group->CanCMS) return true; foreach($groups as $group) {
if($group->CanCMS)
return true;
}
} }
} }
//----------------------------------------------------------------------------------------// //------------------- HELPER METHODS -----------------------------------//
/**
* Get the complete name of the member
*
* @return string Returns the first- and surname of the member. If the ID
* of the member is equal 0, only the surname is returned.
* @todo Check for what this method is used! (Markus)
*/
public function getTitle() { public function getTitle() {
if($this->getField('ID') === 0) if($this->getField('ID') === 0)
return $this->getField('Surname'); return $this->getField('Surname');
return $this->getField('Surname') . ', ' . $this->getField('FirstName'); return $this->getField('Surname') . ', ' . $this->getField('FirstName');
} }
/**
* Get the complete name of the member
*
* @return string Returns the first- and surname of the member.
*/
public function getName() { public function getName() {
return $this->FirstName . ' ' . $this->Surname; return $this->FirstName . ' ' . $this->Surname;
} }
public function setName( $name ) {
$nameParts = explode( ' ', $name ); /**
$this->Surname = array_pop( $nameParts ); * Set first- and surname
$this->FirstName = join( ' ', $nameParts ); *
* This method assumes that the last part of the name is the surname, e.g.
* <i>A B C</i> will result in firstname <i>A B</i> and surname <i>C</i>
*
* @param string $name The name
*/
public function setName($name) {
$nameParts = explode(' ', $name);
$this->Surname = array_pop($nameParts);
$this->FirstName = join(' ', $nameParts);
} }
public function splitName( $name ) {
return $this->setName( $name ); /**
* Alias for {@link setName}
*
* @param string $name The name
* @see setName()
*/
public function splitName($name) {
return $this->setName($name);
} }
//----------------------------------------------------------------------------------------// //---------------------------------------------------------------------//
/**
* @todo Figure out what this function does and document it! (Markus)
*/
public function Groups() { public function Groups() {
$groups = $this->getManyManyComponents("Groups"); $groups = $this->getManyManyComponents("Groups");
$unsecure = DataObject::get("Group_Unsecure", ""); $unsecure = DataObject::get("Group_Unsecure", "");
if($unsecure) foreach($unsecure as $unsecureItem) { if($unsecure) {
$groups->push($unsecureItem); foreach($unsecure as $unsecureItem) {
$groups->push($unsecureItem);
}
} }
$groupIDs = $groups->column(); $groupIDs = $groups->column();
@ -303,17 +487,13 @@ class Member extends DataObject {
return $result; return $result;
} }
public function isInGroup($groupID) {
$groups = $this->Groups();
foreach($groups as $group) {
if($group->ID == $groupID) return true;
}
return false;
}
/**
* @todo Figure out what this function does and document it! (Markus)
*/
public function map($filter = "", $sort = "", $blank="") { public function map($filter = "", $sort = "", $blank="") {
$ret = new SQLMap(singleton('Member')->extendedSQL($filter, $sort)); $ret = new SQLMap(singleton('Member')->extendedSQL($filter, $sort));
if($blank){ if($blank) {
$blankMember = new Member(); $blankMember = new Member();
$blankMember->Surname = $blank; $blankMember->Surname = $blank;
$blankMember->ID = 0; $blankMember->ID = 0;
@ -324,72 +504,102 @@ class Member extends DataObject {
} }
public static function mapInGroups( $groups = null ) { /**
* @todo Figure out what this function does and document it! (Markus)
if( !$groups ) */
public static function mapInGroups($groups = null) {
if(!$groups)
return Member::map(); return Member::map();
$groupIDList = array(); $groupIDList = array();
if( is_a( $groups, 'DataObjectSet' ) ) if(is_a($groups, 'DataObjectSet')) {
foreach( $groups as $group ) foreach( $groups as $group )
$groupIDList[] = $group->ID; $groupIDList[] = $group->ID;
elseif( is_array( $groups ) ) } elseif(is_array($groups)) {
$groupIDList = $groups; $groupIDList = $groups;
else } else {
$groupIDList[] = $groups; $groupIDList[] = $groups;
}
if( empty( $groupIDList ) ) if(empty($groupIDList))
return Member::map(); return Member::map();
return new SQLMap( singleton('Member')->extendedSQL( "`GroupID` IN (" . implode( ',', $groupIDList ) . ")", "Surname, FirstName", "", "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`") ); return new SQLMap(singleton('Member')->extendedSQL(
"`GroupID` IN (" . implode( ',', $groupIDList ) .
")", "Surname, FirstName", "", "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`"));
} }
/** /**
* Return a map of all members in the groups given that have CMS permissions * Get a map of all members in the groups given that have CMS permissions
* Defaults to all groups with CMS permissions *
* If no groups are passed, all groups with CMS permissions will be used.
*
* @param array $groups Groups to consider or NULL to use all groups with
* CMS permissions.
* @return SQLMap Returns a map of all members in the groups given that
* have CMS permissions.
*/ */
public static function mapInCMSGroups( $groups = null ) { public static function mapInCMSGroups($groups = null) {
if( !$groups || $groups->Count() == 0 ) if(!$groups || $groups->Count() == 0) {
$groups = DataObject::get('Group',"", "", "INNER JOIN `Permission` ON `Permission`.GroupID = `Group`.ID AND `Permission`.Code IN ('ADMIN', 'CMS_ACCESS_AssetAdmin')"); $groups = DataObject::get('Group', "", "",
"INNER JOIN `Permission` ON `Permission`.GroupID = `Group`.ID AND `Permission`.Code IN ('ADMIN', 'CMS_ACCESS_AssetAdmin')");
}
$groupIDList = array(); $groupIDList = array();
if( is_a( $groups, 'DataObjectSet' ) ) if(is_a($groups, 'DataObjectSet')) {
foreach( $groups as $group ) foreach($groups as $group)
$groupIDList[] = $group->ID; $groupIDList[] = $group->ID;
elseif( is_array( $groups ) ) } elseif(is_array($groups)) {
$groupIDList = $groups; $groupIDList = $groups;
}
/*if( empty( $groupIDList ) ) /*if( empty( $groupIDList ) )
return Member::map(); */ return Member::map(); */
$filterClause = ($groupIDList) ? "`GroupID` IN (" . implode( ',', $groupIDList ) . ")" : ""; $filterClause = ($groupIDList)
? "`GroupID` IN (" . implode( ',', $groupIDList ) . ")"
: "";
return new SQLMap( singleton('Member')->extendedSQL( $filterClause, "Surname, FirstName", "", "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID` INNER JOIN `Group` ON `Group`.`ID`=`GroupID`") ); return new SQLMap(singleton('Member')->extendedSQL($filterClause,
"Surname, FirstName", "",
"INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID` INNER JOIN `Group` ON `Group`.`ID`=`GroupID`"));
} }
/**
* When passed an array of groups, and a component set of groups, this function
* will return the array of groups the member is NOT in.
* @param grouplist an array of group code names.
* @param memberGroups a component set of groups ( set to $this->groups() by default )
*/
public function memberNotInGroups($groupList,$memberGroups = null){
if(!$memberGroups) $memberGroups = $this->Groups();
foreach($memberGroups as $group){ /**
if(in_array($group->Code,$groupList)){ * Get the groups in which the member is NOT in
$index = array_search($group->Code,$groupList); *
* When passed an array of groups, and a component set of groups, this
* function will return the array of groups the member is NOT in.
*
* @param array $groupList An array of group code names.
* @param array $memberGroups A component set of groups (if set to NULL,
* $this->groups() will be used)
* @return array Groups in which the member is NOT in.
*/
public function memberNotInGroups($groupList, $memberGroups = null){
if(!$memberGroups)
$memberGroups = $this->Groups();
foreach($memberGroups as $group) {
if(in_array($group->Code, $groupList)) {
$index = array_search($group->Code, $groupList);
unset($groupList[$index]); unset($groupList[$index]);
} }
} }
return $groupList; return $groupList;
} }
/** /**
* Return a FieldSet of fields that would appropriate for editing this member. * Return a {@link FieldSet} of fields that would appropriate for editing
* this member.
*
* @return FieldSet Return a FieldSet of fields that would appropriate for
* editing this member.
*/ */
public function getCMSFields() { public function getCMSFields() {
$fields = new FieldSet( $fields = new FieldSet(
@ -413,12 +623,19 @@ class Member extends DataObject {
return $fields; return $fields;
} }
function unsubscribeFromNewsletter( $newsletterType ) {
/**
* Unsubscribe from newsletter
*
* @param NewsletterType $newsletterType Newsletter type to unsubscribe
* from
*/
function unsubscribeFromNewsletter(NewsletterType $newsletterType) {
// record today's date in unsubscriptions // record today's date in unsubscriptions
// this is a little bit redundant // this is a little bit redundant
$unsubscribeRecord = new Member_UnsubscribeRecord(); $unsubscribeRecord = new Member_UnsubscribeRecord();
$unsubscribeRecord->unsubscribe( $this, $newsletterType ); $unsubscribeRecord->unsubscribe($this, $newsletterType);
$this->Groups()->remove( $newsletterType->GroupID ); $this->Groups()->remove($newsletterType->GroupID);
} }
function requireDefaultRecords() { function requireDefaultRecords() {
@ -431,161 +648,208 @@ class Member extends DataObject {
} }
} }
/** /**
* Special kind of ComponentSet that has special methods for manipulating a user's membership * Special kind of {@link ComponentSet} that has special methods for
* manipulating a user's membership
*/ */
class Member_GroupSet extends ComponentSet { class Member_GroupSet extends ComponentSet {
/** /**
* Control group membership with a number of checkboxes. * Control group membership with a number of checkboxes.
* - If the checkbox fields are present in $data, then the member will be added to the group with the same codename. * - If the checkbox fields are present in $data, then the member will be
* - If the checkbox fields are *NOT* present in $data, then the member willb e removed from the group with the same codename. * added to the group with the same codename.
* @param checkboxes an array list of the checkbox fieldnames (Only values are used.) eg array(0,1,2); * - If the checkbox fields are *NOT* present in $data, then the member
* @param data The form data. usually in the format array(0 => 2) (just pass the checkbox data from your form); * will be removed from the group with the same codename.
*
* @param array $checkboxes An array list of the checkbox fieldnames (only
* values are used). E.g. array(0, 1, 2)
* @param array $data The form data. Uually in the format array(0 => 2)
* (just pass the checkbox data from your form)
*/ */
function setByCheckboxes($checkboxes, $data) { function setByCheckboxes(array $checkboxes, array $data) {
foreach($checkboxes as $checkbox) { foreach($checkboxes as $checkbox) {
if($data[$checkbox]){ if($data[$checkbox]) {
$add[] = $checkbox; $add[] = $checkbox;
}else{ } else {
$remove[] = $checkbox; $remove[] = $checkbox;
} }
} }
if($add)$this->addManyByCodename($add);
if($remove) $this->removeManyByCodename($remove); if($add)
$this->addManyByCodename($add);
if($remove)
$this->removeManyByCodename($remove);
} }
/** /**
* Allows you to set groups based on a checkboxsetfield. * Allows you to set groups based on a CheckboxSetField
* (pass the form element from your post data directly to this method, and it
* will update the groups and add and remove the member as appropriate)
* @param checkboxsetField - the CheckboxSetField (with data) from your form.
* *
* On the form setup * Pass the form element from your post data directly to this method, and
* * it will update the groups and add and remove the member as appropriate.
$fields->push(
new CheckboxSetField(
"NewsletterSubscriptions",
"Receive email notification of events in ",
$sourceitems = DataObject::get("NewsletterType")->toDropDownMap("GroupID","Title"),
$selectedgroups = $member->Groups()->Map("ID","ID")
)
);
* *
* On the form setup:
* *
* <code>
* $fields->push(
* new CheckboxSetField(
* "NewsletterSubscriptions",
* "Receive email notification of events in ",
* $sourceitems = DataObject::get("NewsletterType")->toDropDownMap("GroupID","Title"),
* $selectedgroups = $member->Groups()->Map("ID","ID")
* )
* );
* </code>
* *
* On the form handler: * On the form handler:
$groups = $member->Groups();
$checkboxfield = $form->Fields()->fieldByName("NewsletterSubscriptions");
$groups->setByCheckboxSetField($checkboxfield);
* *
* <code>
* $groups = $member->Groups();
* $checkboxfield = $form->Fields()->fieldByName("NewsletterSubscriptions");
* $groups->setByCheckboxSetField($checkboxfield);
* </code>
*
* @param CheckboxSetField $checkboxsetfield The CheckboxSetField (with
* data) from your form.
*/ */
function setByCheckboxSetField($checkboxsetfield){ function setByCheckboxSetField(CheckboxSetField $checkboxsetfield) {
// Get the values from the formfield. // Get the values from the formfield.
$values = $checkboxsetfield->Value(); $values = $checkboxsetfield->Value();
$sourceItems = $checkboxsetfield->getSource(); $sourceItems = $checkboxsetfield->getSource();
if($sourceItems){ if($sourceItems) {
// If (some) values are present, add and remove as necessary. // If (some) values are present, add and remove as necessary.
if($values){ if($values) {
// update the groups based on the selections // update the groups based on the selections
foreach($sourceItems as $k => $item){ foreach($sourceItems as $k => $item) {
if(in_array($k,$values)){ if(in_array($k,$values)) {
$add[] = $k; $add[] = $k;
}else{ } else {
$remove[] = $k; $remove[] = $k;
} }
} }
// else we should be removing all from the necessary groups. // else we should be removing all from the necessary groups.
}else{ } else {
$remove = $sourceItems; $remove = $sourceItems;
} }
if($add)$this->addManyByGroupID($add); if($add)
if($remove) $this->RemoveManyByGroupID($remove); $this->addManyByGroupID($add);
}else{ if($remove)
USER_ERROR("Member::setByCheckboxSetField() - No source items could be found for checkboxsetfield ". $checkboxsetfield->Name(),E_USER_WARNING); $this->RemoveManyByGroupID($remove);
} else {
USER_ERROR("Member::setByCheckboxSetField() - No source items could be found for checkboxsetfield " .
$checkboxsetfield->Name(), E_USER_WARNING);
} }
} }
/** /**
* Adds this member to the groups based on the * Adds this member to the groups based on the group IDs
* groupID. *
* @param array $ids Group identifiers.
*/ */
function addManyByGroupID($groupIds){ function addManyByGroupID($groupIds){
$groups = $this->getGroupsFromIDs($groupIds); $groups = $this->getGroupsFromIDs($groupIds);
if($groups){ if($groups) {
foreach($groups as $group){ foreach($groups as $group) {
$this->add($group); $this->add($group);
} }
} }
} }
/** /**
* Removes the member from many groups based on * Removes the member from many groups based on the group IDs
* the group ID. *
* @param array $ids Group identifiers.
*/ */
function removeManyByGroupID($groupIds){ function removeManyByGroupID($groupIds) {
$groups = $this->getGroupsFromIDs($groupIds); $groups = $this->getGroupsFromIDs($groupIds);
if($groups){ if($groups) {
foreach($groups as $group){ foreach($groups as $group) {
$this->remove($group); $this->remove($group);
} }
} }
} }
/** /**
* Returns the groups from an array of GroupIDs * Returns the groups from an array of group IDs
*
* @param array $ids Group identifiers.
* @return mixed Returns the groups from the array of Group IDs.
*/ */
function getGroupsFromIDs($ids){ function getGroupsFromIDs($ids){
if($ids && count($ids) > 1){ if($ids && count($ids) > 1) {
return DataObject::get("Group","ID IN (". implode(",",$ids) .")"); return DataObject::get("Group", "ID IN (" . implode(",", $ids) . ")");
}else{ } else {
return DataObject::get_by_id("Group",$ids[0]); return DataObject::get_by_id("Group", $ids[0]);
} }
} }
/** /**
* Adds this member to the groups passed. * Adds this member to the groups based on the group codenames
*
* @param array $codenames Group codenames
*/ */
function addManyByCodename($codenames) { function addManyByCodename($codenames) {
$groups = $this->codenamesToGroups($codenames); $groups = $this->codenamesToGroups($codenames);
if($groups){ if($groups) {
foreach($groups as $group){ foreach($groups as $group){
$this->add($group); $this->add($group);
} }
} }
} }
/** /**
* Removes this member from the groups passed. * Removes this member from the groups based on the group codenames
*
* @param array $codenames Group codenames
*/ */
function removeManyByCodename($codenames) { function removeManyByCodename($codenames) {
$groups = $this->codenamesToGroups($codenames); $groups = $this->codenamesToGroups($codenames);
if($groups){ if($groups) {
foreach($groups as $group){ foreach($groups as $group) {
$this->remove($group); $this->remove($group);
} }
} }
} }
/** /**
* Helper function to return the appropriate group via a codename. * Helper function to return the appropriate groups via a codenames
*
* @param array $codenames Group codenames
* @return array Returns the the appropriate groups.
*/ */
protected function codenamesToGroups($codenames) { protected function codenamesToGroups($codenames) {
$list = "'" . implode("', '", $codenames) . "'"; $list = "'" . implode("', '", $codenames) . "'";
$output = DataObject::get("Group", "Code IN ($list)"); $output = DataObject::get("Group", "Code IN ($list)");
// Some are missing - throw warnings // Some are missing - throw warnings
if(!$output || $output->Count() != sizeof($list)) { if(!$output || ($output->Count() != sizeof($list))) {
foreach($codenames as $codename) $missing[$codename] = $codename; foreach($codenames as $codename)
if($output) foreach($output as $record) unset($missing[$record->Code]); $missing[$codename] = $codename;
if($missing) user_error("The following group-codes aren't matched to any groups: " . implode(", ", $missing) . ". You probably need to link up the correct group codes in phpMyAdmin", E_USER_WARNING);
if($output) {
foreach($output as $record)
unset($missing[$record->Code]);
}
if($missing)
user_error("The following group-codes aren't matched to any groups: " .
implode(", ", $missing) .
". You probably need to link up the correct group codes in phpMyAdmin",
E_USER_WARNING);
} }
return $output; return $output;
@ -594,9 +858,12 @@ class Member_GroupSet extends ComponentSet {
/**
* Class used as template to send an email to new members
*/
class Member_SignupEmail extends Email_Template { class Member_SignupEmail extends Email_Template {
protected protected
$from = 'ask@perweek.co.nz', $from = '', // setting a blank from address uses the site's default administrator email
$to = '$Email', $to = '$Email',
$subject = "Thanks for signing up", $subject = "Thanks for signing up",
$body = ' $body = '
@ -644,17 +911,19 @@ class Member_SignupEmail extends Email_Template {
function MemberData() { function MemberData() {
return $this->template_data->listOfFields( return $this->template_data->listOfFields(
"FirstName","Surname","Email", "FirstName", "Surname", "Email",
"Phone","Mobile","Street", "Phone", "Mobile", "Street",
"Suburb","City","Postcode","DriversLicense5A","DriversLicense5B" "Suburb", "City", "Postcode", "DriversLicense5A", "DriversLicense5B"
); );
} }
} }
/**
* Send an email saying that the password has been reset.
*/
/**
* Class used as template to send an email saying that the password has been
* changed
*/
class Member_ChangePasswordEmail extends Email_Template { class Member_ChangePasswordEmail extends Email_Template {
protected $from = ''; // setting a blank from address uses the site's default administrator email protected $from = ''; // setting a blank from address uses the site's default administrator email
protected $subject = "Your password has been changed"; protected $subject = "Your password has been changed";
@ -662,31 +931,57 @@ class Member_ChangePasswordEmail extends Email_Template {
protected $to = '$Email'; protected $to = '$Email';
} }
/**
* Class used as template to send the forgot password email
*/
class Member_ForgotPasswordEmail extends Email_Template { class Member_ForgotPasswordEmail extends Email_Template {
protected $from = ''; protected $from = ''; // setting a blank from address uses the site's default administrator email
protected $subject = "Your password"; protected $subject = "Your password";
protected $ss_template = 'ForgotPasswordEmail'; protected $ss_template = 'ForgotPasswordEmail';
protected $to = '$Email'; protected $to = '$Email';
} }
/** /**
* Record to keep track of which records a member has unsubscribed from and when * Record to keep track of which records a member has unsubscribed from and
* when
*
* @todo Check if that email stuff ($from, $to, $subject, $body) is needed
* here! (Markus)
*/ */
class Member_UnsubscribeRecord extends DataObject { class Member_UnsubscribeRecord extends DataObject {
static $has_one = array( static $has_one = array(
'NewsletterType' => 'NewsletterType', 'NewsletterType' => 'NewsletterType',
'Member' => 'Member' 'Member' => 'Member'
); );
/**
* Unsubscribe the member from a specific newsletter type
*
* @param int|Member $member Member object or ID
* @param int|NewsletterType $newsletterType Newsletter type object or ID
*/
function unsubscribe($member, $newsletterType) {
// $this->UnsubscribeDate()->setVal( 'now' );
$this->MemberID = (is_numeric($member))
? $member
: $member->ID;
$this->NewsletterTypeID = (is_numeric($newletterType))
? $newsletterType
: $newsletterType->ID;
$this->write();
}
function unsubscribe( $member, $newsletterType ) {
// $this->UnsubscribeDate()->setVal( 'now' );
$this->MemberID = ( is_numeric( $member ) ) ? $member : $member->ID;
$this->NewsletterTypeID = ( is_numeric( $newletterType ) ) ? $newsletterType : $newsletterType->ID;
$this->write();
}
protected protected
$from = 'ask@perweek.co.nz', $from = '', // setting a blank from address uses the site's default administrator email
$to = '$Email', $to = '$Email',
$subject = "Your password has been changed", $subject = "Your password has been changed",
$body = ' $body = '
@ -698,9 +993,19 @@ class Member_UnsubscribeRecord extends DataObject {
<p>Your password has been changed. Please keep this email, for future reference.</p>'; <p>Your password has been changed. Please keep this email, for future reference.</p>';
} }
class Member_Validator extends RequiredFields {
protected $customRequired = array('FirstName', 'Email', 'Password');
/**
* Member Validator
*/
class Member_Validator extends RequiredFields {
protected $customRequired = array('FirstName', 'Email'); //, 'Password');
/**
* Constructor
*/
public function __construct() { public function __construct() {
$required = func_get_args(); $required = func_get_args();
if(isset($required[0]) && is_array($required[0])) { if(isset($required[0]) && is_array($required[0])) {
@ -708,24 +1013,43 @@ class Member_Validator extends RequiredFields {
} }
$required = array_merge($required, $this->customRequired); $required = array_merge($required, $this->customRequired);
parent::__construct($required); parent::__construct($required);
} }
/**
* Check if the submitted member data is valid
*
* Check if a member with that email doesn't already exist, or if it does
* that it is this member.
*
* @param array $data Submitted data
* @return bool Returns TRUE if the submitted data is valid, otherwise
* FALSE.
*/
function php($data) { function php($data) {
$valid = parent::php($data); $valid = parent::php($data);
// Check if a member with that email doesn't already exist, or if it does that it is this member. $member = DataObject::get_one('Member',
$member = DataObject::get_one('Member', "Email = '". Convert::raw2sql($data['Email']) ."'"); "Email = '". Convert::raw2sql($data['Email']) ."'");
// if we are in a complex table field popup, use ctf[childID], else use ID
$id = (isset($_REQUEST['ctf']['childID'])) ? $_REQUEST['ctf']['childID'] : $_REQUEST['ID']; // if we are in a complex table field popup, use ctf[childID], else use
// ID
$id = (isset($_REQUEST['ctf']['childID']))
? $_REQUEST['ctf']['childID']
: $_REQUEST['ID'];
if(is_object($member) && $member->ID != $id) { if(is_object($member) && $member->ID != $id) {
$emailField = $this->form->dataFieldByName('Email'); $emailField = $this->form->dataFieldByName('Email');
$this->validationError($emailField->id(), "There already exists a member with this email", "required"); $this->validationError($emailField->id(),
"There already exists a member with this email",
"required");
$valid = false; $valid = false;
} }
return $valid; return $valid;
} }
} }
?> ?>

View File

@ -1,5 +1,13 @@
<?php <?php
/**
* Member authenticator
*
* @author Markus Lanthaler <markus@silverstripe.com>
*/
/** /**
* Authenticator for the default "member" method * Authenticator for the default "member" method
* *
@ -11,18 +19,25 @@ class MemberAuthenticator extends Authenticator {
* Method to authenticate an user * Method to authenticate an user
* *
* @param array $RAW_data Raw data to authenticate the user * @param array $RAW_data Raw data to authenticate the user
* @param Form $form Optional: If passed, better error messages can be
* produced by using
* {@link Form::sessionMessage()}
* @return bool|Member Returns FALSE if authentication fails, otherwise * @return bool|Member Returns FALSE if authentication fails, otherwise
* the member object * the member object
*/ */
public function authenticate(array $RAW_data) { public function authenticate(array $RAW_data, Form $form = null) {
$SQL_user = Convert::raw2sql($RAW_data['Email']); $SQL_user = Convert::raw2sql($RAW_data['Email']);
$SQL_password = Convert::raw2sql($RAW_data['Password']); $SQL_password = Convert::raw2sql($RAW_data['Password']);
$member = DataObject::get_one( $member = DataObject::get_one(
"Member", "Email = '$SQL_user' And Password = '$SQL_password'"); "Member", "Email = '$SQL_user' AND Password = '$SQL_password'");
if($member) { if($member) {
Session::clear("BackURL"); Session::clear("BackURL");
} else if(!is_null($form)) {
$form->sessionMessage(
"That doesn't seem to be the right email address or password. Please try again.",
"bad");
} }
return $member; return $member;
@ -32,11 +47,13 @@ class MemberAuthenticator extends Authenticator {
/** /**
* Method that creates the login form for this authentication method * Method that creates the login form for this authentication method
* *
* @param Controller The parent controller, necessary to create the
* appropriate form action tag
* @return Form Returns the login form to use with this authentication * @return Form Returns the login form to use with this authentication
* method * method
*/ */
public function getLoginForm() { public static function getLoginForm(Controller $controller) {
return Object::create("MemberLoginForm", $this, "LoginForm"); return Object::create("MemberLoginForm", $controller, "LoginForm");
} }
} }

View File

@ -1,4 +1,11 @@
<?php <?php
/**
* Log-in form for the "member" authentication method
*/
/** /**
* Log-in form for the "member" authentication method * Log-in form for the "member" authentication method
*/ */
@ -7,13 +14,22 @@ class MemberLoginForm extends Form {
/** /**
* Constructor * Constructor
* *
* @param $controller * @param Controller $controller The parent controller, necessary to
* @param $name * create the appropriate form action tag.
* @param $fields * @param string $name The method on the controller that will return this
* @param $actions * form object.
* @param $checkCurrentUser * @param FieldSet|FormField $fields All of the fields in the form - a
* {@link FieldSet} of {@link FormField}
* objects.
* @param FieldSet|FormAction $actions All of the action buttons in the
* form - a {@link FieldSet} of
* {@link FormAction} objects
* @param bool $checkCurrentUser If set to TRUE, it will be checked if a
* the user is currently logged in, and if
* so, only a logout button will be rendered
*/ */
function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true) { function __construct($controller, $name, $fields = null, $actions = null,
$checkCurrentUser = true) {
$customCSS = project() . '/css/member_login.css'; $customCSS = project() . '/css/member_login.css';
if(Director::fileExists($customCSS)) { if(Director::fileExists($customCSS)) {
@ -24,19 +40,21 @@ class MemberLoginForm extends Form {
$backURL = $_REQUEST['BackURL']; $backURL = $_REQUEST['BackURL'];
} else { } else {
$backURL = Session::get('BackURL'); $backURL = Session::get('BackURL');
//Session::clear("BackURL"); don't clear the back URL here! Should be used until the right password is entered!
} }
if($checkCurrentUser && Member::currentUserID()) { if($checkCurrentUser && Member::currentUserID()) {
$fields = new FieldSet(); $fields = new FieldSet();
$actions = new FieldSet(new FormAction("logout", "Log in as someone else")); $actions = new FieldSet(new FormAction("logout",
"Log in as someone else"));
} else { } else {
if(!$fields) { if(!$fields) {
$fields = new FieldSet( $fields = new FieldSet(
new HiddenField("AuthenticationMethod", null, "Member"), new HiddenField("AuthenticationMethod", null, "Member"),
new TextField("Email", "Email address", Session::get('SessionForms.MemberLoginForm.Email')), new TextField("Email", "Email address",
Session::get('SessionForms.MemberLoginForm.Email')),
new EncryptField("Password", "Password"), new EncryptField("Password", "Password"),
new CheckboxField("Remember", "Remember me next time?",true) new CheckboxField("Remember", "Remember me next time?",
Session::get('SessionForms.MemberLoginForm.Remember'))
); );
} }
if(!$actions) { if(!$actions) {
@ -60,7 +78,8 @@ class MemberLoginForm extends Form {
*/ */
protected function getMessageFromSession() { protected function getMessageFromSession() {
parent::getMessageFromSession(); parent::getMessageFromSession();
if(($member = Member::currentUser()) && !Session::get('MemberLoginForm.force_message')) { if(($member = Member::currentUser()) &&
!Session::get('MemberLoginForm.force_message')) {
$this->message = "You're logged in as $member->FirstName."; $this->message = "You're logged in as $member->FirstName.";
} }
Session::set('MemberLoginForm.force_message', false); Session::set('MemberLoginForm.force_message', false);
@ -75,18 +94,21 @@ class MemberLoginForm extends Form {
* @param array $data Submitted data * @param array $data Submitted data
*/ */
public function dologin($data) { public function dologin($data) {
if($this->performLogin($data)){ if($this->performLogin($data)) {
Session::clear('SessionForms.MemberLoginForm.Email');
Session::clear('SessionForms.MemberLoginForm.Remember');
if($backURL = $_REQUEST['BackURL']) { if($backURL = $_REQUEST['BackURL']) {
Session::clear("BackURL"); Session::clear("BackURL");
Session::clear('SessionForms.MemberLoginForm.Email');
Director::redirect($backURL); Director::redirect($backURL);
} else } else
Director::redirectBack(); Director::redirectBack();
} else { } else {
Session::set('SessionForms.MemberLoginForm.Email', $data['Email']); Session::set('SessionForms.MemberLoginForm.Email', $data['Email']);
if($badLoginURL = Session::get("BadLoginURL")){ Session::set('SessionForms.MemberLoginForm.Remember',
isset($data['Remember']));
if($badLoginURL = Session::get("BadLoginURL")) {
Director::redirect($badLoginURL); Director::redirect($badLoginURL);
} else { } else {
Director::redirectBack(); Director::redirectBack();
@ -96,13 +118,16 @@ class MemberLoginForm extends Form {
/** /**
* Log out * Log out form handler method
* *
* @todo Figure out for what this method is used! Is it really used at all? * This method is called when the user clicks on "logout" on the form
* created when the parameter <i>$checkCurrentUser</i> of the
* {@link __construct constructor} was set to TRUE and the user was
* currently logged in.
*/ */
public function logout(){ public function logout() {
$s = new Security(); $s = new Security();
return $s->logout(); $s->logout();
} }
@ -113,20 +138,16 @@ class MemberLoginForm extends Form {
* @return Member Returns the member object on successful authentication * @return Member Returns the member object on successful authentication
* or NULL on failure. * or NULL on failure.
*/ */
public function performLogin($data){ public function performLogin($data) {
if($member = MemberAuthenticator::authenticate($data)) { if($member = MemberAuthenticator::authenticate($data, $this)) {
$firstname = Convert::raw2xml($member->FirstName); $firstname = Convert::raw2xml($member->FirstName);
$this->sessionMessage("Welcome Back, {$firstname}", "good"); Session::set("Security.Message.message", "Welcome Back, {$firstname}");
$member->LogIn(); Session::set("Security.Message.type", "good");
if(isset($data['Remember'])) { $member->LogIn(isset($data['Remember']));
// Deliberately obscure...
Cookie::set('alc_enc',base64_encode("$data[Email]:$data[Password]"));
}
return $member; return $member;
} else { } else {
$this->sessionMessage("That doesn't seem to be the right email address or password. Please try again.", "bad");
return null; return null;
} }
} }
@ -135,13 +156,15 @@ class MemberLoginForm extends Form {
/** /**
* Forgot password form handler method * Forgot password form handler method
* *
* This method is called when the user clicks on "Log in" * This method is called when the user clicks on "I've lost my password"
* *
* @param array $data Submitted data * @param array $data Submitted data
*/ */
function forgotPassword($data) { function forgotPassword($data) {
$SQL_data = Convert::raw2sql($data); $SQL_data = Convert::raw2sql($data);
if($data['Email'] && $member = DataObject::get_one("Member", "Member.Email = '$SQL_data[Email]'")) {
if($data['Email'] && $member = DataObject::get_one("Member",
"Member.Email = '$SQL_data[Email]'")) {
if(!$member->Password) { if(!$member->Password) {
$member->createNewPassword(); $member->createNewPassword();
$member->write(); $member->write();
@ -151,7 +174,9 @@ class MemberLoginForm extends Form {
Director::redirect('Security/passwordsent/' . urlencode($data['Email'])); Director::redirect('Security/passwordsent/' . urlencode($data['Email']));
} else if($data['Email']) { } else if($data['Email']) {
$this->sessionMessage("Sorry, but I don't recognise the email address. Maybe you need to sign up, or perhaps you used another email address?", "bad"); $this->sessionMessage(
"Sorry, but I don't recognise the email address. Maybe you need to sign up, or perhaps you used another email address?",
"bad");
Director::redirectBack(); Director::redirectBack();
} else { } else {

View File

@ -1,5 +1,13 @@
<?php <?php
/**
* OpenID authenticator and controller
*
* @author Markus Lanthaler <markus@silverstripe.com>
*/
/** /**
* Require the OpenID consumer code. * Require the OpenID consumer code.
*/ */
@ -29,10 +37,13 @@ class OpenIDAuthenticator extends Authenticator {
* Method to authenticate an user * Method to authenticate an user
* *
* @param array $RAW_data Raw data to authenticate the user * @param array $RAW_data Raw data to authenticate the user
* @param Form $form Optional: If passed, better error messages can be
* produced by using
* {@link Form::sessionMessage()}
* @return bool|Member Returns FALSE if authentication fails, otherwise * @return bool|Member Returns FALSE if authentication fails, otherwise
* the member object * the member object
*/ */
public function authenticate(array $RAW_data) { public function authenticate(array $RAW_data, Form $form = null) {
$openid = $RAW_data['OpenIDURL']; $openid = $RAW_data['OpenIDURL'];
$trust_root = Director::absoluteBaseURL(); $trust_root = Director::absoluteBaseURL();
@ -59,9 +70,27 @@ class OpenIDAuthenticator extends Authenticator {
// No auth request means we can't begin OpenID. // No auth request means we can't begin OpenID.
if(!$auth_request) { if(!$auth_request) {
displayError("Authentication error; not a valid OpenID."); if(!is_null($form)) {
$form->sessionMessage("That doesn't seem to be a valid OpenID " .
"or i-name identifier. Please try again.",
"bad");
}
return false;
} }
$SQL_user = Convert::raw2sql($auth_request->endpoint->claimed_id);
if(!($member = DataObject::get_one("Member", "Email = '$SQL_user'"))) {
if(!is_null($form)) {
$form->sessionMessage("Either your account is not enabled for " .
"OpenID/i-name authentication " .
"or the entered identifier is wrong. " .
"Please try again.",
"bad");
}
return false;
}
/** /**
* @todo Check if the POST request should be send directly (without rendering a form) * @todo Check if the POST request should be send directly (without rendering a form)
@ -97,6 +126,7 @@ class OpenIDAuthenticator extends Authenticator {
"</title></head>", "</title></head>",
"<body onload='document.getElementById(\"".$form_id."\").submit()'>", "<body onload='document.getElementById(\"".$form_id."\").submit()'>",
$form_html, $form_html,
"<p>Click &quot;Continue&quot; to login. You are only seeing this because you appear to have JavaScript disabled.</p>",
"</body></html>"); "</body></html>");
print implode("\n", $page_contents); print implode("\n", $page_contents);
@ -109,11 +139,13 @@ class OpenIDAuthenticator extends Authenticator {
/** /**
* Method that creates the login form for this authentication method * Method that creates the login form for this authentication method
* *
* @param Controller The parent controller, necessary to create the
* appropriate form action tag
* @return Form Returns the login form to use with this authentication * @return Form Returns the login form to use with this authentication
* method * method
*/ */
public function getLoginForm() { public static function getLoginForm(Controller $controller) {
return Object::create("OpenIDLoginForm", $this, "LoginForm"); return Object::create("OpenIDLoginForm", $controller, "LoginForm");
} }
} }
@ -135,11 +167,96 @@ class OpenIDAuthenticator_Controller extends Controller {
function run($requestParams) { function run($requestParams) {
parent::init(); parent::init();
if(isset($_GET['debug_profile'])) Profiler::mark("OpenIDAuthenticator_Controller"); if(isset($_GET['debug_profile']))
Profiler::mark("OpenIDAuthenticator_Controller");
die("Not implemented yet!");
if(isset($_GET['debug_profile'])) Profiler::unmark("OpenIDAuthenticator_Controller"); /**
* This is where the example will store its OpenID information.
* You should change this path if you want the example store to be
* created elsewhere. After you're done playing with the example
* script, you'll have to remove this directory manually.
*/
$store_path = TEMP_FOLDER;
if(!file_exists($store_path) && !mkdir($store_path)) {
print "Could not create the FileStore directory '$store_path'. ".
" Please check the effective permissions.";
exit(0);
}
$store = new Auth_OpenID_FileStore($store_path);
$consumer = new Auth_OpenID_Consumer($store, new SessionWrapper());
// Complete the authentication process using the server's response.
$response = $consumer->complete();
if($response->status == Auth_OpenID_CANCEL) {
Session::set("Security.Message.message",
"The verification was cancelled. Please try again.");
Session::set("Security.Message.type", "bad");
if(isset($_GET['debug_profile']))
Profiler::unmark("OpenIDAuthenticator_Controller");
Director::redirect("Security/login");
} else if($response->status == Auth_OpenID_FAILURE) {
Session::set("Security.Message.message", // use $response->message ??
"The OpenID/i-name authentication failed.");
Session::set("Security.Message.type", "bad");
if(isset($_GET['debug_profile']))
Profiler::unmark("OpenIDAuthenticator_Controller");
Director::redirect("Security/login");
} else if($response->status == Auth_OpenID_SUCCESS) {
$openid = $response->identity_url;
$user = $openid;
if($response->endpoint->canonicalID) {
$user = $response->endpoint->canonicalID;
}
if(isset($_GET['debug_profile']))
Profiler::unmark("OpenIDAuthenticator_Controller");
$SQL_user = Convert::raw2sql($user);
if($member = DataObject::get_one("Member", "Email = '$SQL_user'")) {
$firstname = Convert::raw2xml($member->FirstName);
Session::set("Security.Message.message", "Welcome Back, {$firstname}");
Session::set("Security.Message.type", "good");
$member->LogIn(
Session::get('SessionForms.OpenIDLoginForm.Remember'));
Session::clear('SessionForms.OpenIDLoginForm.OpenIDURL');
Session::clear('SessionForms.OpenIDLoginForm.Remember');
if($backURL = Session::get("BackURL")) {
Session::clear("BackURL");
Director::redirect($backURL);
} else {
Director::redirectBack();
}
} else {
Session::set("Security.Message.message",
"Login failed. Please try again.");
Session::set("Security.Message.type", "bad");
if($badLoginURL = Session::get("BadLoginURL")) {
Director::redirect($badLoginURL);
} else {
Director::redirectBack();
}
}
}
} }
@ -166,7 +283,7 @@ class OpenIDAuthenticator_Controller extends Controller {
* *
* @author Markus Lanthaler <markus@silverstripe.com> * @author Markus Lanthaler <markus@silverstripe.com>
*/ */
class SessionWrapper { class SessionWrapper extends Auth_Yadis_PHPSession {
/** /**
* Set a session key/value pair. * Set a session key/value pair.

View File

@ -1,5 +1,12 @@
<?php <?php
/**
* OpenID log-in form
*
* @author Markus Lanthaler <markus@silverstripe.com>
*/
/** /**
* OpenID log-in form * OpenID log-in form
@ -11,11 +18,19 @@ class OpenIDLoginForm extends Form {
/** /**
* Constructor * Constructor
* *
* @param $controller * @param Controller $controller The parent controller, necessary to
* @param $name * create the appropriate form action tag.
* @param $fields * @param string $name The method on the controller that will return this
* @param $actions * form object.
* @param $checkCurrentUser * @param FieldSet|FormField $fields All of the fields in the form - a
* {@link FieldSet} of {@link FormField}
* objects.
* @param FieldSet|FormAction $actions All of the action buttons in the
* form - a {@link FieldSet} of
* {@link FormAction} objects
* @param bool $checkCurrentUser If set to TRUE, it will be checked if a
* the user is currently logged in, and if
* so, only a logout button will be rendered
*/ */
function __construct($controller, $name, $fields = null, $actions = null, function __construct($controller, $name, $fields = null, $actions = null,
$checkCurrentUser = true) { $checkCurrentUser = true) {
@ -28,19 +43,20 @@ class OpenIDLoginForm extends Form {
$backURL = $_REQUEST['BackURL']; $backURL = $_REQUEST['BackURL'];
} else { } else {
$backURL = Session::get('BackURL'); $backURL = Session::get('BackURL');
//Session::clear("BackURL"); don't clear the back URL here! Should be used until the right password is entered!
} }
if($checkCurrentUser && Member::currentUserID()) { if($checkCurrentUser && Member::currentUserID()) {
$fields = new FieldSet(); $fields = new FieldSet();
$actions = new FieldSet(new FormAction("logout", "Log in as someone else")); $actions = new FieldSet(new FormAction("logout",
"Log in as someone else"));
} else { } else {
if(!$fields) { if(!$fields) {
$fields = new FieldSet( $fields = new FieldSet(
new HiddenField("AuthenticationMethod", null, "OpenID"), new HiddenField("AuthenticationMethod", null, "OpenID"),
new TextField("OpenIDURL", "OpenID URL", new TextField("OpenIDURL", "OpenID URL",
Session::get('SessionForms.OpenIDLoginForm.OpenIDURL')), Session::get('SessionForms.OpenIDLoginForm.OpenIDURL')),
new CheckboxField("Remember", "Remember me next time?", true) new CheckboxField("Remember", "Remember me next time?",
Session::get('SessionForms.OpenIDLoginForm.Remember'))
); );
} }
if(!$actions) { if(!$actions) {
@ -79,60 +95,34 @@ class OpenIDLoginForm extends Form {
* @param array $data Submitted data * @param array $data Submitted data
*/ */
public function dologin($data) { public function dologin($data) {
if($this->performLogin($data)){ Session::set('SessionForms.OpenIDLoginForm.Remember',
isset($data['Remember']));
if($backURL = $_REQUEST['BackURL']) { OpenIDAuthenticator::authenticate($data, $this);
Session::clear("BackURL");
Session::clear('SessionForms.OpenIDLoginForm.OpenIDURL');
Director::redirect($backURL);
} else
Director::redirectBack();
// If the OpenID authenticator returns, an error occured!
Session::set('SessionForms.OpenIDLoginForm.OpenIDURL',
$data['OpenIDURL']);
if($badLoginURL = Session::get("BadLoginURL")){
Director::redirect($badLoginURL);
} else { } else {
Session::set('SessionForms.OpenIDLoginForm.OpenIDURL', $data['OpenIDURL']); Director::redirectBack();
if($badLoginURL = Session::get("BadLoginURL")){
Director::redirect($badLoginURL);
} else {
Director::redirectBack();
}
} }
} }
/** /**
* Log out * Log out form handler method
* *
* @todo Figure out for what this method is used! Is it really used at all? * This method is called when the user clicks on "logout" on the form
* created when the parameter <i>$checkCurrentUser</i> of the
* {@link __construct constructor} was set to TRUE and the user was
* currently logged in.
*/ */
public function logout() { public function logout() {
$s = new Security(); $s = new Security();
return $s->logout(); $s->logout();
}
/**
* Try to authenticate the user
*
* @param array Submitted data
* @return Member Returns the member object on successful authentication
* or NULL on failure.
*/
public function performLogin(array $data) {
if($member = OpenIDAuthenticator::authenticate($data)) {
$firstname = Convert::raw2xml($member->FirstName);
$this->sessionMessage("Welcome Back, {$firstname}", "good");
$member->LogIn();
if(isset($data['Remember'])) {
// Deliberately obscure...
Cookie::set('alc_enc',base64_encode("$data[OpenIDURL]"));
}
return $member;
} else {
$this->sessionMessage("Login failed. Please try again.", "bad");
return null;
}
} }
} }

View File

@ -18,20 +18,30 @@ class Security extends Controller {
protected static $strictPathChecking = false; protected static $strictPathChecking = false;
/** /**
* Register that we've had a permission failure trying to view the given page. * Register that we've had a permission failure trying to view the given page
*
* This will redirect to a login page. * This will redirect to a login page.
* @param page The controller that you were on to cause the permission failure.
* @param messageSet The message to show to the user. This can be a string, or a map of different
* messages for different contexts. If you pass an array, you can use the following keys:
* - default: The default message
* - logInAgain: The message to show if the user has just logged out and the
* - alreadyLoggedIn: The message to show if the user is already logged in and lacks the permission to access the item.
* If you don't provide a messageSet, a default will be used. * If you don't provide a messageSet, a default will be used.
*
* @param $page The controller that you were on to cause the permission
* failure.
* @param string|array $messageSet The message to show to the user. This
* can be a string, or a map of different
* messages for different contexts.
* If you pass an array, you can use the
* following keys:
* - default: The default message
* - logInAgain: The message to show
* if the user has just
* logged out and the
* - alreadyLoggedIn: The message to
* show if the user
* is already logged
* in and lacks the
* permission to
* access the item.
*/ */
static function permissionFailure($page = null, $messageSet = null) { static function permissionFailure($page, $messageSet = null) {
// $loginForm = singleton('Security')->LoginForm();
//user_error('debug', E_USER_ERROR);
// Prepare the messageSet provided // Prepare the messageSet provided
if(!$messageSet) { if(!$messageSet) {
$messageSet = array( $messageSet = array(
@ -47,24 +57,24 @@ class Security extends Controller {
if(Member::currentUserID()) { if(Member::currentUserID()) {
// user_error( 'PermFailure with member', E_USER_ERROR ); // user_error( 'PermFailure with member', E_USER_ERROR );
$message = $messageSet['alreadyLoggedIn'] ? $messageSet['alreadyLoggedIn'] : $messageSet['default']; $message = $messageSet['alreadyLoggedIn']
//$_SESSION['LoginForm']['force_form'] = true; ? $messageSet['alreadyLoggedIn']
//$_SESSION['LoginForm']['type'] = 'warning'; : $messageSet['default'];
Member::logout(); Member::logout();
} else if(substr(Director::history(),0,15) == 'Security/logout') { } else if(substr(Director::history(),0,15) == 'Security/logout') {
$message = $messageSet['logInAgain'] ? $messageSet['logInAgain'] : $messageSet['default']; $message = $messageSet['logInAgain']
? $messageSet['logInAgain']
: $messageSet['default'];
} else { } else {
$message = $messageSet['default']; $message = $messageSet['default'];
} }
/* $loginForm->sessionMessage($message, 'warning'); Session::set("Security.Message.message", $message);
Session::set("SecurityMessage.Error.message", $message); Session::set("Security.Message.type", 'warning');
Session::set("SecurityMessage.Error.type", $type);*/
// $_SESSION['LoginForm']['message'] = $message;
Session::set("BackURL", $_SERVER['REQUEST_URI']); Session::set("BackURL", $_SERVER['REQUEST_URI']);
if(Director::is_ajax()) { if(Director::is_ajax()) {
@ -85,10 +95,10 @@ class Security extends Controller {
switch($_REQUEST['AuthenticationMethod']) switch($_REQUEST['AuthenticationMethod'])
{ {
case 'Member': case 'Member':
return MemberAuthenticator::GetLoginForm(); return MemberAuthenticator::GetLoginForm($this);
break; break;
case 'OpenID': case 'OpenID':
return OpenIDAuthenticator::GetLoginForm(); return OpenIDAuthenticator::GetLoginForm($this);
break; break;
} }
} }
@ -99,13 +109,16 @@ class Security extends Controller {
/** /**
* Get the login forms for all available authentication methods * Get the login forms for all available authentication methods
* *
* @return array Returns an array of available login forms (array of Form
* objects).
*
* @todo Check how to activate/deactivate authentication methods * @todo Check how to activate/deactivate authentication methods
*/ */
function GetLoginForms() function GetLoginForms()
{ {
$forms = array(); $forms = array();
array_push($forms, MemberAuthenticator::GetLoginForm()); array_push($forms, MemberAuthenticator::GetLoginForm($this));
array_push($forms, OpenIDAuthenticator::GetLoginForm()); array_push($forms, OpenIDAuthenticator::GetLoginForm($this));
return $forms; return $forms;
} }
@ -115,6 +128,7 @@ class Security extends Controller {
* Get a link to a security action * Get a link to a security action
* *
* @param string $action Name of the action * @param string $action Name of the action
* @return string Returns the link to the given action
*/ */
function Link($action = null) { function Link($action = null) {
return "Security/$action"; return "Security/$action";
@ -130,15 +144,18 @@ class Security extends Controller {
* they should go. * they should go.
*/ */
function logout($redirect = true) { function logout($redirect = true) {
Cookie::set('alc_enc',null); if($member = Member::currentUser())
Cookie::forceExpiry('alc_enc'); $member->logOut();
Session::clear("loggedInAs");
if($redirect) Director::redirectBack(); if($redirect)
Director::redirectBack();
} }
/** /**
* Show the "login" page
* *
* @return string Returns the "login" page as HTML code.
*/ */
function login() { function login() {
Requirements::javascript("jsparty/behaviour.js"); Requirements::javascript("jsparty/behaviour.js");
@ -164,9 +181,23 @@ class Security extends Controller {
foreach($forms as $form) foreach($forms as $form)
$content .= $form->forTemplate(); $content .= $form->forTemplate();
$customisedController = $controller->customise(array( if(strlen($message = Session::get('Security.Message.message')) > 0) {
"Content" => $content $message_type = Session::get('Security.Message.type');
)); if($message_type == 'bad') {
$message = "<p class=\"message $message_type\">$message</p>";
} else {
$message = "<p>$message</p>";
}
$customisedController = $controller->customise(array(
"Content" => $message,
"Form" => $content
));
} else {
$customisedController = $controller->customise(array(
"Content" => $content
));
}
return $customisedController->renderWith("Page"); return $customisedController->renderWith("Page");
} }
@ -174,7 +205,9 @@ class Security extends Controller {
/** /**
* Show the "lost password" page
* *
* @return string Returns the "lost password " page as HTML code.
*/ */
function lostpassword() { function lostpassword() {
Requirements::javascript("jsparty/prototype.js"); Requirements::javascript("jsparty/prototype.js");
@ -189,7 +222,8 @@ class Security extends Controller {
$controller = new Page_Controller($tmpPage); $controller = new Page_Controller($tmpPage);
$customisedController = $controller->customise(array( $customisedController = $controller->customise(array(
"Content" => "<p>Enter your e-mail address and we will send you a password</p>", "Content" =>
"<p>Enter your e-mail address and we will send you a password</p>",
"Form" => $this->LostPasswordForm(), "Form" => $this->LostPasswordForm(),
)); ));
@ -199,7 +233,9 @@ class Security extends Controller {
/** /**
* Show the "password sent" page
* *
* @return string Returns the "password sent" page as HTML code.
*/ */
function passwordsent() { function passwordsent() {
Requirements::javascript("jsparty/behaviour.js"); Requirements::javascript("jsparty/behaviour.js");
@ -216,7 +252,8 @@ class Security extends Controller {
$email = $this->urlParams['ID']; $email = $this->urlParams['ID'];
$customisedController = $controller->customise(array( $customisedController = $controller->customise(array(
"Title" => "Password sent to '$email'", "Title" => "Password sent to '$email'",
"Content" => "<p>Thank you, your password has been sent to '$email'.</p>", "Content" =>
"<p>Thank you, your password has been sent to '$email'.</p>",
)); ));
//Controller::$currentController = $controller; //Controller::$currentController = $controller;
@ -225,7 +262,9 @@ class Security extends Controller {
/** /**
* Factory method for the lost password form
* *
* @return Form Returns the lost password form
*/ */
function LostPasswordForm() { function LostPasswordForm() {
return new MemberLoginForm($this, "LostPasswordForm", new FieldSet( return new MemberLoginForm($this, "LostPasswordForm", new FieldSet(
@ -238,17 +277,23 @@ class Security extends Controller {
/** /**
* Authenticate using the given email and password, returning the appropriate member object if * Authenticate using the given email and password, returning the
* appropriate member object if
*
* @return bool|Member Returns FALSE if authentication fails, otherwise
* the member object
*/ */
static function authenticate($RAW_email, $RAW_password) { static function authenticate($RAW_email, $RAW_password) {
$SQL_email = Convert::raw2sql($RAW_email); $SQL_email = Convert::raw2sql($RAW_email);
$SQL_password = Convert::raw2sql($RAW_password); $SQL_password = Convert::raw2sql($RAW_password);
// Default login (see {@setDetaultAdmin()}) // Default login (see {@setDetaultAdmin()})
if( $RAW_email == self::$username && $RAW_password == self::$password && !empty( self::$username ) && !empty( self::$password ) ) { if(($RAW_email == self::$username) && ($RAW_password == self::$password)
&& !empty(self::$username) && !empty(self::$password)) {
$member = self::findAnAdministrator(); $member = self::findAnAdministrator();
} else { } else {
$member = DataObject::get_one("Member", "Email = '$SQL_email' And Password = '$SQL_password'"); $member = DataObject::get_one("Member",
"Email = '$SQL_email' And Password = '$SQL_password'");
} }
return $member; return $member;
@ -257,8 +302,12 @@ class Security extends Controller {
/** /**
* Return a member with administrator privileges * Return a member with administrator privileges
*
* @return Member Returns a member object that has administrator
* privileges.
*/ */
static function findAnAdministrator($username = 'admin', $password = 'password') { static function findAnAdministrator($username = 'admin',
$password = 'password') {
$permission = DataObject::get_one("Permission", "`Code` = 'ADMIN'"); $permission = DataObject::get_one("Permission", "`Code` = 'ADMIN'");
$adminGroup = null; $adminGroup = null;
@ -292,10 +341,10 @@ class Security extends Controller {
/** /**
* This will set a static default-admin (e.g. "td") which is not existing as * This will set a static default-admin (e.g. "td") which is not existing
* a database-record. By this workaround we can test pages in dev-mode with * as a database-record. By this workaround we can test pages in dev-mode
* a unified login. Submitted login-credentials are first checked against * with a unified login. Submitted login-credentials are first checked
* this static information in {@authenticate()}. * against this static information in {@authenticate()}.
* *
* @param $username String * @param $username String
* @param $password String (Cleartext) * @param $password String (Cleartext)
@ -310,10 +359,12 @@ class Security extends Controller {
/** /**
* Set strict path checking. This prevents sharing of the session * Set strict path checking
* across several sites in the domain.
* *
* @param strictPathChecking boolean to enable or disable strict patch checking. * This prevents sharing of the session across several sites in the domain.
*
* @param boolean $strictPathChecking To enable or disable strict patch
* checking.
*/ */
static function setStrictPathChecking($strictPathChecking) { static function setStrictPathChecking($strictPathChecking) {
self::$strictPathChecking = $strictPathChecking; self::$strictPathChecking = $strictPathChecking;
@ -321,7 +372,9 @@ class Security extends Controller {
/** /**
* Get strict path checking
* *
* @return boolean Status of strict path checking
*/ */
static function getStrictPathChecking() { static function getStrictPathChecking() {
return self::$strictPathChecking; return self::$strictPathChecking;