| Server IP : 54.36.91.62 / Your IP : 216.73.217.112 Web Server : Apache System : Linux webm013.cluster127.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : coopiak ( 151928) PHP Version : 8.3.23 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/c/o/o/coopiak/dansnotreville-fr/nice/libraries/CBLib/CB/Database/Table/ |
Upload File : |
<?php
/**
* CBLib, Community Builder Library(TM)
* @version $Id: 5/4/14 1:08 AM $
* @package CB\Database\Table
* @copyright (C) 2004-2023 www.joomlapolis.com / Lightning MultiCom SA - and its licensors, all rights reserved
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU/GPL version 2
*/
namespace CB\Database\Table;
use CBLib\Application\Application;
use CBLib\Database\DatabaseDriverInterface;
use CBLib\Registry\GetterInterface;
// Temporary:
use \CBuser;
use \cbTabs;
use \cbNotification;
use \CBLib\Language\CBTxt;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Table\User;
use Joomla\CMS\User\UserHelper;
use Joomla\Registry\Registry;
defined('CBLIB') or die();
/**
* CB\Database\Table\UserTable Class implementation
*
* TODO: This UserTable is still WIP, only inherited methods should be considered stable
*/
class UserTable extends ComprofilerTable
{
/** @var string */
public $name = null;
/** @var string */
public $username = null;
/** @var string */
public $alias = null;
/** @var string */
public $email = null;
/** @var string */
public $password = null;
/** @var int */
public $block = null;
/** @var int */
public $sendEmail = null;
/**
* @var array
* @deprecated 2.0 (use ViewAccessLevels and Permissions instead of directly User's Groups (except for editing the user)
*/
public $gids = array();
/** @var string (SQL:Date) */
public $registerDate = null;
/** @var string (SQL:Date) */
public $lastvisitDate = null;
/** @var string (SQL:Date) */
public $lastResetTime = null;
/** @var int */
public $resetCount = null;
/** @var string (SQL:Date) */
public $lastupdatedate = null;
/** @var string */
public $activation = null;
/** @var string */
public $otpKey = null;
/** @var string */
public $otep = null;
/** @var int */
public $requireReset = null;
/** @var string */
public $authProvider = null;
/** @var string */
public $params = null;
/** @var string */
protected $_cmsUserTable = '#__users';
/** @var string */
protected $_cmsUserTableKey = 'id';
/** @var string */
protected $_cmsUserTableUsername = 'username';
/** @var string */
protected $_cmsUserTableEmail = 'email';
/** @var string */
protected $_cmsUserTableGid = 'gid';
/**
* CMS User object
* @var \Joomla\CMS\User\User
*/
protected $_cmsUser = null;
/**
* CMS User object data to bind
* @var array
*/
protected $_cmsUserData = [];
/**
* CB user table row
* @var ComprofilerTable
*/
protected $_comprofilerUser = null;
/**
* CB Tabs
* @var cbTabs
*/
protected $_cbTabs = null;
/**
* Fields from Cms User table (this array is initialized in constructor calling function _reinitNonComprofileVars())
* @var array
*/
protected $_nonComprofilerVars = array( 'name', 'username', 'email', 'password', 'params', 'block', 'sendEmail', 'gids', 'registerDate', 'activation', 'lastvisitDate', 'lastResetTime', 'resetCount', 'otpKey', 'otep', 'requireReset', 'authProvider' );
/**
* Fields from Cms User table that can be bound from frontend when a user is saveSafely in frontend (additional safety measure)
* @var array
*/
protected $_frontendNonComprofilerVars = array( 'name', 'username', 'email', 'password', 'params' );
/**
* Constructor to set table and key field
* Can be overloaded/supplemented by the child class
*
* @param DatabaseDriverInterface $db CB Database object
* @param string $table Name of the table in the db schema relating to child class
* @param string|array $key Name of the primary key field in the table
*/
public function __construct( DatabaseDriverInterface $db = null, $table = null, $key = null )
{
parent::__construct( $db, $table, $key );
$this->_cmsUserTableGid = null;
$this->_reinitNonComprofileVars();
}
/**
* Resets public properties
*
* @param mixed $value The value to set all properties to, default is null
*/
public function reset( $value = null )
{
parent::reset( $value );
$this->_reinitNonComprofileVars();
}
/**
* Returns an array of all properties names (excluding private)
*
* @return array
*/
public function getAllProperties()
{
$object = $this;
if ( ! $object->id ) {
// We need custom field properties and we can't get them if there is no id; so lets load existing user to try and get them:
$object = CBuser::getMyUserDataInstance();
}
return array_filter( array_keys( get_object_vars( $object ) ), function( $k )
{
return ( substr( $k, 0, 1 ) != '_' );
});
}
/**
* Initializes non-comprofiler vars for CMS users table
*
* J2.5.5 introduced 2 new columns 'lastResetTime' and 'resetCount' to the CMS users table, but forgot to introduce it to JUser (Joomla bug #28703). JUser thus gets those only in J2.5.6.
* This function attempts to get the real database columns names that get loaded into JUser (and into $this UserTable object) into $this->_nonComprofilerVars
*
* @since 1.8.1
*
* @return void
*/
protected function _reinitNonComprofileVars() {
static $cache = array();
if ( ! $cache ) {
if ( $this->_cmsUser ) {
$obj = $this->_cmsUser;
} else {
$obj = Application::Cms()->getCmsUser( $this->id )->asCmsUser();
}
$tableBased = false;
// Try getting the users table column names:
if ( is_callable( array( $obj, 'getTable' ) ) ) {
/** @var User $jUserTable */
$jUserTable = $obj->getTable();
if ( is_callable( array( $jUserTable, 'getFields' ) ) ) {
// Get the fields of the table instead of the variables of the user object itself:
// (Joomla 2.5 does anyway call that function on reset() when it load()s the user table object, and then caches the result, so it is fast)
$obj = $jUserTable->getFields();
$tableBased = true;
}
}
// Sets the keys for non-private variables based on:
if ( $tableBased ) {
// based on table:
$cache = array_keys( $obj );
} else {
// based on object:
foreach ( $obj as $k => $v ) {
if ( $k[0] != '_' ) {
$cache[] = $k;
}
}
}
// Now also adds the private CMS/CB variables gid and gids:
foreach ( array( 'gid', 'gids' ) as $k ) {
if ( ! in_array( $k, $cache ) ) {
$cache[] = $k;
}
}
}
// Reset the list of not comprofiler table columns completely:
$this->_nonComprofilerVars = $cache;
}
/**
* Loads a row from the database into $this object by primary key
*
* @param int|array $keys [Optional]: Primary key value or array of primary keys to match. If not specified, the value of current key is used
* @return boolean Result from the database operation
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
* @throws \UnexpectedValueException
*/
public function load( $keys = null )
{
$cmsTableRefs = $this->getCmsTableReferences();
$cmsTableRefs[$this->_cmsUserTableKey] = 'u';
$where = $this->getSafeWhereStatements( $keys, $this->getCmsTableReferences(), 'c' );
if ( empty( $where ) ) {
return false;
}
//BB fix : resets default values to all object variables, because NULL SQL fields do not overide existing variables ! TODO: May be removed for 2.0!
$primaryKeys = array_keys( $this->getPrimaryKeysTypes() );
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
// Added check for _cbTabs here compared to parent::load:
if ( ( ! in_array( $name, $primaryKeys ) ) && ( $name != '_cbTabs' ) && ($name != "_db") && ($name != "_tbl") && ($name != "_tbl_key") && ( substr( $name, 0 , 10 ) != "_history__" ) ) {
$this->$name = $value;
}
}
//end of BB fix.
$this->reset();
/*
$query = "SELECT *"
. "\n FROM " . $this->_tbl . " c, " . $this->_cmsUserTable . " u"
. "\n WHERE c." . $this->_tbl_key . " = u." . $this->_cmsUserTableKey
. " AND c." . $this->_tbl_key . " = " . (int) $oid
;
$this->_db->setQuery( $query );
// the following is needed for being able to edit a backend user in CB from CMS which is not yet synchronized with CB:
*/
$query = 'SELECT c.*, u.*' // don't use * as in case the left join is null, the second loaded id would overwrite the first id with null
. "\n FROM " . $this->_cmsUserTable . ' AS u'
. "\n LEFT JOIN " . $this->_tbl . ' AS c ON c.' . $this->_tbl_key . ' = u.' . $this->_cmsUserTableKey
// . " WHERE u." . $this->_cmsUserTableKey . ' = ' . (int) $oid
. "\n WHERE " . implode( ' AND ', $where )
;
$this->_db->setQuery( $query );
$arr = $this->_db->loadAssoc( );
if ( empty( $arr ) ) {
// We didn't find an entry in the CMS users table, let's try in ComprofilerTable:
$cmsTableRefs[$this->_tbl_key] = 'c';
$where = $this->getSafeWhereStatements( $keys, $this->getCmsTableReferences(), 'c' );
$query = 'SELECT u.*, c.*' // don't use * as in case the left join is null, the second loaded id would overwrite the first id with null
. "\n FROM " . $this->_tbl . ' AS c'
. "\n LEFT JOIN " . $this->_cmsUserTable . ' AS u ON c.' . $this->_tbl_key . ' = u.' . $this->_cmsUserTableKey
// . " WHERE c." . $this->_tbl_key . ' = ' . (int) $oid
. "\n WHERE " . implode( ' AND ', $where )
;
$this->_db->setQuery( $query );
$arr = $this->_db->loadAssoc( );
}
if ( ! empty( $arr ) ) {
$this->bindThisUserFromDbArray( $arr, $this->{$this->_tbl_key} );
return true;
} else {
return false;
}
}
/**
* Returns array with keys of CMS table containing $tableReferenceKey
* (useful for $this->getSafeWhereStatements())
*
* @param string $tableReferenceKey [optional] default = 'u'
* @return array
*/
protected function getCmsTableReferences( $tableReferenceKey = 'u' )
{
static $cmsTableReferences = array();
if ( empty( $cmsTableReferences ) ) {
foreach ( $this->_nonComprofilerVars as $key ) {
$cmsTableReferences[$key] = $tableReferenceKey;
}
}
return $cmsTableReferences;
}
/**
* Copy the named array or object content into this object as vars
* All $arr values are filled in vars of object
* @access private
*
* @param array $arr The input array
* @param int $oid id
*/
public function bindThisUserFromDbArray( $arr, $oid = null ) {
foreach ( $arr as $kk => $v ) {
$this->$kk = $v;
}
if ( $oid ) {
// in case the left join is null, the second loaded id will be NULL and override id:
$k = $this->_tbl_key;
$this->$k = (int) $oid;
}
$this->afterBindFromDatabase();
}
/**
* This function should be called just after binding the UserTable object from database
* to load the gids
* and to fix the CMS database storage bugs.
* It should be avoided externally, but is used by cb.lists.php
*/
public function afterBindFromDatabase( ) {
$gids = array_values( Application::Cms()->getCmsUser( $this->id )->getAuthorisedGroups( false ) );
foreach ( $gids as $k => $v ) {
$gids[$k] = (string) $v;
}
$this->gids = $gids;
}
/**
* Loads a list of UserTable into an existing array if they are not already in it
* (indexed by key of this table)
* @since 1.4 (experimental)
*
* @param array $usersIds array of id to load
* @param array $objectsArray IN/OUT (int) id => $class (e.g. UserTable) with method bindThisUserFromDbArray
* @param string $class
*/
public function loadUsersMatchingIdIntoList( $usersIds, &$objectsArray, $class ) {
// avoids re-loading already loaded ids:
$usersIds = array_diff( $usersIds, array_keys( $objectsArray ) );
$idsCount = count( $usersIds );
if ( $idsCount > 0 ) {
// in case the left join is null, the second loaded u.id will be NULL and override id:
$query = 'SELECT *, u.' . $this->_cmsUserTableKey
. "\n FROM " . $this->_cmsUserTable . ' AS u'
. "\n LEFT JOIN " . $this->_tbl . ' AS c ON c.' . $this->_tbl_key . ' = u.' . $this->_cmsUserTableKey
. " WHERE u." . $this->_cmsUserTableKey . ( $idsCount == 1 ? ' = ' . (int) end( $usersIds ) : ' IN (' . implode( ',', cbArrayToInts( $usersIds ) ) . ')' );
$this->_db->setQuery( $query );
$resultsArray = $this->_db->loadAssocList( $this->_cmsUserTableKey );
if ( is_array($resultsArray) ) {
foreach ( $resultsArray as $k => $value ) {
$objectsArray[(int) $k] = new $class( $this->_db ); // self (CBuser class has method below too)
/** @var self[]|CBuser[] $objectsArray */
$objectsArray[(int) $k]->bindThisUserFromDbArray( $value );
}
}
unset( $resultsArray );
}
}
/**
* Loads user username from database
*
* @param string $username
* @return boolean TRUE: success, FALSE: error in database access
*/
public function loadByUsername( $username ) {
return $this->load( array( $this->_cmsUserTableUsername => $username ) );
}
/**
* Loads user username from database
*
* @param string $email
* @return boolean TRUE: success, FALSE: error in database access
*/
public function loadByEmail( $email ) {
return $this->load( array( $this->_cmsUserTableEmail => $email ) );
}
public function bindSafely( &$array, $ui, $reason, &$oldUserComplete ) {
global $_CB_framework, $ueConfig, $_PLUGINS;
// Some basic sanitizations and securitizations:
$this->id = (int) $this->id;
if ( $ui == 1 ) {
if ( $this->id ) {
// Front-end edit user: no changes in gids and confirmed/approved states
$this->gids = $oldUserComplete->gids;
$this->block = (int) $oldUserComplete->block;
$this->sendEmail = (int) $oldUserComplete->sendEmail;
$this->confirmed = (int) $oldUserComplete->confirmed;
$this->approved = (int) $oldUserComplete->approved;
} else {
// Front-end user registration: handle this here, so it is available to all plugins:
$this->gids = array( (int) $_CB_framework->getCfg( 'new_usertype' ) );
if ( $ueConfig['reg_admin_approval'] == 0) {
$this->approved = 1;
} else {
$this->approved = 0;
$this->block = 1;
}
if ( $ueConfig['reg_confirmation'] == 0 ) {
$this->confirmed = 1;
} else {
$this->confirmed = 0;
$this->block = 1;
}
if ( ( $this->confirmed == 1 ) && ( $this->approved == 1 ) ) {
$this->block = 0;
} else {
$this->block = 1;
}
$this->sendEmail = 0;
}
// Nb.: Backend user edit and new user are handled in core plugin CBfield_userparams field handler class
}
// By default, don't touch the hashed password, unless a new password is set by the saveTabsContents binding:
$this->password = null;
$this->_original_email = $this->email; // needed for checkSafely()
// Process the fields in form by CB field plugins:
$_PLUGINS->loadPluginGroup('user');
$this->_cbTabs = new cbTabs( 0, $ui, null, false );
$this->_cbTabs->saveTabsContents( $this, $array, $reason );
$errors = $_PLUGINS->getErrorMSG( false );
if ( count( $errors ) > 0 ) {
$this->_error = $errors;
return false;
}
// Now do CMS-specific stuff, specially bugs-workarounds:
$postCopy = array();
if ( $ui == 1 ) {
$vars = $this->_frontendNonComprofilerVars;
} else {
$vars = $this->_nonComprofilerVars;
}
$isJooml4 = checkJversion( '4.0+' );
foreach ( $vars as $k ) {
if ( ( $k === 'authProvider' ) && ( ! $isJooml4 ) ) {
// Joomla 3 doesn't have this column so skip it
continue;
}
if ( isset( $this->$k ) ) {
$postCopy[$k] = $this->$k;
}
}
if ( isset( $postCopy['password'] ) ) {
$postCopy['verifyPass'] = $postCopy['password']; // Mambo and Joomla 1.0 has it in password2 and checks it in bind() !
$postCopy['password2'] = $postCopy['password']; // Joomla 1.5 has it in password2 and checks it in bind() !
}
$this->_mapUsers();
$row =& $this->_cmsUser;
$pwd = $row->password; // maybe cleartext at that stage.
if ( $pwd == '' ) {
$pwd = null; // empty: don't update/change
$this->password = null;
}
if ( checkJversion( '4.0+' ) && isset( $postCopy['params'] ) && is_string( $postCopy['params'] ) ) {
$postCopy['params'] = (array) json_decode( $postCopy['params'] );
}
$rowBindResult = $row->bind( $postCopy ); // This modifies $postCopy and hashes password in $postCopy and in $row !
if ( ! $rowBindResult ) {
$this->_error = array( stripslashes( $row->getError() ) );
return false;
}
$row->password = $pwd; // restore cleartext password at this stage.
return true;
}
protected function checkSafely() {
if ( $this->_cmsUser === null ) {
$this->_mapUsers(); //TODO: Not even sure if this is still needed, since it was used in this function for working around a bug in Joomla 1.0 and below only.
}
return true;
}
/**
* Copy the named array or object content into this object as vars
* only existing vars of object are filled.
* When undefined in array, object variables are kept.
*
* WARNING: DOES addslashes / escape BY DEFAULT
*
* Can be overridden or overloaded.
*
* @param array|object $array The input array or object
* @param string $ignore Fields to ignore
* @param string $prefix Prefix for the array keys
* @return boolean TRUE: ok, FALSE: error on array binding
*/
public function bind( $array, $ignore='', $prefix = null )
{
$bind = parent::bind( $array, $ignore, $prefix );
if ( $bind ) {
if ( ( $this->gids !== null ) && is_string( $this->gids ) && ( strlen( $this->gids ) > 0 ) ) {
if ( ( $this->gids[0] === '{' ) || ( $this->gids[0] === '[' ) ) {
$gids = json_decode( $this->gids );
} else {
$gids = explode( '|*|', $this->gids );
}
$this->gids = cbToArrayOfInt( $gids );
}
}
return $bind;
}
/**
* Binds data to the CMS user object
*
* @param array $data
* @return void
*/
public function bindData( array $data ): void
{
$this->_cmsUserData = \array_replace_recursive( $this->_cmsUserData, $data );
}
/**
* If table key (id) is NULL : inserts new rows
* otherwise updates existing row in the database tables
*
* Can be overridden or overloaded by the child classes
*
* @param boolean $updateNulls TRUE: null object variables are also updated, FALSE: not.
* @return boolean TRUE if successful otherwise FALSE
*
* @throws \RuntimeException
*/
public function store( $updateNulls = false ) {
global $_CB_framework, $ueConfig;
$this->id = (int) $this->id;
$isNew = ( $this->id == 0 );
$oldUsername = null;
$oldGids = array();
$oldBlock = null;
if ( ! $isNew ) {
// get actual username to update sessions in case:
$sql = 'SELECT ' . $this->_db->NameQuote( $this->_cmsUserTableUsername )
. ', ' . $this->_db->NameQuote( 'block' )
. ' FROM ' . $this->_db->NameQuote( $this->_cmsUserTable ) . ' WHERE ' . $this->_db->NameQuote( $this->_cmsUserTableKey ) . ' = ' . (int) $this->user_id;
$this->_db->setQuery( $sql );
$oldEntry = null;
if ( $this->_db->loadObject( $oldEntry ) ) {
/** @var \Joomla\CMS\User\User $oldEntry */
$oldUsername = $oldEntry->username;
$gids = array_values( Application::Cms()->getCmsUser( $this->id )->getAuthorisedGroups( false ) );
foreach ( $gids as $k => $v ) {
$gids[$k] = (string) $v;
}
$oldGids = $gids;
$oldBlock = $oldEntry->block;
}
}
if ( ( ! $isNew ) && ( $this->confirmed == 0 ) && ( $this->cbactivation == '' ) && ( $ueConfig['reg_confirmation'] != 0 ) ) {
$this->_setActivationCode();
}
// creates CMS and CB objects:
$this->_mapUsers();
// remove the previous email set in bindSafely() and needed for checkSafely():
unset( $this->_original_email );
// stores first into CMS to get id of user if new:
$this->_cmsUser->groups = $this->gids;
$result = $this->_cmsUser->save();
if ( ! $result ) {
$this->_error = $this->_cmsUser->getError();
if ( class_exists( '\Joomla\CMS\Language\Text' ) ) {
$this->_error = Text::_( $this->_error );
}
}
if ( $result ) {
// synchronize id and user_id:
if ( $isNew ) {
$this->id = $this->_cmsUser->id;
$this->_comprofilerUser->id = $this->_cmsUser->id;
if ( ( $this->confirmed == 0 ) && ( $this->cbactivation == '' ) && ( $ueConfig['reg_confirmation'] != 0 ) ) {
$this->_setActivationCode();
}
}
// stores CB user into comprofiler: if new, inserts, otherwise updates:
if ( $this->user_id == 0 ) {
$this->user_id = $this->_cmsUser->id;
$this->_comprofilerUser->user_id = $this->user_id;
$result = $this->_comprofilerUser->storeNew( $updateNulls );
} else {
$result = $this->_comprofilerUser->store( $updateNulls );
}
if ( ! $result ) {
$this->_error = $this->_comprofilerUser->getError();
}
}
if ( $result ) {
// update the ACL:
$query = 'SELECT m.id AS aro_id, a.group_id FROM #__user_usergroup_map AS a'
. "\n INNER JOIN #__usergroups AS m ON m.id= a.group_id"
. "\n WHERE a.user_id = " . (int) $this->id;
$this->_db->setQuery( $query );
$aro_group = null;
$result = $this->_db->loadObject( $aro_group );
/** @var \StdClass $aro_group */
if ( $result && ( ! $isNew ) && ( ( $oldUsername != $this->username ) || ( ! self::_ArraysEquivalent( $oldGids, $this->gids ) ) || ( ( $oldBlock == 0 ) && ( $this->block == 1 ) ) ) ) {
// Update current sessions state if there is a change in gid or in username:
if ( $this->block == 0 ) {
$query = 'UPDATE #__session '
. "\n SET username = " . $this->_db->Quote( $this->username )
. "\n WHERE userid = " . (int) $this->id;
$this->_db->setQuery( $query );
$result = $this->_db->query();
// Clear usergroup and access caches on gid change (fixing bug #5461):
$this->_cmsUser->clearAccessRights();
// This is needed for instant adding of groups to logged-in user (fixing bug #3581):
$session = Application::Cms()->getSession();
$jUser = $session->get( 'user' );
if ( $jUser->id == $this->id ) {
Application::Cms()->getCmsUser( (int) $this->id )->resetCache();
$session->set( 'user', Application::Cms()->getCmsUser( (int) $this->id )->asCmsUser() );
}
} else {
// logout user now that user login has been blocked:
if ( $_CB_framework->myId() == $this->id ) {
$_CB_framework->logout();
}
$this->_db->setQuery( "DELETE FROM #__session WHERE userid = " . (int) $this->id ); //TBD: check if this is enough for J 1.5
$result = $this->_db->query();
}
}
if ( ! $result ) {
$this->_error = $this->_db->getErrorMsg();
return false;
}
}
return $result;
}
/**
* Are all of the int values of array $a1 in array $a2 and the other way around too (means arrays contain same integer values) ?
*
* @param array $a1
* @param array $a2
* @return boolean
*/
protected static function _ArraysEquivalent( $a1, $a2 ) {
cbArrayToInts( $a1 );
cbArrayToInts( $a2 );
return self::_allValuesOfArrayInArray( $a1, $a2 ) && self::_allValuesOfArrayInArray( $a2, $a1 );
}
/**
* Are all of the values of array $a1 in array $a2 ?
*
* @param array $a1
* @param array $a2
* @return boolean
*/
protected static function _allValuesOfArrayInArray( $a1, $a2 ) {
foreach ( $a1 as $v ) {
if ( ! in_array( $v, $a2 ) ) {
return false;
}
}
return true;
}
/**
* Store an array of values to user object
* Used only in banUser function in FE: TODO: Change usage in banUser ?
*
* @param $values
* @param bool $triggers
* @return bool
*/
public function storeDatabaseValues( $values, $triggers = true ) {
global $_CB_framework, $_PLUGINS;
if ( $this->id && is_array( $values ) && $values ) {
$ui = $_CB_framework->getUi();
$userVars = array_keys( get_object_vars( $this ) );
$user = new UserTable( $this->_db );
$oldUserComplete = new UserTable( $this->_db );
foreach ( $userVars as $k ) {
if ( substr( $k, 0, 1 ) != '_' ) {
$user->set( $k, $this->get( $k ) );
$oldUserComplete->set( $k, $this->get( $k ) );
}
}
foreach ( $values as $name => $value ) {
if ( in_array( $name, $userVars ) ) {
$user->set( $name, $value );
}
}
if ( $triggers ) {
if ( $ui == 1 ) {
$_PLUGINS->trigger( 'onBeforeUserUpdate', array( &$user, &$user, &$oldUserComplete, &$oldUserComplete ) );
} elseif ( $ui == 2 ) {
$_PLUGINS->trigger( 'onBeforeUpdateUser', array( &$user, &$user, &$oldUserComplete ) );
}
}
if ( isset( $values['password'] ) ) {
$clearTextPassword = $user->get( 'password' );
$user->set( 'password', $this->hashAndSaltPassword( $clearTextPassword ) );
} else {
$clearTextPassword = null;
$user->set( 'password', null );
}
$return = $user->store();
if ( $clearTextPassword ) {
$user->set( 'password', $clearTextPassword );
}
if ( $triggers ) {
if ( $return ) {
if ( $ui == 1 ) {
$_PLUGINS->trigger( 'onAfterUserUpdate', array( &$user, &$user, $oldUserComplete ) );
} elseif ( $ui == 2 ) {
$_PLUGINS->trigger( 'onAfterUpdateUser', array( &$user, &$user, $oldUserComplete ) );
}
}
}
$error = $user->getError();
if ( $error ) {
$this->set( '_error', $error );
}
unset( $user, $oldUserComplete );
return $return;
}
return false;
}
/**
* Store a single value to user object
*
* @param $name
* @param $value
* @param bool $triggers
* @return bool
*/
public function storeDatabaseValue( $name, $value, $triggers = true ) {
return $this->storeDatabaseValues( array( $name => $value ), $triggers );
}
/**
* Updates only in database $this->block
*
* @param bool $triggers
* @return bool
*/
public function storeBlock( $triggers = true ) {
if ( $this->id ) {
return $this->storeDatabaseValue( 'block', (int) $this->block, $triggers );
}
return false;
}
/**
* Updates only in database the cleartext $this->password
*
* @param bool $triggers
* @return bool
*/
public function storePassword( $triggers = true ) {
if ( $this->id ) {
return $this->storeDatabaseValue( 'password', $this->password, $triggers );
}
return false;
}
/**
* Updates only in database $this->approved
*
* @param bool $triggers
* @return bool
*/
public function storeApproved( $triggers = true ) {
if ( $this->id ) {
return $this->storeDatabaseValue( 'approved', (int) $this->approved, $triggers );
}
return false;
}
/**
* Updates only in database $this->approved
*
* @param bool $triggers
* @return bool
*/
public function storeConfirmed( $triggers = true ) {
if ( $this->id ) {
return $this->storeDatabaseValue( 'confirmed', (int) $this->confirmed, $triggers );
}
return false;
}
/**
* Saves a new or existing CB+CMS user
* WARNINGS:
* - You must verify authorization of user to perform this (user checkCBpermissions() )
* - You must $this->load() existing user first
*
* @param array $array Raw unfiltered input, typically $_POST
* @param int $ui 1 = Front-end (limitted rights), 2 = Backend (almost unlimitted), 0 = automated (full)
* @param string $reason 'edit' or 'register'
* @return boolean|array Result (or array if success and $reason == 'register')
*/
public function saveSafely( &$array, $ui, $reason ) {
global $_CB_framework, $ueConfig, $_PLUGINS;
// Get current user state and store it into $oldUserComplete:
$oldUserComplete = new UserTable( $this->_db );
foreach ( array_keys( get_object_vars( $this ) ) as $k ) {
if( substr( $k, 0, 1 ) != '_' ) { // ignore internal vars
$oldUserComplete->$k = $this->$k;
}
}
if ( $oldUserComplete->gids === null ) {
$oldUserComplete->gids = array();
}
// 1) Process and validate the fields in form by CB field plugins:
// 2) Bind the fields to CMS User:
$bindResults = $this->bindSafely( $array, $ui, $reason, $oldUserComplete );
if ( $bindResults ) {
// It's ok to use raw fields below as we've already validated in bindSafely with saveTabContents
// Check if username is missing:
if ( $this->username == '' ) {
// We don't have a username! Lets try to find one based off configured fallback:
$fallbackField = ( isset( $ueConfig['usernamefallback'] ) && $ueConfig['usernamefallback'] ? $ueConfig['usernamefallback'] : 'name' );
// Lets see if our fallback exists and that it's a valid string that has a value:
if ( $fallbackField == 'random' ) {
$this->username = $this->getRandomPassword();
$this->_cmsUser->username = $this->username;
} elseif ( isset( $this->$fallbackField ) && is_string( $this->$fallbackField ) && ( $this->$fallbackField != '' ) ) {
$this->username = $this->$fallbackField;
$this->_cmsUser->username = $this->username;
}
// Check if we have a username now:
if ( ( $this->username == '' ) && ( $this->email != '' ) ) {
// Oh no! We still don't have one! Force to email as backup:
$this->username = $this->email;
$this->_cmsUser->username = $this->username;
}
// Ok, one more try; lets see if we have a username now:
if ( ( $this->username == '' ) && ( $this->name != '' ) ) {
// What in the world! We still don't have one! Force to name as backup:
$this->username = $this->name;
$this->_cmsUser->username = $this->username;
}
// Now lets see if we finally have a username:
if ( $this->username != '' ) {
// We do! Awesome! Now lets format it so it'll validate in Joomla by removing disallowed characters, all duplicate spacing, and replacing spaces with underscore:
$this->username = preg_replace( '/[<>\\\\"%();&\']+/', '', trim( $this->username ) );
$this->_cmsUser->username = $this->username;
}
}
// Check if name is missing:
if ( $this->name == '' ) {
// Yup, it's missing; lets force it to username as backup:
$this->name = $this->username;
$this->_cmsUser->name = $this->name;
}
if ( ! $this->checkSafely() ) {
$bindResults = false;
}
}
// For new registrations or backend user creations, set registration date and password if neeeded:
$isNew = ( ! $this->id );
$newCBuser = ( $oldUserComplete->user_id == null );
if ( $isNew ) {
$this->registerDate = $this->_db->getUtcDateTime();
$this->lastvisitDate = $this->_db->getNullDate();
if ( ! isset( $this->activation ) ) {
$this->activation = $this->_db->getNullDate();
}
if ( ! isset( $this->resetCount ) ) {
$this->resetCount = 0;
}
if ( ! isset( $this->otpKey ) ) {
$this->otpKey = '';
}
if ( ! isset( $this->otep ) ) {
$this->otep = '';
}
if ( ! isset( $this->requireReset ) ) {
$this->requireReset = 0;
}
if ( ( ! isset( $this->authProvider ) ) && checkJversion( '4.0+' ) ) {
$this->authProvider = '';
}
}
if ( $bindResults ) {
if ( $isNew ) {
if ( $this->password == null ) {
$this->setRandomPassword();
$ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users.
}
}
// In backend only: if group has been changed and where original group was a Super Admin: check if there is at least a super-admin left:
if ( $ui == 2 ) {
$myGids = $_CB_framework->acl->get_groups_below_me( null, true );
$i_am_super_admin = Application::MyUser()->isSuperAdmin();
if ( ! $isNew ) {
// Joomla-ACL checks:
if ( $i_am_super_admin && ( $_CB_framework->myId() == $this->id ) ) {
// Check that a fool Super User does not block himself:
if ( $this->block && ! $oldUserComplete->block ) {
$this->_error = 'Super Users can not block themselves';
return false;
}
// Check that a fool Super User does not demote himself from Super-User rights:
if ( $this->gids != $oldUserComplete->gids ) {
$staysSuperUser = Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids,'core.admin', null );
if ( ! $staysSuperUser ) {
$this->_error = 'You cannot demote yourself from your Super User permission';
return false;
}
}
}
// Check that a non-Super User/non-admin does not demote an admin or a Super user:
if ( $this->gids != $oldUserComplete->gids ) {
if ( ( ! $i_am_super_admin )
&& ! ( Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.admin', 'com_comprofiler' )
|| ( Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.manage', 'com_users' )
&& Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.edit', 'com_users' )
&& Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.edit.state', 'com_users' ) ) ) )
{
// I am not a Super User and not an Users administrator:
$userIsSuperUser = Application::User( (int) $this->id )->isSuperAdmin();
// User is super-user: Check if he stays so:
if ( $userIsSuperUser ) {
$staysSuperUser = Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids, 'core.admin', null );
if ( ! $staysSuperUser ) {
$this->_error = 'You cannot remove a Super User permission. Only Super Users can do that.';
return false;
}
}
$userCanAdminUsers = ( Application::User( (int) $this->id )->isAuthorizedToPerformActionOnAsset( 'core.manage', 'com_users' ) || Application::User( (int) $this->id )->isAuthorizedToPerformActionOnAsset( 'core.manage', 'com_comprofiler' ) )
&& Application::User( (int) $this->id )->isAuthorizedToPerformActionOnAsset( 'core.edit', 'com_users' )
&& Application::User( (int) $this->id )->isAuthorizedToPerformActionOnAsset( 'core.edit.state', 'com_users' );
// User is users-administrator: check if he can stay so:
if ( $userCanAdminUsers ) {
$staysUserAdmin = ( Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids, 'core.manage', 'com_users' ) || Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids, 'core.manage', null ) )
&& Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids, 'core.edit', 'com_users' )
&& Application::CmsPermissions()->checkGroupsForActionOnAsset( $this->gids, 'core.edit.state', 'com_users' );
if ( ! $staysUserAdmin ) {
$this->_error = 'An users manager cannot be demoted by a non-administrator';
return false;
}
}
}
}
}
// Security check to avoid creating/editing user to higher level than himself: CB response to artf4529.
if ( ( ! $i_am_super_admin ) && ( $this->gids != $oldUserComplete->gids ) ) {
// Does user try to edit a user that has higher groups ?
if ( count( array_diff( $this->gids, $myGids ) ) != 0 ) {
$this->_error = 'Unauthorized attempt to change an user at higher level than allowed !';
return false;
}
// Does the user try to demote higher levels ?
if ( array_diff( $this->gids, $myGids ) != array_diff( $oldUserComplete->gids, $myGids ) ) {
$this->_error = 'Unauthorized attempt to change higher groups of an user than allowed !';
return false;
}
}
}
}
if ( $reason == 'edit' ) {
if ( $ui == 1 ) {
$_PLUGINS->trigger( 'onBeforeUserUpdate', array( &$this, &$this, &$oldUserComplete, &$oldUserComplete ) );
} elseif ( $ui == 2 ) {
if ( $isNew || $newCBuser ) {
$_PLUGINS->trigger( 'onBeforeNewUser', array( &$this, &$this, false ) );
} else {
$_PLUGINS->trigger( 'onBeforeUpdateUser', array( &$this, &$this, &$oldUserComplete ) );
}
}
} elseif ( $reason == 'register' ) {
$_PLUGINS->trigger( 'onBeforeUserRegistration', array( &$this, &$this ) );
}
$beforeResult = ! $_PLUGINS->is_errors();
if ( ! $beforeResult ) {
$this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones.
}
// Saves tab plugins:
// on edits, user params and block/email/approved/confirmed are done in cb.core predefined fields.
// So now calls this and more (CBtabs are already created in $this->bindSafely() ).
$pluginTabsResult = true;
if ( $reason == 'edit' ) {
$this->_cbTabs->savePluginTabs( $this, $array );
$pluginTabsResult = ! $_PLUGINS->is_errors();
if ( ! $pluginTabsResult ) {
$this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones.
}
}
$clearTextPassword = $this->password;
if ( $bindResults && $beforeResult && $pluginTabsResult ) {
// Hashes password for CMS storage:
if ( $clearTextPassword ) {
$hashedPassword = $this->hashAndSaltPassword( $clearTextPassword );
$this->password = $hashedPassword;
}
// Stores user if it's a new user:
if ( $isNew ) {
if ( ! $this->store() ) {
return false;
}
}
// Restores cleartext password for the saveRegistrationPluginTabs:
$this->password = $clearTextPassword;
if ( $isNew ) {
// Sets the instance of user, to avoid reload from database, and loss of the cleartext password.
CBuser::setUserGetCBUserInstance( $this );
}
}
if ( ( $reason === 'register' ) || ( ( $ui === 2 ) && ( $reason === 'edit' ) && ( $isNew || $newCBuser ) ) ) {
// call here since we got to have a user id:
$registerResults = array();
$registerResults['tabs'] = $this->_cbTabs->saveRegistrationPluginTabs( $this, $array );
if ( $_PLUGINS->is_errors() ) {
if ( $bindResults && $beforeResult && $pluginTabsResult ) {
$plugins_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones.
if ( $isNew ) {
// if it was a new user, and plugin gave error, revert the creation:
$this->delete();
}
$this->_error = $plugins_error;
} else {
$this->_error = $_PLUGINS->getErrorMSG( false ); // $_PLUGIN collects all error messages, incl. previous ones.
}
$pluginTabsResult = false;
}
}
if ( $bindResults && $beforeResult && $pluginTabsResult ) {
$this->_cbTabs->commitTabsContents( $this, $array, $reason );
$commit_errors = $_PLUGINS->getErrorMSG( false );
if ( count( $commit_errors ) > 0 ) {
$this->_error = $commit_errors;
$bindResults = false;
}
}
if ( ! ( $bindResults && $beforeResult && $pluginTabsResult ) ) {
$this->_cbTabs->rollbackTabsContents( $this, $array, $reason );
// Normal error exit point:
$_PLUGINS->trigger( 'onSaveUserError', array( &$this, $this->_error, $reason ) );
if ( is_array( $this->_error ) ) {
$this->_error = implode( '<br />', $this->_error );
}
return false;
}
// Stores the user (again if it's a new as the plugins might have changed the user record):
if ( $clearTextPassword ) {
$this->password = $hashedPassword;
}
if ( ! $this->store() ) {
return false;
}
// Restores cleartext password for the onAfter and activation events:
$this->password = $clearTextPassword;
// Triggers onAfter and activateUser events:
if ( $reason == 'edit' ) {
if ( $ui == 1 ) {
$_PLUGINS->trigger( 'onAfterUserUpdate', array( &$this, &$this, $oldUserComplete ) );
} elseif ( $ui == 2 ) {
if ( $isNew || $newCBuser ) {
if ( $isNew ) {
$ueConfig['emailpass'] = 1; // set this global to 1 to force password to be sent to new users.
}
$_PLUGINS->trigger( 'onAfterNewUser', array( &$this, &$this, false, true ) );
if ( $this->block == 0 && $this->approved == 1 && $this->confirmed ) {
activateUser( $this, 2, 'NewUser', false, $isNew );
}
} else {
if ( ( ! ( ( $oldUserComplete->approved == 1 || $oldUserComplete->approved == 2 ) && $oldUserComplete->confirmed ) )
&& ($this->approved == 1 && $this->confirmed ) )
{
// first time a just registered and confirmed user got approved in backend through save user:
if( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == "1" ) && ( $this->password == '' ) ) {
// generate the password is auto-generated and not set by the admin at this occasion:
$this->setRandomPassword();
$pwd = $this->hashAndSaltPassword( $this->password );
$this->_db->setQuery( "UPDATE #__users SET password=" . $this->_db->Quote($pwd) . " WHERE id = " . (int) $this->id );
$this->_db->query();
}
}
$_PLUGINS->trigger( 'onAfterUpdateUser', array( &$this, &$this, $oldUserComplete ) );
if ( ( ! ( ( $oldUserComplete->approved == 1 || $oldUserComplete->approved == 2 ) && $oldUserComplete->confirmed ) )
&& ($this->approved == 1 && $this->confirmed ) )
{
// first time a just registered and confirmed user got approved in backend through save user:
activateUser( $this, 2, 'UpdateUser', false );
}
}
}
} elseif ( $reason == 'register' ) {
$registerResults['after'] = $_PLUGINS->trigger( 'onAfterUserRegistration', array( &$this, &$this, true ) );
$registerResults['ok'] = true;
return $registerResults;
}
return true;
}
/**
* Generic check for whether dependencies exist for this object in the db schema
* Should be overridden if checks need to be done before delete()
*
* @param int $oid key index (only int supported here)
* @return boolean
*/
public function canDelete( $oid = null )
{
if ( $oid === null ) {
$k = $this->_tbl_key;
$oid = $this->$k;
}
if ( Application::MyUser()->isSuperAdmin() ) {
if ( $oid == Application::MyUser()->getUserId() ) {
$this->setError( CBTxt::T( 'You cannot delete yourself.' ) );
return false;
}
return true;
}
if ( Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.manage', 'com_users' )
&& Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.edit.state', 'com_users' )
&& ( ! Application::User( (int) $oid )->isSuperAdmin() ) ) {
return true;
}
$this->setError( CBTxt::T( 'Not Authorized' ) );
return false;
}
/**
* Deletes this record (no checks)
*
* @param int $oid Key id of row to delete (otherwise it's the one of $this)
* @param bool $cbUserOnly True: delete CB user only, False: delete CB and CMS user
* @return boolean
*/
public function delete( $oid = null, $cbUserOnly = false ) {
global $_CB_framework, $_PLUGINS;
$k = $this->_tbl_key;
if ( $oid ) {
$this->$k = (int) $oid;
}
static $cache = array();
if ( ! isset( $cache[$this->id] ) ) {
$cache[$this->id] = true;
} elseif ( $cache[$this->id] ) {
// User already being deleted; block double delete in same operation:
return false;
}
$_PLUGINS->loadPluginGroup( 'user' );
$_PLUGINS->trigger( 'onBeforeDeleteUser', array( $this ) );
if ( $_PLUGINS->is_errors() ) {
$this->setError( $_PLUGINS->getErrorMSG() );
return false;
} else {
deleteAvatar( $this->avatar );
$reports = new UserReportTable();
$reports->deleteUserReports( $this->id );
$views = new UserViewTable();
$views->deleteUserViews( $this->id );
if ( ! $cbUserOnly ) {
// Give pre-notice the user is deleted so we don't loop in CMS handling of user delete:
$cmsUser = Application::Cms()->getCmsUser( $this->id )->asCmsUser();
try {
$cmsUser->delete( $this->id );
} catch ( \RuntimeException $e ) {
$this->setError( $e->getMessage() );
return false;
}
}
if ( ! parent::delete( $oid ) ) {
return false;
}
$query = 'DELETE'
. "\n FROM " . $this->_db->NameQuote( '#__session' )
. "\n WHERE " . $this->_db->NameQuote( 'userid' ) . " = " . (int) $this->id;
$this->_db->setQuery( $query );
$this->_db->query();
$_PLUGINS->trigger( 'onAfterDeleteUser', array( $this, true ) );
}
return true;
}
public function checkin( $oid = null ) {
$this->_mapUsers(); // TODO: Not sure if this is even needed anymore as the checkin function doesn't exist anymore for JUSers, so old code got removed
return true;
}
protected function _mapUsers() {
if ( $this->_cmsUser === null ) {
$this->_cmsUser = Application::Cms()->getCmsUser( $this->id )->asCmsUser();
}
if ( $this->_comprofilerUser === null ) {
$this->_comprofilerUser = new ComprofilerTable( $this->_db );
}
$nullsAreUpdatingInSave = checkJversion( '4.0+' );
foreach ( get_object_vars( $this ) as $name => $value ) {
if ( $name === '_cmsUserData' ) {
if ( ! $value ) {
continue;
}
if ( ! $this->_cmsUser->bind( $value ) ) {
$this->_error = $this->_cmsUser->getError();
}
} elseif ( $name[0] != '_' ) {
if ( in_array( $name, $this->_nonComprofilerVars ) ) {
if ( ( $name === 'authProvider' ) && ( ! $nullsAreUpdatingInSave ) ) {
// Joomla 3 doesn't have this column so skip it
continue;
}
if ( $name == 'params' ) {
// Update the _params object with an up to date registry:
$this->_cmsUser->setParameters( new Registry( $value ) );
// Continue with regular binding as the params value should always be the json string:
}
if ( ( $value !== null ) || ! $nullsAreUpdatingInSave ) {
$this->_cmsUser->$name = $value;
}
} else {
$this->_comprofilerUser->$name = $value;
}
}
}
$this->_cmsUser->id = $this->id;
$this->_comprofilerUser->id = $this->id;
$this->_comprofilerUser->user_id = $this->id;
}
/**
* Gets a random password in clear-text
*
* @param int $length
* @return string
*/
public function getRandomPassword( $length = 12 ) {
global $_PLUGINS;
// Fire a trigger to allow the password generation functionality to be completely replaced or length adjusted:
$password = implode( '', $_PLUGINS->trigger( 'onUserRandomPassword', array( $this, &$length ) ) );
if ( ! $password ) {
// If password generation wasn't overridden by a plugin lets generate one using Joomla API:
jimport( 'joomla.plugin.helper' );
$password = UserHelper::genRandomPassword( $length );
}
return $password;
}
/**
* Sets a random password in clear-text into $this->password
*
* @param int $length
*/
public function setRandomPassword( $length = 12 ) {
$this->password = $this->getRandomPassword( $length );
}
/**
* Generate the hashed/salted/encoded password for the database
* and to check the password at login:
* if $row provided, it is checking the existing password (and update if needed)
* if not provided, it will generate a new hashed password
*
* @param string $passwd cleartext
* @return string salted/hashed password
*/
public function hashAndSaltPassword( $passwd ) {
$cmsUser = Application::Cms()->getCmsUser( 0 )->asCmsUser();
$data = array( 'password' => $passwd );
$cmsUser->bind( $data );
return $cmsUser->password;
}
/**
* Generate the hashed/salted/encoded password for the database
* and to check the password at login:
* if $row provided, it is checking the existing password (and update if needed)
* if not provided, it will generate a new hashed password
*
* @param string $passwd cleartext
* @return boolean TRUE/FALSE on password check
*/
public function verifyPassword( $passwd ) {
jimport( 'joomla.plugin.helper' );
return UserHelper::verifyPassword( $passwd, Application::Cms()->getCmsUser( (int) $this->id )->asCmsUser()->password );
}
public function _setActivationCode( ) {
global $_CB_framework;
$randomHash = md5( cbMakeRandomString() );
$scrambleSeed = (int) hexdec(substr( md5 ( $_CB_framework->getCfg( 'secret' ) . $_CB_framework->getCfg( 'db' ) ), 0, 7));
$scrambledId = $scrambleSeed ^ ( (int) $this->id );
$this->cbactivation = 'reg' . $randomHash . sprintf( '%08x', $scrambledId );
// for CMS compatibility (and JFusion compatibility):
$this->activation = $randomHash;
}
public function checkActivationCode( $confirmcode ) {
return ( $this->cbactivation === $confirmcode );
}
public function removeActivationCode( ) {
$query = 'UPDATE ' . $this->_db->NameQuote( '#__comprofiler' )
. "\n SET " . $this->_db->NameQuote( 'cbactivation' ) . ' = ' . $this->_db->Quote( '' )
. "\n WHERE " . $this->_db->NameQuote( 'id' ) . ' = ' . (int) $this->id;
$this->_db->setQuery( $query );
if ( $this->_db->query() ) {
$this->cbactivation = '';
$query = 'UPDATE ' . $this->_db->NameQuote( $this->_cmsUserTable )
. "\n SET " . $this->_db->NameQuote( 'activation' ) . ' = ' . $this->_db->Quote( '' )
. "\n WHERE " . $this->_db->NameQuote( 'id' ) . ' = ' . (int) $this->id;
$this->_db->setQuery( $query );
if ( $this->_db->query() ) {
$this->activation = '';
}
} else {
global $_CB_framework;
if ( $_CB_framework->getUi() != 0 ) {
trigger_error( 'SQL-unblock2 error: ' . $this->_db->getErrorMsg(), E_USER_WARNING );
}
}
}
/**
* Gets user_id out of the activation code. WARNING: do not trust the user id until full activation code is checked.
*
* @static
* @param string $confirmcode
* @return int|null
*/
static public function getUserIdFromActivationCode( $confirmcode ) {
global $_CB_framework;
$lengthConfirmcode = strlen( $confirmcode );
if ($lengthConfirmcode == ( 3+32+8 ) ) {
$scrambleSeed = (int) hexdec(substr( md5 ( $_CB_framework->getCfg( 'secret' ) . $_CB_framework->getCfg( 'db' ) ), 0, 7));
$unscrambledId = $scrambleSeed ^ ( (int) hexdec(substr( $confirmcode, 3+32 ) ) );
return $unscrambledId;
}
return null;
}
/**
* Changes the confirmation state of a user
*
* @param int $state 0: Pending, 1: Confirmed
* @param string $messages The messages returned by activateUser when approved
* @return bool
*/
public function confirmUser( $state, &$messages = null ) {
global $_CB_framework, $ueConfig, $_PLUGINS;
if ( $this->confirmed == $state ) {
return true;
}
if ( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == 1 ) && ( $state == 1 ) && ( $this->approved == 1 ) ) {
$this->setRandomPassword();
}
$_PLUGINS->trigger( 'onBeforeUserConfirm', array( &$this, &$state ) );
if ( $_PLUGINS->is_errors() ) {
$this->setError( $_PLUGINS->getErrorMSG( false ) );
return false;
}
$this->confirmed = (int) $state;
if ( $this->storeConfirmed( false ) ) {
if ( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == 1 ) && ( $state == 1 ) && ( $this->approved == 1 ) ) {
$this->storePassword( false );
}
$_PLUGINS->trigger( 'onAfterUserConfirm', array( $this, $state ) );
if ( $state == 1 ) {
$messages = activateUser( $this, $_CB_framework->getUi(), 'UserConfirmation', ( $_CB_framework->getUi() != 2 ) );
}
return true;
}
return false;
}
/**
* Changes the approval state of a user
*
* @param int $state 0: Pending, 1: Approved, 2: Rejected
* @param string $messages The messages returned by activateUser when approved or reason for approval rejection
* @return bool
*/
public function approveUser( $state, &$messages = null )
{
global $_CB_framework, $ueConfig, $_PLUGINS;
if ( $this->approved == $state ) {
return true;
}
if ( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == 1 ) && ( $state == 1 ) ) {
$this->setRandomPassword();
}
$_PLUGINS->trigger( 'onBeforeUserApproval', array( &$this, &$state ) );
if ( $_PLUGINS->is_errors() ) {
$this->setError( $_PLUGINS->getErrorMSG( false ) );
return false;
}
$this->approved = (int) $state;
if ( $this->storeApproved( false ) ) {
if ( isset( $ueConfig['emailpass'] ) && ( $ueConfig['emailpass'] == 1 ) && ( $state == 1 ) ) {
$this->storePassword( false );
}
$_PLUGINS->trigger( 'onAfterUserApproval', array( $this, $state ) );
if ( $state == 1 ) {
$messages = activateUser( $this, $_CB_framework->getUi(), 'UserApproval', false );
} elseif ( $state == 2 ) {
$cbNotification = new cbNotification();
$savedLanguage = CBTxt::setLanguage( $this->getUserLanguage() );
$cbNotification->sendFromSystem( (int) $this->id, CBTxt::T( 'UE_REG_REJECT_SUB', 'Your sign up request has been rejected!' ), CBTxt::T( 'UE_USERREJECT_MSG', 'Your sign up at [sitename] has been rejected for the following reason: [reason]', array( '[sitename]' => $_CB_framework->getCfg( 'sitename' ), '[reason]' => $messages ) ) );
CBTxt::setLanguage( $savedLanguage );
}
return true;
}
return false;
}
/**
* Changes the block state of a user
*
* @param int $state 0: Unblocked, 1: Blocked
* @return bool
*/
public function blockUser( $state )
{
global $_PLUGINS;
if ( $this->block == $state ) {
return true;
}
$_PLUGINS->trigger( 'onBeforeUserBlocking', array( &$this, &$state ) );
if ( $_PLUGINS->is_errors() ) {
$this->setError( $_PLUGINS->getErrorMSG( false ) );
return false;
}
$this->block = (int) $state;
if ( $this->storeBlock( false ) ) {
$_PLUGINS->trigger( 'onAfterUserBlocking', array( $this, $state ) );
return true;
}
return false;
}
/**
* Changes the ban state of a user
*
* @param int $state 0: Unbanned, 1: Banned, 2: Pending
* @param null|UserTable $by The user that is banning or unbanning
* @param null|string $reason The reason for the ban or unban
* @return bool
*/
public function banUser( $state, $by = null, $reason = null )
{
global $_CB_framework, $ueConfig, $_PLUGINS;
if ( $this->banned == $state ) {
return true;
}
if ( ! $by ) {
$by = CBuser::getMyUserDataInstance();
}
$values = array();
$_PLUGINS->trigger( 'onBeforeUserBan', array( &$this, &$state, &$by, &$reason ) );
if ( $_PLUGINS->is_errors() ) {
$this->setError( $_PLUGINS->getErrorMSG( false ) );
return false;
}
$values['banned'] = (int) $state;
if ( $reason ) {
$values['bannedreason'] = $reason;
}
if ( $state == 0 ) {
$values['unbannedby'] = (int) $by->id;
$values['unbanneddate'] = $_CB_framework->getUTCDate();
} elseif ( $state == 1 ) {
$values['bannedby'] = (int) $by->id;
$values['banneddate'] = $_CB_framework->getUTCDate();
}
if ( $this->storeDatabaseValues( $values, false ) ) {
$_PLUGINS->trigger( 'onAfterUserBan', array( $this, $state, $by, $reason ) );
$cbNotification = new cbNotification();
$savedLanguage = CBTxt::setLanguage( $this->getUserLanguage() );
if ( $state == 0 ) {
$cbNotification->sendFromSystem( (int) $this->id, CBTxt::T( 'UE_UNBANUSER_SUB', 'User Profile Unbanned' ), CBTxt::T( 'UE_UNBANUSER_MSG', 'Your user profile was unbanned by an administrator. Your profile is now visible to all users again.' ) );
} elseif ( $state == 1 ) {
$cbNotification->sendFromSystem( (int) $this->id, CBTxt::T( 'UE_BANUSER_SUB', 'User Profile Banned.' ), CBTxt::T( 'UE_BANUSER_MSG', 'Your user profile was banned by an administrator. Please log in and review why it was banned.' ) );
} elseif ( $state == 2 ) {
if ( isset( $ueConfig['emailpass'] ) && ( $ueConfig['moderatorEmail'] == 1 ) && ( $_CB_framework->getUi() != 2 ) ) {
$cbNotification->sendToModerators( CBTxt::T( 'UE_UNBANUSERREQUEST_SUB', 'Unban Request Pending Review' ), CBTxt::T( 'UE_UNBANUSERREQUEST_MSG', 'A user has submitted a request to unban their profile. Please log in and take the appropriate action.' ) );
}
}
CBTxt::setLanguage( $savedLanguage );
return true;
}
return false;
}
/**
* Gets the language of user (or empty string if language is not set)
*
* @return string The language of $this user
*/
public function getUserLanguage( )
{
return Application::Cms()->getCmsUser( (int) $this->id )->asCmsUser()->getParam('language', '' );
}
/**
* Returns the users formatted named based off configuration
*
* @param null|int $format
* @return string
*/
public function getFormattedName( $format = null )
{
static $cache = array();
if ( ! $format ) {
$format = Application::Config()->get( 'name_format', 3, GetterInterface::INT );
}
$userId = $this->get( 'id', 0, GetterInterface::INT );
// If name has already been parsed for the user then output the cached response (skip for guest objects):
if ( $userId && isset( $cache[$userId][$format] ) ) {
return $cache[$userId][$format];
}
$username = $this->get( 'username', null, GetterInterface::STRING );
$name = $this->get( 'name', null, GetterInterface::STRING );
$firstName = $this->get( 'firstname', null, GetterInterface::STRING );
$middleName = $this->get( 'middlename', null, GetterInterface::STRING );
$lastName = $this->get( 'lastname', null, GetterInterface::STRING );
if ( $name && ( ! $firstName ) && ( ! $middleName ) && ( ! $lastName ) && ( $format >= 10 ) ) {
// We're missing first, middle, and last name with a format that requires them so lets try and get them from name:
$posMiddleName = strpos( $name, ' ' );
$posLastName = strrpos( $name, ' ' );
if ( $posLastName !== false ) {
$lastName = substr( $name, ( $posLastName + 1 ) );
$firstName = substr( $name, 0, $posMiddleName );
if ( $posMiddleName !== $posLastName ) {
$middleName = substr( $name, ( $posMiddleName + 1 ), ( $posLastName - $posMiddleName -1 ) );
} else {
$middleName = null;
}
} else {
$firstName = null;
$lastName = $name;
}
} elseif ( $name && ( ! $firstName ) && ( ! $lastName ) && ( $format >= 5 ) ) {
// We're missing first and last name with a format that requires them so lets try and get them from name:
$posLastName = strrpos( $name, ' ' );
if ( $posLastName !== false ) {
$firstName = substr( $name, 0, $posLastName );
$lastName = substr( $name, ( $posLastName + 1 ) );
} else {
$firstName = null;
$lastName = $name;
}
}
switch ( $format ) {
case 1: // Name Only
$formatted = $name;
break;
case 2: // Name (Username)
$formatted = $name . ( $username ? ' (' . $username . ')' : null );
break;
case 4: // Username (Name)
$formatted = $username . ( $name ? ' (' . $name . ')' : null );
break;
case 5: // First Name Only
$formatted = $firstName;
break;
case 6: // Last Name Only
$formatted = $lastName;
break;
case 7: // First Name and Last Initial
$formatted = trim( $firstName . ( $lastName ? ' ' . cbutf8_substr( $lastName, 0, 1 ) . '.' : null ) );
break;
case 8: // First Initial and Last Name
$formatted = trim( ( $firstName ? cbutf8_substr( $firstName, 0, 1 ) . '. ' : null ) . $lastName );
break;
case 9: // First and Last Initial
$formatted = ( $firstName ? cbutf8_substr( $firstName, 0, 1 ) : null ) . ( $lastName ? cbutf8_substr( $lastName, 0, 1 ) : null );
break;
case 10: // First Initial, Middle Initial, and Last Name
$formatted = trim( ( $firstName ? cbutf8_substr( $firstName, 0, 1 ) : null ) . ( $middleName ? cbutf8_substr( $middleName, 0, 1 ) : null ) . ( $firstName || $middleName ? ' ' : null ) . $lastName );
break;
case 11: // First Name, Middle Initial, and Last Name
$formatted = trim( $firstName . ' ' . ( $middleName ? cbutf8_substr( $middleName, 0, 1 ) . '. ' : null ) . $lastName );
break;
case 12: // Custom
$custom = CBTxt::T( Application::Config()->get( 'custom_name_format', null, GetterInterface::STRING ) );
if ( ! $custom ) {
// Stop here and use fallback output:
$formatted = null;
} else {
$names = array( '[username]' => $username,
'[name]' => $name,
'[firstname]' => $firstName,
'[firstinitial]' => ( $firstName ? cbutf8_substr( $firstName, 0, 1 ) : null ),
'[middlename]' => $middleName,
'[middleinitial]' => ( $middleName ? cbutf8_substr( $middleName, 0, 1 ) : null ),
'[lastname]' => $lastName,
'[lastinitial]' => ( $lastName ? cbutf8_substr( $lastName, 0, 1 ) : null )
);
// Replace name fields first since they may have required parsing above and would conflict with field name parsing in replaceUserVars:
$custom = str_replace( array_keys( $names ), array_values( $names ), $custom );
$formatted = trim( CBuser::getInstance( $userId, false )->replaceUserVars( $custom, true, false, array(), false ) );
}
break;
case 3: // Username Only
default:
$formatted = $username;
break;
}
if ( ! $formatted ) {
// The format couldn't construct a name so lets try to fallback to something:
if ( $username ) {
$formatted = $username;
} elseif ( $name ) {
// This is a fallback for pseudo users where username won't exist (e.g. public email form sent from public user):
$formatted = $name;
} else {
$formatted = CBTxt::T( 'UE_UNNAMED_USER', 'Unnamed user' );
}
}
$formatted = htmlspecialchars( $formatted );
if ( $userId ) {
// Cache it so we don't have to go through all of the above all over again.. this function gets called A LOT:
$cache[$userId][$format] = $formatted;
}
return $formatted;
}
/**
* Checks edit state permissions for toggles
* Used by Backend XML only
*
* @return bool
*/
private function canToggle()
{
if ( Application::MyUser()->isSuperAdmin() ) {
return true;
}
if ( Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.manage', 'com_users' )
&& Application::MyUser()->isAuthorizedToPerformActionOnAsset( 'core.edit.state', 'com_users' )
&& ( ! Application::User( (int) $this->id )->isSuperAdmin() ) ) {
return true;
}
$this->setError( CBTxt::T( 'Not Authorized' ) );
return false;
}
/**
* Toggles confirmation state of a user
* Used by Backend XML only
* @deprecated Do not use directly, only for XML users backend
*
* @param int $value
* @return bool
*/
public function toggleUserConfirm( $value )
{
if ( ! $this->canToggle() ) {
return false;
}
return $this->confirmUser( $value );
}
/**
* Toggles approval state of a user
* Used by Backend XML only
* @deprecated Do not use directly, only for XML users backend
*
* @param int $value
* @return bool
*/
public function toggleUserApproval( $value )
{
if ( ! $this->canToggle() ) {
return false;
}
return $this->approveUser( $value );
}
/**
* Toggles block state of a user
* Used by Backend XML only
* @deprecated Do not use directly, only for XML users backend
*
* @param int $value
* @return bool
*/
public function toggleUserBlock( $value )
{
if ( $value && ( $this->id == Application::MyUser()->getUserId() ) ) {
$this->setError( CBTxt::T( 'You cannot block yourself.' ) );
return false;
}
if ( ! $this->canToggle() ) {
return false;
}
return $this->blockUser( $value );
}
/**
* Toggles ban state of a user
* Used by Backend XML only
* @deprecated Do not use directly, only for XML users backend
*
* @param int $value
* @return bool
*/
public function toggleUserBan( $value )
{
if ( ! $this->canToggle() ) {
return false;
}
return $this->banUser( $value );
}
}