<?php
/**
* @version	$Id: users_event_handler.php 13250 2010-03-17 10:24:22Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class UsersEventHandler extends kDBEventHandler
	{
		/**
		 * Allows to override standart permission mapping
		 *
		 */
		function mapPermissions()
		{
			parent::mapPermissions();
			$permissions = Array (
				// admin
				'OnSetPersistantVariable'	=>	Array('self' => 'view'), // because setting to logged in user only
				'OnUpdateRootPassword'		=>	Array('self' => true),
				'OnUpdatePassword'		=>	Array('self' => true),

				// front
				'OnRefreshForm'				=>	Array('self' => true),

				'OnForgotPassword'			=>	Array('self' => true),
				'OnResetPassword'			=>	Array('self' => true),
				'OnResetPasswordConfirmed'	=>	Array('self' => true),

				'OnSubscribeQuery'			=>	Array('self' => true),
				'OnSubscribeUser'			=>	Array('self' => true),

				'OnRecommend'				=>	Array('self' => true),

				'OnItemBuild'				=>	Array('self' => true),
				'OnMassResetSettings'	=> Array('self' => 'edit'),
				'OnMassCloneUsers'	=> Array('self' => 'add'),
			);

			$this->permMapping = array_merge($this->permMapping, $permissions);
		}

		/**
		 * Shows only admins when required
		 *
		 * @param kEvent $event
		 */
		function SetCustomQuery(&$event)
		{
			$object =& $event->getObject();
			/* @var $object kDBList */

			if ($event->Special == 'admins') {
				$object->addFilter('primary_filter', 'ug.GroupId = 11');
			}

			if ($event->Special == 'regular') {
				$object->addFilter('primary_filter', 'ug.GroupId <> 11 OR ug.GroupId IS NULL ');
			}

			if (!$this->Application->isAdminUser) {
				$object->addFilter('status_filter', '%1$s.Status = '.STATUS_ACTIVE);
			}

			if ($event->Special == 'online') {
				$object->addFilter('online_users_filter', 's.PortalUserId IS NOT NULL');
			}

			if ($event->Special == 'group') {
				$group_id = $this->Application->GetVar('g_id');
				if ($group_id !== false) {
					// show only users, that user doesn't belong to current group
					$table_name = $this->Application->GetTempName(TABLE_PREFIX.'UserGroup', 'prefix:g');
					$sql = 'SELECT PortalUserId
							FROM ' . $table_name . '
							WHERE GroupId = ' . (int)$group_id;
					$user_ids = $this->Conn->GetCol($sql);
//					array_push($user_ids); // Guest & Everyone groups are set dynamically
					if ($user_ids) {
						$object->addFilter('already_member_filter', '%1$s.PortalUserId NOT IN ('.implode(',', $user_ids).')');
					}
				}
			}
		}

		/**
		 * Checks permissions of user
		 *
		 * @param kEvent $event
		 */
		function CheckPermission(&$event)
		{
			if ($event->Name == 'OnLogin' || $event->Name == 'OnLogout') {
				// permission is checked in OnLogin event directly
				return true;
			}

			if (!$this->Application->isAdminUser) {
				$user_id = $this->Application->RecallVar('user_id');
				$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));

				if ($event->Name == 'OnCreate' && $user_id == -2) {
					// "Guest" can create new users
					return true;
				}

				if ($event->Name == 'OnUpdate' && $user_id > 0) {
					$user_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true));
					foreach ($items_info as $id => $field_values) {
						if ($id != $user_id) {
							// registered users can update their record only
							return false;
						}

						$user_dummy->Load($id);
						$status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField'));

						if ($user_dummy->GetDBField($status_field) != STATUS_ACTIVE) {
							// not active user is not allowed to update his record (he could not activate himself manually)
							return false;
						}

						if (isset($field_values[$status_field]) && $user_dummy->GetDBField($status_field) != $field_values[$status_field]) {
							// user can't change status by himself
							return false;
						}
					}
					return true;
				}

				if ($event->Name == 'OnUpdate' && $user_id <= 0) {
					// guests are not allowed to update their record, because they don't have it :)
					return false;
				}
			}

			return parent::CheckPermission($event);
		}

		/**
		 * Handles session expiration (redirects to valid template)
		 *
		 * @param kEvent $event
		 */
		function OnSessionExpire(&$event)
		{
			$this->Application->resetCounters('UserSession');

			// place 2 of 2 (also in kHTTPQuery::getRedirectParams)
			$admin_url_params = Array (
				'm_cat_id' => 0, // category means nothing on admin login screen
				'm_wid' => '', // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets)
				'pass' => 'm', // don't pass any other (except "m") prefixes to admin session expiration template
				'expired' => 1, // expiration mark to show special error on login screen
				'no_pass_through' => 1, // this way kApplication::HREF won't add them again
			);

			if ($this->Application->isAdmin) {
				$this->Application->Redirect('index', $admin_url_params, '', 'index.php');
			}

			if ($this->Application->GetVar('admin') == 1) {
				// Front-End showed in admin's right frame
				$session_admin =& $this->Application->recallObject('Session.admin');
				/* @var $session_admin Session */

				if (!$session_admin->LoggedIn()) {
					// front-end session created from admin session & both expired
					$this->Application->DeleteVar('admin');
					$this->Application->Redirect('index', $admin_url_params, '', 'admin/index.php');
				}
			}

			// Front-End session expiration
			$get = $this->Application->HttpQuery->getRedirectParams();
			$t = $this->Application->GetVar('t');
			$get['js_redirect'] = $this->Application->ConfigValue('UseJSRedirect');
			$this->Application->Redirect($t ? $t : 'index', $get);
		}

		/**
		 * Checks user data and logs it in if allowed
		 *
		 * OnLogin is called from u:autoLoginUser and password is supplied
		 * OnLogin is called from u:OnAutoLoginUser supplying cookie with encoded username & password
		 *
		 * @param kEvent $event
		 */
		function OnLogin(&$event)
		{
			// persistent session data after login is not refreshed, because redirect will follow in any case
			$prefix_special = $this->Application->isAdmin ? 'u.current' : 'u'; // "u" used on front not to change theme
			$object =& $this->Application->recallObject($prefix_special, null, Array('skip_autoload' => true));
			$password = $this->Application->GetVar('password');

			$invalid_pseudo = $this->Application->isAdmin ? 'la_invalid_password' : 'lu_invalid_password';
			$remember_login_cookie = $this->Application->GetVar('remember_login');

			if (!$password && !$remember_login_cookie) {
				$object->SetError('ValidateLogin', 'invalid_password', $invalid_pseudo);
				$event->status = erFAIL;
				return false;
			}

			$email_as_login = $this->Application->ConfigValue('Email_As_Login');
			list ($login_field, $submit_field) = $email_as_login && !$this->Application->isAdmin ? Array('Email', 'email') : Array('Login', 'login');
			$login_value = $this->Application->GetVar($submit_field);

			// process "Save Username" checkbox
			if ($this->Application->isAdmin) {
				$save_username = $this->Application->GetVar('cb_save_username') ? $login_value : '';
				$this->Application->Session->SetCookie('save_username', $save_username, adodb_mktime() + 31104000); // 1 year expiration
				$this->Application->SetVar('save_username', $save_username); // cookie will be set on next refresh, but refresh won't occur if login error present, so duplicate cookie in HTTPQuery
			}

			$super_admin = ($login_value == 'super-root') && $this->verifySuperAdmin();
			if ($this->Application->isAdmin && ($login_value == 'root') || ($super_admin && $login_value == 'super-root')) {
				// logging in "root" (admin only)

				$login_value = 'root';

				$root_password = $this->Application->ConfigValue('RootPass');
				$password_formatter =& $this->Application->recallObject('kPasswordFormatter');
				$test = $password_formatter->EncryptPassword($password, 'b38');
				if ($root_password != $test) {
					$object->SetError('ValidateLogin', 'invalid_password', $invalid_pseudo);
					$event->status = erFAIL;
					return false;
				}
				elseif ($this->checkLoginPermission($login_value)) {
					$user_id = -1;
					$object->Load($user_id);
					$object->SetDBField('Login', $login_value);

					$session =& $this->Application->recallObject('Session');
					$session->SetField('PortalUserId', $user_id);
//					$session->SetField('GroupList', implode(',', $groups) );
					$this->Application->SetVar('u.current_id', $user_id);
					$this->Application->StoreVar('user_id', $user_id);

					$this->Application->LoadPersistentVars();

					if ($super_admin) {
						$this->Application->StoreVar('super_admin', 1);
					}

					$this->Application->HandleEvent($dummy, 'session-log:OnStartSession');
					$this->processLoginRedirect($event, $password);
					$this->_processInterfaceLanguage($event);
					return true;
				}
				else {
					$object->SetError('ValidateLogin', 'invalid_license', 'la_invalid_license');
					$event->status = erFAIL;
					return false;
				}
			}

			/*$sql = 'SELECT PortalUserId FROM '.$object->TableName.' WHERE (%s = %s) AND (Password = MD5(%s))';
			$user_id = $this->Conn->GetOne( sprintf($sql, $login_field, $this->Conn->qstr($login_value), $this->Conn->qstr($password) ) );*/

			if ($remember_login_cookie) {
				$user_info = explode('|', $remember_login_cookie); // 0 - username, 1 - md5(password)

				$sql = 'SELECT PortalUserId
						FROM '.$object->TableName.'
						WHERE (Email = %1$s OR Login = %1$s) AND (Password = %2$s)';
				$user_id = $this->Conn->GetOne( sprintf($sql, $this->Conn->qstr($user_info[0]), $this->Conn->qstr($user_info[1]) ) );
			} else {
				$sql = 'SELECT PortalUserId
						FROM '.$object->TableName.'
						WHERE (Email = %1$s OR Login = %1$s) AND (Password = MD5(%2$s))';
				$user_id = $this->Conn->GetOne( sprintf($sql, $this->Conn->qstr($login_value), $this->Conn->qstr($password) ) );
			}

			if ($user_id) {
				$object->Load($user_id);
				if (!$this->checkBanRules($object)) {
					$event->status = erFAIL;
					return false;
				}
				if ($object->GetDBField('Status') == STATUS_ACTIVE) {
					$groups = $object->getMembershipGroups(true);
					if(!$groups) $groups = Array();
					array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup') );
					$this->Application->StoreVar( 'UserGroups', implode(',', $groups) );

					if ($this->checkLoginPermission($login_value)) {
						$session =& $this->Application->recallObject('Session');
						$session->SetField('PortalUserId', $user_id);
						$session->SetField('GroupList', implode(',', $groups) );
						$this->Application->SetVar('u.current_id', $user_id);
						$this->Application->StoreVar('user_id', $user_id);

						$this->Application->LoadPersistentVars();

						if (!$remember_login_cookie) {
							// don't change last login time when auto-login is used
							$this_login = (int)$this->Application->RecallPersistentVar('ThisLogin');
							$this->Application->StorePersistentVar('LastLogin', $this_login);
							$this->Application->StorePersistentVar('ThisLogin', adodb_mktime());
						}

						if ($this->Application->GetVar('cb_remember_login') == 1) {
							// remember username & password when "Remember Login" checkbox us checked (when user is using login form on Front-End)
		                    $remember_login_cookie = $login_value . '|' . md5($password);
							$this->Application->Session->SetCookie('remember_login', $remember_login_cookie, adodb_mktime() + 2592000); // 30 days
						}

						$this->Application->HandleEvent($dummy, 'session-log:OnStartSession');
					}
					else {
						$object->Load(-2);
						$object->SetError('ValidateLogin', 'no_permission', 'lu_no_permissions');
						$event->status = erFAIL;
					}

					if (!$remember_login_cookie) {
						$this->processLoginRedirect($event, $password);
						$this->_processInterfaceLanguage($event);
					}
				}
				else {
					$event->redirect = $this->Application->GetVar('pending_disabled_template');
				}
			}
			else
			{
				$object->SetID(-2);
				$object->SetError('ValidateLogin', 'invalid_password', $invalid_pseudo);
				$event->status = erFAIL;
			}

			$event->SetRedirectParam('pass', 'all');
//			$event->SetRedirectParam('pass_category', 1); // to test
		}

		/**
		 * Sets correct interface language after sucessful login, based on user settings
		 *
		 * @param kEvent $event
		 */
		function _processInterfaceLanguage(&$event)
		{
			if (($event->status != erSUCCESS) || !$this->Application->isAdmin) {
				return ;
			}

			$is_root = $this->Application->RecallVar('user_id') == -1;

			$object =& $this->Application->recallObject('u.current');
			/* @var $object kDBItem */

			$user_language_id = $is_root ? $this->Application->RecallPersistentVar('AdminLanguage') : $object->GetDBField('AdminLanguage');

			$sql = 'SELECT LanguageId, IF(LanguageId = ' . (int)$user_language_id . ', 2, AdminInterfaceLang) AS SortKey
					FROM ' . TABLE_PREFIX . 'Language
					WHERE Enabled = 1
					HAVING SortKey <> 0
					ORDER BY SortKey DESC';
			$language_info = $this->Conn->GetRow($sql);
			$language_id = $language_info && $language_info['LanguageId'] ? $language_info['LanguageId'] : $user_language_id;

			if ($user_language_id != $language_id) {
				// first admin login OR language was delelted or disabled
				if ($is_root) {
					$this->Application->StorePersistentVar('AdminLanguage', $language_id);
				}
				else {
					$object->SetDBField('AdminLanguage', $language_id);
					$object->Update();
				}
			}

			$event->SetRedirectParam('m_lang', $language_id); // data
			$this->Application->Session->SetField('Language', $language_id); // interface
		}

		/**
		 * [HOOK] Auto-Logins Front-End user when "Remember Login" cookie is found
		 *
		 * @param kEvent $event
		 */
		function OnAutoLoginUser(&$event)
		{
			$remember_login_cookie = $this->Application->GetVar('remember_login');

			if (!$remember_login_cookie || $this->Application->isAdmin || $this->Application->LoggedIn()) {
				return ;
			}

			$event->CallSubEvent('OnLogin');
		}

		/**
		 * Checks that user is allowed to use super admin mode
		 *
		 * @return bool
		 */
		function verifySuperAdmin()
		{
			$sa_mode = ipMatch(defined('SA_IP') ? SA_IP : '');
			return $sa_mode || $this->Application->isDebugMode();
		}

		/**
		 * Enter description here...
		 *
		 * @param string $user_name
		 * @return bool
		 */
		function checkLoginPermission($user_name)
		{
			$ret = true;
			if ($this->Application->isAdmin) {
				$modules_helper =& $this->Application->recallObject('ModulesHelper');

				if ($user_name != 'root') {
					// root is virtual user, so allow him to login to admin in any case
					$ret = $this->Application->CheckPermission('ADMIN', 1);
				}
			}
			else {
				$ret = $this->Application->CheckPermission('LOGIN', 1);
			}

			return $ret;
		}

		/**
		 * Process all required data and redirect logged-in user
		 *
		 * @param kEvent $event
		 */
		function processLoginRedirect(&$event, $password)
		{
			$prefix_special = $this->Application->isAdmin ? 'u.current' : 'u'; // "u" used on front not to change theme
			$object =& $this->Application->recallObject($prefix_special, null, Array('skip_autoload' => true));

			$next_template = $this->Application->GetVar('next_template');

			if ($next_template) {
				$event->redirect = $next_template;
			}

			if ($this->Application->ConfigValue('UseJSRedirect')) {
				$event->SetRedirectParam('js_redirect', 1);
			}

			$sync_manager =& $this->Application->recallObjectP('UsersSyncronizeManager', null, Array(), 'InPortalSyncronize');
			$sync_manager->performAction('LoginUser', $object->GetDBField('Login'), $password);
			$this->Application->resetCounters('UserSession');
		}

		/**
		 * Called when user logs in using old in-portal
		 *
		 * @param kEvent $event
		 */
		function OnInpLogin(&$event)
		{
			$sync_manager =& $this->Application->recallObjectP('UsersSyncronizeManager', null, Array(), 'InPortalSyncronize');
			$sync_manager->performAction('LoginUser', $event->getEventParam('user'), $event->getEventParam('pass') );

			if ($event->redirect && is_string($event->redirect)) {
				// some real template specified instead of true
				$this->Application->Redirect($event->redirect, $event->redirect_params);
			}
		}

		/**
		 * Called when user logs in using old in-portal
		 *
		 * @param kEvent $event
		 */
		function OnInpLogout(&$event)
		{
			$sync_manager =& $this->Application->recallObjectP('UsersSyncronizeManager', null, Array(), 'InPortalSyncronize');
			$sync_manager->performAction('LogoutUser');
		}

		function OnLogout(&$event)
		{
			$sync_manager =& $this->Application->recallObjectP('UsersSyncronizeManager', null, Array(), 'InPortalSyncronize');
			$sync_manager->performAction('LogoutUser');

			$this->Application->HandleEvent($dummy, 'session-log:OnEndSession');

			$this->Application->SetVar('u.current_id', -2);
			$object =& $this->Application->recallObject('u.current', null, Array('skip_autoload' => true));
			$object->Load(-2);

			$this->Application->DestroySession();

			$session =& $this->Application->recallObject('Session');
			/* @var $session Session */

			$group_list = $this->Application->ConfigValue('User_GuestGroup') . ',' . $this->Application->ConfigValue('User_LoggedInGroup');

			$session->SetField('PortalUserId', -2);
			$session->SetField('GroupList', $group_list);
			$this->Application->StoreVar('user_id', -2, true);
			$this->Application->StoreVar('UserGroups', $group_list, true);

			if ($this->Application->ConfigValue('UseJSRedirect')) {
				$event->SetRedirectParam('js_redirect', 1);
			}

			$this->Application->resetCounters('UserSession');
			$this->Application->Session->SetCookie('remember_login', '', adodb_mktime() - 3600);

			$event->SetRedirectParam('pass', 'all');
		}

		/**
		 * Prefill states dropdown with correct values
		 *
		 * @param kEvent $event
		 * @access public
		 */
		function OnPrepareStates(&$event)
		{
			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
			$cs_helper->PopulateStates($event, 'State', 'Country');

			$object =& $event->getObject();

			if( $object->isRequired('Country') && $cs_helper->CountryHasStates( $object->GetDBField('Country') ) ) $object->setRequired('State', true);
			$object->setLogin();
		}

		/**
		 * Redirects user after succesfull registration to confirmation template (on Front only)
		 *
		 * @param kEvent $event
		 */
		function OnAfterItemCreate(&$event)
		{
			$this->saveUserImages($event);

			if ($this->Application->GetVar('skip_set_primary')) return;
			$is_subscriber = $this->Application->GetVar('IsSubscriber');
			if(!$is_subscriber)
			{
				$object =& $event->getObject();

				$ug_table = TABLE_PREFIX.'UserGroup';
				if ($object->mode == 't') {
					$ug_table = $this->Application->GetTempName($ug_table, 'prefix:'.$event->Prefix);
				}

				$sql = 'UPDATE '.$ug_table.'
						SET PrimaryGroup = 0
						WHERE PortalUserId = '.$object->GetDBField('PortalUserId');
				$this->Conn->Query($sql);

				// set primary group to user
				if ($this->Application->isAdminUser && $this->Application->GetVar('user_group')) {
					// while in admin you can set any group for new users
					$group_id = $this->Application->GetVar('user_group');
				}
				else {
					$group_id = $object->GetDBField('UserGroup');

					if ($group_id) {
						// check, that group is allowed for Front-End
						$sql = 'SELECT GroupId
								FROM ' . TABLE_PREFIX . 'PortalGroup
								WHERE GroupId = ' . (int)$group_id . ' AND FrontRegistration = 1';
						$group_id = $this->Conn->GetOne($sql);
					}

					if (!$group_id) {
						// when group not selected -> use default group
						$group_id = $this->Application->ConfigValue('User_NewGroup');
					}
				}

				$sql = 'REPLACE INTO '.$ug_table.'(PortalUserId,GroupId,PrimaryGroup) VALUES (%s,%s,1)';
				$this->Conn->Query( sprintf($sql, $object->GetID(), $group_id) );
			}
		}

		/**
		 * Login user if possible, if not then redirect to corresponding template
		 *
		 * @param kEvent $event
		 */
		function autoLoginUser(&$event)
		{
			$object =& $event->getObject();
			$this->Application->SetVar('u.current_id', $object->GetID() );

			if($object->GetDBField('Status') == STATUS_ACTIVE && !$this->Application->ConfigValue('User_Password_Auto'))
			{
				$email_as_login = $this->Application->ConfigValue('Email_As_Login');
				list($login_field, $submit_field) = $email_as_login ? Array('Email', 'email') : Array('Login', 'login');

				$this->Application->SetVar($submit_field, $object->GetDBField($login_field) );
				$this->Application->SetVar('password', $object->GetDBField('Password_plain') );

				$event->CallSubEvent('OnLogin');
			}
		}


		/**
		 * When creating user & user with such email exists then force to use OnUpdate insted of ?
		 *
		 * @param kEvent $event
		 */
		function OnSubstituteSubscriber(&$event)
		{
			$ret = false;
			$object =& $event->getObject( Array('skip_autoload' => true) );
			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
			if($items_info)
			{
				list($id, $field_values) = each($items_info);
				$user_email = isset($field_values['Email']) ? $field_values['Email'] : false;
				if($user_email)
				{
					// check if is subscriber
					$verify_user =& $this->Application->recallObject('u.verify', null, Array('skip_autoload' => true) );
					$verify_user->Load($user_email, 'Email');
					if( $verify_user->isLoaded() && $verify_user->isSubscriberOnly() )
					{
						$items_info = Array( $verify_user->GetDBField('PortalUserId') => $field_values );
						$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
						$ret = true;
					}
				}
			}

			if( isset($event->MasterEvent) )
			{
				$event->MasterEvent->setEventParam('is_subscriber_only', $ret);
			}
			else
			{
				$event->setEventParam('is_subscriber_only', $ret);
			}
		}


		/**
		 * Enter description here...
		 *
		 * @param kEvent $event
		 * @return bool
		 */
		function isSubscriberOnly(&$event)
		{
			$event->CallSubEvent('OnSubstituteSubscriber');
			$is_subscriber = false;
			if( $event->getEventParam('is_subscriber_only') )
			{
				$is_subscriber = true;
				$object =& $event->getObject( Array('skip_autoload' => true) );
				$this->OnUpdate($event);
				if($event->status == erSUCCESS)
				{
					$this->OnAfterItemCreate($event);
					$object->SendEmailEvents();
					if (!$this->Application->isAdmin && ($event->status == erSUCCESS) && $event->redirect) {
						$this->autoLoginUser($event);
					}
				}
			}
			return $is_subscriber;
		}

		/**
		 * Creates new user
		 *
		 * @param kEvent $event
		 */
		function OnCreate(&$event)
		{
			if (!$this->Application->isAdminUser) {
				$this->setUserStatus($event);
			}

			if (!$this->isSubscriberOnly($event)) {
				$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
				$cs_helper->CheckStateField($event, 'State', 'Country');

				$object =& $event->getObject( Array('skip_autoload' => true) );
				/* @var $object kDBItem */
				if ($this->Application->ConfigValue('User_Password_Auto')) {
					$pass = makepassword4(rand(5,8));
					$object->SetField('Password', $pass);
					$object->SetField('VerifyPassword', $pass);
					$this->Application->SetVar('user_password',$pass);
				}
				parent::OnCreate($event);

				$this->Application->SetVar('u.current_id', $object->getID() ); // for affil:OnRegisterAffiliate after hook

				$this->setNextTemplate($event);

				if (!$this->Application->isAdmin && ($event->status == erSUCCESS) && $event->redirect) {
					$object->SendEmailEvents();
					$this->autoLoginUser($event);
				}
			}
		}

		/**
		 * Set's new user status based on config options
		 *
		 * @param kEvent $event
		 */
		function setUserStatus(&$event)
		{
			$object =& $event->getObject( Array('skip_autoload' => true) );

			$new_users_allowed = $this->Application->ConfigValue('User_Allow_New');

			switch ($new_users_allowed) {
				case 1: // Immediate
					$object->SetDBField('Status', STATUS_ACTIVE);
					$next_template = $this->Application->GetVar('registration_confirm_template');
					if ($next_template) {
						$event->redirect = $next_template;
					}
					break;

				case 3: // Upon Approval
				case 4: // Email Activation
					$next_template = $this->Application->GetVar('registration_confirm_pending_template');
					if ($next_template) {
						$event->redirect = $next_template;
					}
					$object->SetDBField('Status', STATUS_PENDING);
					break;

				case 2: // Not Allowed
					$object->SetDBField('Status', STATUS_DISABLED);
					break;
			}
		}

		/**
		 * Set's new unique resource id to user
		 *
		 * @param kEvent $event
		 */
		function OnBeforeItemCreate(&$event)
		{
			$this->_makePasswordRequired($event);

			$email_as_login = $this->Application->ConfigValue('Email_As_Login');
			$object =& $event->getObject();
			if (!$this->checkBanRules($object)) {
				$event->status = erFAIL;
				return false;
			}
			if ($email_as_login) {
				$object->Fields['Email']['error_msgs']['unique'] = $this->Application->Phrase('lu_user_and_email_already_exist');
			}

		}

		/**
		 * Set's new unique resource id to user
		 *
		 * @param kEvent $event
		 */
		function OnAfterItemValidate(&$event)
		{
			$object =& $event->getObject();
			$resource_id = $object->GetDBField('ResourceId');
			if (!$resource_id)
			{
				$object->SetDBField('ResourceId', $this->Application->NextResourceId() );
			}
		}


		/**
		 * Enter description here...
		 *
		 * @param kEvent $event
		 */
		function OnRecommend(&$event)
		{
			$friend_email = $this->Application->GetVar('friend_email');
			$friend_name = $this->Application->GetVar('friend_email');

			// used for error reporting only -> rewrite code + theme (by Alex)
			$object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
			/* @var $object UsersItem */

			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $friend_email))
		    {
		    	/*$cutoff = adodb_mktime() + (int)$this->Application->ConfigValue('Suggest_MinInterval');
				$sql = 'SELECT *
						FROM ' . TABLE_PREFIX . 'SuggestMail
						WHERE email = ' . $this->Conn->qstr($friend_email) . ' AND sent < ' . $cutoff;
             	if ($this->Conn->GetRow($sql) !== false) {
             		$object->SetError('Email', 'send_error', 'lu_email_already_suggested');
					$event->status = erFAIL;
					return ;
             	}*/

		    	$send_params = Array ();
				$send_params['to_email'] = $friend_email;
				$send_params['to_name'] = $friend_name;

				$user_id = $this->Application->RecallVar('user_id');
				$email_event =& $this->Application->EmailEventUser('USER.SUGGEST', $user_id, $send_params);
				$email_event =& $this->Application->EmailEventAdmin('USER.SUGGEST');

				if ($email_event->status == erSUCCESS){
					/*$fields_hash = Array (
						'email' => $friend_email,
						'sent' => adodb_mktime(),
					);

					$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SuggestMail');*/

					$event->redirect_params = array('opener' => 's', 'pass' => 'all');
					$event->redirect = $this->Application->GetVar('template_success');
				}
				else {
//					$event->redirect_params = array('opener' => 's', 'pass' => 'all');
//					$event->redirect = $this->Application->GetVar('template_fail');

					$object->SetError('Email', 'send_error', 'lu_email_send_error');
					$event->status = erFAIL;
				}
		    }
		    else {
		    	$object->SetError('Email', 'invalid_email', 'lu_InvalidEmail');
				$event->status = erFAIL;
		    }
		}

		/**
		 * Saves address changes and mades no redirect
		 *
		 * @param kEvent $event
		 */
		function OnUpdateAddress(&$event)
		{
			$object =& $event->getObject( Array('skip_autoload' => true) );

			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
			if($items_info)
			{
				list($id,$field_values) = each($items_info);
				if($id > 0) $object->Load($id);
				$object->SetFieldsFromHash($field_values);
				$object->setID($id);
				$object->Validate();
			}

			$event->redirect = false;
		}

		/**
		 * Validate subscriber's email & store it to session -> redirect to confirmation template
		 *
		 * @param kEvent $event
		 */
		function OnSubscribeQuery(&$event)
		{
			$user_email = $this->Application->GetVar('subscriber_email');
			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $user_email)) {

				$object =& $this->Application->recallObject($this->Prefix.'.subscriber', null, Array('skip_autoload' => true));
				/* @var $object UsersItem */

				$this->Application->StoreVar('SubscriberEmail', $user_email);

				$object->Load($user_email, 'Email');
				if ($object->isLoaded()) {
					$group_info = $this->GetGroupInfo($object->GetID());
					$event->redirect = $this->Application->GetVar($group_info ? 'unsubscribe_template' : 'subscribe_template');
				}
				else {
					$event->redirect = $this->Application->GetVar('subscribe_template');
					$this->Application->StoreVar('SubscriberEmail', $user_email);
				}
			}
			else {
				// used for error reporting only -> rewrite code + theme (by Alex)
				$object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
				/* @var $object UsersItem */

				$object->SetError('SubscribeEmail', 'invalid_email', 'lu_InvalidEmail');
				$event->status = erFAIL;
			}
		}

		/**
		 * Subscribe/Unsubscribe user based on email stored in previous step
		 *
		 * @param kEvent $event
		 */
		function OnSubscribeUser(&$event)
		{
			$object = &$this->Application->recallObject($this->Prefix.'.subscriber', null, Array('skip_autoload' => true));
			/* @var $object UsersItem */

			$user_email = $this->Application->RecallVar('SubscriberEmail');
			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $user_email)) {
				$this->RemoveRequiredFields($object);
				$object->Load($user_email, 'Email');

				if ($object->isLoaded()) {
					$group_info = $this->GetGroupInfo($object->GetID());

					if ($group_info){
						if ($event->getEventParam('no_unsubscribe')) return;

						if ($group_info['PrimaryGroup']){
							// delete user
							$object->Delete();
						}
						else {
							$this->RemoveSubscriberGroup($object->GetID());
						}

						$event->redirect = $this->Application->GetVar('unsubscribe_ok_template');
					}
					else {
						$this->AddSubscriberGroup($object->GetID(), 0);
						$event->redirect = $this->Application->GetVar('subscribe_ok_template');
					}
				}
				else {
					$object->SetField('Email', $user_email);
					$object->SetField('Login', $user_email);
					$object->SetDBField('dob', 1);
					$object->SetDBField('dob_date', 1);
					$object->SetDBField('dob_time', 1);
					$object->SetDBField('Status', STATUS_ACTIVE); // make user subscriber Active by default
					$ip = getenv('HTTP_X_FORWARDED_FOR')?getenv('HTTP_X_FORWARDED_FOR'):getenv('REMOTE_ADDR');
					$object->SetDBField('ip', $ip);

					$this->Application->SetVar('IsSubscriber', 1);

					if ($object->Create()) {
						$this->AddSubscriberGroup($object->GetID(), 1);
						$event->redirect = $this->Application->GetVar('subscribe_ok_template');
					}

					$this->Application->SetVar('IsSubscriber', 0);
				}
			}
		}

		function AddSubscriberGroup($user_id, $is_primary)
		{
			$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
			$sql = 'INSERT INTO ' . TABLE_PREFIX . 'UserGroup
						(PortalUserId, GroupId, PrimaryGroup) VALUES (%s, %s, ' . $is_primary . ')';
			$this->Conn->Query( sprintf($sql, $user_id, $group_id) );

			$this->Application->EmailEventAdmin('USER.SUBSCRIBE');
			$this->Application->EmailEventUser('USER.SUBSCRIBE', $user_id);
		}

		function RemoveSubscriberGroup($user_id)
		{
			$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
			$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserGroup
						WHERE PortalUserId = ' . $user_id . '
						AND GroupId = ' . $this->Application->ConfigValue('User_SubscriberGroup');
			$this->Conn->Query($sql);

			$this->Application->EmailEventAdmin('USER.UNSUBSCRIBE');
			$this->Application->EmailEventUser('USER.UNSUBSCRIBE', $user_id);
		}

		/**
		 * Allows to detect user subscription status (subscribed or not)
		 *
		 * @param int $user_id
		 * @return bool
		 */
		function GetGroupInfo($user_id)
		{
			$sql = 'SELECT * FROM ' . TABLE_PREFIX . 'UserGroup
						WHERE (PortalUserId = ' . $user_id . ')
						AND (GroupId = ' . $this->Application->ConfigValue('User_SubscriberGroup') . ')';
			return $this->Conn->GetRow($sql);
		}

		function OnForgotPassword(&$event)
		{
			$user_object =& $this->Application->recallObject('u.forgot', null, Array('skip_autoload' => true));
			/* @var $user_object UsersItem */

			// used for error reporting only -> rewrite code + theme (by Alex)
			$user_current_object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
			/* @var $user_current_object UsersItem */

			$username = $this->Application->GetVar('username');
			$email = $this->Application->GetVar('email');
			$found = false;
			$allow_reset = true;

			if (strlen($username)) {
				$user_object->Load($username, 'Login');
				if ($user_object->isLoaded()) {
					$found = ($user_object->GetDBField("Login")==$username && $user_object->GetDBField("Status")==1) && strlen($user_object->GetDBField("Password"));
				}
			}
			else if(strlen($email)) {
				$user_object->Load($email, 'Email');
				if ($user_object->isLoaded()) {
					$found = ($user_object->GetDBField("Email")==$email && $user_object->GetDBField("Status")==1) && strlen($user_object->GetDBField("Password"));
				}
			}

			if ($user_object->isLoaded()) {
				$PwResetConfirm 	= $user_object->GetDBField('PwResetConfirm');
				$PwRequestTime 		= $user_object->GetDBField('PwRequestTime');
				$PassResetTime 		= $user_object->GetDBField('PassResetTime');
				//$MinPwResetDelay 	= $user_object->GetDBField('MinPwResetDelay');
				$MinPwResetDelay 	= $this->Application->ConfigValue('Users_AllowReset');

				$allow_reset = (strlen($PwResetConfirm) ?
				adodb_mktime() > $PwRequestTime + $MinPwResetDelay :
				adodb_mktime() > $PassResetTime + $MinPwResetDelay);
			}

			if ($found && $allow_reset) {
				$this->Application->StoreVar('tmp_user_id', $user_object->GetDBField("PortalUserId"));
				$this->Application->StoreVar('tmp_email', $user_object->GetDBField("Email"));

				$confirm_template = $this->Application->GetVar('reset_confirm_template');
				if (!$confirm_template) {
					$this->Application->SetVar('reset_confirm_template', 'platform/login/forgotpass_reset');
				}
				$this->Application->EmailEventUser('USER.PSWDC', $user_object->GetDBField('PortalUserId'));

				$event->redirect = $this->Application->GetVar('template_success');
			}
			else {
				if (!strlen($username) && !strlen($email)) {
					$user_current_object->SetError('Login', 'forgotpw_nodata', 'lu_ferror_forgotpw_nodata');
					$user_current_object->SetError('Email', 'forgotpw_nodata', 'lu_ferror_forgotpw_nodata');
				}
				else {
					if ($allow_reset) {
						if (strlen($username)) {
							$user_current_object->SetError('Login', 'unknown_username', 'lu_ferror_unknown_username');
						}
						if (strlen($email)) {
							$user_current_object->SetError('Email', 'unknown_email', 'lu_ferror_unknown_email');
						}
					}
					else {
						if (strlen($username)) {
							$user_current_object->SetError('Login', 'reset_denied', 'lu_ferror_reset_denied');
						}

						if (strlen($email)) {
							$user_current_object->SetError('Email', 'reset_denied', 'lu_ferror_reset_denied');
						}
					}
				}

				if($user_current_object->FieldErrors){
					$event->redirect = false;
				}
			}
		}

		/**
		 * Enter description here...
		 *
		 * @param kEvent $event
		 */
		function OnResetPassword(&$event)
		{
			$user_object =& $this->Application->recallObject('u.forgot');

			if($user_object->Load($this->Application->RecallVar('tmp_user_id'))){

				$this->Application->EmailEventUser('USER.PSWDC', $user_object->GetDBField("PortalUserId"));
				$event->redirect = $this->Application->GetVar('template_success');

				$m_cat_id = $this->Application->findModule('Name', 'In-Commerce', 'RootCat');
				$this->Application->SetVar('m_cat_id', $m_cat_id);
				$event->SetRedirectParam('pass', 'm');
			}
		}

		function OnResetPasswordConfirmed(&$event)
		{
	     	// used for error reporting only -> rewrite code + theme (by Alex)
	     	$user_current_object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true));// TODO: change theme too
			/* @var $user_current_object UsersItem */

			$passed_key = trim($this->Application->GetVar('user_key'));

		    if (!$passed_key) {
				$event->redirect_params = Array('opener' => 's', 'pass' => 'all');
				$event->redirect = false;

				$user_current_object->SetError('PwResetConfirm', 'code_is_not_valid', 'lu_code_is_not_valid');
		    }

		    $user_object =& $this->Application->recallObject('u.forgot', null, Array('skip_autoload' => true));
			/* @var $user_object UsersItem */

			$user_object->Load($passed_key, 'PwResetConfirm');

		    if ($user_object->isLoaded()) {
		    	$exp_time = $user_object->GetDBField('PwRequestTime') + 3600;
		    	$user_object->SetDBField('PwResetConfirm', '');
		      	$user_object->SetDBField('PwRequestTime', 0);

		      	if ($exp_time > adodb_mktime()) {
			    	$newpw = makepassword4();

			    	$this->Application->StoreVar('password', $newpw);

			      	$user_object->SetField('Password', $newpw);
			      	$user_object->SetField('VerifyPassword', $newpw);

			      	$user_object->SetDBField('PassResetTime', adodb_mktime());
			      	$user_object->SetDBField('PwResetConfirm', '');
			      	$user_object->SetDBField('PwRequestTime', 0);
			      	$user_object->Update();

			      	$this->Application->SetVar('ForgottenPassword', $newpw);

			     	$email_event_user 	=& $this->Application->EmailEventUser('USER.PSWD', $user_object->GetDBField('PortalUserId'));
			     	$email_event_admin 	=& $this->Application->EmailEventAdmin('USER.PSWD');

			     	$this->Application->DeleteVar('ForgottenPassword');

			      	if ($email_event_user->status == erSUCCESS) {
						$event->redirect_params = array('opener' => 's', 'pass' => 'all');
						$event->redirect = $this->Application->GetVar('template_success');
					}
		      	} else {
		      		$user_current_object->SetError('PwResetConfirm', 'code_expired', 'lu_code_expired');
		      		$event->redirect = false;
		      	}
		    } else {
		    	$user_current_object->SetError('PwResetConfirm', 'code_is_not_valid', 'lu_code_is_not_valid');
		    	$event->redirect = false;
		    }
		}

		function OnUpdate(&$event)
		{
			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
			$cs_helper->CheckStateField($event, 'State', 'Country');

			parent::OnUpdate($event);

			$this->setNextTemplate($event);
		}

		/**
		 * Enter description here...
		 *
		 * @param kEvent $event
		 */
		function setNextTemplate(&$event)
		{
			if ($this->Application->isAdmin) {
				return ;
			}

			$event->redirect_params['opener'] = 's';
			$object =& $event->getObject();

			if ($object->GetDBField('Status') == STATUS_ACTIVE) {
				$next_template = $this->Application->GetVar('next_template');

				if ($next_template) {
					$event->redirect = $next_template;
				}
			}
		}

		/**
		 * Delete users from groups if their membership is expired
		 *
		 * @param kEvent $event
		 */
		function OnCheckExpiredMembership(&$event)
		{
			// send pre-expiration reminders: begin
			$pre_expiration = adodb_mktime() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24;
			$sql = 'SELECT PortalUserId, GroupId
					FROM '.TABLE_PREFIX.'UserGroup
					WHERE (MembershipExpires IS NOT NULL) AND (ExpirationReminderSent = 0) AND (MembershipExpires < '.$pre_expiration.')';

			$skip_clause = $event->getEventParam('skip_clause');
			if ($skip_clause) {
				$sql .= ' AND !('.implode(') AND !(', $skip_clause).')';
			}

			$records = $this->Conn->Query($sql);
			if ($records) {
				$conditions = Array();
				foreach ($records as $record) {
					$email_event_user =& $this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRATION.NOTICE', $record['PortalUserId']);
					$email_event_admin =& $this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE');
					$conditions[] = '(PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'].')';
				}
				$sql = 'UPDATE '.TABLE_PREFIX.'UserGroup
						SET ExpirationReminderSent = 1
						WHERE '.implode(' OR ', $conditions);
				$this->Conn->Query($sql);
			}
			// send pre-expiration reminders: end

			// remove users from groups with expired membership: begin
			$sql = 'SELECT PortalUserId
					FROM '.TABLE_PREFIX.'UserGroup
					WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
			$user_ids = $this->Conn->GetCol($sql);
			if ($user_ids) {
				foreach ($user_ids as $id) {
					$email_event_user =& $this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRED', $id);
					$email_event_admin =& $this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRED');
				}
			}
			$sql = 'DELETE FROM '.TABLE_PREFIX.'UserGroup
					WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
			$this->Conn->Query($sql);
			// remove users from groups with expired membership: end
		}

		/**
		 * Enter description here...
		 *
		 * @param kEvent $event
		 */
		function OnRefreshForm(&$event)
		{
			$event->redirect = false;
			$item_info = $this->Application->GetVar($event->Prefix_Special);
			list($id, $fields) = each($item_info);

			$object =& $event->getObject( Array('skip_autoload' => true) );
			$object->setID($id);
			$object->IgnoreValidation = true;
			$object->SetFieldsFromHash($fields);
		}

		/**
		 * Sets persistant variable
		 *
		 * @param kEvent $event
		 */
		function OnSetPersistantVariable(&$event)
		{
			$field =  $this->Application->GetVar('field');
			$value = $this->Application->GetVar('value');
			$this->Application->StorePersistentVar($field, $value);

			$force_tab = $this->Application->GetVar('SetTab');
			if ($force_tab) {
				$this->Application->StoreVar('force_tab', $force_tab);
			}
		}

		/**
		 * Overwritten to return user from order by special .ord
		 *
		 * @param kEvent $event
		 */
		function getPassedID(&$event)
		{
			switch ($event->Special) {
				case 'ord':
					$order =& $this->Application->recallObject('ord');
					/* @var $order OrdersItem */

					$id = $order->GetDBField('PortalUserId');
					break;

				case 'profile':
					$id = $this->Application->GetVar('user_id');
					if (!$id) {
						// if none user_id given use current user id
						$id = $this->Application->RecallVar('user_id');
					}
					break;

				default:
					$id = parent::getPassedID($event);
					break;
			}

			return $id;
		}

		/**
		 * Allows to change root password
		 *
		 * @param kEvent $event
		 */
		function OnUpdateRootPassword(&$event)
		{
			return $this->OnUpdatePassword($event);
		}

		/**
		 * Allows to change root password
		 *
		 * @param kEvent $event
		 */
		function OnUpdatePassword(&$event)
		{
			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
			if (!$items_info) return ;
			list ($id, $field_values) = each($items_info);
			$user_id = $this->Application->RecallVar('user_id');
			if ($id == $user_id && ($user_id > 0 || $user_id == -1)) {
				$user_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true));
				/* @var $user_dummy kDBItem */

				$user_dummy->Load($id);
				$status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField'));

				if ($user_dummy->GetDBField($status_field) != STATUS_ACTIVE) {
					// not active user is not allowed to update his record (he could not activate himself manually)
					return false;
				}
			}

			if ($user_id == -1) {
				$object =& $event->getObject( Array('skip_autoload' => true) );
				/* @var $object UsersItem */

				// put salt to user's config
				$field_options = $object->GetFieldOptions('RootPassword');
				$field_options['salt'] = 'b38';
				$object->SetFieldOptions('RootPassword', $field_options);
				$verify_options = $object->GetFieldOptions('VerifyRootPassword');
				$verify_options['salt'] = 'b38';
				$object->SetFieldOptions('VerifyRootPassword', $verify_options);

				// this is internal hack to allow root/root passwords for dev
				if ($this->Application->isDebugMode() && $field_values['RootPassword'] == 'root') {
					$this->Application->ConfigHash['Min_Password'] = 4;
				}

				$this->RemoveRequiredFields($object);
				$object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass'));
	 			$object->SetFieldsFromHash($field_values);
	 			$object->setID(-1);
	 			$status = $object->Validate();
				if ($status) {
					// validation on, password match too
					$fields_hash = 	Array (
						'VariableValue' => $object->GetDBField('RootPassword')
					);
					$conf_table = $this->Application->getUnitOption('conf', 'TableName');
					$this->Conn->doUpdate($fields_hash, $conf_table, 'VariableName = "RootPass"');
					$event->SetRedirectParam('opener', 'u');
				}
				else {
					$event->status = erFAIL;
					$event->redirect = false;
					return;
				}
			}
			else {
				$object =& $event->getObject();
				$object->SetFieldsFromHash($field_values);

				if (!$object->Update()) {
					$event->status = erFAIL;
					$event->redirect = false;
				}
			}

			$event->SetRedirectParam('opener', 'u');
			$event->redirect == true;
		}

		/**
		 * Apply custom processing to item
		 *
		 * @param kEvent $event
		 */
		function customProcessing(&$event, $type)
		{
			if ($event->Name == 'OnCreate' && $type == 'before') {
				$object =& $event->getObject();
				/* @var $object kDBItem */

				// if auto password has not been set already - store real one - to be used in email events
				if (!$this->Application->GetVar('user_password')) {
					$this->Application->SetVar('user_password', $object->GetDirtyField('Password'));
					$object->SetDBField('Password_plain', $object->GetDirtyField('Password'));
				}

				// validate here, because subscribing procedure should not validate captcha code
				if ($this->Application->ConfigValue('RegistrationCaptcha')) {
					$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
					/* @var $captcha_helper kCaptchaHelper */

					$captcha_helper->validateCode($event, false);
				}
			}
		}

		function OnMassResetSettings(&$event)
		{
			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
				$event->status = erFAIL;
				return;
			}

			$ids = $this->StoreSelectedIDs($event);

			$default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId');
			if (in_array($default_user_id, $ids)) {
				array_splice($ids, array_search($default_user_id, $ids), 1);
			}
			if ($ids) {
				$q = 'DELETE FROM '.TABLE_PREFIX.'PersistantSessionData WHERE PortalUserId IN ('.join(',', $ids).') AND
							 (VariableName LIKE "%_columns_%"
							 OR
							 VariableName LIKE "%_filter%"
							 OR
							 VariableName LIKE "%_PerPage%")';
				$this->Conn->Query($q);
			}
			$this->clearSelectedIDs($event);
		}

		/**
		 * Checks, that currently loaded item is allowed for viewing (non permission-based)
		 *
		 * @param kEvent $event
		 * @return bool
		 */
		function checkItemStatus(&$event)
		{
			$object =& $event->getObject();
			if (!$object->isLoaded()) {
				return true;
			}

			$virtual_users = Array (-1, -2); // root, Guest
			return ($object->GetDBField('Status') == STATUS_ACTIVE) || in_array($object->GetID(), $virtual_users);
		}

		/**
		 * Sends approved/declined email event on user status change
		 *
		 * @param kEvent $event
		 */
		function OnAfterItemUpdate(&$event)
		{
			$this->saveUserImages($event);

			$object =& $event->getObject();
			/* @var $object UsersItem */

			if (!$this->Application->isAdmin || $object->IsTempTable()) {
				return ;
			}

			$this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status'));
		}

		/**
		 * Stores user's original Status before overwriting with data from temp table
		 *
		 * @param kEvent $event
		 */
		function OnBeforeDeleteFromLive(&$event)
		{
			$user_status = $this->Application->GetVar('user_status');
			if (!$user_status) {
				$user_status = Array ();
			}

			$user_id = $event->getEventParam('id');
			if ($user_id > 0) {
				$user_status[$user_id] = $this->getUserStatus($user_id);
				$this->Application->SetVar('user_status', $user_status);
			}
		}

		/**
		 * Sends approved/declined email event on user status change (in temp tables during editing)
		 *
		 * @param kEvent $event
		 */
		function OnAfterCopyToLive(&$event)
		{
			$temp_id = $event->getEventParam('temp_id');
			if ($temp_id == 0) {
				// this is new user create, don't send email events
				return ;
			}

			$new_status = $this->getUserStatus($temp_id);
			$user_status = $this->Application->GetVar('user_status');

			$this->sendStatusChangeEvent($temp_id, $user_status[$temp_id], $new_status);
		}

		/**
		 * Returns user status (active, pending, disabled) based on ID and temp mode setting
		 *
		 * @param int $user_id
		 * @return int
		 */
		function getUserStatus($user_id)
		{
			$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
			$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');

			$sql = 'SELECT Status
					FROM '.$table_name.'
					WHERE '.$id_field.' = '.$user_id;
			return $this->Conn->GetOne($sql);
		}

		/**
		 * Sends approved/declined email event on user status change
		 *
		 * @param int $user_id
		 * @param int $prev_status
		 * @param int $new_status
		 */
		function sendStatusChangeEvent($user_id, $prev_status, $new_status)
		{
			$status_events = Array (
				STATUS_ACTIVE	=>	'USER.APPROVE',
				STATUS_DISABLED	=>	'USER.DENY',
			);
			$email_event = isset($status_events[$new_status]) ? $status_events[$new_status] : false;

			if (($prev_status != $new_status) && $email_event) {
				$this->Application->EmailEventUser($email_event, $user_id);
				$this->Application->EmailEventAdmin($email_event);
			}
		}

		/**
		 * OnAfterConfigRead for users
		 *
		 * @param kEvent $event
		 */
		function OnAfterConfigRead(&$event)
		{
			parent::OnAfterConfigRead($event);

			// 1. arrange user registration countries
			$first_country = $this->Application->ConfigValue('User_Default_Registration_Country');
			if ($first_country) {
				// update user country dropdown sql
				$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
				$fields['Country']['options_sql'] = preg_replace('/ORDER BY (.*)/', 'ORDER BY IF (DestId = '.$first_country.', 1, 0) DESC, \\1', $fields['Country']['options_sql']);
				$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
			}

			// 2. set default user registration group
			$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
			$virtual_fields['UserGroup']['default'] = $this->Application->ConfigValue('User_NewGroup');
			$this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields);

			// 3. allow avatar upload on Front-End
			$file_helper =& $this->Application->recallObject('FileHelper');
			/* @var $file_helper FileHelper */

			$file_helper->createItemFiles($event->Prefix, true); // create image fields

			if ($this->Application->isAdminUser) {
				// 4. when in administrative console, then create all users with Active status
				$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
//				$fields['Password']['required'] = 1; // set password required (will broke approve/decline buttons)
				$fields['Status']['default'] = STATUS_ACTIVE;
				$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);

				// 5. remove groups tab on editing forms when AdvancedUserManagement config variable not set
				if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
					$edit_tab_presets = $this->Application->getUnitOption($event->Prefix, 'EditTabPresets');

					foreach ($edit_tab_presets as $preset_name => $preset_tabs) {
						if (array_key_exists('groups', $preset_tabs)) {
							unset($edit_tab_presets[$preset_name]['groups']);

							if (count($edit_tab_presets[$preset_name]) == 1) {
								// only 1 tab left -> remove it too
								$edit_tab_presets[$preset_name] = Array ();
							}
						}
					}

					$this->Application->setUnitOption($event->Prefix, 'EditTabPresets', $edit_tab_presets);
				}
			}

		}

		/**
		 * OnMassCloneUsers
		 *
		 * @param kEvent $event
		 */
		function OnMassCloneUsers(&$event)
		{
			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
				$event->status = erFAIL;
				return;
			}

			$event->status=erSUCCESS;
			$ids = $this->StoreSelectedIDs($event);

			$this->Application->SetVar('skip_set_primary', 1); // otherwise it will default primary group, search for skip_set_primary above
			$temp_handler =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
			/* @var $temp_handler kTempTablesHandler */
			$cloned_users = $temp_handler->CloneItems($event->Prefix, '', $ids);
			$this->clearSelectedIDs($event);
		}

		/**
		 * When cloning users, reset password (set random)
		 *
		 * @param kEvent $event
		 */
		function OnBeforeClone(&$event)
		{
			$object =& $event->getObject();
			/* @var $object kDBItem */
			$object->setRequired('Password', 0);
			$object->setRequired('VerifyPassword', 0);
			$object->SetDBField('Password', rand(100000000, 999999999));
			$object->SetDBField('CreatedOn', adodb_mktime());
			$object->SetDBField('ResourceId', false); // this will reset it

			// change email cause it should be unique
			$object->NameCopy(array(), $object->GetID(), 'Email', 'copy%1$s.%2$s');

			$object->UpdateFormattersSubFields();
		}

		/**
		 * Copy user groups after copying user
		 *
		 * @param kEvent $event
		 */
		function OnAfterClone(&$event)
		{
			$id = $event->getEventParam('id');
			$original_id = $event->getEventParam('original_id');

			$sql = 'INSERT '.TABLE_PREFIX."UserGroup SELECT $id, GroupId, MembershipExpires, PrimaryGroup, 0 FROM ".TABLE_PREFIX."UserGroup WHERE PortalUserId = $original_id";
			$this->Conn->Query($sql);
		}

		/**
		 * Saves selected ids to session
		 *
		 * @param kEvent $event
		 */
		function OnSaveSelected(&$event)
		{
			$this->StoreSelectedIDs($event);

			// remove current ID, otherwise group selector will use it in filters
			$this->Application->DeleteVar($event->getPrefixSpecial(true).'_id');
		}

		/**
		 * Adds selected link to listing
		 *
		 * @param kEvent $event
		 */
		function OnProcessSelected(&$event)
		{
			$event->SetRedirectParam('opener', 'u');
			$user_ids = $this->getSelectedIDs($event, true);
			$this->clearSelectedIDs($event);

			$dst_field = $this->Application->RecallVar('dst_field');
			if ($dst_field != 'PrimaryGroupId') {
				return ;
			}

			$group_ids = $this->Application->GetVar('g');
			$primary_group_id = $group_ids ? array_shift( array_keys($group_ids) ) : false;

			if (!$user_ids || !$primary_group_id) {
				return ;
			}

			$table_name = $this->Application->getUnitOption('ug', 'TableName');

			$sql = 'SELECT PortalUserId
					FROM '.$table_name.'
					WHERE (GroupId = '.$primary_group_id.') AND (PortalUserId IN ('.implode(',', $user_ids).'))';
			$existing_members = $this->Conn->GetCol($sql);

			// 1. reset primary group mark
			$sql = 'UPDATE '.$table_name.'
					SET PrimaryGroup = 0
					WHERE PortalUserId IN ('.implode(',', $user_ids).')';
			$this->Conn->Query($sql);

			foreach ($user_ids as $user_id) {
				if (in_array($user_id, $existing_members)) {
					// 2. already member of that group -> just make primary
					$sql = 'UPDATE '.$table_name.'
							SET PrimaryGroup = 1
							WHERE (PortalUserId = '.$user_id.') AND (GroupId = '.$primary_group_id.')';
					$this->Conn->Query($sql);
				}
				else {
					// 3. not member of that group -> make member & make primary
					$fields_hash = Array (
						'GroupId'		=>	$primary_group_id,
						'PortalUserId'	=>	$user_id,
						'PrimaryGroup'	=>	1,
					);
					$this->Conn->doInsert($fields_hash, $table_name);
				}
			}
		}

		/**
		 * Loads user images
		 *
		 * @param kEvent $event
		 */
		function OnAfterItemLoad(&$event)
		{
			parent::OnAfterItemLoad($event);

			// linking existing images for item with virtual fields
			$image_helper =& $this->Application->recallObject('ImageHelper');
			/* @var $image_helper ImageHelper */

			$object =& $event->getObject();
			/* @var $object kDBItem */

			$image_helper->LoadItemImages($object);
		}

		/**
		 * Save user images
		 *
		 * @param kEvent $event
		 */
		function saveUserImages(&$event)
		{
			if (!$this->Application->isAdmin) {
				$image_helper =& $this->Application->recallObject('ImageHelper');
				/* @var $image_helper ImageHelper */

				$object =& $event->getObject();
				/* @var $object kDBItem */

				// process image upload in virtual fields
				$image_helper->SaveItemImages($object);
			}
		}

		/**
		 * Checks, if given user fields matches at least one of defined ban rules
		 *
		 * @param kDBItem $object
		 * @return bool
		 */
		function checkBanRules(&$object)
		{
			$table = $this->Application->getUnitOption('ban-rule', 'TableName');
			if (!$this->Conn->TableFound($table)) {
				// when ban table not found -> assume user is ok by default
				return true;
			}

			$sql = 'SELECT *
					FROM '.$table.'
					WHERE ItemType = 6 AND Status = ' . STATUS_ACTIVE . '
					ORDER BY Priority DESC';
			$rules = $this->Conn->Query($sql);

			$found = false;
			foreach ($rules as $rule) {
				$field = $rule['ItemField'];

				$this_value = strtolower( $object->GetDBField($field) );
				$test_value = strtolower( $rule['ItemValue'] );

				switch ($rule['ItemVerb']) {
					/*case 0: // any
						$found = true;
						break;*/

					case 1: // is
						if ($this_value == $test_value) {
							$found = true;
						}
						break;

					/*case 2: // is not
						if ($this_value != $test_value) {
							$found = true;
						}
						break;*/

					case 3: // contains
						if (strstr($this_value, $test_value)) {
							$found = true;
						}
						break;


					/*case 4: // not contains
						if (!strstr($this_value, $test_value)) {
							$found = true;
						}
						break;

					case 5: // Greater Than
						if ($test_value > $this_value) {
							$found = true;
						}
						break;

					case 6: // Less Than
						if ($test_value < $this_value) {
							$found = true;
						}
						break;

					case 7: // exists
						if (strlen($this_value) > 0) {
							$found = true;
						}
						break;

					case 8: // unique
						if ($this->ValueExists($field, $this_value)) {
							$found = true;
						}
						break;*/
				}

				if ($found) {
					break;
				}
			}

			return !$found;
		}

		/**
		 * Makes password required for new users
		 *
		 * @param kEvent $event
		 */
		function OnPreCreate(&$event)
		{
			parent::OnPreCreate($event);

			if ($event->status == erSUCCESS) {
				$this->_makePasswordRequired($event);
			}
		}

		/**
		 * Makes password required for new users
		 *
		 * @param kEvent $event
		 */
		function OnNew(&$event)
		{
			parent::OnNew($event);

			if ($event->status == erSUCCESS) {
				$this->_makePasswordRequired($event);
			}
		}

		/**
		 * Makes password required for new users
		 *
		 * @param kEvent $event
		 */
		function _makePasswordRequired(&$event)
		{
			$object =& $event->getObject();
			/* @var $object kDBItem */

			$required_fields = Array ('Password', 'Password_plain', 'VerifyPassword', 'VerifyPassword_plain');
			foreach ($required_fields as $required_field) {
				$object->setRequired($required_field);
			}
		}
	}
