diff --git a/core/control/ContentController.php b/core/control/ContentController.php index 937695801..ba17be469 100644 --- a/core/control/ContentController.php +++ b/core/control/ContentController.php @@ -7,9 +7,9 @@ * section of an application. * * 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() - * and Content(), will be passed along to the data record, - * + * which is stored in $this->dataRecord. Any unrecognised method calls, for example, Title() + * and Content(), will be passed along to the data record, + * * 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. */ @@ -26,7 +26,7 @@ class ContentController extends Controller { parent::__construct(); } - + public function Link($action = null) { return Director::baseURL() . $this->RelativeLink($action); } @@ -34,7 +34,7 @@ class ContentController extends Controller { if($this->URLSegment){ if($action == "index") $action = ""; - + // '&' in a URL is apparently naughty $action = preg_replace('/&/', '&', $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); } } - + //----------------------------------------------------------------------------------// // These flexible data methods remove the need for custom code to do simple stuff - + /* * Return the children of the given page. * $parentRef can be a page number or a URLSegment @@ -62,12 +62,12 @@ class ContentController extends Controller { } } - + public function Page($url) { $SQL_url = Convert::raw2sql($url); return DataObject::get_one('SiteTree', "URLSegment = '$SQL_url'"); } - + public function init() { parent::init(); @@ -84,7 +84,7 @@ class ContentController extends Controller { singleton('SiteTree')->extend('contentcontrollerInit', $this); Director::set_site_mode('site'); - + // Check permissions if($this->dataRecord && !$this->dataRecord->can('View')) Security::permissionFailure($this); @@ -92,7 +92,7 @@ class ContentController extends Controller { Requirements::themedCSS("typography"); Requirements::themedCSS("form"); } - + /** * Get the project name * @@ -109,9 +109,9 @@ class ContentController extends Controller { public function data() { return $this->dataRecord; } - + /*--------------------------------------------------------------------------------*/ - + /** * 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. * Level(1) will return the main menu item that we're currently inside, etc. */ - + public function Level($level) { $parent = $this->data(); $stack = array($parent); @@ -157,25 +157,28 @@ class ContentController extends Controller { return isset($stack[$level-1]) ? $stack[$level-1] : null; } - + public function Menu($level) { return $this->getMenu($level); } - + public function Section2() { return $this->Level(2)->URLSegment; } - + /** * 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() { - return Object::create('LoginForm', $this, "LoginForm"); + return MemberAuthenticator::getLoginForm($this); } - + public function SilverStripeNavigator() { $member = Member::currentUser(); - + if(Director::isDev() || ($member && $member->isCMSUser())) { Requirements::css('sapphire/css/SilverStripeNavigator.css'); @@ -188,8 +191,8 @@ class ContentController extends Controller { var w = window.open(this.href,windowName(this.target)); w.focus(); return false; - } - } + } + } }); function windowName(suffix) { @@ -213,9 +216,9 @@ JS $thisPage = $this->Link(); $cmsLink = ''; } - + $archiveLink = ""; - + if($date = Versioned::current_archived_date()) { $dateObj = Object::create('Datetime', $date, null); // $dateObj->setVal($date); @@ -224,7 +227,7 @@ JS $liveLink = "Published Site"; $stageLink = "Draft Site"; $message = "
"; - + } else if(Versioned::current_stage() == 'Stage') { $stageLink = "Draft Site"; $liveLink = "Published Site"; @@ -235,7 +238,7 @@ JS $stageLink = "Draft Site"; $message = " "; } - + if($member) { $firstname = Convert::raw2xml($member->FirstName); $surname = Convert::raw2xml($member->Surame); @@ -254,7 +257,7 @@ JS
+ * $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 handler:
- $groups = $member->Groups();
- $checkboxfield = $form->Fields()->fieldByName("NewsletterSubscriptions");
- $groups->setByCheckboxSetField($checkboxfield);
*
+ *
+ * $groups = $member->Groups();
+ * $checkboxfield = $form->Fields()->fieldByName("NewsletterSubscriptions");
+ * $groups->setByCheckboxSetField($checkboxfield);
+ *
+ *
+ * @param CheckboxSetField $checkboxsetfield The CheckboxSetField (with
+ * data) from your form.
*/
- function setByCheckboxSetField($checkboxsetfield){
-
+ function setByCheckboxSetField(CheckboxSetField $checkboxsetfield) {
// Get the values from the formfield.
$values = $checkboxsetfield->Value();
$sourceItems = $checkboxsetfield->getSource();
- if($sourceItems){
+ if($sourceItems) {
// If (some) values are present, add and remove as necessary.
- if($values){
+ if($values) {
// update the groups based on the selections
- foreach($sourceItems as $k => $item){
- if(in_array($k,$values)){
+ foreach($sourceItems as $k => $item) {
+ if(in_array($k,$values)) {
$add[] = $k;
- }else{
+ } else {
$remove[] = $k;
}
}
// else we should be removing all from the necessary groups.
- }else{
+ } else {
$remove = $sourceItems;
}
- if($add)$this->addManyByGroupID($add);
- if($remove) $this->RemoveManyByGroupID($remove);
+ if($add)
+ $this->addManyByGroupID($add);
- }else{
- USER_ERROR("Member::setByCheckboxSetField() - No source items could be found for checkboxsetfield ". $checkboxsetfield->Name(),E_USER_WARNING);
+ if($remove)
+ $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
- * groupID.
+ * Adds this member to the groups based on the group IDs
+ *
+ * @param array $ids Group identifiers.
*/
function addManyByGroupID($groupIds){
$groups = $this->getGroupsFromIDs($groupIds);
- if($groups){
- foreach($groups as $group){
+ if($groups) {
+ foreach($groups as $group) {
$this->add($group);
}
}
-
}
+
/**
- * Removes the member from many groups based on
- * the group ID.
+ * Removes the member from many groups based on the group IDs
+ *
+ * @param array $ids Group identifiers.
*/
- function removeManyByGroupID($groupIds){
+ function removeManyByGroupID($groupIds) {
$groups = $this->getGroupsFromIDs($groupIds);
- if($groups){
- foreach($groups as $group){
+ if($groups) {
+ foreach($groups as $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){
- if($ids && count($ids) > 1){
- return DataObject::get("Group","ID IN (". implode(",",$ids) .")");
- }else{
- return DataObject::get_by_id("Group",$ids[0]);
+ if($ids && count($ids) > 1) {
+ return DataObject::get("Group", "ID IN (" . implode(",", $ids) . ")");
+ } else {
+ 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) {
$groups = $this->codenamesToGroups($codenames);
- if($groups){
+ if($groups) {
foreach($groups as $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) {
$groups = $this->codenamesToGroups($codenames);
- if($groups){
- foreach($groups as $group){
+ if($groups) {
+ foreach($groups as $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) {
$list = "'" . implode("', '", $codenames) . "'";
$output = DataObject::get("Group", "Code IN ($list)");
// Some are missing - throw warnings
- if(!$output || $output->Count() != sizeof($list)) {
- foreach($codenames as $codename) $missing[$codename] = $codename;
- 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);
+ if(!$output || ($output->Count() != sizeof($list))) {
+ foreach($codenames as $codename)
+ $missing[$codename] = $codename;
+
+ 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;
@@ -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 {
protected
- $from = 'ask@perweek.co.nz',
+ $from = '', // setting a blank from address uses the site's default administrator email
$to = '$Email',
$subject = "Thanks for signing up",
$body = '
@@ -644,17 +911,19 @@ class Member_SignupEmail extends Email_Template {
function MemberData() {
return $this->template_data->listOfFields(
- "FirstName","Surname","Email",
- "Phone","Mobile","Street",
- "Suburb","City","Postcode","DriversLicense5A","DriversLicense5B"
+ "FirstName", "Surname", "Email",
+ "Phone", "Mobile", "Street",
+ "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 {
protected $from = ''; // setting a blank from address uses the site's default administrator email
protected $subject = "Your password has been changed";
@@ -662,31 +931,57 @@ class Member_ChangePasswordEmail extends Email_Template {
protected $to = '$Email';
}
+
+
+/**
+ * Class used as template to send the forgot password email
+ */
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 $ss_template = 'ForgotPasswordEmail';
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 {
- static $has_one = array(
- 'NewsletterType' => 'NewsletterType',
- 'Member' => 'Member'
- );
+ static $has_one = array(
+ 'NewsletterType' => 'NewsletterType',
+ '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
- $from = 'ask@perweek.co.nz',
+ $from = '', // setting a blank from address uses the site's default administrator email
$to = '$Email',
$subject = "Your password has been changed",
$body = '
@@ -698,9 +993,19 @@ class Member_UnsubscribeRecord extends DataObject {
Your password has been changed. Please keep this email, for future reference.
'; } -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() { $required = func_get_args(); if(isset($required[0]) && is_array($required[0])) { @@ -708,24 +1013,43 @@ class Member_Validator extends RequiredFields { } $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) { $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', "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']; + $member = DataObject::get_one('Member', + "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(is_object($member) && $member->ID != $id) { $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; } return $valid; } } + + ?> \ No newline at end of file diff --git a/security/MemberAuthenticator.php b/security/MemberAuthenticator.php index 5368529cc..8c63605cd 100644 --- a/security/MemberAuthenticator.php +++ b/security/MemberAuthenticator.php @@ -1,5 +1,13 @@ + */ + + + /** * Authenticator for the default "member" method * @@ -11,18 +19,25 @@ class MemberAuthenticator extends Authenticator { * Method to authenticate an 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 * 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_password = Convert::raw2sql($RAW_data['Password']); $member = DataObject::get_one( - "Member", "Email = '$SQL_user' And Password = '$SQL_password'"); + "Member", "Email = '$SQL_user' AND Password = '$SQL_password'"); if($member) { 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; @@ -32,11 +47,13 @@ class MemberAuthenticator extends Authenticator { /** * 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 * method */ - public function getLoginForm() { - return Object::create("MemberLoginForm", $this, "LoginForm"); + public static function getLoginForm(Controller $controller) { + return Object::create("MemberLoginForm", $controller, "LoginForm"); } } diff --git a/security/MemberLoginForm.php b/security/MemberLoginForm.php index fa54d0e84..55a303f0b 100644 --- a/security/MemberLoginForm.php +++ b/security/MemberLoginForm.php @@ -1,4 +1,11 @@ message = "You're logged in as $member->FirstName."; } Session::set('MemberLoginForm.force_message', false); @@ -75,18 +94,21 @@ class MemberLoginForm extends Form { * @param array $data Submitted 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']) { Session::clear("BackURL"); - Session::clear('SessionForms.MemberLoginForm.Email'); Director::redirect($backURL); } else Director::redirectBack(); } else { 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); } else { 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 $checkCurrentUser 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(); - return $s->logout(); + $s->logout(); } @@ -113,20 +138,16 @@ class MemberLoginForm extends Form { * @return Member Returns the member object on successful authentication * or NULL on failure. */ - public function performLogin($data){ - if($member = MemberAuthenticator::authenticate($data)) { + public function performLogin($data) { + if($member = MemberAuthenticator::authenticate($data, $this)) { $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[Email]:$data[Password]")); - } + Session::set("Security.Message.message", "Welcome Back, {$firstname}"); + Session::set("Security.Message.type", "good"); + + $member->LogIn(isset($data['Remember'])); return $member; } else { - $this->sessionMessage("That doesn't seem to be the right email address or password. Please try again.", "bad"); return null; } } @@ -135,13 +156,15 @@ class MemberLoginForm extends Form { /** * 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 */ function forgotPassword($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) { $member->createNewPassword(); $member->write(); @@ -151,7 +174,9 @@ class MemberLoginForm extends Form { Director::redirect('Security/passwordsent/' . urlencode($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(); } else { diff --git a/security/OpenIDAuthenticator.php b/security/OpenIDAuthenticator.php index 375e1fb1e..d924584ce 100644 --- a/security/OpenIDAuthenticator.php +++ b/security/OpenIDAuthenticator.php @@ -1,5 +1,13 @@ + */ + + + /** * Require the OpenID consumer code. */ @@ -29,10 +37,13 @@ class OpenIDAuthenticator extends Authenticator { * Method to authenticate an 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 * the member object */ - public function authenticate(array $RAW_data) { + public function authenticate(array $RAW_data, Form $form = null) { $openid = $RAW_data['OpenIDURL']; $trust_root = Director::absoluteBaseURL(); @@ -59,9 +70,27 @@ class OpenIDAuthenticator extends Authenticator { // No auth request means we can't begin OpenID. 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) @@ -97,6 +126,7 @@ class OpenIDAuthenticator extends Authenticator { "", "", $form_html, + "Click "Continue" to login. You are only seeing this because you appear to have JavaScript disabled.
", "