2007-07-19 12:40:28 +02:00
< ? php
2007-09-16 02:44:30 +02:00
/**
* The member class which represents the users of the system
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-09-16 02:44:30 +02:00
*/
2007-07-19 12:40:28 +02:00
class Member extends DataObject {
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
static $db = array (
2009-02-02 00:49:53 +01:00
'FirstName' => 'Varchar' ,
'Surname' => 'Varchar' ,
'Email' => 'Varchar' ,
2009-11-14 02:14:32 +01:00
'Password' => 'Varchar(160)' ,
2012-11-09 01:37:35 +01:00
'RememberLoginToken' => 'Varchar(160)' , // Note: this currently holds a hash, not a token.
2009-02-02 00:49:53 +01:00
'NumVisit' => 'Int' ,
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
'LastVisited' => 'SS_Datetime' ,
2007-09-16 03:59:59 +02:00
'Bounced' => 'Boolean' , // Note: This does not seem to be used anywhere.
2012-11-09 01:37:35 +01:00
'AutoLoginHash' => 'Varchar(160)' ,
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
'AutoLoginExpired' => 'SS_Datetime' ,
2009-11-14 02:14:32 +01:00
// This is an arbitrary code pointing to a PasswordEncryptor instance,
// not an actual encryption algorithm.
// Warning: Never change this field after its the first password hashing without
// providing a new cleartext password as well.
'PasswordEncryption' => " Varchar(50) " ,
2010-12-09 09:27:07 +01:00
'Salt' => 'Varchar(50)' ,
2008-04-26 08:31:52 +02:00
'PasswordExpiry' => 'Date' ,
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
'LockedOutUntil' => 'SS_Datetime' ,
2008-04-26 08:31:52 +02:00
'Locale' => 'Varchar(6)' ,
2009-09-10 04:42:26 +02:00
// handled in registerFailedLogin(), only used if $lock_out_after_incorrect_logins is set
2010-06-30 07:02:29 +02:00
'FailedLoginCount' => 'Int' ,
// In ISO format
'DateFormat' => 'Varchar(30)' ,
'TimeFormat' => 'Varchar(30)' ,
2007-07-19 12:40:28 +02:00
);
2007-09-15 23:51:37 +02:00
2007-07-19 12:40:28 +02:00
static $belongs_many_many = array (
2009-02-02 00:49:53 +01:00
'Groups' => 'Group' ,
2007-07-19 12:40:28 +02:00
);
2007-09-14 21:10:18 +02:00
2008-10-16 02:49:51 +02:00
static $has_one = array ();
2009-02-02 00:49:53 +01:00
2008-11-18 02:48:37 +01:00
static $has_many = array ();
2009-02-02 00:49:53 +01:00
2007-10-02 06:55:55 +02:00
static $many_many = array ();
2008-10-16 02:49:51 +02:00
2007-10-02 06:55:55 +02:00
static $many_many_extraFields = array ();
2007-09-14 20:23:28 +02:00
2008-11-24 00:28:16 +01:00
static $default_sort = '"Surname", "FirstName"' ;
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
static $indexes = array (
'Email' => true ,
2009-03-30 05:04:37 +02:00
//'AutoLoginHash' => Array('type'=>'unique', 'value'=>'AutoLoginHash', 'ignoreNulls'=>true) //Removed due to duplicate null values causing MSSQL problems
2007-07-19 12:40:28 +02:00
);
2007-09-14 20:23:28 +02:00
2008-02-25 03:10:37 +01:00
static $notify_password_change = false ;
2008-03-10 22:28:35 +01:00
/**
* All searchable database columns
* in this object , currently queried
* with a " column LIKE '%keywords%'
* statement .
*
* @ var array
* @ todo Generic implementation of $searchable_fields on DataObject ,
* with definition for different searching algorithms
* ( LIKE , FULLTEXT ) and default FormFields to construct a searchform .
*/
static $searchable_fields = array (
2008-08-26 03:45:52 +02:00
'FirstName' ,
'Surname' ,
'Email' ,
);
static $summary_fields = array (
2009-03-04 04:44:11 +01:00
'FirstName' => 'First Name' ,
'Surname' => 'Last Name' ,
'Email' => 'Email' ,
2010-02-16 04:07:30 +01:00
);
/**
* @ var Array See { @ link set_title_columns ()}
*/
static $title_format = null ;
2008-04-26 08:31:52 +02:00
2009-02-02 00:49:53 +01:00
/**
* The unique field used to identify this member .
* By default , it ' s " Email " , but another common
* field could be Username .
*
* @ var string
*/
protected static $unique_identifier_field = 'Email' ;
2008-04-26 08:31:52 +02:00
/**
* { @ link PasswordValidator } object for validating user ' s password
*/
protected static $password_validator = null ;
/**
* The number of days that a password should be valid for .
* By default , this is null , which means that passwords never expire
*/
protected static $password_expiry_days = null ;
2008-04-26 08:32:05 +02:00
protected static $lock_out_after_incorrect_logins = null ;
2008-04-26 08:31:52 +02:00
2009-10-12 05:27:41 +02:00
/**
* If this is set , then a session cookie with the given name will be set on log - in ,
* and cleared on logout .
*/
protected static $login_marker_cookie = null ;
2010-08-10 02:14:19 +02:00
/**
* Indicates that when a { @ link Member } logs in , Member : session_regenerate_id ()
* should be called as a security precaution .
*
* This doesn 't always work, especially if you' re trying to set session cookies
* across an entire site using the domain parameter to session_set_cookie_params ()
*
* @ var boolean
*/
protected static $session_regenerate_id = true ;
public static function set_session_regenerate_id ( $bool ) {
self :: $session_regenerate_id = $bool ;
}
2010-03-04 23:59:03 +01:00
/**
* Ensure the locale is set to something sensible by default .
*/
public function populateDefaults () {
parent :: populateDefaults ();
2010-07-26 06:15:55 +02:00
$this -> Locale = i18n :: get_locale ();
2010-03-04 23:59:03 +01:00
}
2010-03-10 06:11:05 +01:00
function requireDefaultRecords () {
// Default groups should've been built by Group->requireDefaultRecords() already
// Find or create ADMIN group
$adminGroups = Permission :: get_groups_by_permission ( 'ADMIN' );
2010-03-29 05:11:15 +02:00
if ( ! $adminGroups ) {
2010-03-10 06:11:05 +01:00
singleton ( 'Group' ) -> requireDefaultRecords ();
$adminGroups = Permission :: get_groups_by_permission ( 'ADMIN' );
}
2010-03-29 05:11:15 +02:00
$adminGroup = $adminGroups -> First ();
2010-03-10 06:11:05 +01:00
// Add a default administrator to the first ADMIN group found (most likely the default
// group created through Group->requireDefaultRecords()).
$admins = Permission :: get_members_by_permission ( 'ADMIN' );
if ( ! $admins ) {
// Leave 'Email' and 'Password' are not set to avoid creating
// persistent logins in the database. See Security::setDefaultAdmin().
$admin = Object :: create ( 'Member' );
$admin -> FirstName = _t ( 'Member.DefaultAdminFirstname' , 'Default Admin' );
$admin -> write ();
$admin -> Groups () -> add ( $adminGroup );
}
}
2010-03-04 23:59:03 +01:00
2009-10-12 05:27:41 +02:00
/**
* If this is called , then a session cookie will be set to " 1 " whenever a user
* logs in . This lets 3 rd party tools , such as apache ' s mod_rewrite , detect
* whether a user is logged in or not and alter behaviour accordingly .
*
* One known use of this is to bypass static caching for logged in users . This is
* done by putting this into _config . php
* < pre >
* Member :: set_login_marker_cookie ( " SS_LOGGED_IN " );
* </ pre >
*
* And then adding this condition to each of the rewrite rules that make use of
* the static cache .
* < pre >
* RewriteCond % { HTTP_COOKIE } ! SS_LOGGED_IN = 1
* </ pre >
*
* @ param $cookieName string The name of the cookie to set .
*/
static function set_login_marker_cookie ( $cookieName ) {
self :: $login_marker_cookie = $cookieName ;
}
2007-09-15 23:51:37 +02:00
2007-09-16 02:32:48 +02:00
/**
2010-02-05 01:39:15 +01:00
* Check if the passed password matches the stored one ( if the member is not locked out ) .
2007-09-16 02:32:48 +02:00
*
2010-02-05 01:39:15 +01:00
* @ param string $password
* @ return ValidationResult
2007-09-16 02:32:48 +02:00
*/
public function checkPassword ( $password ) {
2010-02-05 01:39:15 +01:00
$result = $this -> canLogIn ();
2009-11-14 02:14:32 +01:00
$spec = Security :: encrypt_password (
$password ,
$this -> Salt ,
$this -> PasswordEncryption ,
$this
);
$e = $spec [ 'encryptor' ];
2010-02-05 01:39:15 +01:00
if ( ! $e -> compare ( $this -> Password , $spec [ 'password' ])) {
$result -> error ( _t (
'Member.ERRORWRONGCRED' ,
'That doesn\'t seem to be the right e-mail address or password. Please try again.'
));
}
return $result ;
2008-04-26 08:32:05 +02:00
}
2010-02-05 01:39:15 +01:00
/**
* Returns a valid { @ link ValidationResult } if this member can currently log in , or an invalid
* one with error messages to display if the member is locked out .
*
* You can hook into this with a " canLogIn " method on an attached extension .
*
* @ return ValidationResult
*/
public function canLogIn () {
$result = new ValidationResult ();
if ( $this -> isLockedOut ()) {
$result -> error ( _t (
'Member.ERRORLOCKEDOUT' ,
'Your account has been temporarily disabled because of too many failed attempts at ' .
'logging in. Please try again in 20 minutes.'
));
}
$this -> extend ( 'canLogIn' , $result );
return $result ;
}
2008-04-26 08:32:05 +02:00
/**
* Returns true if this user is locked out
*/
public function isLockedOut () {
return $this -> LockedOutUntil && time () < strtotime ( $this -> LockedOutUntil );
2007-09-16 02:32:48 +02:00
}
2008-02-25 03:10:37 +01:00
/**
* Regenerate the session_id .
2008-03-10 22:28:35 +01:00
* This wrapper is here to make it easier to disable calls to session_regenerate_id (), should you need to .
* They have caused problems in certain
2008-02-25 03:10:37 +01:00
* quirky problems ( such as using the Windmill 0.3 . 6 proxy ) .
*/
static function session_regenerate_id () {
2010-08-10 02:14:19 +02:00
if ( ! self :: $session_regenerate_id ) return ;
2008-08-13 03:43:09 +02:00
// This can be called via CLI during testing.
if ( Director :: is_cli ()) return ;
2009-02-02 00:49:53 +01:00
$file = '' ;
$line = '' ;
2009-10-21 04:21:05 +02:00
// @ is to supress win32 warnings/notices when session wasn't cleaned up properly
// There's nothing we can do about this, because it's an operating system function!
if ( ! headers_sent ( $file , $line )) @ session_regenerate_id ( true );
2009-02-02 00:49:53 +01:00
}
/**
* Get the field used for uniquely identifying a member
* in the database . { @ see Member :: $unique_identifier_field }
*
* @ return string
*/
static function get_unique_identifier_field () {
return self :: $unique_identifier_field ;
}
/**
* Set the field used for uniquely identifying a member
* in the database . { @ see Member :: $unique_identifier_field }
*
* @ param $field The field name to set as the unique field
*/
static function set_unique_identifier_field ( $field ) {
self :: $unique_identifier_field = $field ;
2008-04-26 08:31:52 +02:00
}
/**
* Set a { @ link PasswordValidator } object to use to validate member ' s passwords .
*/
static function set_password_validator ( $pv ) {
self :: $password_validator = $pv ;
}
2008-09-18 05:59:00 +02:00
/**
* Returns the current { @ link PasswordValidator }
*/
static function password_validator () {
return self :: $password_validator ;
}
2008-04-26 08:31:52 +02:00
/**
* Set the number of days that a password should be valid for .
* Set to null ( the default ) to have passwords never expire .
*/
static function set_password_expiry ( $days ) {
self :: $password_expiry_days = $days ;
}
2008-04-26 08:32:05 +02:00
/**
* Configure the security system to lock users out after this many incorrect logins
*/
static function lock_out_after_incorrect_logins ( $numLogins ) {
self :: $lock_out_after_incorrect_logins = $numLogins ;
}
2008-04-26 08:31:52 +02:00
function isPasswordExpired () {
if ( ! $this -> PasswordExpiry ) return false ;
return strtotime ( date ( 'Y-m-d' )) >= strtotime ( $this -> PasswordExpiry );
2008-02-25 03:10:37 +01:00
}
2007-09-16 02:32:48 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Logs this member in
*
2008-03-10 22:28:35 +01:00
* @ param bool $remember If set to TRUE , the member will be logged in automatically the next time .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
function logIn ( $remember = false ) {
2008-02-25 03:10:37 +01:00
self :: session_regenerate_id ();
2007-07-19 12:40:28 +02:00
Session :: set ( " loggedInAs " , $this -> ID );
2009-10-12 05:27:41 +02:00
// This lets apache rules detect whether the user has logged in
if ( self :: $login_marker_cookie ) Cookie :: set ( self :: $login_marker_cookie , 1 , 0 );
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
$this -> NumVisit ++ ;
2007-09-14 21:10:18 +02:00
if ( $remember ) {
2012-11-09 01:37:35 +01:00
// Store the hash and give the client the cookie with the token.
2010-12-05 01:47:12 +01:00
$generator = new RandomGenerator ();
2012-11-09 01:37:35 +01:00
$token = $generator -> randomToken ( 'sha1' );
$hash = $this -> encryptWithUserSettings ( $token );
$this -> RememberLoginToken = $hash ;
2009-09-10 05:23:31 +02:00
Cookie :: set ( 'alc_enc' , $this -> ID . ':' . $token , 90 , null , null , null , true );
2007-09-14 21:10:18 +02:00
} else {
$this -> RememberLoginToken = null ;
Cookie :: set ( 'alc_enc' , null );
Cookie :: forceExpiry ( 'alc_enc' );
}
2008-04-26 08:32:05 +02:00
// Clear the incorrect log-in count
if ( self :: $lock_out_after_incorrect_logins ) {
2009-09-10 04:42:26 +02:00
$this -> FailedLoginCount = 0 ;
2008-04-26 08:32:05 +02:00
}
2009-02-02 00:49:53 +01:00
// Don't set column if its not built yet (the login might be precursor to a /dev/build...)
if ( array_key_exists ( 'LockedOutUntil' , DB :: fieldList ( 'Member' ))) {
$this -> LockedOutUntil = null ;
}
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
$this -> write ();
2008-08-12 22:59:32 +02:00
// Audit logging hook
$this -> extend ( 'memberLoggedIn' );
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2009-04-29 03:20:24 +02:00
/**
* Check if the member ID logged in session actually
* has a database record of the same ID . If there is
* no logged in user , FALSE is returned anyway .
*
* @ return boolean TRUE record found FALSE no record found
*/
static function logged_in_session_exists () {
if ( $id = Member :: currentUserID ()) {
if ( $member = DataObject :: get_by_id ( 'Member' , $id )) {
if ( $member -> exists ()) return true ;
}
}
return false ;
}
2007-09-14 21:10:18 +02:00
/**
* 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 .
*/
static function autoLogin () {
2009-08-11 11:16:34 +02:00
// Don't bother trying this multiple times
self :: $_already_tried_to_auto_log_in = true ;
2007-09-15 23:43:04 +02:00
if ( strpos ( Cookie :: get ( 'alc_enc' ), ':' ) && ! Session :: get ( " loggedInAs " )) {
list ( $uid , $token ) = explode ( ':' , Cookie :: get ( 'alc_enc' ), 2 );
2007-09-15 02:27:40 +02:00
$SQL_uid = Convert :: raw2sql ( $uid );
2007-09-14 21:10:18 +02:00
2008-11-24 10:31:14 +01:00
$member = DataObject :: get_one ( " Member " , " \" Member \" . \" ID \" = ' $SQL_uid ' " );
2007-09-15 02:27:40 +02:00
2009-03-17 23:25:22 +01:00
// check if autologin token matches
2012-11-09 01:37:35 +01:00
$hash = $member -> encryptWithUserSettings ( $token );
if ( $member && ( ! $member -> RememberLoginToken || $member -> RememberLoginToken != $hash )) {
2007-10-02 23:57:12 +02:00
$member = null ;
}
2007-09-14 21:10:18 +02:00
if ( $member ) {
2008-02-25 03:10:37 +01:00
self :: session_regenerate_id ();
2007-09-14 21:10:18 +02:00
Session :: set ( " loggedInAs " , $member -> ID );
2009-10-12 05:27:41 +02:00
// This lets apache rules detect whether the user has logged in
2010-03-15 06:28:40 +01:00
if ( self :: $login_marker_cookie ) Cookie :: set ( self :: $login_marker_cookie , 1 , 0 , null , null , false , true );
2010-12-05 01:47:12 +01:00
$generator = new RandomGenerator ();
2012-11-09 01:37:35 +01:00
$token = $generator -> randomToken ( 'sha1' );
$hash = $member -> encryptWithUserSettings ( $token );
$member -> RememberLoginToken = $hash ;
2010-03-15 06:28:40 +01:00
Cookie :: set ( 'alc_enc' , $member -> ID . ':' . $token , 90 , null , null , false , true );
2007-09-14 21:10:18 +02:00
$member -> NumVisit ++ ;
$member -> write ();
2008-08-12 22:59:32 +02:00
// Audit logging hook
2008-08-14 05:30:32 +02:00
$member -> extend ( 'memberAutoLoggedIn' );
2007-09-14 21:10:18 +02:00
}
}
}
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Logs this member out .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
function logOut () {
2007-07-19 12:40:28 +02:00
Session :: clear ( " loggedInAs " );
2009-10-12 05:27:41 +02:00
if ( self :: $login_marker_cookie ) Cookie :: set ( self :: $login_marker_cookie , null , 0 );
2008-02-25 03:10:37 +01:00
self :: session_regenerate_id ();
2007-09-14 21:10:18 +02:00
2008-08-12 07:59:15 +02:00
$this -> extend ( 'memberLoggedOut' );
2007-09-14 21:10:18 +02:00
$this -> RememberLoginToken = null ;
Cookie :: set ( 'alc_enc' , null );
Cookie :: forceExpiry ( 'alc_enc' );
$this -> write ();
2008-08-12 22:59:32 +02:00
// Audit logging hook
$this -> extend ( 'memberLoggedOut' );
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2012-11-09 01:37:35 +01:00
/**
* Utility for generating secure password hashes for this member .
*/
public function encryptWithUserSettings ( $string ) {
if ( ! $string ) return null ;
// If the algorithm or salt is not available, it means we are operating
// on legacy account with unhashed password. Do not hash the string.
if ( ! $this -> PasswordEncryption ) {
return $string ;
}
// We assume we have PasswordEncryption and Salt available here.
$e = PasswordEncryptor :: create_for_algorithm ( $this -> PasswordEncryption );
return $e -> encrypt ( $string , $this -> Salt );
}
2007-09-14 21:10:18 +02:00
/**
2012-11-09 01:37:35 +01:00
* Generate an auto login token which can be used to reset the password ,
* at the same time hashing it and storing in the database .
2007-09-16 02:32:48 +02:00
*
2008-03-10 22:28:35 +01:00
* @ param int $lifetime The lifetime of the auto login hash in days ( by default 2 days )
2007-09-16 02:32:48 +02:00
*
2012-11-09 01:37:35 +01:00
* @ returns string Token that should be passed to the client ( but NOT persisted ) .
*
2008-03-10 22:28:35 +01:00
* @ todo Make it possible to handle database errors such as a " duplicate key " error
2007-09-14 21:10:18 +02:00
*/
2012-11-09 01:37:35 +01:00
public function generateAutologinTokenAndStoreHash ( $lifetime = 2 ) {
2007-09-16 02:32:48 +02:00
do {
2010-12-05 01:47:12 +01:00
$generator = new RandomGenerator ();
2012-11-09 01:37:35 +01:00
$token = $generator -> randomToken ();
$hash = $this -> encryptWithUserSettings ( $token );
2008-11-23 01:31:06 +01:00
} while ( DataObject :: get_one ( 'Member' , " \" AutoLoginHash \" = ' $hash ' " ));
2007-09-14 20:23:28 +02:00
2007-09-16 02:32:48 +02:00
$this -> AutoLoginHash = $hash ;
$this -> AutoLoginExpired = date ( 'Y-m-d' , time () + ( 86400 * $lifetime ));
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
$this -> write ();
2012-11-09 01:37:35 +01:00
return $token ;
}
/**
* @ deprecated 2.4
*/
public function generateAutologinHash ( $lifetime = 2 ) {
user_error (
'Member::generateAutologinHash is deprecated - tokens are no longer saved directly into the database ' .
'in plaintext. Use the return value of the Member::generateAutologinTokenAndHash to get the token ' .
'instead.' ,
E_USER_ERROR );
}
/**
* Check the token against the member .
*
* @ param string $autologinToken
*
* @ returns bool Is token valid ?
*/
public function validateAutoLoginToken ( $autologinToken ) {
$hash = $this -> encryptWithUserSettings ( $autologinToken );
$member = DataObject :: get_one (
'Member' ,
" \" AutoLoginHash \" =' " . $hash . " ' AND \" AutoLoginExpired \" > " . DB :: getConn () -> now ()
);
return ( bool ) $member ;
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-16 02:32:48 +02:00
* Return the member for the auto login hash
*
* @ param bool $login Should the member be logged in ?
2007-07-19 12:40:28 +02:00
*/
2008-08-11 07:18:18 +02:00
static function member_from_autologinhash ( $RAW_hash , $login = false ) {
2007-09-14 21:10:18 +02:00
$SQL_hash = Convert :: raw2sql ( $RAW_hash );
2007-09-14 20:23:28 +02:00
2009-03-11 22:50:03 +01:00
$member = DataObject :: get_one ( 'Member' , " \" AutoLoginHash \" =' " . $SQL_hash . " ' AND \" AutoLoginExpired \" > " . DB :: getConn () -> now ());
2007-09-14 20:23:28 +02:00
2007-09-16 02:32:48 +02:00
if ( $login && $member )
2007-07-19 12:40:28 +02:00
$member -> logIn ();
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
return $member ;
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
* Send signup , change password or forgot password informations to an user
*
2008-03-10 22:28:35 +01:00
* @ param string $type Information type to send ( " signup " , " changePassword " or " forgotPassword " )
* @ param array $data Additional data to pass to the email ( can be used in the template )
2007-09-14 21:10:18 +02:00
*/
2007-09-16 02:32:48 +02:00
function sendInfo ( $type = 'signup' , $data = null ) {
2007-07-19 12:40:28 +02:00
switch ( $type ) {
2007-09-14 21:10:18 +02:00
case " signup " :
2008-02-25 03:10:37 +01:00
$e = Object :: create ( 'Member_SignupEmail' );
2007-09-14 21:10:18 +02:00
break ;
case " changePassword " :
2008-02-25 03:10:37 +01:00
$e = Object :: create ( 'Member_ChangePasswordEmail' );
2007-09-14 21:10:18 +02:00
break ;
case " forgotPassword " :
2008-02-25 03:10:37 +01:00
$e = Object :: create ( 'Member_ForgotPasswordEmail' );
2007-09-14 21:10:18 +02:00
break ;
2007-07-19 12:40:28 +02:00
}
2007-09-16 02:32:48 +02:00
if ( is_array ( $data )) {
foreach ( $data as $key => $value )
$e -> $key = $value ;
}
2007-07-19 12:40:28 +02:00
$e -> populateTemplate ( $this );
2009-09-18 05:07:15 +02:00
$e -> setTo ( $this -> Email );
2007-07-19 12:40:28 +02:00
$e -> send ();
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2007-12-02 22:28:41 +01:00
* Returns the fields for the member form - used in the registration / profile module .
* It should return fields that are editable by the admin and the logged - in user .
2007-09-14 21:10:18 +02:00
*
* @ return FieldSet Returns a { @ link FieldSet } containing the fields for
* the member form .
*/
2010-01-22 02:42:45 +01:00
public function getMemberFormFields () {
$fields = parent :: getFrontendFields ();
$fields -> replaceField ( 'Password' , $password = new ConfirmedPasswordField (
'Password' ,
$this -> fieldLabel ( 'Password' ),
null ,
null ,
( bool ) $this -> ID
));
$password -> setCanBeEmpty ( true );
$fields -> replaceField ( 'Locale' , new DropdownField (
'Locale' ,
$this -> fieldLabel ( 'Locale' ),
2010-03-04 23:59:03 +01:00
i18n :: get_existing_translations ()
2010-01-22 02:42:45 +01:00
));
$fields -> removeByName ( 'RememberLoginToken' );
$fields -> removeByName ( 'NumVisit' );
$fields -> removeByName ( 'LastVisited' );
$fields -> removeByName ( 'Bounced' );
$fields -> removeByName ( 'AutoLoginHash' );
$fields -> removeByName ( 'AutoLoginExpired' );
$fields -> removeByName ( 'PasswordEncryption' );
$fields -> removeByName ( 'Salt' );
$fields -> removeByName ( 'PasswordExpiry' );
$fields -> removeByName ( 'FailedLoginCount' );
$fields -> removeByName ( 'LastViewed' );
$fields -> removeByName ( 'LockedOutUntil' );
$this -> extend ( 'updateMemberFormFields' , $fields );
2007-12-02 22:28:41 +01:00
return $fields ;
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
function getValidator () {
return new Member_Validator ();
}
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Returns the current logged in user
*
* @ return bool | Member Returns the member object of the current logged in
* user or FALSE .
2007-07-19 12:40:28 +02:00
*/
static function currentUser () {
2009-08-11 11:16:34 +02:00
$id = Member :: currentUserID ();
2007-09-14 21:10:18 +02:00
if ( $id ) {
2008-11-24 00:28:16 +01:00
return DataObject :: get_one ( " Member " , " \" Member \" . \" ID \" = $id " );
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
* Get the ID of the current logged in user
*
* @ return int Returns the ID of the current logged in user or 0.
*/
2007-07-19 12:40:28 +02:00
static function currentUserID () {
$id = Session :: get ( " loggedInAs " );
2009-08-11 11:16:34 +02:00
if ( ! $id && ! self :: $_already_tried_to_auto_log_in ) {
2007-09-14 21:10:18 +02:00
self :: autoLogin ();
$id = Session :: get ( " loggedInAs " );
}
2007-07-19 12:40:28 +02:00
return is_numeric ( $id ) ? $id : 0 ;
}
2009-08-11 11:16:34 +02:00
private static $_already_tried_to_auto_log_in = false ;
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/*
2008-03-10 22:28:35 +01:00
* Generate a random password , with randomiser to kick in if there ' s no words file on the
2007-09-14 21:10:18 +02:00
* filesystem .
*
* @ return string Returns a random password .
*/
2008-02-25 03:10:37 +01:00
static function create_new_password () {
2007-10-25 03:51:53 +02:00
if ( file_exists ( Security :: get_word_list ())) {
$words = file ( Security :: get_word_list ());
2007-09-14 21:10:18 +02:00
list ( $usec , $sec ) = explode ( ' ' , microtime ());
srand ( $sec + (( float ) $usec * 100000 ));
$word = trim ( $words [ rand ( 0 , sizeof ( $words ) - 1 )]);
$number = rand ( 10 , 999 );
return $word . $number ;
} else {
$random = rand ();
$string = md5 ( $random );
$output = substr ( $string , 0 , 6 );
return $output ;
}
}
/**
2009-02-02 00:49:53 +01:00
* Event handler called before writing to the database .
2007-09-14 21:10:18 +02:00
*/
2007-07-19 12:40:28 +02:00
function onBeforeWrite () {
2008-03-11 02:30:29 +01:00
if ( $this -> SetPassword ) $this -> Password = $this -> SetPassword ;
2010-03-09 21:10:49 +01:00
// If a member with the same "unique identifier" already exists with a different ID, don't allow merging.
// Note: This does not a full replacement for safeguards in the controller layer (e.g. in a registration form),
// but rather a last line of defense against data inconsistencies.
2009-02-02 00:49:53 +01:00
$identifierField = self :: $unique_identifier_field ;
if ( $this -> $identifierField ) {
2010-03-09 21:10:49 +01:00
// Note: Same logic as Member_Validator class
$idClause = ( $this -> ID ) ? sprintf ( " AND \" Member \" . \" ID \" <> %d " , ( int ) $this -> ID ) : '' ;
$existingRecord = DataObject :: get_one (
'Member' ,
sprintf (
" \" %s \" = '%s' %s " ,
$identifierField ,
Convert :: raw2sql ( $this -> $identifierField ),
$idClause
)
);
2007-07-19 12:40:28 +02:00
if ( $existingRecord ) {
2010-04-22 03:39:24 +02:00
throw new ValidationException ( new ValidationResult ( false , sprintf (
_t (
'Member.ValidationIdentifierFailed' ,
'Can\'t overwrite existing member #%d with identical identifier (%s = %s))' ,
PR_MEDIUM ,
'The values in brackets show a fieldname mapped to a value, usually denoting an existing email address'
),
2010-03-09 21:10:49 +01:00
$existingRecord -> ID ,
$identifierField ,
$this -> $identifierField
2010-04-22 03:39:24 +02:00
)));
2007-07-19 12:40:28 +02:00
}
}
2010-03-09 21:10:49 +01:00
2008-07-21 00:36:58 +02:00
// We don't send emails out on dev/tests sites to prevent accidentally spamming users.
// However, if TestMailer is in use this isn't a risk.
2008-10-01 20:32:55 +02:00
if (
( Director :: isLive () || Email :: mailer () instanceof TestMailer )
2009-05-27 02:09:23 +02:00
&& $this -> isChanged ( 'Password' )
2008-10-01 20:32:55 +02:00
&& $this -> record [ 'Password' ]
&& Member :: $notify_password_change
) {
$this -> sendInfo ( 'changePassword' );
}
2010-03-09 21:10:49 +01:00
2009-11-14 02:14:32 +01:00
// The test on $this->ID is used for when records are initially created.
// Note that this only works with cleartext passwords, as we can't rehash
// existing passwords.
2010-07-06 03:54:55 +02:00
if (( ! $this -> ID && $this -> Password ) || $this -> isChanged ( 'Password' )) {
2008-03-11 02:30:29 +01:00
// Password was changed: encrypt the password according the settings
2009-11-14 02:14:32 +01:00
$encryption_details = Security :: encrypt_password (
$this -> Password , // this is assumed to be cleartext
$this -> Salt ,
$this -> PasswordEncryption ,
$this
);
2010-07-06 03:54:55 +02:00
2009-11-14 02:14:32 +01:00
// Overwrite the Password property with the hashed value
2010-07-26 06:15:55 +02:00
$this -> Password = $encryption_details [ 'password' ];
2008-03-11 02:30:29 +01:00
$this -> Salt = $encryption_details [ 'salt' ];
$this -> PasswordEncryption = $encryption_details [ 'algorithm' ];
2010-03-09 21:10:49 +01:00
2008-04-26 08:31:52 +02:00
// If we haven't manually set a password expiry
2009-05-27 02:09:23 +02:00
if ( ! $this -> isChanged ( 'PasswordExpiry' )) {
2008-04-26 08:31:52 +02:00
// then set it for us
if ( self :: $password_expiry_days ) {
$this -> PasswordExpiry = date ( 'Y-m-d' , time () + 86400 * self :: $password_expiry_days );
} else {
$this -> PasswordExpiry = null ;
}
}
2008-03-11 02:30:29 +01:00
}
2010-03-09 21:10:49 +01:00
2008-12-04 23:38:32 +01:00
// save locale
if ( ! $this -> Locale ) {
$this -> Locale = i18n :: get_locale ();
}
2010-07-26 06:15:55 +02:00
2007-07-19 12:40:28 +02:00
parent :: onBeforeWrite ();
}
2008-04-26 08:31:52 +02:00
function onAfterWrite () {
parent :: onAfterWrite ();
2009-05-27 02:09:23 +02:00
if ( $this -> isChanged ( 'Password' )) {
2008-04-26 08:31:52 +02:00
MemberPassword :: log ( $this );
}
}
2011-03-09 03:49:41 +01:00
/**
* If any admin groups are requested , deny the whole save operation .
*
* @ param Array $ids Database IDs of Group records
* @ return boolean
*/
function onChangeGroups ( $ids ) {
// Filter out admin groups to avoid privilege escalation,
// unless the current user is an admin already
if ( ! Permission :: checkMember ( $this , 'ADMIN' )) {
$adminGroups = Permission :: get_groups_by_permission ( 'ADMIN' );
$adminGroupIDs = ( $adminGroups ) ? $adminGroups -> column ( 'ID' ) : array ();
return count ( array_intersect ( $ids , $adminGroupIDs )) == 0 ;
} else {
return true ;
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2008-11-03 02:57:16 +01:00
* Check if the member is in one of the given groups .
2007-09-14 21:10:18 +02:00
*
2008-11-03 02:57:16 +01:00
* @ param array | DataObjectSet $groups Collection of { @ link Group } DataObjects to check
* @ param boolean $strict Only determine direct group membership if set to true ( Default : false )
* @ return bool Returns TRUE if the member is in one of the given groups , otherwise FALSE .
2007-07-19 12:40:28 +02:00
*/
2008-11-03 02:57:16 +01:00
public function inGroups ( $groups , $strict = false ) {
2008-11-03 14:48:04 +01:00
if ( $groups ) foreach ( $groups as $group ) {
2008-11-03 02:57:16 +01:00
if ( $this -> inGroup ( $group , $strict )) return true ;
}
return false ;
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2008-11-03 02:57:16 +01:00
* Check if the member is in the given group or any parent groups .
2007-09-14 21:10:18 +02:00
*
2008-09-22 18:07:28 +02:00
* @ param int | Group | string $group Group instance , Group Code or ID
2008-11-03 02:57:16 +01:00
* @ param boolean $strict Only determine direct group membership if set to TRUE ( Default : FALSE )
2008-09-22 18:07:28 +02:00
* @ return bool Returns TRUE if the member is in the given group , otherwise FALSE .
2007-07-19 12:40:28 +02:00
*/
2008-11-03 02:57:16 +01:00
public function inGroup ( $group , $strict = false ) {
2008-09-22 18:07:28 +02:00
if ( is_numeric ( $group )) {
$groupCheckObj = DataObject :: get_by_id ( 'Group' , $group );
} elseif ( is_string ( $group )) {
$SQL_group = Convert :: raw2sql ( $group );
2008-11-24 10:31:14 +01:00
$groupCheckObj = DataObject :: get_one ( 'Group' , " \" Code \" = ' { $SQL_group } ' " );
2008-11-03 02:57:16 +01:00
} elseif ( $group instanceof Group ) {
2008-09-22 18:07:28 +02:00
$groupCheckObj = $group ;
} else {
user_error ( 'Member::inGroup(): Wrong format for $group parameter' , E_USER_ERROR );
}
2009-04-29 02:07:39 +02:00
if ( ! $groupCheckObj ) return false ;
2008-11-03 02:57:16 +01:00
$groupCandidateObjs = ( $strict ) ? $this -> getManyManyComponents ( " Groups " ) : $this -> Groups ();
2008-11-03 14:48:04 +01:00
if ( $groupCandidateObjs ) foreach ( $groupCandidateObjs as $groupCandidateObj ) {
2008-11-03 02:57:16 +01:00
if ( $groupCandidateObj -> ID == $groupCheckObj -> ID ) return true ;
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
return false ;
}
2008-10-16 03:04:38 +02:00
2010-06-06 04:02:34 +02:00
/**
* Adds the member to a group . This will create the group if the given
* group code does not return a valid group object .
*
* @ param string $groupcode
* @ param string Title of the group
*/
public function addToGroupByCode ( $groupcode , $title = " " ) {
$group = DataObject :: get_one ( 'Group' , " \" Code \" = ' " . Convert :: raw2sql ( $groupcode ) . " ' " );
if ( $group ) {
$this -> Groups () -> add ( $group );
}
else {
if ( ! $title ) $title = $groupcode ;
$group = new Group ();
$group -> Code = $groupcode ;
$group -> Title = $title ;
$group -> write ();
$this -> Groups () -> add ( $group );
}
}
2008-10-16 03:04:38 +02:00
/**
* Returns true if this user is an administrator .
* Administrators have access to everything .
*
2008-11-05 00:31:33 +01:00
* @ deprecated Use Permission :: check ( 'ADMIN' ) instead
2008-10-16 03:04:38 +02:00
* @ return Returns TRUE if this user is an administrator .
*/
function isAdmin () {
2008-11-05 00:31:33 +01:00
return Permission :: checkMember ( $this , 'ADMIN' );
2008-10-16 03:04:38 +02:00
}
2010-02-16 04:07:30 +01:00
/**
* @ param Array $columns Column names on the Member record to show in { @ link getTitle ()} .
* @ param String $sep Separator
*/
static function set_title_columns ( $columns , $sep = ' ' ) {
if ( ! is_array ( $columns )) $columns = array ( $columns );
self :: $title_format = array ( 'columns' => $columns , 'sep' => $sep );
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
//------------------- HELPER METHODS -----------------------------------//
2007-07-19 12:40:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2010-02-16 04:07:30 +01:00
* Get the complete name of the member , by default in the format " <Surname>, <FirstName> " .
* Falls back to showing either field on its own .
*
* You can overload this getter with { @ link set_title_format ()}
* and { @ link set_title_sql ()} .
2007-09-14 21:10:18 +02:00
*
* @ return string Returns the first - and surname of the member . If the ID
2010-02-16 04:07:30 +01:00
* of the member is equal 0 , only the surname is returned .
2007-09-14 21:10:18 +02:00
*/
2007-07-19 12:40:28 +02:00
public function getTitle () {
2010-02-16 04:07:30 +01:00
if ( self :: $title_format ) {
$values = array ();
foreach ( self :: $title_format [ 'columns' ] as $col ) {
$values [] = $this -> getField ( $col );
}
return join ( self :: $title_format [ 'sep' ], $values );
}
2007-07-19 12:40:28 +02:00
if ( $this -> getField ( 'ID' ) === 0 )
return $this -> getField ( 'Surname' );
2009-04-17 08:00:32 +02:00
else {
if ( $this -> getField ( 'Surname' ) && $this -> getField ( 'FirstName' )){
return $this -> getField ( 'Surname' ) . ', ' . $this -> getField ( 'FirstName' );
} elseif ( $this -> getField ( 'Surname' )){
return $this -> getField ( 'Surname' );
} elseif ( $this -> getField ( 'FirstName' )){
return $this -> getField ( 'FirstName' );
} else {
return null ;
}
}
2007-09-14 20:23:28 +02:00
}
2010-06-30 07:02:29 +02:00
2010-02-16 04:07:30 +01:00
/**
* Return a SQL CONCAT () fragment suitable for a SELECT statement .
* Useful for custom queries which assume a certain member title format .
*
* @ param String $tableName
* @ return String SQL
*/
static function get_title_sql ( $tableName = 'Member' ) {
2010-05-21 06:44:45 +02:00
// This should be abstracted to SSDatabase concatOperator or similar.
$op = ( DB :: getConn () instanceof MSSQLDatabase ) ? " + " : " || " ;
2010-02-16 04:07:30 +01:00
if ( self :: $title_format ) {
2010-02-16 04:09:27 +01:00
$columnsWithTablename = array ();
foreach ( self :: $title_format [ 'columns' ] as $column ) {
2010-04-30 07:24:58 +02:00
$columnsWithTablename [] = " \" $tableName\ " . \ " $column\ " " ;
2010-02-16 04:09:27 +01:00
}
2010-05-21 06:44:45 +02:00
return " ( " . join ( " $op ' " . self :: $title_format [ 'sep' ] . " ' $op " , $columnsWithTablename ) . " ) " ;
2010-02-16 04:07:30 +01:00
} else {
2010-05-21 06:44:45 +02:00
return " ( \" $tableName\ " . \ " Surname \" $op ' ' $op \" $tableName\ " . \ " FirstName \" ) " ;
2010-02-16 04:07:30 +01:00
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
* Get the complete name of the member
*
* @ return string Returns the first - and surname of the member .
*/
2007-07-19 12:40:28 +02:00
public function getName () {
2010-08-13 01:55:38 +02:00
return ( $this -> Surname ) ? trim ( $this -> FirstName . ' ' . $this -> Surname ) : $this -> FirstName ;
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
/**
* Set first - and surname
*
* 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 );
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
/**
* Alias for { @ link setName }
*
* @ param string $name The name
* @ see setName ()
*/
public function splitName ( $name ) {
return $this -> setName ( $name );
2007-07-19 12:40:28 +02:00
}
2010-06-30 07:02:29 +02:00
/**
* Override the default getter for DateFormat so the
* default format for the user ' s locale is used
* if the user has not defined their own .
*
* @ return string ISO date format
*/
public function getDateFormat () {
if ( $this -> getField ( 'DateFormat' )) {
return $this -> getField ( 'DateFormat' );
} elseif ( $this -> getField ( 'Locale' )) {
require_once 'Zend/Date.php' ;
return Zend_Locale_Format :: getDateFormat ( $this -> Locale );
} else {
return i18n :: get_date_format ();
}
}
/**
* Override the default getter for TimeFormat so the
* default format for the user ' s locale is used
* if the user has not defined their own .
*
* @ return string ISO date format
*/
public function getTimeFormat () {
if ( $this -> getField ( 'TimeFormat' )) {
return $this -> getField ( 'TimeFormat' );
} elseif ( $this -> getField ( 'Locale' )) {
require_once 'Zend/Date.php' ;
return Zend_Locale_Format :: getTimeFormat ( $this -> Locale );
} else {
return i18n :: get_time_format ();
}
}
2007-09-14 21:10:18 +02:00
//---------------------------------------------------------------------//
2007-07-19 12:40:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2007-09-15 02:23:44 +02:00
* Get a " many-to-many " map that holds for all members their group
* memberships
*
* @ return Member_GroupSet Returns a map holding for all members their
* group memberships .
2007-09-14 21:10:18 +02:00
*/
2007-07-19 12:40:28 +02:00
public function Groups () {
$groups = $this -> getManyManyComponents ( " Groups " );
$groupIDs = $groups -> column ();
$collatedGroups = array ();
2008-10-16 07:43:57 +02:00
if ( $groups ) {
foreach ( $groups as $group ) {
$collatedGroups = array_merge (( array ) $collatedGroups , $group -> collateAncestorIDs ());
}
2007-07-19 12:40:28 +02:00
}
$table = " Group_Members " ;
2007-09-14 20:23:28 +02:00
if ( count ( $collatedGroups ) > 0 ) {
2007-07-19 12:40:28 +02:00
$collatedGroups = implode ( " , " , array_unique ( $collatedGroups ));
2009-10-30 01:23:02 +01:00
$unfilteredGroups = singleton ( 'Group' ) -> instance_get ( " \" Group \" . \" ID \" IN ( $collatedGroups ) " , " \" Group \" . \" ID \" " , " " , " " , " Member_GroupSet " );
2008-04-26 08:35:03 +02:00
$result = new ComponentSet ();
// Only include groups where allowedIPAddress() returns true
2008-08-13 03:43:09 +02:00
$ip = isset ( $_SERVER [ 'REMOTE_ADDR' ]) ? $_SERVER [ 'REMOTE_ADDR' ] : null ;
2008-04-26 08:35:03 +02:00
foreach ( $unfilteredGroups as $group ) {
2008-08-13 03:43:09 +02:00
if ( $group -> allowedIPAddress ( $ip )) $result -> push ( $group );
2008-04-26 08:35:03 +02:00
}
2007-07-19 12:40:28 +02:00
} else {
$result = new Member_GroupSet ();
}
2008-10-16 07:43:57 +02:00
$result -> setComponentInfo ( " many-to-many " , $this , " Member " , $table , " Group " );
2007-07-19 12:40:28 +02:00
return $result ;
}
2007-09-14 21:10:18 +02:00
/**
2007-09-15 02:23:44 +02:00
* Get member SQLMap
*
* @ param string $filter Filter for the SQL statement ( WHERE clause )
* @ param string $sort Sorting function ( ORDER clause )
* @ param string $blank Shift a blank member in the items
* @ return SQLMap Returns an SQLMap that returns all Member data .
*
* @ todo Improve documentation of this function ! ( Markus )
2007-09-14 21:10:18 +02:00
*/
2007-07-19 12:40:28 +02:00
public function map ( $filter = " " , $sort = " " , $blank = " " ) {
$ret = new SQLMap ( singleton ( 'Member' ) -> extendedSQL ( $filter , $sort ));
2007-09-14 21:10:18 +02:00
if ( $blank ) {
2008-07-11 01:52:08 +02:00
$blankMember = Object :: create ( 'Member' );
2007-07-19 12:40:28 +02:00
$blankMember -> Surname = $blank ;
$blankMember -> ID = 0 ;
2010-08-10 07:42:33 +02:00
$ret -> getItems () -> unshift ( $blankMember );
2007-07-19 12:40:28 +02:00
}
2007-09-15 02:23:44 +02:00
2007-07-19 12:40:28 +02:00
return $ret ;
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2007-09-15 02:23:44 +02:00
* Get a member SQLMap of members in specific groups
*
* @ param mixed $groups Optional groups to include in the map . If NULL is
* passed , all groups are returned , i . e .
* { @ link map ()} will be called .
* @ return SQLMap Returns an SQLMap that returns all Member data .
* @ see map ()
*
* @ todo Improve documentation of this function ! ( Markus )
2007-09-14 21:10:18 +02:00
*/
public static function mapInGroups ( $groups = null ) {
if ( ! $groups )
2007-07-19 12:40:28 +02:00
return Member :: map ();
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
$groupIDList = array ();
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
if ( is_a ( $groups , 'DataObjectSet' )) {
2007-07-19 12:40:28 +02:00
foreach ( $groups as $group )
$groupIDList [] = $group -> ID ;
2007-09-14 21:10:18 +02:00
} elseif ( is_array ( $groups )) {
2007-07-19 12:40:28 +02:00
$groupIDList = $groups ;
2007-09-14 21:10:18 +02:00
} else {
2007-07-19 12:40:28 +02:00
$groupIDList [] = $groups ;
2007-09-14 21:10:18 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
if ( empty ( $groupIDList ))
2007-09-14 20:23:28 +02:00
return Member :: map ();
2007-09-14 21:10:18 +02:00
return new SQLMap ( singleton ( 'Member' ) -> extendedSQL (
2008-11-23 01:31:06 +01:00
" \" GroupID \" IN ( " . implode ( ',' , $groupIDList ) .
" ) " , " Surname, FirstName " , " " , " INNER JOIN \" Group_Members \" ON \" MemberID \" = \" Member \" . \" ID \" " ));
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Get a map of all members in the groups given that have 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 .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
public static function mapInCMSGroups ( $groups = null ) {
if ( ! $groups || $groups -> Count () == 0 ) {
2008-08-13 00:14:36 +02:00
$perms = array ( 'ADMIN' , 'CMS_ACCESS_AssetAdmin' );
$cmsPerms = singleton ( 'CMSMain' ) -> providePermissions ();
if ( ! empty ( $cmsPerms )) {
$perms = array_unique ( array_merge ( $perms , array_keys ( $cmsPerms )));
}
2008-08-18 02:51:55 +02:00
$SQL_perms = " ' " . implode ( " ', ' " , Convert :: raw2sql ( $perms )) . " ' " ;
2008-08-13 00:14:36 +02:00
2007-09-14 21:10:18 +02:00
$groups = DataObject :: get ( 'Group' , " " , " " ,
2008-11-24 10:31:14 +01:00
" INNER JOIN \" Permission \" ON \" Permission \" . \" GroupID \" = \" Group \" . \" ID \" AND \" Permission \" . \" Code \" IN ( $SQL_perms ) " );
2007-09-14 21:10:18 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
$groupIDList = array ();
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
if ( is_a ( $groups , 'DataObjectSet' )) {
2009-02-02 00:49:53 +01:00
foreach ( $groups as $group ) {
2007-07-19 12:40:28 +02:00
$groupIDList [] = $group -> ID ;
2009-02-02 00:49:53 +01:00
}
2007-09-14 21:10:18 +02:00
} elseif ( is_array ( $groups )) {
2007-07-19 12:40:28 +02:00
$groupIDList = $groups ;
2007-09-14 21:10:18 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
$filterClause = ( $groupIDList )
2008-11-23 01:31:06 +01:00
? " \" GroupID \" IN ( " . implode ( ',' , $groupIDList ) . " ) "
2007-09-14 21:10:18 +02:00
: " " ;
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
return new SQLMap ( singleton ( 'Member' ) -> extendedSQL ( $filterClause ,
" Surname, FirstName " , " " ,
2008-11-23 01:31:06 +01:00
" INNER JOIN \" Group_Members \" ON \" MemberID \" = \" Member \" . \" ID \" INNER JOIN \" Group \" ON \" Group \" . \" ID \" = \" GroupID \" " ));
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-09-14 20:23:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Get the groups in which the member is NOT in
*
* 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 .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
public function memberNotInGroups ( $groupList , $memberGroups = null ){
2009-02-02 00:49:53 +01:00
if ( ! $memberGroups ) $memberGroups = $this -> Groups ();
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
foreach ( $memberGroups as $group ) {
if ( in_array ( $group -> Code , $groupList )) {
$index = array_search ( $group -> Code , $groupList );
2007-07-19 12:40:28 +02:00
unset ( $groupList [ $index ]);
}
}
2009-02-02 00:49:53 +01:00
2007-07-19 12:40:28 +02:00
return $groupList ;
}
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* 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 .
2007-07-19 12:40:28 +02:00
*/
public function getCMSFields () {
2010-06-30 07:02:29 +02:00
require_once ( 'Zend/Date.php' );
2008-10-14 02:29:16 +02:00
$fields = parent :: getCMSFields ();
2010-02-03 02:20:12 +01:00
2008-10-09 19:31:32 +02:00
$mainFields = $fields -> fieldByName ( " Root " ) -> fieldByName ( " Main " ) -> Children ;
2010-02-03 02:20:12 +01:00
2008-11-22 04:33:00 +01:00
$password = new ConfirmedPasswordField (
'Password' ,
2009-01-05 07:19:48 +01:00
null ,
2008-11-22 04:33:00 +01:00
null ,
null ,
true // showOnClick
);
2007-11-12 03:58:27 +01:00
$password -> setCanBeEmpty ( true );
2009-01-05 07:19:48 +01:00
if ( ! $this -> ID ) $password -> showOnClick = false ;
2008-10-09 19:31:32 +02:00
$mainFields -> replaceField ( 'Password' , $password );
2008-10-03 18:23:56 +02:00
2008-10-09 19:31:32 +02:00
$mainFields -> insertBefore (
2008-10-16 15:26:50 +02:00
new HeaderField ( 'MemberDetailsHeader' , _t ( 'Member.PERSONALDETAILS' , " Personal Details " , PR_MEDIUM , 'Headline for formfields' )),
2008-10-03 18:23:56 +02:00
'FirstName'
);
2008-10-09 19:31:32 +02:00
$mainFields -> insertBefore (
2008-10-16 15:26:50 +02:00
new HeaderField ( 'MemberUserDetailsHeader' , _t ( 'Member.USERDETAILS' , " User Details " , PR_MEDIUM , 'Headline for formfields' )),
2008-10-03 18:23:56 +02:00
'Email'
);
2008-10-09 19:31:32 +02:00
$mainFields -> replaceField ( 'Locale' , new DropdownField (
2008-10-03 18:23:56 +02:00
" Locale " ,
_t ( 'Member.INTERFACELANG' , " Interface Language " , PR_MEDIUM , 'Language of the CMS' ),
2010-03-04 23:59:03 +01:00
i18n :: get_existing_translations ()
2008-10-03 18:23:56 +02:00
));
2008-10-09 19:31:32 +02:00
$mainFields -> removeByName ( 'Bounced' );
$mainFields -> removeByName ( 'RememberLoginToken' );
$mainFields -> removeByName ( 'AutoLoginHash' );
$mainFields -> removeByName ( 'AutoLoginExpired' );
$mainFields -> removeByName ( 'PasswordEncryption' );
$mainFields -> removeByName ( 'PasswordExpiry' );
$mainFields -> removeByName ( 'LockedOutUntil' );
2010-02-15 23:57:04 +01:00
if ( ! self :: $lock_out_after_incorrect_logins ) {
$mainFields -> removeByName ( 'FailedLoginCount' );
}
2008-10-09 19:31:32 +02:00
$mainFields -> removeByName ( 'Salt' );
$mainFields -> removeByName ( 'NumVisit' );
$mainFields -> removeByName ( 'LastVisited' );
2008-10-14 02:29:16 +02:00
2008-10-03 18:23:56 +02:00
$fields -> removeByName ( 'Subscriptions' );
2010-02-23 01:41:27 +01:00
2008-10-03 18:23:56 +02:00
// Groups relation will get us into logical conflicts because
// Members are displayed within group edit form in SecurityAdmin
$fields -> removeByName ( 'Groups' );
2010-02-23 01:41:27 +01:00
if ( Permission :: check ( 'EDIT_PERMISSIONS' )) {
$groupsField = new TreeMultiselectField ( 'Groups' , false , 'Group' );
$fields -> findOrMakeTab ( 'Root.Groups' , singleton ( 'Group' ) -> i18n_plural_name ());
$fields -> addFieldToTab ( 'Root.Groups' , $groupsField );
2010-02-23 01:57:38 +01:00
// Add permission field (readonly to avoid complicated group assignment logic).
// This should only be available for existing records, as new records start
// with no permissions until they have a group assignment anyway.
if ( $this -> ID ) {
$permissionsField = new PermissionCheckboxSetField_Readonly (
'Permissions' ,
singleton ( 'Permission' ) -> i18n_plural_name (),
'Permission' ,
'GroupID' ,
// we don't want parent relationships, they're automatically resolved in the field
$this -> getManyManyComponents ( 'Groups' )
);
$fields -> findOrMakeTab ( 'Root.Permissions' , singleton ( 'Permission' ) -> i18n_plural_name ());
$fields -> addFieldToTab ( 'Root.Permissions' , $permissionsField );
}
2010-02-23 01:41:27 +01:00
}
2010-06-30 07:02:29 +02:00
$defaultDateFormat = Zend_Locale_Format :: getDateFormat ( $this -> Locale );
$dateFormatMap = array (
'MMM d, yyyy' => Zend_Date :: now () -> toString ( 'MMM d, yyyy' ),
'yyyy/MM/dd' => Zend_Date :: now () -> toString ( 'yyyy/MM/dd' ),
'MM/dd/yyyy' => Zend_Date :: now () -> toString ( 'MM/dd/yyyy' ),
'dd/MM/yyyy' => Zend_Date :: now () -> toString ( 'dd/MM/yyyy' ),
);
$dateFormatMap [ $defaultDateFormat ] = Zend_Date :: now () -> toString ( $defaultDateFormat )
. sprintf ( ' (%s)' , _t ( 'Member.DefaultDateTime' , 'default' ));
$mainFields -> push (
$dateFormatField = new Member_DatetimeOptionsetField (
'DateFormat' ,
$this -> fieldLabel ( 'DateFormat' ),
$dateFormatMap
)
);
$dateFormatField -> setValue ( $this -> DateFormat );
$defaultTimeFormat = Zend_Locale_Format :: getTimeFormat ( $this -> Locale );
$timeFormatMap = array (
'h:mm a' => Zend_Date :: now () -> toString ( 'h:mm a' ),
'H:mm' => Zend_Date :: now () -> toString ( 'H:mm' ),
);
$timeFormatMap [ $defaultTimeFormat ] = Zend_Date :: now () -> toString ( $defaultTimeFormat )
. sprintf ( ' (%s)' , _t ( 'Member.DefaultDateTime' , 'default' ));
$mainFields -> push (
$timeFormatField = new Member_DatetimeOptionsetField (
'TimeFormat' ,
$this -> fieldLabel ( 'TimeFormat' ),
$timeFormatMap
)
);
$timeFormatField -> setValue ( $this -> TimeFormat );
2009-02-02 00:49:53 +01:00
$this -> extend ( 'updateCMSFields' , $fields );
2008-10-09 19:31:32 +02:00
return $fields ;
2007-07-19 12:40:28 +02:00
}
2008-10-03 20:30:27 +02:00
2009-04-29 02:07:39 +02:00
/**
*
* @ param boolean $includerelations a boolean value to indicate if the labels returned include relation fields
*
*/
function fieldLabels ( $includerelations = true ) {
$labels = parent :: fieldLabels ( $includerelations );
2008-10-03 20:30:27 +02:00
2010-02-03 02:20:12 +01:00
$labels [ 'FirstName' ] = _t ( 'Member.FIRSTNAME' , 'First Name' );
$labels [ 'Surname' ] = _t ( 'Member.SURNAME' , 'Surname' );
$labels [ 'Email' ] = _t ( 'Member.EMAIL' , 'Email' );
2008-11-02 21:04:10 +01:00
$labels [ 'Password' ] = _t ( 'Member.db_Password' , 'Password' );
$labels [ 'NumVisit' ] = _t ( 'Member.db_NumVisit' , 'Number of Visits' );
$labels [ 'LastVisited' ] = _t ( 'Member.db_LastVisited' , 'Last Visited Date' );
$labels [ 'PasswordExpiry' ] = _t ( 'Member.db_PasswordExpiry' , 'Password Expiry Date' , PR_MEDIUM , 'Password expiry date' );
$labels [ 'LockedOutUntil' ] = _t ( 'Member.db_LockedOutUntil' , 'Locked out until' , PR_MEDIUM , 'Security related date' );
$labels [ 'Locale' ] = _t ( 'Member.db_Locale' , 'Interface Locale' );
2009-04-29 02:07:39 +02:00
if ( $includerelations ){
$labels [ 'Groups' ] = _t ( 'Member.belongs_many_many_Groups' , 'Groups' , PR_MEDIUM , 'Security Groups this member belongs to' );
}
2008-10-03 20:30:27 +02:00
return $labels ;
}
2007-11-09 02:10:45 +01:00
2009-02-04 00:33:28 +01:00
/**
* Users can view their own record .
* Otherwise they ' ll need ADMIN or CMS_ACCESS_SecurityAdmin permissions .
* This is likely to be customized for social sites etc . with a looser permission model .
*/
function canView ( $member = null ) {
if ( ! $member || ! ( is_a ( $member , 'Member' )) || is_numeric ( $member )) $member = Member :: currentUser ();
// decorated access checks
$results = $this -> extend ( 'canView' , $member );
2009-12-03 06:43:58 +01:00
if ( $results && is_array ( $results )) {
if ( ! min ( $results )) return false ;
else return true ;
}
2009-02-04 00:33:28 +01:00
// members can usually edit their own record
2009-12-03 06:43:58 +01:00
if ( $member && $this -> ID == $member -> ID ) return true ;
2009-02-04 00:33:28 +01:00
if (
Permission :: checkMember ( $member , 'ADMIN' )
|| Permission :: checkMember ( $member , 'CMS_ACCESS_SecurityAdmin' )
) {
return true ;
}
return false ;
}
/**
* Users can edit their own record .
* Otherwise they ' ll need ADMIN or CMS_ACCESS_SecurityAdmin permissions
*/
2008-11-25 23:34:57 +01:00
function canEdit ( $member = null ) {
2009-02-04 00:33:28 +01:00
if ( ! $member || ! ( is_a ( $member , 'Member' )) || is_numeric ( $member )) $member = Member :: currentUser ();
// decorated access checks
$results = $this -> extend ( 'canEdit' , $member );
2009-12-03 06:43:58 +01:00
if ( $results && is_array ( $results )) {
if ( ! min ( $results )) return false ;
else return true ;
}
2009-02-04 00:33:28 +01:00
2009-11-19 00:21:51 +01:00
// No member found
if ( ! ( $member && $member -> exists ())) return false ;
2010-09-16 07:20:09 +02:00
// If the requesting member is not an admin, but has access to manage members,
// he still can't edit other members with ADMIN permission.
// This is a bit weak, strictly speaking he shouldn't be allowed to
// perform any action that could change the password on a member
// with "higher" permissions than himself, but thats hard to determine.
if ( ! Permission :: checkMember ( $member , 'ADMIN' ) && Permission :: checkMember ( $this , 'ADMIN' )) return false ;
2009-02-04 00:33:28 +01:00
return $this -> canView ( $member );
}
/**
* Users can edit their own record .
* Otherwise they ' ll need ADMIN or CMS_ACCESS_SecurityAdmin permissions
*/
function canDelete ( $member = null ) {
if ( ! $member || ! ( is_a ( $member , 'Member' )) || is_numeric ( $member )) $member = Member :: currentUser ();
2007-11-09 02:21:26 +01:00
2009-02-04 00:33:28 +01:00
// decorated access checks
$results = $this -> extend ( 'canDelete' , $member );
2009-12-03 06:43:58 +01:00
if ( $results && is_array ( $results )) {
if ( ! min ( $results )) return false ;
else return true ;
}
2008-11-25 23:34:57 +01:00
2009-11-19 00:21:51 +01:00
// No member found
if ( ! ( $member && $member -> exists ())) return false ;
2009-02-04 00:33:28 +01:00
return $this -> canEdit ( $member );
2007-11-09 02:10:45 +01:00
}
2008-04-26 08:31:52 +02:00
/**
* Validate this member object .
*/
function validate () {
$valid = parent :: validate ();
2009-05-27 02:09:23 +02:00
if ( ! $this -> ID || $this -> isChanged ( 'Password' )) {
2008-10-02 04:45:58 +02:00
if ( $this -> Password && self :: $password_validator ) {
2008-04-26 08:31:52 +02:00
$valid -> combineAnd ( self :: $password_validator -> validate ( $this -> Password , $this ));
}
}
2009-05-27 02:09:23 +02:00
if (( ! $this -> ID && $this -> SetPassword ) || $this -> isChanged ( 'SetPassword' )) {
2008-10-02 04:45:58 +02:00
if ( $this -> SetPassword && self :: $password_validator ) {
2008-04-26 08:36:03 +02:00
$valid -> combineAnd ( self :: $password_validator -> validate ( $this -> SetPassword , $this ));
}
}
2008-04-26 08:31:52 +02:00
return $valid ;
}
2009-11-14 02:14:32 +01:00
/**
* Change password . This will cause rehashing according to
* the `PasswordEncryption` property .
*
* @ param String $password Cleartext password
*/
2008-04-26 08:31:52 +02:00
function changePassword ( $password ) {
$this -> Password = $password ;
$valid = $this -> validate ();
if ( $valid -> valid ()) {
$this -> AutoLoginHash = null ;
$this -> write ();
}
return $valid ;
}
2008-04-26 08:32:05 +02:00
/**
* Tell this member that someone made a failed attempt at logging in as them .
* This can be used to lock the user out temporarily if too many failed attempts are made .
*/
function registerFailedLogin () {
if ( self :: $lock_out_after_incorrect_logins ) {
// Keep a tally of the number of failed log-ins so that we can lock people out
2009-09-10 04:42:26 +02:00
$this -> FailedLoginCount = $this -> FailedLoginCount + 1 ;
$this -> write ();
2008-04-26 08:32:05 +02:00
2009-09-10 04:42:26 +02:00
if ( $this -> FailedLoginCount >= self :: $lock_out_after_incorrect_logins ) {
2008-04-26 08:32:05 +02:00
$this -> LockedOutUntil = date ( 'Y-m-d H:i:s' , time () + 15 * 60 );
$this -> write ();
}
}
}
2009-08-10 06:32:39 +02:00
/**
* Get the HtmlEditorConfig for this user to be used in the CMS .
2010-02-23 02:00:52 +01:00
* This is set by the group . If multiple configurations are set ,
* the one with the highest priority wins .
*
2009-08-10 06:32:39 +02:00
* @ return string
*/
function getHtmlEditorConfigForCMS () {
$currentName = '' ;
$currentPriority = 0 ;
foreach ( $this -> Groups () as $group ) {
$configName = $group -> HtmlEditorConfig ;
2010-02-23 02:00:52 +01:00
if ( $configName ) {
$config = HtmlEditorConfig :: get ( $group -> HtmlEditorConfig );
if ( $config && $config -> getOption ( 'priority' ) > $currentPriority ) {
$currentName = $configName ;
}
2009-08-10 06:32:39 +02:00
}
}
// If can't find a suitable editor, just default to cms
return $currentName ? $currentName : 'cms' ;
}
2007-07-19 12:40:28 +02:00
}
/**
2007-09-14 21:10:18 +02:00
* Special kind of { @ link ComponentSet } that has special methods for
* manipulating a user ' s membership
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-07-19 12:40:28 +02:00
*/
class Member_GroupSet extends ComponentSet {
/**
2007-09-14 20:23:28 +02:00
* Control group membership with a number of checkboxes .
2007-09-14 21:10:18 +02:00
* - 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 * NOT * present in $data , then the member
* 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 )
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
function setByCheckboxes ( array $checkboxes , array $data ) {
2007-07-19 12:40:28 +02:00
foreach ( $checkboxes as $checkbox ) {
2007-09-14 21:10:18 +02:00
if ( $data [ $checkbox ]) {
2007-09-14 20:23:28 +02:00
$add [] = $checkbox ;
2007-09-14 21:10:18 +02:00
} else {
2007-07-19 12:40:28 +02:00
$remove [] = $checkbox ;
2007-09-14 20:23:28 +02:00
}
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
if ( $add )
$this -> addManyByCodename ( $add );
if ( $remove )
$this -> removeManyByCodename ( $remove );
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Allows you to set groups based on a CheckboxSetField
2007-09-14 20:23:28 +02:00
*
2007-09-14 21:10:18 +02:00
* 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 .
2007-09-14 20:23:28 +02:00
*
2007-09-14 21:10:18 +02:00
* On the form setup :
2007-09-14 20:23:28 +02:00
*
2007-09-14 21:10:18 +02:00
* < 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 >
2007-09-14 20:23:28 +02:00
*
* On the form handler :
*
2007-09-14 21:10:18 +02:00
* < code >
* $groups = $member -> Groups ();
* $checkboxfield = $form -> Fields () -> fieldByName ( " NewsletterSubscriptions " );
* $groups -> setByCheckboxSetField ( $checkboxfield );
* </ code >
*
* @ param CheckboxSetField $checkboxsetfield The CheckboxSetField ( with
* data ) from your form .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
function setByCheckboxSetField ( CheckboxSetField $checkboxsetfield ) {
2007-09-14 20:23:28 +02:00
// Get the values from the formfield.
2007-07-19 12:40:28 +02:00
$values = $checkboxsetfield -> Value ();
$sourceItems = $checkboxsetfield -> getSource ();
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
if ( $sourceItems ) {
2007-07-19 12:40:28 +02:00
// If (some) values are present, add and remove as necessary.
2007-09-14 21:10:18 +02:00
if ( $values ) {
2007-07-19 12:40:28 +02:00
// update the groups based on the selections
2007-09-14 21:10:18 +02:00
foreach ( $sourceItems as $k => $item ) {
if ( in_array ( $k , $values )) {
2007-07-19 12:40:28 +02:00
$add [] = $k ;
2007-09-14 21:10:18 +02:00
} else {
2007-07-19 12:40:28 +02:00
$remove [] = $k ;
2007-09-14 20:23:28 +02:00
}
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
// else we should be removing all from the necessary groups.
2007-09-14 21:10:18 +02:00
} else {
2008-02-25 03:10:37 +01:00
$remove = array_keys ( $sourceItems );
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
if ( $add )
$this -> addManyByGroupID ( $add );
if ( $remove )
$this -> RemoveManyByGroupID ( $remove );
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
} else {
USER_ERROR ( " Member::setByCheckboxSetField() - No source items could be found for checkboxsetfield " .
$checkboxsetfield -> Name (), E_USER_WARNING );
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Adds this member to the groups based on the group IDs
*
* @ param array $ids Group identifiers .
2007-07-19 12:40:28 +02:00
*/
function addManyByGroupID ( $groupIds ){
$groups = $this -> getGroupsFromIDs ( $groupIds );
2007-09-14 21:10:18 +02:00
if ( $groups ) {
foreach ( $groups as $group ) {
2007-07-19 12:40:28 +02:00
$this -> add ( $group );
}
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Removes the member from many groups based on the group IDs
*
* @ param array $ids Group identifiers .
2007-07-19 12:40:28 +02:00
*/
2007-09-14 21:10:18 +02:00
function removeManyByGroupID ( $groupIds ) {
2007-07-19 12:40:28 +02:00
$groups = $this -> getGroupsFromIDs ( $groupIds );
2007-09-14 21:10:18 +02:00
if ( $groups ) {
foreach ( $groups as $group ) {
2007-07-19 12:40:28 +02:00
$this -> remove ( $group );
}
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* 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 .
2007-07-19 12:40:28 +02:00
*/
function getGroupsFromIDs ( $ids ){
2007-09-14 21:10:18 +02:00
if ( $ids && count ( $ids ) > 1 ) {
2008-11-24 10:31:14 +01:00
return DataObject :: get ( " Group " , " \" ID \" IN ( " . implode ( " , " , $ids ) . " ) " );
2007-09-14 21:10:18 +02:00
} else {
return DataObject :: get_by_id ( " Group " , $ids [ 0 ]);
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Adds this member to the groups based on the group codenames
*
* @ param array $codenames Group codenames
2007-07-19 12:40:28 +02:00
*/
function addManyByCodename ( $codenames ) {
$groups = $this -> codenamesToGroups ( $codenames );
2007-09-14 21:10:18 +02:00
if ( $groups ) {
2007-07-19 12:40:28 +02:00
foreach ( $groups as $group ){
$this -> add ( $group );
}
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Removes this member from the groups based on the group codenames
*
* @ param array $codenames Group codenames
2007-07-19 12:40:28 +02:00
*/
function removeManyByCodename ( $codenames ) {
$groups = $this -> codenamesToGroups ( $codenames );
2007-09-14 21:10:18 +02:00
if ( $groups ) {
foreach ( $groups as $group ) {
2007-07-19 12:40:28 +02:00
$this -> remove ( $group );
2007-09-14 20:23:28 +02:00
}
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
2007-07-19 12:40:28 +02:00
/**
2007-09-14 21:10:18 +02:00
* Helper function to return the appropriate groups via a codenames
*
* @ param array $codenames Group codenames
* @ return array Returns the the appropriate groups .
2007-07-19 12:40:28 +02:00
*/
protected function codenamesToGroups ( $codenames ) {
$list = " ' " . implode ( " ', ' " , $codenames ) . " ' " ;
2008-11-24 10:31:14 +01:00
$output = DataObject :: get ( " Group " , " \" Code \" IN ( $list ) " );
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
// Some are missing - throw warnings
2007-09-14 21:10:18 +02:00
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 );
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
return $output ;
}
}
2008-02-25 03:10:37 +01:00
/**
* Form for editing a member profile .
* @ package sapphire
* @ subpackage security
*/
2007-09-27 23:16:23 +02:00
class Member_ProfileForm extends Form {
function __construct ( $controller , $name , $member ) {
Requirements :: clear ();
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: css ( CMS_DIR . '/css/typography.css' );
Requirements :: css ( CMS_DIR . '/css/cms_right.css' );
2009-11-26 06:08:08 +01:00
Requirements :: javascript ( SAPPHIRE_DIR . " /thirdparty/prototype/prototype.js " );
Requirements :: javascript ( SAPPHIRE_DIR . " /thirdparty/behaviour/behaviour.js " );
Requirements :: javascript ( SAPPHIRE_DIR . " /javascript/prototype_improvements.js " );
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: javascript ( THIRDPARTY_DIR . " /scriptaculous/scriptaculous.js " );
Requirements :: javascript ( THIRDPARTY_DIR . " /scriptaculous/controls.js " );
2009-11-26 06:08:08 +01:00
Requirements :: javascript ( SAPPHIRE_DIR . " /javascript/layout_helpers.js " );
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: css ( SAPPHIRE_DIR . " /css/Form.css " );
2007-09-27 23:16:23 +02:00
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: css ( SAPPHIRE_DIR . " /css/MemberProfileForm.css " );
2007-09-28 01:06:15 +02:00
2007-09-27 23:16:23 +02:00
2010-06-30 07:02:29 +02:00
$fields = $member -> getCMSFields ();
2007-09-27 23:16:23 +02:00
$fields -> push ( new HiddenField ( 'ID' , 'ID' , $member -> ID ));
$actions = new FieldSet (
2010-07-23 05:38:33 +02:00
new FormAction ( 'dosave' , _t ( 'CMSMain.SAVE' , 'Save' ))
2007-09-27 23:16:23 +02:00
);
2010-03-09 21:09:09 +01:00
$validator = new Member_Validator ();
2007-09-27 23:16:23 +02:00
parent :: __construct ( $controller , $name , $fields , $actions , $validator );
$this -> loadDataFrom ( $member );
}
function dosave ( $data , $form ) {
2010-03-09 21:09:09 +01:00
// don't allow ommitting or changing the ID
if ( ! isset ( $data [ 'ID' ]) || $data [ 'ID' ] != Member :: currentUserID ()) {
return Director :: redirectBack ();
}
2007-09-27 23:16:23 +02:00
2010-03-09 21:09:09 +01:00
$SQL_data = Convert :: raw2sql ( $data );
2007-09-27 23:16:23 +02:00
$member = DataObject :: get_by_id ( " Member " , $SQL_data [ 'ID' ]);
2007-10-21 21:53:57 +02:00
if ( $SQL_data [ 'Locale' ] != $member -> Locale ) {
$form -> addErrorMessage ( " Generic " , _t ( 'Member.REFRESHLANG' ), " good " );
2007-09-27 23:16:23 +02:00
}
$form -> saveInto ( $member );
$member -> write ();
2008-12-04 23:38:32 +01:00
$closeLink = sprintf (
'<small><a href="' . $_SERVER [ 'HTTP_REFERER' ] . '" onclick="javascript:window.top.GB_hide(); return false;">(%s)</a></small>' ,
_t ( 'ComplexTableField.CLOSEPOPUP' , 'Close Popup' )
);
$message = _t ( 'Member.PROFILESAVESUCCESS' , 'Successfully saved.' ) . ' ' . $closeLink ;
$form -> sessionMessage ( $message , 'good' );
2007-09-27 23:16:23 +02:00
Director :: redirectBack ();
}
}
2007-09-14 21:10:18 +02:00
/**
* Class used as template to send an email to new members
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-09-14 21:10:18 +02:00
*/
2008-06-12 11:29:05 +02:00
class Member_SignupEmail extends Email {
2007-10-25 04:47:45 +02:00
protected $from = '' ; // setting a blank from address uses the site's default administrator email
protected $subject = '' ;
protected $body = '' ;
function __construct () {
2009-09-18 05:07:15 +02:00
parent :: __construct ();
2007-10-25 04:47:45 +02:00
$this -> subject = _t ( 'Member.EMAILSIGNUPSUBJECT' , " Thanks for signing up " );
$this -> body = '
2007-11-06 06:42:28 +01:00
< h1 > ' . _t(' Member . GREETING ',' Welcome ') . ' , $FirstName .</ h1 >
2007-10-25 04:47:45 +02:00
< p > ' . _t(' Member . EMAILSIGNUPINTRO1 ',' Thanks for signing up to become a new member , your details are listed below for future reference . ') . ' </ p >
< p > ' . _t(' Member . EMAILSIGNUPINTRO2 ',' You can login to the website using the credentials listed below ') . ' :
2007-07-19 12:40:28 +02:00
< ul >
2007-10-25 04:47:45 +02:00
< li >< strong > ' . _t(' Member . EMAIL ') . ' </ strong > $Email </ li >
< li >< strong > ' . _t(' Member . PASSWORD ') . ' :</ strong > $Password </ li >
2007-07-19 12:40:28 +02:00
</ ul >
</ p >
2007-09-14 20:23:28 +02:00
2007-10-25 04:47:45 +02:00
< h3 > ' . _t(' Member . CONTACTINFO ',' Contact Information ') . ' </ h3 >
2007-07-19 12:40:28 +02:00
< ul >
2007-10-25 04:47:45 +02:00
< li >< strong > ' . _t(' Member . NAME ',' Name ') . ' :</ strong > $FirstName $Surname </ li >
2007-07-19 12:40:28 +02:00
<% if Phone %>
2007-10-25 04:47:45 +02:00
< li >< strong > ' . _t(' Member . PHONE ',' Phone ') . ' :</ strong > $Phone </ li >
2007-07-19 12:40:28 +02:00
<% end_if %>
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
<% if Mobile %>
2007-10-25 04:47:45 +02:00
< li >< strong > ' . _t(' Member . MOBILE ',' Mobile ') . ' :</ strong > $Mobile </ li >
2007-07-19 12:40:28 +02:00
<% end_if %>
2007-09-14 20:23:28 +02:00
2007-10-25 04:47:45 +02:00
< li >< strong > ' . _t(' Member . ADDRESS ',' Address ') . ' :</ strong >
< br />
$Number $Street $StreetType < br />
$Suburb < br />
$City $Postcode
</ li >
2007-09-14 20:23:28 +02:00
2007-10-25 04:47:45 +02:00
</ ul >
' ;
}
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
/**
* Class used as template to send an email saying that the password has been
* changed
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-09-14 21:10:18 +02:00
*/
2008-06-12 11:29:05 +02:00
class Member_ChangePasswordEmail extends Email {
2007-07-19 12:40:28 +02:00
protected $from = '' ; // setting a blank from address uses the site's default administrator email
2007-10-25 04:47:45 +02:00
protected $subject = '' ;
2007-07-19 12:40:28 +02:00
protected $ss_template = 'ChangePasswordEmail' ;
2007-10-25 04:47:45 +02:00
function __construct () {
2009-09-18 05:07:15 +02:00
parent :: __construct ();
2007-10-25 04:47:45 +02:00
$this -> subject = _t ( 'Member.SUBJECTPASSWORDCHANGED' , " Your password has been changed " , PR_MEDIUM , 'Email subject' );
}
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
/**
* Class used as template to send the forgot password email
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-09-14 21:10:18 +02:00
*/
2008-06-12 11:29:05 +02:00
class Member_ForgotPasswordEmail extends Email {
2007-09-14 21:10:18 +02:00
protected $from = '' ; // setting a blank from address uses the site's default administrator email
2007-10-25 04:47:45 +02:00
protected $subject = '' ;
2007-07-19 12:40:28 +02:00
protected $ss_template = 'ForgotPasswordEmail' ;
2007-10-25 04:47:45 +02:00
function __construct () {
2009-09-18 05:07:15 +02:00
parent :: __construct ();
2007-10-25 04:47:45 +02:00
$this -> subject = _t ( 'Member.SUBJECTPASSWORDRESET' , " Your password reset link " , PR_MEDIUM , 'Email subject' );
}
2007-07-19 12:40:28 +02:00
}
2007-09-14 21:10:18 +02:00
/**
* Member Validator
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage security
2007-09-14 21:10:18 +02:00
*/
2007-07-19 12:40:28 +02:00
class Member_Validator extends RequiredFields {
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
protected $customRequired = array ( 'FirstName' , 'Email' ); //, 'Password');
/**
* Constructor
*/
2007-07-19 12:40:28 +02:00
public function __construct () {
$required = func_get_args ();
if ( isset ( $required [ 0 ]) && is_array ( $required [ 0 ])) {
$required = $required [ 0 ];
}
$required = array_merge ( $required , $this -> customRequired );
2007-09-14 20:23:28 +02:00
2007-09-14 21:10:18 +02:00
parent :: __construct ( $required );
2007-07-19 12:40:28 +02:00
}
2007-09-14 20:23:28 +02:00
2007-07-19 12:40:28 +02:00
2007-09-14 21:10:18 +02:00
/**
2007-09-16 18:55:47 +02:00
* Check if the submitted member data is valid ( server - side )
2007-09-14 21:10:18 +02:00
*
* 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 .
*/
2007-07-19 12:40:28 +02:00
function php ( $data ) {
$valid = parent :: php ( $data );
2009-02-02 00:49:53 +01:00
$identifierField = Member :: get_unique_identifier_field ();
$SQL_identifierField = Convert :: raw2sql ( $data [ $identifierField ]);
$member = DataObject :: get_one ( 'Member' , " \" $identifierField\ " = '{$SQL_identifierField}' " );
2007-09-14 20:23:28 +02:00
2009-02-02 00:49:53 +01:00
// if we are in a complex table field popup, use ctf[childID], else use ID
if ( isset ( $_REQUEST [ 'ctf' ][ 'childID' ])) {
2008-02-25 03:10:37 +01:00
$id = $_REQUEST [ 'ctf' ][ 'childID' ];
2009-02-02 00:49:53 +01:00
} elseif ( isset ( $_REQUEST [ 'ID' ])) {
2008-02-25 03:10:37 +01:00
$id = $_REQUEST [ 'ID' ];
2009-02-02 00:49:53 +01:00
} else {
2008-02-25 03:10:37 +01:00
$id = null ;
2009-02-02 00:49:53 +01:00
}
2008-02-25 03:10:37 +01:00
if ( $id && is_object ( $member ) && $member -> ID != $id ) {
2009-02-02 00:49:53 +01:00
$uniqueField = $this -> form -> dataFieldByName ( $identifierField );
$this -> validationError (
$uniqueField -> id (),
sprintf (
_t (
'Member.VALIDATIONMEMBEREXISTS' ,
'A member already exists with the same %s'
),
strtolower ( $identifierField )
),
'required'
);
2007-07-19 12:40:28 +02:00
$valid = false ;
}
2007-09-14 20:23:28 +02:00
2007-09-16 18:55:47 +02:00
// Execute the validators on the extensions
if ( $this -> extension_instances ) {
foreach ( $this -> extension_instances as $extension ) {
2009-11-03 03:33:53 +01:00
if ( method_exists ( $extension , 'hasMethod' ) && $extension -> hasMethod ( 'updatePHP' )) {
2007-09-16 18:55:47 +02:00
$valid &= $extension -> updatePHP ( $data , $this -> form );
}
}
}
2007-07-19 12:40:28 +02:00
return $valid ;
}
2007-09-16 18:55:47 +02:00
/**
* Check if the submitted member data is valid ( client - side )
*
* @ param array $data Submitted data
* @ return bool Returns TRUE if the submitted data is valid , otherwise
* FALSE .
*/
function javascript () {
$js = parent :: javascript ();
// Execute the validators on the extensions
if ( $this -> extension_instances ) {
foreach ( $this -> extension_instances as $extension ) {
2009-11-03 03:33:53 +01:00
if ( method_exists ( $extension , 'hasMethod' ) && $extension -> hasMethod ( 'updateJavascript' )) {
2007-09-16 18:55:47 +02:00
$extension -> updateJavascript ( $js , $this -> form );
}
}
}
return $js ;
}
2010-03-04 23:59:03 +01:00
2007-07-19 12:40:28 +02:00
}
2010-06-30 07:02:29 +02:00
/**
* @ package sapphire
* @ subpackage security
*/
class Member_DatetimeOptionsetField extends OptionsetField {
function Field () {
Requirements :: css ( SAPPHIRE_DIR . '/css/MemberDatetimeOptionsetField.css' );
Requirements :: javascript ( THIRDPARTY_DIR . '/thirdparty/jquery/jquery.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/javascript/MemberDatetimeOptionsetField.js' );
$options = '' ;
$odd = 0 ;
$source = $this -> getSource ();
foreach ( $source as $key => $value ) {
2010-07-15 06:05:14 +02:00
// convert the ID to an HTML safe value (dots are not replaced, as they are valid in an ID attribute)
2010-07-15 06:04:06 +02:00
$itemID = $this -> id () . '_' . preg_replace ( '/[^\.a-zA-Z0-9\-\_]/' , '_' , $key );
2010-06-30 07:02:29 +02:00
if ( $key == $this -> value ) {
$useValue = false ;
$checked = " checked= \" checked \" " ;
} else {
$checked = " " ;
}
$odd = ( $odd + 1 ) % 2 ;
$extraClass = $odd ? " odd " : " even " ;
$extraClass .= " val " . preg_replace ( '/[^a-zA-Z0-9\-\_]/' , '_' , $key );
$disabled = ( $this -> disabled || in_array ( $key , $this -> disabledItems )) ? " disabled= \" disabled \" " : " " ;
2010-07-15 06:04:06 +02:00
$ATT_key = Convert :: raw2att ( $key );
$options .= " <li class= \" " . $extraClass . " \" ><input id= \" $itemID\ " name = \ " $this->name\ " type = \ " radio \" value= \" $key\ " $checked $disabled class = \ " radio \" /> <label title= \" $ATT_key\ " for = \ " $itemID\ " > $value </ label ></ li > \n " ;
2010-06-30 07:02:29 +02:00
}
// Add "custom" input field
$value = ( $this -> value && ! array_key_exists ( $this -> value , $this -> source )) ? $this -> value : null ;
$checked = ( $value ) ? " checked= \" checked \" " : '' ;
$options .= " <li class= \" valCustom \" > "
. sprintf ( " <input id= \" %s_custom \" name= \" %s \" type= \" radio \" value= \" __custom__ \" class= \" radio \" %s /> " , $itemID , $this -> name , $checked )
. sprintf ( '<label for="%s_custom">%s:</label>' , $itemID , _t ( 'MemberDatetimeOptionsetField.Custom' , 'Custom' ))
. sprintf ( " <input class= \" customFormat \" name= \" %s_custom \" value= \" %s \" /> \n " , $this -> name , $value )
. sprintf ( " <input type= \" hidden \" class= \" formatValidationURL \" value= \" %s \" /> " , $this -> Link () . '/validate' );
$options .= ( $value ) ? sprintf (
'<span class="preview">(%s: "%s")</span>' ,
_t ( 'MemberDatetimeOptionsetField.Preview' , 'Preview' ),
Zend_Date :: now () -> toString ( $value )
) : '' ;
2010-07-01 05:11:47 +02:00
$options .= " <a class= \" formattingHelpToggle \" href= \" # \" > " . _t ( 'MemberDatetimeOptionsetField.TOGGLEHELP' , 'Toggle formatting help' ) . " </a> " ;
2010-06-30 07:02:29 +02:00
$options .= " <div class= \" formattingHelpText \" > " ;
$options .= $this -> getFormattingHelpText ();
$options .= " </div> " ;
$options .= " </li> \n " ;
$id = $this -> id ();
return " <ul id= \" $id\ " class = \ " optionset { $this -> extraClass () } \" > \n $options </ul> \n " ;
}
/**
* @ todo Put this text into a template ?
*/
function getFormattingHelpText () {
2010-06-30 07:47:52 +02:00
$output = '<ul>' ;
$output .= '<li>YYYY = ' . _t ( 'MemberDatetimeOptionsetField.FOURDIGITYEAR' , 'Four-digit year' , 40 , 'Help text describing what "YYYY" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>YY = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITYEAR' , 'Two-digit year' , 40 , 'Help text describing what "YY" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>MMMM = ' . _t ( 'MemberDatetimeOptionsetField.FULLNAMEMONTH' , 'Full name of month (e.g. June)' , 40 , 'Help text describing what "MMMM" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>MMM = ' . _t ( 'MemberDatetimeOptionsetField.SHORTMONTH' , 'Short name of month (e.g. Jun)' , 40 , 'Help text letting describing what "MMM" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>MM = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITMONTH' , 'Two-digit month (01=January, etc.)' , 40 , 'Help text describing what "MM" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>M = ' . _t ( 'MemberDatetimeOptionsetField.MONTHNOLEADING' , 'Month digit without leading zero' , 40 , 'Help text describing what "M" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>dd = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITDAY' , 'Two-digit day of month' , 40 , 'Help text describing what "dd" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>d = ' . _t ( 'MemberDatetimeOptionsetField.DAYNOLEADING' , 'Day of month without leading zero' , 40 , 'Help text describing what "d" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>hh = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITHOUR' , 'Two digits of hour (00 through 23)' , 40 , 'Help text describing what "hh" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>h = ' . _t ( 'MemberDatetimeOptionsetField.HOURNOLEADING' , 'Hour without leading zero' , 40 , 'Help text describing what "h" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>mm = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITMINUTE' , 'Two digits of minute (00 through 59)' , 40 , 'Help text describing what "mm" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>m = ' . _t ( 'MemberDatetimeOptionsetField.MINUTENOLEADING' , 'Minute without leading zero' , 40 , 'Help text describing what "m" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>ss = ' . _t ( 'MemberDatetimeOptionsetField.TWODIGITSECOND' , 'Two digits of second (00 through 59)' , 40 , 'Help text describing what "ss" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>s = ' . _t ( 'MemberDatetimeOptionsetField.DIGITSDECFRACTIONSECOND' , 'One or more digits representing a decimal fraction of a second' , 40 , 'Help text describing what "s" means in ISO date formatting' ) . '</li>' ;
$output .= '<li>a = ' . _t ( 'MemberDatetimeOptionsetField.AMORPM' , 'AM (Ante meridiem) or PM (Post meridiem)' , 40 , 'Help text describing what "a" means in ISO date formatting' ) . '</li>' ;
$output .= '</ul>' ;
return $output ;
2010-06-30 07:02:29 +02:00
}
function setValue ( $value ) {
if ( $value == '__custom__' ) {
$value = isset ( $_REQUEST [ $this -> name . '_custom' ]) ? $_REQUEST [ $this -> name . '_custom' ] : null ;
}
if ( $value ) {
parent :: setValue ( $value );
}
}
function validate () {
$value = isset ( $_POST [ $this -> name . '_custom' ]) ? $_POST [ $this -> name . '_custom' ] : null ;
if ( ! $value ) return true ; // no custom value, don't validate
// Check that the current date with the date format is valid or not
$validator = $this -> form ? $this -> form -> getValidator () : null ;
require_once 'Zend/Date.php' ;
$date = Zend_Date :: now () -> toString ( $value );
$valid = Zend_Date :: isDate ( $date , $value );
if ( $valid ) {
return true ;
} else {
if ( $validator ) {
2010-06-30 07:47:52 +02:00
$validator -> validationError ( $this -> name , _t ( 'MemberDatetimeOptionsetField.DATEFORMATBAD' , " Date format is invalid " ), " validation " , false );
2010-06-30 07:02:29 +02:00
}
return false ;
}
}
2012-11-09 01:37:35 +01:00
}