<?php
/*---------------------------------------------\
|											   |
| Author:       Andrey Brykin (Drunya)         |
| Version:      1.3.7.3                         |
| Project:      CMS                            |
| package       CMS Fapos                      |
| subpackege    Users Module                   |
| copyright     ©Andrey Brykin 2010-2011       |
|----------------------------------------------|
|											   |
| any partial or not partial extension         |
| CMS Fapos,without the consent of the         |
| author, is illegal                           |
|----------------------------------------------|
| Любое распространение                        |
| CMS Fapos или ее частей,                     |
| без согласия автора, является не законным    |
\---------------------------------------------*/





Class UsersModule extends Module {
	
	
	/**
	 * @template  layout for module
	 * @var string
	 */
	public $template = 'users';
	
	/**
	 * @module_title  title of module
	 * @var string
	 */
	public $module_title = 'Пользователи';
	
	/**
	 * @module module indentifier
	 * @var string
	 */
	public $module = 'users';
	



	// Функция возвращает html списка пользователей форума
	public function index() {
		//turn access
		$this->ACL->turn(array('users', 'view_list'));
		
		// Выбираем из БД количество пользователей - это нужно для
		// построения постраничной навигации
		$total = $this->DB->select('users', DB_COUNT);

		// Проверяем передан ли номер текущей страницы (постраничная навигация)
		if ( isset($_GET['page']) ) {
			$page = (int)$_GET['page'];
			if ( $page < 1 ) $page = 1;
		} else {
			$page = 1;
		}
		$cntPages = ceil( $total / Config::read('users_per_page', 'users'));
		if ( $page > $cntPages ) $page = $cntPages;
		if ( $cntPages > 1 ) {
			$pages = pagination($page, $cntPages, '/users/');
		} else {
			$pages = '';
		}
		
		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Users list');
		$nav['{PAGINATION}'] = $pages;
		$nav['{META}'] = __('All users') . $total;
		$this->_globalize($nav);
		
		if (count($total) == 0) return $this->_view(__('No users'));
		
		
		//order by
		$order = 'puttime';
		if (!empty($_GET['sort'])) {
			$permision = array('puttime', 'status', 'last_visit', 'name');
			if (in_array($_GET['sort'], $permision)) $order = $_GET['sort'];
		} 
		
		$res = $this->DB->select('users', DB_ALL, array(
			'order' => mysql_real_escape_string($order) . ' ASC',
			'page' => $page,
			'limit' => Config::read('users_per_page', 'users')
		));
		if (is_object($this->AddFields) && count($res) > 0) {
			$res = $this->AddFields->mergeRecords($res);
		}

		// Выводим "шапку" таблицы
		$table_users = $this->Parser->getAndParse('userstable.html');
		$t_markets = array();
	

		$info = '';
		foreach ($res as $user) {
			$markets = array();
			$html_user = $this->Parser->getAndParse('vievuser.html');
			

			$user_name = get_link(h($user['name']), '/users/info/' . $user['id']);
			
			$adm_panel = '';
			if ($this->ACL->turn(array('users', 'edit_users'), false)) {
				$adm_panel = get_link(get_img('/sys/img/edit_16x16.png', 
				array('alt' => __('Edit'), 'title' => __('Edit'))),
				'/users/edit_form_by_admin/' . $user['id']);
			}
			
			
			$status = $this->ACL->get_user_group($user['status']);
			$status = h($status['title']);
			$puttime = h($user['puttime']);
			$posts = h($user['posts']);
			if ( isset( $_SESSION['user'] ) ) {
				$pm = get_link(__('Write'), '/users/send_msg_form/' . $user['id']);
			} else {
				$pm = __('You are not authorized');
			}
			if ( !empty( $user['url'] ) )
				$url = get_link(h($user['url']), h($user['url']), array('target' => '_blank'));
			else
				$url = '&nbsp;';
			if ( !empty( $user['icq'] ) )
				$icq = h($user['icq']);
			else
				$icq = '&nbsp;';

				
				
			$markets['{USER_NAME}'] = $user_name;
			$markets['{MODER_PANEL}'] = $adm_panel;
			$markets['{POSTS}'] = $posts;
			$markets['{STATUS}'] = $status;
			$markets['{PUTTIME}'] = $puttime;
			$markets['{USER_PM}'] = $pm;
			if (is_object($this->AddFields)) {
				foreach ($user as $key => $value) {
					if (preg_match('#^add_field_[\d]+$#i', $key))				
					$markets['{' . strtoupper($key) . '}'] = h($value);
				}
			}
			
			$info .= $this->_replaceMarkets($markets, $html_user);
		}
		
		
		$t_markets['{COUNT_USERS}'] = $total;
		$t_markets['{USERS}'] = $info;
		$html = $this->_replaceMarkets($t_markets, $table_users);

		
		return $this->_view($html);
	}



	
	/**
	 * @param string $key
	 * if exists, user say "YES" and ready to register
	 */
	public function add_form($key = null) {
		// Registration denied
		if (!Config::read('open_reg')) {
			return showInfoMessage(__('Registration denied'), '/');
		}
		
	
		// View rules
		if (empty($key)) {
			$rules = $this->DB->select('users_settings', DB_FIRST, array(
				'cond' => array(
					'type' => 'reg_rules'
				)
			));
			$template = $this->Parser->getAndParse('viewrules.html');
			$markets = array();
			$markets['{RULES}'] = $rules[0]['values'];
			$markets['{REG_URL}'] = get_url('/users/add_form/yes');
			$content = $this->_replaceMarkets($markets, $template);
			$this->_view($content);
			die();
		}
		
		// View Register Form
		$tpl = $this->Parser->getAndParse('addnewuserform.html' );
		$markets = array();
		
		// Add fields
		if (is_object($this->AddFields)) {
			$_addFields = $this->AddFields->getInputs();
			foreach($_addFields as $k => $field) {
				$markets['{' . strtoupper($k) . '}'] = $field;
			}
		}
		
		if ( isset( $_SESSION['captcha_keystring'] ) ) unset( $_SESSION['captcha_keystring'] );
		$html = '';
		// Если при заполнении формы были допущены ошибки
		if ( isset( $_SESSION['FpsForm'] ) ) {
			$info      = $this->Parser->getAndParse('infomessage.html' );
			$info      = str_replace( '{INFO_MESSAGE}', $_SESSION['FpsForm']['error'], $info );
			$html      = $html.$info."\n";
			$name      = h( $_SESSION['FpsForm']['login'] );
			$email     = h( $_SESSION['FpsForm']['email'] );
			$timezone  = $_SESSION['FpsForm']['timezone'];
			$icq       = h( $_SESSION['FpsForm']['icq'] );
			$url       = h( $_SESSION['FpsForm']['url'] );
			$about     = h( $_SESSION['FpsForm']['about'] );
			$signature = h( $_SESSION['FpsForm']['signature'] );
			unset( $_SESSION['FpsForm'] );
		} else {
			$name      = '';
			$email     = '';
			$timezone  = 0;
			$icq       = '';
			$url       = '';
			$about     = '';
			$signature = '';
		}

		
		$kcaptcha = get_url('/sys/inc/kcaptcha/kc.php?'.session_name().'='.session_id());
		$markets['{ACTION}']  = get_url('/users/add/');
		$markets['{LOGIN}']  = $name;
		$markets['{EMAIL}']  = $email;
		$markets['{ICQ}']  = $icq;
		$markets['{URL}']  = $url;
		$markets['{ABOUT}']  = $about;
		$markets['{SIGNATURE}']  = $signature;
		$markets['{CAPTCHA}']  = $kcaptcha;
		$markets['{KEYSTRING}']  = '';

		$options = '';
		for ( $i = -12; $i <= 12; $i++ ) {
			if ( $i < 1 )
			  $value = $i . __('Hours');
			else
			  $value = '+' . $i . __('Hours');
			if ( $i == $timezone )
			  $options = $options . '<option value="'.$i.'" selected>'.$value.'</option>'."\n";
			else
			  $options = $options . '<option value="'.$i.'">'.$value.'</option>'."\n";
		}
		$markets['{OPTIONS}']  = $options;
		$markets['{SERVERTIME}']  = date( "d.m.Y H:i:s" );
		$tpl = $this->_replaceMarkets($markets, $tpl);
		
		
		$html = $html . $tpl;
		return $this->_view($html);
	}





	/**
	 * Write into base and check data. Also work for additional fields.
	 */
	public function add() {
		// Если не переданы данные формы - значит функция была вызвана по ошибке
		if ( !isset($_POST['login']) or
			!isset($_POST['password']) or
			!isset($_POST['confirm']) or
			!isset($_POST['email']) or
			!isset($_POST['timezone']) or
			!isset($_POST['icq']) or
			!isset($_POST['url']) or
			!isset($_POST['about']) or
			!isset($_POST['signature']) or
			!isset($_POST['keystring']) or
			!isset($_FILES['avatar'])
		) {
			redirect('/users/add_form/yes');
		}

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$name      = mb_substr( $_POST['login'], 0, 30 );
		$password  = mb_substr( $_POST['password'], 0, 30 );
		$confirm   = mb_substr( $_POST['confirm'], 0, 30 );
		$email     = mb_substr( $_POST['email'], 0, 60 );
		$icq       = mb_substr( $_POST['icq'], 0, 12 );
		$url       = mb_substr( $_POST['url'], 0, 60 );
		$about     = mb_substr( $_POST['about'], 0, 1000 );
		$signature = mb_substr( $_POST['signature'], 0, 500 );
		$keystring = mb_substr( $_POST['keystring'], 0, 6 );

		// Обрезаем лишние пробелы
		$name      = trim( $name );
		$password  = trim( $password );
		$confirm   = trim( $confirm );
		$email     = trim( $email );
		$icq       = trim( $icq );
		$url       = trim( $url );
		$about     = trim( $about );
		$signature = trim( $signature );
		$keystring = trim( $keystring );

		
		// Проверяем, заполнены ли обязательные поля
		$error = '';
		// Additional fields checker
		if (is_object($this->AddFields)) {
			$_addFields = $this->AddFields->checkFields();
			if (is_string($_addFields)) $error .= $_addFields; 
		}
		
		
		$valobj = new Validate;
		if ( empty( $name ) )               		
			$error = $error.'<li>' . __('Empty field "name"') . '</li>'."\n";
		if ( empty( $password ) )                 
			$error = $error.'<li>' . __('Empty field "pass"') . '</li>'."\n";
		if ( empty( $confirm ) )                  	
			$error = $error.'<li>' . __('Empty field "confirm pass"') . '</li>'."\n";
		if ( empty( $email ) )                    	
			$error = $error.'<li>' . __('Empty field "email"') . '</li>'."\n";
		if ( empty( $keystring ) ) 					
			$error = $error.'<li>' . __('Empty field "code"') . '</li>'."\n";
		
		
		// check login
		if (!empty($name) and mb_strlen($name) < 3 || mb_strlen($name) > 20)
			$error = $error.'<li>' . __('Wrong "name" lenght') . '</li>'."\n";
			
		// Проверяем, не слишком ли короткий пароль
		if (!empty($password) and mb_strlen($password) < Config::read('min_password_lenght'))
			$error = $error.'<li>' . sprintf(__('Wery short pass'), Config::read('min_password_lenght')) . '</li>'."\n";
		// Проверяем, совпадают ли пароли
		if (!empty($password) and !empty($confirm) and $password != $confirm)
			$error = $error.'<li>' . __('Passwords is diferend') . '</li>'."\n";
		// Проверяем поле "код"
		if (!empty($keystring)) {
			// Проверяем поле "код" на недопустимые символы
			if (!$valobj->cha_val($keystring, V_CAPTCHA))
				$error = $error.'<li>' . __('Wrong chars in field "code"') . '</li>'."\n";
													
			if (!isset($_SESSION['captcha_keystring'])) {
				if (file_exists(R . 'sys/logs/captcha_keystring_' . session_id() . '-' . date("Y-m-d") . '.dat')) {
					$_SESSION['captcha_keystring'] = file_get_contents(R . 'sys/logs/captcha_keystring_' 
					. session_id() . '-' . date("Y-m-d") . '.dat');
				}
			}
			if (!isset($_SESSION['captcha_keystring']) || $_SESSION['captcha_keystring'] != $keystring)
				$error = $error.'<li>' . __('Wrong protection code') . '</li>'."\n";
		}
		unset($_SESSION['captcha_keystring']);

		
		// Проверяем поля формы на недопустимые символы
		if (!empty($name) and !$valobj->cha_val($name, V_LOGIN))          
			$error = $error.'<li>' . __('Wrong chars in field "name"') . '</li>'."\n";
		if (!empty($password) and !$valobj->cha_val($password, V_LOGIN))  
			$error = $error.'<li>' . __('Wrong chars in field "pass"') . '</li>'."\n";
		if (!empty($confirm) and !$valobj->cha_val($confirm, V_LOGIN))   
			$error = $error.'<li>' . __('Wrong chars in field "confirm pass"') . '</li>'."\n";
		if (!empty($icq) and !$valobj->cha_val($icq, V_INT))              
			$error = $error.'<li>' . __('Wrong chars in field "ICQ"') . '</li>'."\n";
		if (!empty($about) and !$valobj->cha_val($about, V_TEXT))         
			$error = $error.'<li>' . __('Wrong chars in field "interes"') . '</li>'."\n";
		if (!empty($signature) and !$valobj->cha_val($signature, V_TEXT)) 
			$error = $error.'<li>' . __('Wrong chars in field "gignature"') . '</li>'."\n";
		// Проверяем корректность e-mail
		if (!empty($email) and !$valobj->cha_val($email, V_MAIL))
			$error = $error.'<li>' . __('Wrong chars in filed "e-mail"') . '</li>'."\n";
		// Проверяем корректность URL домашней странички
		if (!empty($url) and !$valobj->cha_val($url, V_URL))
			$error = $error.'<li>' . __('Wrong chars in filed "URL"') . '</li>'."\n";


		// kirilic
		$rus = array( "А","а","В","Е","е","К","М","Н","О","о","Р","р","С","с","Т","Х","х" );
		// latin
		$eng = array( "A","a","B","E","e","K","M","H","O","o","P","p","C","c","T","X","x" );
		$new_name = preg_replace( "#[^- _0-9a-zА-Яа-я]#i", "", $name );
		// Заменяем русские буквы латинскими
		$eng_new_name = str_replace( $rus, $eng, $new_name );
		// Заменяем латинские буквы русскими
		$rus_new_name = str_replace( $eng, $rus, $new_name );
		// Формируем SQL-запрос
		$res = $this->DB->query("SELECT * FROM `" . $this->DB->getFullTableName('users') . "`
			WHERE name LIKE '".mysql_real_escape_string( $new_name )."' OR
			name LIKE '".mysql_real_escape_string( $eng_new_name )."' OR
			name LIKE '".mysql_real_escape_string( $rus_new_name )."';");


		if (count($res) > 0) $error = $error.'<li>' . sprintf(__('Name already state'), $new_name) . '</li>'."\n";
		
		/* check avatar */
		$tmp_key = rand(0, 9999999);
		if (!empty($_FILES['avatar']['name'])) {
			$path = R . 'sys/tmp/images/' . $tmp_key . '.jpg';
			$ext = strrchr( $_FILES['avatar']['name'], "." );
			$extensions = array( ".jpg", ".gif", ".bmp", ".png", '.JPG', ".GIF", ".BMP", ".PNG");
			if (!in_array($ext, $extensions)) {
				$error = $error.'<li>' . __('Wrong avatar') . '</li>'."\n";
				$check_image = true;
			}
			if ($_FILES['avatar']['size'] > Config::read('max_avatar_size', 'users')) {
				$error = $error.'<li>'. sprintf(__('Avatar is wery big')
				, (Config::read('max_avatar_size', 'users') / 1024)) .'</li>'."\n";
				$check_image = true;
			}
			if (!isset($check_image) && move_uploaded_file($_FILES['avatar']['tmp_name'], $path)) {
				chmod($path, 0644);
				@$sizes = resampleImage($path, $path, 100);
				if (!$sizes) {
					@unlink($path);
					$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
				}
			} else {
				$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
			}
		}

		$timezone = (int)$_POST['timezone'];
		if ( $timezone < -12 or $timezone > 12 ) $timezone = 0;
		
		// Если были допущены ошибки при заполнении формы - перенаправляем посетителя на страницу регистрации
		if ( !empty( $error ) ) {
			$_SESSION['FpsForm'] = array_merge(array('login' => null, 'email'=> null, 'timezone' => null, 'icq' => null, 'url' => null, 'about' => null, 'signature' => null), $_POST);
			$_SESSION['FpsForm']['error'] 	= '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			redirect('/users/add_form/yes');
		}

		if ( !empty( $url ) and substr($url, 0, 7) != 'http://' ) $url = 'http://'.$url;

		// Уникальный код для активации учетной записи
		$email_activate = Config::read('email_activate');
		$code = (!empty($email_activate)) ? md5(uniqid(rand(), true)) : '';
		// Все поля заполнены правильно - продолжаем регистрацию
		$res = $this->DB->save('users', array(
			'name'  	=> $name,
			'passw' 	=> md5( $password ),
			'email' 	=> $email,
			'timezone' 	=> $timezone,
			'url' 		=> $url,
			'icq' 		=> $icq,
			'about' 	=> $about,
			'signature' => $signature,
			'photo' 	=> '',
			'puttime' 	=> new Expr('NOW()'),
			'last_visit' => new Expr('NOW()'),
			'themes' 	=> 0,
			'status' 	=> 1,
			'activation' => $code
		));
		$id = mysql_insert_id();
		// Additional fields saver
		if (is_object($this->AddFields)) {
			$this->AddFields->save($id, $_addFields);
		}
		
		
		if (file_exists(R . 'sys/tmp/images/' . $tmp_key . '.jpg')) { 
			if (copy(R . 'sys/tmp/images/' . $tmp_key . '.jpg', R . 'sys/avatars/' . $id . '.jpg')) {
				chmod( R . 'sys/avatars/' . $id . '.jpg', 0644 );
			}
			unlink(R . 'sys/tmp/images/' . $tmp_key . '.jpg');
		}
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		
		// Activate by Email
		if (!empty($email_activate)) {
			// Посылаем письмо пользователю с просьбой активировать учетную запись
			$headers = "From: ".$_SERVER['SERVER_NAME']." <" . Config::read('admin_email') . ">\n";
			$headers = $headers."Content-type: text/html; charset=\"utf-8\"\n";
			$headers = $headers."Return-path: <" . Config::read('admin_email') . ">\n";
			$message = '<p>Добро пожаловать на форум '.$_SERVER['SERVER_NAME'].'!</p>'."\n";
			$message = $message.'<p>Пожалуйста сохраните это сообщение. Параметры вашей учётной записи таковы:</p>'."\n";
			$message = $message.'<p>Логин: '.$name.'<br/>Пароль: '.$password.'</p>'."\n";
			$message = $message.'<p>Для активации вашей учетной записи перейдите по ссылке:</p>'."\n";
			$link = 'http://'.$_SERVER['SERVER_NAME'] . '/users/activate/'.$code;
			$message = $message.'<p><a href="'.$link.'">Активировать учетную запись</a></p>'."\n";
			$message = $message.'<p>Не забывайте свой пароль: он хранится в нашей базе в зашифрованном
					 виде, и мы не сможем вам его выслать. Если вы всё же забудете пароль, то сможете
					 запросить новый, который придётся активировать таким же образом, как и вашу
					 учётную запись.</p>'."\n";
			$message = $message.'<p>Спасибо за то, что зарегистрировались на нашем форуме.</p>'."\n";
			$subject = 'Регистрация на форуме '.$_SERVER['SERVER_NAME'];
			mail( $email, $subject, $message, $headers );
			
			if ($this->Log) $this->Log->write('adding user', 'user id(' . $id . ')');
			$msg = 'На Ваш e-mail выслано письмо с просьбой подтвердить регистрацию.
				  Чтобы завершить регистрацию и активировать учетную запись, зайдите
				  по адресу, указанному в письме.';
			
		} else { // Activate without Email
			$msg = __('Registeration complete');
		}
		$html = $this->Parser->getAndParse('infomessage.html' );
		$html = str_replace( '{INFO_MESSAGE}', $msg, $html );
		return $this->_view($html);
	}




	// Активация учетной записи нового пользователя
	public function activate($code = null) {
		// Если не передан параметр $code - значит функция вызвана по ошибке
		if (empty($code) || mb_strlen($code) !== 32) {
			redirect('/');
		}
		// Т.к. код зашифрован с помощью md5, то он представляет собой
		// 32-значное шестнадцатеричное число
		$code = substr( $code, 0, 32 );
		$code = preg_replace( "#[^0-9a-f]#i", '', $code );
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		$res = $this->DB->select('users', DB_FIRST, array('cond' => array('activation' => $code)));
		if (count($res) > 0 ) {
			$id = $res[0]['id'];
			$res = $this->DB->save('users', array(
				'activation' => '',
				'last_visit' => new Expr('NOW()'),
				'id' => $id
			));
			if ($this->Log) $this->Log->write('activate user', 'user id(' . $id . ')');
			return showInfoMessage(__('Accaunt activated'), '/users/login_form/' );
		}
		if ($this->Log) $this->Log->write('wrong activate user', 'activate code(' . $code . ')');
		return showInfoMessage(__('Wrong activation code'), '/');
	}




	/**
	 * Return form to request new password
	 *
	 */
	public function new_password_form() {
		
		$html = '';
		$markets = array();
		if ( isset( $_SESSION['newPasswordForm']['error'] ) ) {
			$info = $this->Parser->getAndParse('infomessage.html' );
			$info = str_replace( '{INFO_MESSAGE}', $_SESSION['newPasswordForm']['error'], $info );
			$html = $info . $html ."\n";
			unset( $_SESSION['newPasswordForm']['error'] );
		}
		
		$tpl = $this->Parser->getAndParse('newpasswordform.html' );
		$markets['{ACTION}'] = get_url('/users/send_new_password/');
		$html = $html . $this->_replaceMarkets($markets, $tpl);
		
		
		// Navigation PAnel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
		. get_link(h($this->module_title), '/users/') . __('Separator') . __('Pass repair');
		$this->_globalize($nav);


		return $this->_view($html);
	}





	// Функция высылает на e-mail пользователя новый пароль
	public function send_new_password() {

		// Если не переданы методом POST логин и e-mail - перенаправляем пользователя
		if ( !isset( $_POST['username'] ) || !isset( $_POST['email'])) {
			redirect('/');
		}

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$name  = mb_substr( $_POST['id'], 0, 30 );
		$email = mb_substr( $_POST['email'], 0, 60 );
		$name  = trim( $name );
		$email = trim( $email );

		// Проверяем, заполнены ли обязательные поля
		$error = "";
		$valobj = new Validate;
		if (empty($name))  	
			$error = $error.'<li>' . __('Empty field "name"') . '</li>'."\n";
		if (empty($email)) 	
			$error = $error.'<li>' . __('Empty field "email"') . '</li>'."\n";

		// Проверяем поля формы на недопустимые символы
		if (!empty($name) and !$valobj->cha_val($name, V_LOGIN) )
			$error = $error.'<li>' . __('Wrong chars in field "name"') . '</li>'."\n";
		// Проверяем корректность e-mail
		if ( !empty( $email ) and !$valobj->cha_val($email, V_MAIL))
			$error = $error.'<li>' . __('Wrong chars in filed "e-mail"') . '</li>'."\n";
		// Проверять существование такого пользователя есть смысл только в том
		// случае, если поля не пустые и не содержат недопустимых символов
		if ( empty( $error ) ) {
			touchDir(R . 'sys/tmp/activate/');
		
			$res = $this->DB->select('users', DB_FIRST, array('cond' => array('name' => $name, 'email' => $email)));
			// Если пользователь с таким логином и e-mail существует
			if (count($res) > 0 && empty($error)) {
				// Небольшой код, который читает содержимое директории activate
				// и удаляет старые файлы для активации пароля (были созданы более суток назад)
				if ( $dir = opendir( R . 'sys/tmp/activate' ) ) {
					$tmp = 24*60*60;
					while (false !== ($file = readdir($dir))) {
						if (is_file($file))
							if ((time() - filemtime($file)) > $tmp) unlink($file);
					}
					closedir($dir);
				}
				// Как происходит процедура восстановления пароля? Пользователь ввел свой логин
				// и e-mail, мы проверяем существование такого пользователя в таблице БД. Потом
				// генерируем с помощью функции getNewPassword() новый пароль, создаем файл с именем
				// md5( $newPassword ) в директории activate. Файл содержит ID пользователя.
				// В качестве кода активации выступает хэш пароля - md5( $newPassword ).
				// Когда пользователь перейдет по ссылке в письме для активации своего нового пароля,
				// мы проверяем наличие в директории activatePassword файла с именем кода активации,
				// и если он существует, активируем новый пароль.
				$id = $res[0]['id'];
				$newPassword = $this->_getNewPassword();
				$code = md5( $newPassword );
				// file_put_contents(R . 'sys/tmp/activate/'.$code, $id );
				$fp = fopen( R . 'sys/tmp/activate/' . $code, "w" );
				fwrite($fp, $id);
				fclose($fp);
				// Посылаем письмо пользователю с просьбой активировать пароль
				$headers = "From: " . $_SERVER['SERVER_NAME'] . " <" . Config::read('admin_email') . ">\n";
				$headers = $headers."Content-type: text/html; charset=\"utf-8\"\n";
				$headers = $headers."Return-path: <" . Config::read('admin_email') . ">\n";
				$message = '<p>Добрый день, '.$name.'!</p>'."\n";
				$message = $message.'<p>Вы получили это письмо потому, что вы (либо кто-то, выдающий себя
						 за вас) попросили выслать новый пароль к вашей учётной записи на форуме '.
						 $_SERVER['SERVER_NAME'].'. Если вы не просили выслать пароль, то не обращайте
						 внимания на это письмо, если же подобные письма будут продолжать приходить,
						 обратитесь к администратору форума</p>'."\n";
				$message = $message.'<p>Прежде чем использовать новый пароль, вы должны его активировать.
						 Для этого перейдите по ссылке:</p>'."\n";
				$link = 'http://'.$_SERVER['SERVER_NAME'] . '/users/activate_password/'.$code;
				$message = $message.'<p><a href="'.$link.'">Активировать пароль</a></p>'."\n";
				$message = $message.'<p>В случае успешной активации вы сможете входить в систему, используя
						 следующий пароль: '.$newPassword.'</p>'."\n";
				$message = $message.'<p>Вы сможете сменить этот пароль на странице редактирования профиля.
						 Если у вас возникнут какие-то трудности, обратитесь к администратору форума.</p>'."\n";
				$subject = 'Активация пароля на форуме '.$_SERVER['SERVER_NAME'];
				mail( $email, $subject, $message, $headers );

				$msg = __('We send mail to your e-mail');
				$html = $this->Parser->getAndParse('infomessage.html' );
				$html = str_replace( '{INFO_MESSAGE}', $msg, $html );

				return $this->_view($html);
				if ($this->Log) $this->Log->write('send new passw', 'name(' . $name . '), mail(' . $email . ')');
			} else {
				$error = $error.'<li>' . __('Wrong login or email') . '</li>'."\n";
			}
		}
		
		
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		if ($this->Log) $this->Log->write('wrong send new passw', 'name(' . $name . '), mail(' . $email . ')');
		// Если были допущены ошибки при заполнении формы - перенаправляем посетителя
		if (!empty($error)) {
			$_SESSION['newPasswordForm']          = array();
			$_SESSION['newPasswordForm']['error'] = '<p class="errorMsg">' . __('Some error in form') . '</p>'."\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			redirect('/users/new_password_form/');
		}

	}




	// Активация нового пароля
	public function activate_password($code = null) {
		if (!isset($code)) redirect('/');

		// Т.к. код активации создан с помощью md5, то он
		// представляет собой 32-значное шестнадцатеричное число
		$code = mb_substr( $code, 0, 32 );
		$code = preg_replace( "#[^0-9a-f]#i", '', $code );
		
		if (empty($code)) redirect('/');
		
		$f_path =  R . 'sys/tmp/activate/' . $code;
		if (is_file($f_path) and  ((time() - filemtime($f_path)) < 24*60*60)) {
			$file = file($f_path);
			unlink($f_path);
			$id_user = (int)trim($file[0]);
			$res = $this->DB->save('users', array(
				'passw' => $code,
				'id' => $id_user
			));
			$message = __('New pass is ready');
			if ($this->Log) $this->Log->write('activate new passw', 'user id(' . $id_user . ')');
		} else {
			$message = __('Error when activate new pass');
			if ($this->Log) $this->Log->write('wrong activate new passw', 'code(' . $code . ')');
		}
		
		$markets = array();
		$html = $this->Parser->getAndParse('infomessage.html');
		$markets['{INFO_MESSAGE}'] = $message;
		$html = $this->_replaceMarkets($markets, $html);
		return $this->_view($html);
	}





	// Функция возвращает случайно сгенерированный пароль
	private function _getNewPassword() {
		$length = rand( 10, 30 );
		$password = '';
		for( $i = 0; $i < $length; $i++ ) {
			$range = rand(1, 3);
			switch( $range ) {
				case 1: $password = $password.chr( rand(48, 57) );  break;
				case 2: $password = $password.chr( rand(65, 90) );  break;
				case 3: $password = $password.chr( rand(97, 122) ); break;
			}
		}
		return $password;
	}




	// Функция возвращает html формы для редактирования данных о пользователе
	public function edit_form() {
		if (!isset($_SESSION['user'])) redirect('/');

		
		//turn access
		$this->ACL->turn(array('users', 'edit_mine'));
		$anket = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => (int)$_SESSION['user']['id'])));
		if (is_object($this->AddFields) && count($anket) > 0) {
			$anket = $this->AddFields->mergeRecords($anket, true);
		}
		
		
		$html = '';
		$markets = array();

		
		// Если при заполнении формы были допущены ошибки
		if (isset($_SESSION['FpsForm'])) {
			$info      = $this->Parser->getAndParse('infomessage.html' );
			$info      = str_replace( '{INFO_MESSAGE}', $_SESSION['FpsForm']['error'], $info );
			$html      = $html . $info."\n";
			$email     = h($_SESSION['FpsForm']['email']);
			$timezone  = h($_SESSION['FpsForm']['timezone']);
			$icq       = h($_SESSION['FpsForm']['icq']);
			$url       = h($_SESSION['FpsForm']['url']);
			$about     = h($_SESSION['FpsForm']['about']);
			$signature = h($_SESSION['FpsForm']['signature']);
			unset( $_SESSION['FpsForm'] );
		} else {
			$email     = h($anket[0]['email']);
			$timezone  = h($anket[0]['timezone']);
			$icq       = h($anket[0]['icq']);
			$url       = h($anket[0]['url']);
			$about     = h($anket[0]['about']);
			$signature = h($anket[0]['signature']);
		}

		
		$tpl = $this->Parser->getAndParse('edituserform.html' );
		$markets['{ACTION}'] = get_url('/users/update/');
		$markets['{EMAIL}'] = $email;
		$markets['{ICQ}'] = $icq;
		$markets['{URL}'] = $url;
		$markets['{ABOUT}'] = $about;
		$markets['{SIGNATURE}'] = $signature;
		
		// Аватар
		if (file_exists(R . 'sys/avatars/' . $anket[0]['id'] . '.jpg')) {
			$markets['{AVATAR}'] = get_url('/sys/avatars/' . $anket[0]['id'] . '.jpg');
		} else {
			$markets['{AVATAR}'] = get_url('/sys/img/noavatar.png');
		}

		$options = '';
		for ( $i = -12; $i <= 12; $i++ ) {
			if ( $i < 1 )
				$value = $i.' часов';
			else
				$value = '+'.$i.' часов';
			if ( $i == $_SESSION['user']['timezone'] )
				$options = $options . '<option value="'.$i.'" selected>'.$value.'</option>'."\n";
			else
				$options = $options . '<option value="'.$i.'">'.$value.'</option>'."\n";
		}
		$markets['{OPTIONS}'] = $options;
		$markets['{SERVERTIME}'] = date( "d.m.Y H:i:s" );
		
		
		// Если ранее был загружен файл - надо предоставить возможность удалить его
		$unlinkfile = '';
		if (is_file(R . 'sys/avatars/' . $_SESSION['user']['id'] . '.jpg')) {
			$unlinkfile = '<input type="checkbox" name="unlink" value="1" />'
			. __('Are you want delete file') . "\n";
		}
		$markets['{UNLINKFILE}'] = $unlinkfile;
		// Additional fields
		foreach ($anket[0] as $key => $value) {
			if (preg_match('#^add_field_[\d]+$#i', $key) && is_object($this->AddFields)) {
				$markets['{' . strtoupper($key) . '}'] = $value;
			}
		}
		
		$markets = $this->Parser->quoteTags($markets);
		$html = $html . $this->_replaceMarkets($markets, $tpl);

		
		// Navigation Panel
		$navi = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Editing');
		$this->_globalize($nav);
		
		return $this->_view($html);
	}




	/**
	 * Update record into Data Base
	 */
	public function update() {
		if ( !isset( $_SESSION['user'] ) ) {
			redirect('/');
		}
		//turn access
		$this->ACL->turn(array('users', 'edit_mine'));

		// Если не переданы данные формы - функция вызвана по ошибке
		if ( !isset( $_POST['password'] ) or
			!isset( $_POST['newpassword'] ) or
			!isset( $_POST['confirm'] ) or
			!isset( $_POST['email'] ) or
			!isset( $_POST['timezone'] ) or
			!isset( $_POST['icq'] ) or
			!isset( $_POST['url'] ) or
			!isset( $_POST['about'] ) or
			!isset( $_POST['signature'] ) or
			!isset( $_FILES['avatar'] )
		) {
			redirect('/');
		}

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$password     = mb_substr( $_POST['password'], 0, 30 );
		$newpassword  = mb_substr( $_POST['newpassword'], 0, 30 );
		$confirm      = mb_substr( $_POST['confirm'], 0, 30 );
		$email        = mb_substr( $_POST['email'], 0, 60 );
		$icq          = mb_substr( $_POST['icq'], 0, 12 );
		$url          = mb_substr( $_POST['url'], 0, 60 );
		$about        = mb_substr( $_POST['about'], 0, 1000 );
		$signature    = mb_substr( $_POST['signature'], 0, 500 );

		// Обрезаем лишние пробелы
		$password     = trim( $password );
		$newpassword  = trim( $newpassword );
		$confirm      = trim( $confirm );
		$email        = trim( $email );
		$icq          = trim( $icq );
		$url          = trim( $url );
		$about        = trim( $about );
		$signature    = trim( $signature );

		// Проверяем, заполнены ли обязательные поля
		$error = '';
		// Additional fields
		if (is_object($this->AddFields)) {
			$_addFields = $this->AddFields->checkFields();
			if (is_string($_addFields)) $error .= $_addFields; 
		}
		
		$valobj = new Validate;
		// Если заполнено поле "Текущий пароль" - значит пользователь
		// хочет изменить его или поменять свой e-mail
		$changePassword = false;
		$changeEmail = false;
		if ( !empty( $_POST['password'] ) ) {
			if ( md5( $_POST['password'] ) != $_SESSION['user']['passw'] ) 
				$error = $error.'<li>' . __('Wrong current pass') . '</li>'."\n";
			// Надо выяснить, что хочет сделать пользователь:
			// поменять свой e-mail, изменить пароль или и то и другое
			if ( !empty( $newpassword ) ) { // хочет изменить пароль
				$changePassword = true;
				if ( empty( $confirm ) ) 	
					$error = $error.'<li>' . __('Empty field "confirm pass"') . '</li>'."\n";
				if (strlen($newpassword) < Config::read('min_password_lenght'))
					$error = $error.'<li>'. sprintf(__('Wery short pass'), Config::read('min_password_lenght')) . '</li>'."\n";
				if (!empty($confirm) and $newpassword != $confirm)
					$error = $error.'<li>' . __('Passwords is diferend') . '</li>'."\n";
				if (!$valobj->cha_val($newpassword, V_LOGIN))
					$error = $error.'<li>' . __('Wrong chars in field "pass"') . '</li>'."\n";
				if (!empty($confirm) and !$valobj->cha_val($confirm, V_LOGIN))
					$error = $error.'<li>' . __('Wrong chars in field "confirm pass"') . '</li>'."\n";
			}
			if ($email != $_SESSION['user']['email']) { // хочет изменить e-mail
				$changeEmail = true;
				if (empty($email)) 				
					$error = $error.'<li>' . __('Empty field "email"') . '</li>'."\n";
				if (!empty($email) and !$valobj->cha_val($email, V_MAIL))
					$error = $error.'<li>' . __('Wrong chars in filed "e-mail"') . '</li>'."\n";
			}
		}
		if (!empty($icq) and !$valobj->cha_val($icq, V_INT))
			$error = $error.'<li>' . __('Wrong chars in field "ICQ"') . '</li>'."\n";
		if (!empty($about) and !$valobj->cha_val($about, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "interes"') . '</li>'."\n";
		if (!empty($signature) and !$valobj->cha_val($signature, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "gignature"') . '</li>'."\n";
		if (!empty($url) and !$valobj->cha_val($url, V_URL))
			$error = $error.'<li>' . __('Wrong chars in filed "URL"') . '</li>'."\n";
		
		
		$tmp_key = rand(0, 9999999);
		if (!empty($_FILES['avatar']['name'])) {
			touchDir(R . 'sys/tmp/images/', 0777);
		
			$path = R . 'sys/tmp/images/' . $tmp_key . '.jpg';
			$ext = strrchr( $_FILES['avatar']['name'], "." );
			$extensions = array( ".jpg", ".gif", ".bmp", ".png", '.JPG', ".GIF", ".BMP", ".PNG");
			if (!in_array($ext, $extensions)) {
				$error = $error.'<li>' . __('Wrong avatar') . '</li>'."\n";
				$check_image = true;
			}
			if ($_FILES['avatar']['size'] > Config::read('max_avatar_size', 'users')) {
				$error = $error.'<li>'. sprintf(__('Avatar is wery big'), Config::read('max_avatar_size', 'users')).'</li>'."\n";
				$check_image = true;
			}
			if (!isset($check_image) && move_uploaded_file($_FILES['avatar']['tmp_name'], $path)) {
				chmod($path, 0644);
				@$sizes = resampleImage($path, $path, 100);
				if (!$sizes) {
					@unlink($path);
					$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
				}
			} else {
				$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
			}
		}

		$timezone = (int)$_POST['timezone'];
		if ($timezone < -12 || $timezone > 12) $timezone = 0;

		
		// if an Errors
		if (!empty($error)) {
			$_SESSION['FpsForm'] = array_merge(array('email' => null, 'timezone' => null, 'icq' => null, 'url' => null, 'about' => null, 'signature' => null), $_POST);
			$_SESSION['FpsForm']['error']     = '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			redirect('/users/edit_form/');
		}

		// Если выставлен флажок "Удалить загруженный ранее файл"
		if (isset( $_POST['unlink']) and is_file(R . 'sys/avatars/' . $_SESSION['user']['id'] . '.jpg')) {
			unlink(R . 'sys/avatars/' . $_SESSION['user']['id'] . '.jpg');
		}
		/* copy and delete tmp image */
		if (file_exists(R . 'sys/tmp/images/' . $tmp_key . '.jpg')) { 
			if (copy(R . 'sys/tmp/images/' . $tmp_key . '.jpg', R . 'sys/avatars/' . $_SESSION['user']['id'] . '.jpg')) {
				chmod( R . 'sys/avatars/' . $_SESSION['user']['id'] . '.jpg', 0644 );
			}
			unlink(R . 'sys/tmp/images/' . $tmp_key . '.jpg');
		}

		// Все поля заполнены правильно - записываем изменения в БД
		if (mb_substr($url, 0, mb_strlen('http://')) !== 'http://') $url = 'http://' . $url;
		
		$tmp 	   = '';
		$user_data = array();
		if ( $changePassword ) {
			$user_data['passw'] 	   = md5($newpassword);
			$_SESSION['user']['passw'] = md5( $newpassword );
		}
		if ( $changeEmail ) {
			$user_data['email'] 	   = $email;
			$_SESSION['user']['email'] = $email;
		}
		$user_data['timezone']  = $timezone;
		$user_data['url'] 		= $url;
		$user_data['icq'] 		= $icq;
		$user_data['about'] 	= $about;
		$user_data['signature'] = $signature;
		$user_data['id'] 		= $_SESSION['user']['id'];
		
		
		$res = $this->DB->save('users', $user_data);
		// Additional fields saving
		if (is_object($this->AddFields)) {
			$this->AddFields->save($_SESSION['user']['id'], $_addFields);
		}
		
		
		// Теперь надо обновить данные о пользователе в массиве $_SESSION['user']
		if ( $changePassword ) $_SESSION['user']['passw'] = md5( $newpassword );
		if ( $changeEmail ) $_SESSION['user']['email'] = $email;
		$_SESSION['user']['timezone']  = $timezone;
		$_SESSION['user']['url']       = $url;
		$_SESSION['user']['icq']       = $icq;
		$_SESSION['user']['about']     = $about;
		$_SESSION['user']['signature'] = $signature;
		
		
		// ... и в массиве $_COOKIE
		if ( isset( $_COOKIE['autologin'] ) ) {
			$path   = "/";
			setcookie( 'autologin', 'yes', time() + 3600 * 24 * Config::read('cookie_time'), $path );
			setcookie( 'id', $_SESSION['user']['id'], time() + 3600 * 24 * Config::read('cookie_time'), $path );
			setcookie( 'password', $_SESSION['user']['passw'], time() + 3600 * 24 * Config::read('cookie_time'), $path );
		}
		if ($this->Log) $this->Log->write('editing user', 'user id(' . $_SESSION['user']['id'] . ')');
		return showInfoMessage(__('Your profile has been changed'), '/');
	}




	/**
	 * Edit form by admin
	 */
	public function edit_form_by_admin($id = null) {
		//turn access
		$this->ACL->turn(array('users', 'edit_users'));
		$id = (int)$id;
		if ( $id < 1 ) redirect('/users/');
		if (!isset($_SESSION['user'])) redirect('/');

		$statusArray = $this->ACL->get_group_info();
		if (!empty($statusArray)) unset($statusArray[0]);
		$html = '';
		$markets = array();
		
		// Получаем данные о пользователе из БД
		$res = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => $id)));
		if (count($res) == 0) return showInfoMessage(__('Can not find user'), '/users/' );
		if (is_object($this->AddFields) && count($res) > 0) {
			$res = $this->AddFields->mergeRecords($res, true);
		}
		$user = $res[0];
		
		// Если при заполнении формы были допущены ошибки
		if ( isset( $_SESSION['FpsForm'] ) ) {
			$info      = $this->Parser->getAndParse('infomessage.html' );
			$info      = str_replace( '{INFO_MESSAGE}', $_SESSION['FpsForm']['error'], $info );
			$html      = $html.$info."\n";
			$name      = h( $_SESSION['FpsForm']['login'] );
			$status    = h($_SESSION['FpsForm']['status']);
			$email     = h( $_SESSION['FpsForm']['email'] );
			$oldEmail  = h( $_SESSION['FpsForm']['oldEmail'] );
			$timezone  = h($_SESSION['FpsForm']['timezone']);
			$icq       = h( $_SESSION['FpsForm']['icq'] );
			$url       = h( $_SESSION['FpsForm']['url'] );
			$about     = h( $_SESSION['FpsForm']['about'] );
			$signature = h( $_SESSION['FpsForm']['signature'] );
			unset( $_SESSION['FpsForm'] );
			
			
		} else {
			$name      = h( $user['name'] );
			$status    = $user['status'];
			$email     = h( $user['email'] );
			$oldEmail  = $email;
			$timezone  = $user['timezone'];
			$icq       = h( $user['icq'] );
			$url       = h( $user['url'] );
			$about     = h( $user['about'] );
			$signature = h( $user['signature'] );
		}

		$userStatus = '<select name="status">'."\n";
		foreach( $statusArray as $key => $value ) {
			if ( $key == $status )
				$userStatus = $userStatus . '<option value="' . $key . '" selected>' . $value['title'] . '</option>'."\n";
			else
				$userStatus = $userStatus . '<option value="' . $key . '">' . $value['title'] . '</option>'."\n";
		}
		$userStatus = $userStatus . '</select>' . "\n";

		
		$tpl = $this->Parser->getAndParse('edituserformbyadmin.html' );
		$markets['{ACTION}'] = get_url('/users/update_by_admin/' . $id);
		$markets['{LOGIN}'] = $name;
		$markets['{STATUS}'] = $userStatus;
		$markets['{EMAIL}'] = $email;
		$markets['{ICQ}'] = $icq;
		$markets['{URL}'] = $url;
		$markets['{ABOUT}'] = $about;
		$markets['{SIGNATURE}'] = $signature;
		
		// Аватар
		if (file_exists(R . 'sys/avatars/' . $user['id'] . '.jpg')) {
			$markets['{AVATAR}'] = get_url('/sys/avatars/' . $user['id'] . '.jpg');
		} else {
			$markets['{AVATAR}'] = get_url('/sys/img/noavatar.png');
		}

		$options = '';
		for ( $i = -12; $i <= 12; $i++ ) {
			if ( $i < 1 )
				$value = $i . __('Hours');
			else
				$value = '+' . $i .  __('Hours');
			if ( $i == $timezone )
				$options = $options . '<option value="'.$i.'" selected>'.$value.'</option>'."\n";
			else
				$options = $options . '<option value="'.$i.'">'.$value.'</option>'."\n";
		}
		$markets['{OPTIONS}'] = $options;
		$markets['{SERVERTIME}'] = date( "d.m.Y H:i:s" );

		
		// Если ранее был загружен файл - надо предоставить возможность удалить его
		$unlinkfile = '';
		if (is_file(R . 'sys/avatars/' . $id . '.jpg')) {
			$unlinkfile = '<br/><input type="checkbox" name="unlink" value="1" />' 
			. __('Are you want delete file') . "\n";
		}
		$activation = (!empty($user['activation'])) 
		? __('Activate') . ' <input name="activation" type="checkbox" value="1" >' : __('Active');
		
		$markets['{UNLINKFILE}'] = $unlinkfile;
		$markets['{ACTIVATION}'] = $activation;
		
		// Additional fields
		foreach ($res[0] as $key => $value) {
			if (preg_match('#^add_field_[\d]+$#i', $key) && is_object($this->AddFields)) {
				$markets['{' . strtoupper($key) . '}'] = $value;
			}
		}
		
		$markets = $this->Parser->quoteTags($markets);
		$html = $html . $this->_replaceMarkets($markets, $tpl);

		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Editing');
		$this->_globalize($nav);
		
		return $this->_view($html);
	}




	// Функция обновляет данные пользователя (только для администратора форума)
	public function update_by_admin($id = null) {
		//turn access
		$this->ACL->turn(array('users', 'edit_users'));
		$id = (int)$id;
		// ID зарегистрированного пользователя не может быть меньше
		// единицы - значит функция вызвана по ошибке
		if ($id < 1) {
			redirect('/users/' );
		}
		// Если профиль пытается редактировать не зарегистрированный
		// пользователь - функция вызвана по ошибке
		if (!isset($_SESSION['user'])) {
			redirect( '/');
		}

		
		// Если не переданы данные формы - функция вызвана по ошибке
		if (!isset( $_POST['status'] ) or
			!isset( $_POST['email'] ) or
			!isset( $_POST['oldEmail'] ) or
			!isset( $_POST['newpassword'] ) or
			!isset( $_POST['confirm'] ) or
			!isset( $_POST['timezone'] ) or
			!isset( $_POST['icq'] ) or
			!isset( $_POST['url'] ) or
			!isset( $_POST['about'] ) or
			!isset( $_POST['signature'] ) or
			!isset( $_FILES['avatar'] )
		) {
			redirect('/');
		}

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$email        = mb_substr( $_POST['email'], 0, 60 );
		$oldEmail     = mb_substr( $_POST['oldEmail'], 0, 60 );
		$newpassword  = mb_substr( $_POST['newpassword'], 0, 30 );
		$confirm      = mb_substr( $_POST['confirm'], 0, 30 );
		$icq          = mb_substr( $_POST['icq'], 0, 12 );
		$url          = mb_substr( $_POST['url'], 0, 60 );
		$about        = mb_substr( $_POST['about'], 0, 1000 );
		$signature    = mb_substr( $_POST['signature'], 0, 500 );

		// Обрезаем лишние пробелы
		$email        = trim( $email );
		$oldEmail     = trim( $oldEmail );
		$newpassword  = trim( $newpassword );
		$confirm      = trim( $confirm );
		$icq          = trim( $icq );
		$url          = trim( $url );
		$about        = trim( $about );
		$signature    = trim( $signature );

		// Проверяем, заполнены ли обязательные поля
		$error = '';
		// Additional fields
		if (is_object($this->AddFields)) {
			$_addFields = $this->AddFields->checkFields();
			if (is_string($_addFields)) $error .= $_addFields; 
		}
		
		$valobj = new Validate;
		// Надо выяснить, что хочет сделать администратор:
		// поменять e-mail, изменить пароль или и то и другое
		$changePassword = false;
		$changeEmail = false;

		if ( !empty( $newpassword ) ) { // хочет изменить пароль
			$changePassword = true;
			if ( empty( $confirm ) )    
				$error = $error.'<li>' . __('Empty field "confirm pass"') . '</li>'."\n";
			if (strlen($newpassword) < Config::read('min_password_lenght'))
				$error = $error.'<li>' . sprintf(__('Wery short pass'), Config::read('min_password_lenght')).'</li>'."\n";
			if (!empty($confirm) and $newpassword != $confirm)
				$error = $error.'<li>' . __('Passwords is diferend') . '</li>'."\n";
			if ( !$valobj->cha_val($newpassword, V_LOGIN))
				$error = $error.'<li>' . __('Wrong chars in field "pass"') . '</li>'."\n";
			if (!empty($confirm) and !$valobj->cha_val($confirm, V_LOGIN))
				$error = $error.'<li>' . __('Wrong chars in field "confirm pass"') . '</li>'."\n";
		}
		if ( $email != $oldEmail ) { // хочет изменить e-mail
			$changeEmail = true;
			if (empty($email)) 		 	
				$error = $error.'<li>' . __('Empty field "email"') . '</li>'."\n";
			if ( !empty( $email ) and !$valobj->cha_val($email, V_MAIL))
				$error = $error.'<li>' . __('Wrong chars in filed "e-mail"') . '</li>'."\n";
		}

		
		// Проверяем поля формы на недопустимые символы
		if (!empty($icq) and !$valobj->cha_val($icq, V_INT))
			$error = $error.'<li>' . __('Wrong chars in field "ICQ"') . '</li>'."\n";
		if (!empty($about) and !$valobj->cha_val($about, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "interes"') . '</li>'."\n";
		if (!empty($signature) and !$valobj->cha_val($signature, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "gignature"') . '</li>'."\n";
		if (!empty($url) and !$valobj->cha_val($url, V_URL))
			$error = $error.'<li>' . __('Wrong chars in filed "URL"') . '</li>'."\n";
		
		
		$tmp_key = rand(0, 9999999);
		if (!empty($_FILES['avatar']['name'])) {
			touchDir(R . 'sys/tmp/images/', 0777);
		
			$path = R . 'sys/tmp/images/' . $tmp_key . '.jpg';
			$ext = strrchr( $_FILES['avatar']['name'], "." );
			$extensions = array( ".jpg", ".gif", ".bmp", ".png", '.JPG', ".GIF", ".BMP", ".PNG");
			if (!in_array($ext, $extensions)) {
				$error = $error.'<li>' . __('Wrong avatar') . '</li>'."\n";
				$check_image = true;
			}
			if ($_FILES['avatar']['size'] > Config::read('max_avatar_size', 'users')) {
				$error = $error.'<li>'. sprintf(__('Avatar is wery big'), Config::read('max_avatar_size', 'users')).'</li>'."\n";
				$check_image = true;
			}
			if (!isset($check_image) && move_uploaded_file($_FILES['avatar']['tmp_name'], $path)) {
				chmod($path, 0644);
				@$sizes = resampleImage($path, $path, 100);
				if (!$sizes) {
					@unlink($path);
					$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
				}
			} else {
				$error = $error.'<li>' . __('Some error in avatar') . '</li>'."\n";
			}
		}


		$status = (int)$_POST['status'];
		$timezone = (int)$_POST['timezone'];
		if ( $timezone < -12 or $timezone > 12 ) $timezone = 0;

		// Errors
		if (!empty($error)) {
			$_SESSION['FpsForm'] = array_merge(array('name' => null, 'status' => null, 'email' => null, 'oldEmail' => null, 'timezone' => null, 'icq' => null, 'url' => null, 'about' => null, 'signature' => null), $_POST);
			$_SESSION['FpsForm']['error'] = '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			redirect('/users/edit_form_by_admin/' . $id );
		}

		// Если выставлен флажок "Удалить загруженный ранее файл"
		if (isset($_POST['unlink']) and is_file(R . 'sys/avatars/' . $id . '.jpg')) {
			unlink(R . 'sys/avatars/' . $id . '.jpg');
		}
		if (file_exists(R . 'sys/tmp/images/' . $tmp_key . '.jpg')) { 
			if (copy(R . 'sys/tmp/images/' . $tmp_key . '.jpg', R . 'sys/avatars/' . $id . '.jpg')) {
				chmod( R . 'sys/avatars/' . $id . '.jpg', 0644 );
			}
			unlink(R . 'sys/tmp/images/' . $tmp_key . '.jpg');
		}

		// Все поля заполнены правильно - записываем изменения в БД
		$tmp = '';
		$user_data = array();
		if ( $changePassword ) {
			$user_data['passw']= md5($newpassword);
		}
		if ( $changeEmail ) {
			$user_data['email']  = $email;
		}
		if (isset($_POST['activation'])) {
			$user_data['activation'] = '';
		}
		$user_data['status']     = $status;
		$user_data['timezone']   = $timezone;
		$user_data['url']        = $url;
		$user_data['icq']        = $icq;
		$user_data['about']      = $about;
		$user_data['signature']  = $signature;
		$user_data['id']         = $id;
		$res = $this->DB->save('users', $user_data, array(), true);
		// Additional fields saving
		if (is_object($this->AddFields)) {
			$this->AddFields->save($id, $_addFields);
		}
		
		if ($this->Log) $this->Log->write('editing user by adm', 'user id(' . $id . ')');
		return showInfoMessage(__('Operation is successful'), '/users/info/' . $id);

	}




	// Функция возврашает информацию о пользователе; ID пользователя передается методом GET
	public function info($id = null) {
		//turn access
		$this->ACL->turn(array('users', 'view_users'));
		$id = (int)$id;
		if ( $id < 1 ) redirect('/users/');
		
		
		$res = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => $id)));
		if (count($res) == 0) return showInfoMessage(__('Can not find user'), '/users/' );
		if (is_object($this->AddFields) && count($res) > 0) {
			$res = $this->AddFields->mergeRecords($res);
		}
		$user = $res[0];

		
		if ( isset( $_SESSION['user'] ) ) {
			$email = get_link(__('Send maail'), '/users/send_mail_form/' . $id);
			$privateMessage = get_link(__('Send PM'), '/users/send_msg_form/' . $id);
		} else {
			$email = __('Only register users');
			$privateMessage = __('Only register users');
		}
		
		
		$res = $this->DB->select('posts', DB_FIRST, array(
				'cond' => array('id_author' => $id),
				'order' => 'time DESC'));
		if (count ($res) > 0) {
			$lastPost = $res[0]['time'];
		} else {
			$lastPost = '';
		}
		
		$status_info = $this->ACL->get_user_group($user['status']);
		

		$html = $this->Parser->getAndParse('showuserinfo.html' );
		$markets = array();
		
		$markets['{USER_ID}'] =      h($user['id']);
		$markets['{NAME}'] =      	 h($user['name']);
		$markets['{REGDATE}'] =   	 h($user['puttime']);
		$markets['{STATUS}'] =    	 h($status_info['title']);
		$markets['{LASTVISIT}'] = 	 h($user['last_visit']);
		$markets['{LASTPOST}'] =  	 h($lastPost);
		$markets['{TOTALPOSTS}'] =	 h($user['posts']);
		$markets['{EMAIL}'] =     	 $email;
		$markets['{URL}'] =          h($user['url']);
		$markets['{ICQ}'] =          h($user['icq']);
		$markets['{ABOUT}'] =        h($user['about']);
		$markets['{RATING}'] =       h($user['rating']);
		$markets['{PRIVATEMESSAGE}'] = $privateMessage;
		if (is_object($this->AddFields)) {
			foreach ($user as $key => $value) {
				if (preg_match('#^add_field_[\d]+$#i', $key))				
				$markets['{' . strtoupper($key) . '}'] = h($value);
			}
		}
		
		// Аватар
		if (file_exists(R . 'sys/avatars/' . $user['id'] . '.jpg')) {
			$markets['{AVATAR}'] = get_url('/sys/avatars/' . $user['id'] . '.jpg');
		} else {
			$markets['{AVATAR}'] = get_url('/sys/img/noavatar.png');
		}
		
		
		// Edit profile link {EDIT_PROFILE_LINK}
		$markets['{EDIT_PROFILE_LINK}'] = '';
		if ($this->ACL->turn(array('users', 'edit_mine'), false) 
		&& (!empty($_SESSION['user']['id']) && $user['id'] === $_SESSION['user']['id'])) {
			$markets['{EDIT_PROFILE_LINK}'] = get_link(__('Edit profile'), '/users/edit_form/');
		} else if ($this->ACL->turn(array('users', 'edit_users'), false)) {
			$markets['{EDIT_PROFILE_LINK}'] = get_link(__('Edit profile'), '/users/edit_form_by_admin/' . $user['id']);
		}
		
		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Profile');
		$this->_globalize($nav);

		
		$html = $this->_replaceMarkets($markets, $html);
		return $this->_view($html);
	}




	// Функция возвращает html формы для отправки личного сообщения
	public function send_msg_form($id = null) {
		// Незарегистрированный пользователь не может отправлять личные сообщения
		if (!isset($_SESSION['user'])) redirect('/');
		$writer_status = (!empty($_SESSION['user']['status'])) ? $_SESSION['user']['status'] : 0;

		
		$html = null;
		$html = $html . $this->_getMessagesMenu();

		$toUser = '';
		if ( isset( $id ) ) {
			$id = (int)$id;
			if ( $id > 0 ) {
				$res = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => $id)));
				if ( $res ) {
					if (count($res) > 0) $toUser = $res[0]['name'];
				}
			}
		}
		
		
		$subject = '';
		if (!empty($_SESSION['response_pm'])) {
			if (preg_match('#^Re(\((\d+)\))?: #i', $_SESSION['response_pm'], $match)) {
				if (!empty($match[2]) && is_numeric($match[2])) {
					$subject = h('Re(' . ((int)$match[2] + 1) . '): ' . mb_substr($_SESSION['response_pm'], 6));
				} else {
					$subject = h('Re(2): ' . mb_substr($_SESSION['response_pm'], 4));
				}
			} else {
				$subject = h('Re: ' . $_SESSION['response_pm']);
			}
			unset($_SESSION['response_pm']);
		}
		$message = '';
		
	
		if (isset($_SESSION['viewMessage']) && !empty($_SESSION['viewMessage']['message'])) {
			$view    = $this->Parser->getAndParse('previewmessage.html' );
			$view    = str_replace( '{MESSAGE}', 
				$this->Textarier->print_page($_SESSION['viewMessage']['message'], $writer_status), $view );
			$html    = $html . $view."\n";
			$toUser  = h( $_SESSION['viewMessage']['toUser'] );
			$subject = h( $_SESSION['viewMessage']['subject'] );
			$message = h( $_SESSION['viewMessage']['message'] );
			unset( $_SESSION['viewMessage'] );
		}

		$action = get_url('/users/send_message');
		// Если при заполнении формы были допущены ошибки
		if ( isset( $_SESSION['sendMessageForm'] ) ) {
			$info    = $this->Parser->getAndParse('infomessage.html');
			$info    = str_replace( '{INFO_MESSAGE}', $_SESSION['sendMessageForm']['error'], $info );
			$html    = $html . $info . "\n";
			$toUser  = h( $_SESSION['sendMessageForm']['toUser'] );
			$subject = h( $_SESSION['sendMessageForm']['subject'] );
			$message = h( $_SESSION['sendMessageForm']['message'] );
			unset( $_SESSION['sendMessageForm'] );
		}

		$tpl = $this->Parser->getAndParse('sendmessageform.html');
		$markets = array();
		
		$markets['{ACTION}'] = $action;
		$markets['{TOUSER}'] = $toUser;
		$markets['{SUBJECT}'] = $subject;
		$markets['{MAIN_TEXT}'] = $message;
		$html = $html . $this->_replaceMarkets($markets, $tpl);
		
		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('PM nav');
		$this->_globalize($nav);

		
		return $this->_view($html);
	}




	// Отправка личного сообщения (добавляется новая запись в таблицу БД TABLE_MESSAGES)
	public function send_message() {
		// Незарегистрированный пользователь не может отправлять личные сообщения
		if ( !isset( $_SESSION['user'] ) ) {
			redirect('/');
		}
		// Если не переданы данные формы - функция вызвана по ошибке
		if ( !isset( $_POST['toUser'] ) or
		   !isset( $_POST['subject'] ) or
		   !isset( $_POST['mainText'] ) )
		{
			redirect('/');
		}

		$msgLen = mb_strlen($_POST['mainText']);

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$toUser  = mb_substr( $_POST['toUser'], 0, 30 );
		$subject = mb_substr( $_POST['subject'], 0, 60 );
		$message = mb_substr( $_POST['mainText'], 0, Config::read('max_message_lenght', 'users') );
		// Обрезаем лишние пробелы
		$toUser  = trim( $toUser );
		$subject = trim( $subject );
		$message = trim( $message );

		// Если пользователь хочет посмотреть на сообщение перед отправкой
		if (isset($_POST['viewMessage'])) {
			$_SESSION['viewMessage']             = array();
			$_SESSION['viewMessage']['toUser']   = $toUser;
			$_SESSION['viewMessage']['subject']  = $subject;
			$_SESSION['viewMessage']['message'] = $message;
			redirect('/users/send_msg_form/' );
		}
		
		// Проверяем, заполнены ли обязательные поля
		$error = '';
		$valobj = new Validate;
		if (empty($toUser)) 		
			$error = $error.'<li>' . __('Empty field "for"') . '</li>'."\n";
		if (empty($subject))  		
			$error = $error.'<li>' . __('Empty field "message title"') . '</li>'."\n";
		if (empty($message))     	
			$error = $error.'<li>' . __('Empty field "text"') . '</li>'."\n";
		if ($msgLen > Config::read('max_message_lenght', 'users') )
			$error = $error.'<li>' . sprintf(__('Very big message'), Config::read('max_message_lenght', 'users')) . '</li>'."\n";
			
			
		// Проверяем поля формы на недопустимые символы
		if (!empty($toUser) && !$valobj->cha_val($toUser, V_LOGIN))
			$error = $error.'<li>' . __('Wrong chars in field "to"') . '</li>'."\n";
		if (!empty($subject) && !$valobj->cha_val($subject, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "subject"') . '</li>'."\n";
			
			
		// Проверяем, есть ли такой пользователь
		if (!empty($toUser)) {
			$to = preg_replace( "#[^- _0-9a-zА-Яа-я]#iu", '', $toUser );
			$res = $this->DB->select('users', DB_FIRST, array('cond' => array('name' => $to)));

			if (count($res) == 0)
				$error = $error.'<li>' . sprintf(__('Not user with this name'), $to) . '</li>'."\n";
			if ((count($res) == 1 ) && ($res[0]['id'] == $_SESSION['user']['id']) )
				$error = $error.'<li>' . __('You can not send message to you') . '</li>'."\n";
									
			//chek max count messages
			if (!empty($res[0]['id'])) {
				$id_to = (int)$res[0]['id'];
				$id_from = (int)$_SESSION['user']['id'];
				$cnt_to = $this->DB->query("SELECT COUNT(*) as cnt FROM `" . $this->DB->getFullTableName('messages') . "` WHERE (`to_user` = '{$id_to}' OR `from_user` = '{$id_to}') AND `id_rmv` != '{$id_to}'");
				$cnt_from = $this->DB->query("SELECT COUNT(*) as cnt FROM `" . $this->DB->getFullTableName('messages') . "` WHERE (`to_user` = '{$id_from}' OR `from_user` = '{$id_from}') AND `id_rmv` != '{$id_from}'");
				if (!empty($cnt_to[0]['cnt']) && $cnt_to[0]['cnt'] >= Config::read('max_count_mess', 'users')) {
					$error = $error.'<li>' . __('This user hav full  messagebox') . '</li>'."\n";
				}
				if (!empty($cnt_from[0]['cnt']) && $cnt_from[0]['cnt'] >= Config::read('max_count_mess', 'users')) {
					$error = $error.'<li>' . __('You hav full  messagebox') . '</li>'."\n";
				}
			}
		}

		
		
		// Errors
		if (!empty($error )) {
			$_SESSION['sendMessageForm'] = array();
			$_SESSION['sendMessageForm']['error'] = '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			$_SESSION['sendMessageForm']['toUser'] = $toUser;
			$_SESSION['sendMessageForm']['subject'] = $subject;
			$_SESSION['sendMessageForm']['message'] = $message;
			redirect('/users/send_msg_form/' );
		}

		// Все поля заполнены правильно - "посылаем" сообщение
		$to = $res[0]['id'];
		$from = $_SESSION['user']['id'];
		
		$res = $this->DB->save('messages', array(
			'to_user'   => $to,
			'from_user' => $from,
			'sendtime'  => new Expr('NOW()'),
			'subject'   => $subject,
			'message'   => $message,
			'id_rmv'    => 0,
			'viewed'    => 0
		));
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		if ($this->Log) $this->Log->write('adding pm message', 'message id(' . mysql_insert_id() . ')');
		return showInfoMessage(__('Message successfuly send'), '/users/out_msg_box/' );
	}





	// Функция возвращает личное сообщение для просмотра пользователем
	public function get_message($id_msg = null) {
		if (!isset($_SESSION['user'])) redirect('/users/');
		if (empty($id_msg)) redirect('/users/in_msg_box/' );
		$idMsg = (int)$id_msg;
		if ($idMsg < 1) redirect('/users/in_msg_box/' );

		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Message');
		$this->_globalize($nav);
		
		
		// Получаем из БД информацию о сообщении.
		// В этом запросе дополнительное условие нужно для того, чтобы
		// пользователь не смог просмотреть чужое сообщение, просто указав
		// ID сообщения в адресной строке браузера
		$res = $this->DB->query("SELECT a.to_user, a.from_user, a.subject, a.sendtime, a.message, a.viewed,
			b.name AS to_user_name, c.name AS from_user_name, c.status AS from_user_status
			FROM `" . $this->DB->getFullTableName('messages') . "` a INNER JOIN `" . $this->DB->getFullTableName('users') . "` b
			ON a.to_user = b.id
			INNER JOIN `" . $this->DB->getFullTableName('users') . "` c
			ON a.from_user = c.id
			WHERE a.`id` = '" . $idMsg . "'
			AND (a.to_user = '" . $_SESSION['user']['id'] . "' OR a.from_user = '" . $_SESSION['user']['id'] . "')
			AND a.id_rmv <> '" . $_SESSION['user']['id'] . "'");

		if (count($res) == 0) {
			redirect('/users/in_msg_box/' );
		}
		
		
		// Далее мы должны выяснить, запрашивается входящее или исходящее
		// сообщение? Это нужно, чтобы правильно указать "Отправитель"
		// или "Получатель" и вывести заголовок страницы: "Входящие"
		// или "Исходящие"
		$message = $res[0];
		if ( $message['to_user'] == $_SESSION['user']['id'] ) {
			$redirect = get_url('/users/in_msg_box/');
			$inBox = true;
		} else {
			$redirect = get_url('/users/out_msg_box/');
			$inBox = false;
		}
		// Формируем заголовок страницы
		if ($inBox)  // Папка "Входящие"
			$h1 = __('PM in');
		else           // Папка "Исходящие"
			$h1 = __('PM on');
		$menu = $this->_getMessagesMenu();

		if ( $inBox ) {
			$in_on = __('From');
			$in_on_user = $message['from_user_name'];
			$in_on_user_id = $message['from_user'];
		} else {
			$in_on = __('To');
			$in_on_user = $message['to_user_name'];
			$in_on_user_id = $message['to_user'];
		}

		if ($inBox)
			$in_on_message = __('Sended');
		else
			$in_on_message = __('Geting');
			
		$sendtime     = $message['sendtime'];
		$message_name = h($message['subject']);
		$message_text = $this->Textarier->print_page($message['message'], $message['from_user_status']);
		$_SESSION['response_pm'] = $message_name;

		
		$html = $this->Parser->getAndParse('vievpmmessage.html');
		$markets = array();
		
		$markets['{H1}'] =            $h1;
		$markets['{MENU}'] =          $menu;
		$markets['{IN_ON}'] =         $in_on;
		$markets['{IN_ON_USER}'] =    $in_on_user;
		$markets['{IN_ON_MESSAGE}'] = $in_on_message;
		$markets['{SENDTIME}'] =      $sendtime;
		$markets['{MESSAGE_NAME}'] =  $message_name;
		$markets['{MESSAGE_TEXT}'] =  $message_text;
		$markets['{RESPONSE}'] =  get_url('/users/send_msg_form/' . $in_on_user_id);
		$html = $this->_replaceMarkets($markets, $html);
		
		// Помечаем сообщение, как прочитанное
		if ($inBox and $message['viewed'] != 1) {
			$this->DB->save('messages', array(
				'viewed' => 1,
				'id' => $idMsg
			));
		}
		
		return $this->_view($html);
	}




	// Папка личных сообщений (входящие)
	public function in_msg_box() {
		if (!isset($_SESSION['user'])) redirect('/');

		
		$menu = $this->_getMessagesMenu();
		$html = $this->Parser->getAndParse('vievinpm.html');
		$markets = array();
		
		// Запрос на выборку входящих сообщений
		// id_rmv - это поле указывает на то, что это сообщение уже удалил
		// один из пользователей. Т.е. сначала id_rmv=0, после того, как
		// сообщение удалил один из пользователей, id_rmv=id_user. И только после
		// того, как сообщение удалит второй пользователь, мы можем удалить
		// запись в таблице БД TABLE_MESSAGES
		$res = $this->DB->query("SELECT a.id, a.subject, a.from_user, a.sendtime, a.viewed, b.name
			FROM `" . $this->DB->getFullTableName('messages') . "` a INNER JOIN `" . $this->DB->getFullTableName('users') . "` b
			ON a.from_user = b.id
			WHERE a.to_user = '" . $_SESSION['user']['id'] . "'
			AND id_rmv <> '" . $_SESSION['user']['id'] . "'
			ORDER BY sendtime DESC");

		if (count($res) == 0) {
			$markets['{MENU}'] = $menu;
			$markets['{MESSAGES}'] = '';
			$html = $this->_replaceMarkets($markets, $html) . __('This dir is empty');
			return $this->_view($html);
		}
			

			
		$info = '';
		foreach($res as $msg) {
			$markets = array();
			$htmlmsg = $this->Parser->getAndParse('viev_in_message.html');

			// Если сообщение еще не прочитано
			$icon = ($msg['viewed'] == 0) ? 'folder_new' : 'folder';
			$icon    = get_img('/sys/img/' . $icon . '.gif');
			$author = $msg['name'];
			$theme = get_link(h($msg['subject']), '/users/get_message/' . $msg['id']);
			$data = $msg['sendtime'];
			$delit = get_link(__('Delete'), '/users/delete_message/' . $msg['id'], array('onClick' => "return confirm('" . __('Are you sure') . "')"));
			
			
			$markets['{ENTITY_ID}'] = $msg['id'];
			$markets['{MESSAGE_ICON}'] = $icon;
			$markets['{AUTHOR_NAME}'] = $author;
			$markets['{THEME_MESSAGE}'] = $theme;
			$markets['{DATA_MESSAGE}'] = $data;
			$markets['{DELIT}'] = $delit;
			$info .= $this->_replaceMarkets($markets, $htmlmsg);
		}
		
		$t_markets = array();
		$t_markets['{MENU}'] = $menu;
		$t_markets['{MESSAGES}'] = $info;
		$html = $this->_replaceMarkets($t_markets, $html);
		
		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('PM nav');
		$this->_globalize($nav);

		
		return $this->_view($html);
	}




	// Папка личных сообщений (исходящие)
	public function out_msg_box() {
		
		if ( !isset( $_SESSION['user'] ) ) {
			redirect('/');
		}

		$menu = $this->_getMessagesMenu();
		$html = $this->Parser->getAndParse('viev_on_pm.html');

		// Запрос на выборку исходящих сообщений
		// id_rmv - это поле указывает на то, что это сообщение уже удалил
		// один из пользователей. Т.е. сначала id_rmv=0, после того, как
		// сообщение удалил один из пользователей, id_rmv=id_user. И только после
		// того, как сообщение удалит второй пользователь, мы можем удалить
		// запись в таблице БД TABLE_MESSAGES
		$res = $this->DB->query("SELECT a.id, a.viewed, a.subject, a.to_user, a.sendtime, b.name
			FROM `" . $this->DB->getFullTableName('messages') . "` a INNER JOIN `" . $this->DB->getFullTableName('users') . "` b
			ON a.to_user = b.id
			WHERE a.from_user = '".$_SESSION['user']['id']."'
			AND id_rmv <> '" . $_SESSION['user']['id'] . "'
			ORDER BY sendtime DESC");
			
	
		if (count($res) == 0) {
			$markets['{MENU}'] = $menu;
			$markets['{MESSAGES}'] = '';
			$html = $this->_replaceMarkets($markets, $html) . __('This dir is empty');
			return $this->_view($html);
		}
			
							

		$info = '';
		foreach ($res as $msg) {
			$markets = array();
			$htmlmsg = $this->Parser->getAndParse('viev_on_message.html');
			$icon    = ($msg['viewed'] == 0) ? 'folder_new' : 'folder';
			$icon    = get_img('/sys/img/' . $icon . '.gif');
			$theme   = get_link(h($msg['subject']), '/users/get_message/' . $msg['id']);
			$data    = $msg['sendtime'];
			$delit   = get_link(__('Delete'), '/users/delete_message/' . $msg['id'], array('onClick' => "return confirm('" . __('Are you sure') . "')"));
			
			
			$markets['{ENTITY_ID}'] = $msg['id'];
			$markets['{MESSAGE_ICON}'] = $icon;
			$markets['{TO_USER}'] = h($msg['name']);
			$markets['{THEME_MESSAGE}'] = $theme;
			$markets['{DATA_MESSAGE}'] = $data;
			$markets['{DELIT}'] = $delit;
			$info .= $this->_replaceMarkets($markets, $htmlmsg);
		}

		$t_markets = array();
		$t_markets['{MENU}'] = $menu;
		$t_markets['{MESSAGES}'] = $info;
		$html = $this->_replaceMarkets($t_markets, $html);

		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('PM nav');
		$this->_globalize($nav);
		
		
		return $this->_view($html);
	}



	/**
	 * Multi message Delete
	 */
	public function delete_message_pack() {
		$this->delete_message();
	}
	

	// Функция удаляет личное сообщение; ID сообщения передается методом GET
	public function delete_message($id_msg = null) {
		if (!isset( $_SESSION['user'])) redirect('/');
		
		$multi_del = true;
		if (empty($_POST['ids']) 
		|| !is_array($_POST['ids'])
		|| count($_POST['ids']) < 1) $multi_del = false;

		$idMsg = (int)$id_msg;
		if ($idMsg < 1 && $multi_del === false) redirect('/');
		
		
		// We create array with ids for delete
		$ids = array();
		if ($multi_del === false) {
			$ids[] = $idMsg;
		} else {
			foreach ($_POST['ids'] as $id) {
				$id = intval($id);
				if ($id < 1) continue;
				$ids[] = $id;
			}
		}
		if (count($ids) < 1) redirect('/');

		
		foreach ($ids as $idMsg) {
			// Далее мы должны выяснить, удаляется входящее или исходящее
			// сообщение. Это нужно, чтобы сделать редирект на нужный ящик.
			// В этом запросе дополнительное условие нужно для того, чтобы
			// пользователь не смог удалить чужое сообщение, просто указав
			// ID сообщения в адресной строке браузера
			$res = $this->DB->query("SELECT `to_user`, `id_rmv`
				FROM `" . $this->DB->getFullTableName('messages') . "`
				WHERE `id` = '" . $idMsg . "' AND
				(`to_user` = '" . $_SESSION['user']['id'] . "' OR `from_user` = '" . $_SESSION['user']['id'] . "')");

			if (count($res) == 0) {
				continue;
			}

			$toUser = $res[0]['to_user'];
			$id_rmv = $res[0]['id_rmv'];
			if ( $toUser == $_SESSION['user']['id'] )
				$redirect = get_url('/users/in_msg_box/');
			else
				$redirect = get_url('/users/out_msg_box/');
			// id_rmv - это поле указывает на то, что это сообщение уже удалил
			// один из пользователей. Т.е. сначала id_rmv=0, после того, как
			// сообщение удалил один из пользователей, id_rmv=id_user. И только после
			// того, как сообщение удалит второй пользователь, мы можем удалить
			// запись в таблице БД 
			if ($id_rmv == 0) {
				$this->DB->save('messages', array(
					'id_rmv' => $_SESSION['user']['id'],
					'id' => $idMsg
				));
			} else {
				$this->DB->query("DELETE FROM `" . $this->DB->getFullTableName('messages') 
				. "` WHERE `id` = '" . $idMsg . "'");
			}
		}
		
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		if ($this->Log) $this->Log->write('delete pm message(s)', 'message(s) id(' . implode(', ', $ids) . ')');
		return showInfoMessage(__('Operation is successful'), $redirect );
	}




	// Функция возвращает меню для раздела "Личные сообщения"
	private function _getMessagesMenu() {

		$html = get_img('/sys/img/msg_inbox.png', array('alt' => __('In box'), 'title' => __('In box')))
			. get_link(__('In box'), '/users/in_msg_box/');
		$html .= get_img('/sys/img/msg_outbox.png', array('alt' => __('On box'), 'title' => __('On box')))
			. get_link(__('On box'), '/users/out_msg_box/');
		$html .= get_img('/sys/img/msg_newpost.png', array('alt' => __('Write PM'), 'title' => __('Write PM')))
			. get_link(__('Write PM'), '/users/send_msg_form/');

		return $html;
	}




	// Функция возвращает html формы для отправки письма через форум
	public function send_mail_form($id = null) {
		if (!isset($_SESSION['user'])) redirect('/');


		$html = '';
		$toUser = '';
		if ( isset( $id ) ) {
			$id = (int)$id;
			if ($id > 0) {
				$res = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => $id)));
				if (count($res) > 0) $toUser = $res[0]['name'];
			}
		}
		
		$tpl = $this->Parser->getAndParse('sendmailform.html');
		$subject = '';
		$message = '';
		$markets = array();

		$markets['{ACTION}'] = get_url('/users/send_mail/');
		// Если при заполнении формы были допущены ошибки
		if ( isset( $_SESSION['sendMailForm'] ) ) {
			$info    = $this->Parser->getAndParse('infomessage.html' );
			$info    = str_replace( '{INFO_MESSAGE}', $_SESSION['sendMailForm']['error'], $info );
			$html    = $html . $info."\n";
			$toUser  = h( $_SESSION['sendMailForm']['toUser'] );
			$subject = h( $_SESSION['sendMailForm']['subject'] );
			$message = h( $_SESSION['sendMailForm']['message'] );
			unset( $_SESSION['sendMailForm'] );
		}

		

		$markets['{TOUSER}'] = $toUser;
		$markets['{SUBJECT}'] = $subject;
		$markets['{MESSAGE}'] = $message;

		$html = $html . $this->_replaceMarkets($markets, $tpl);

		return $this->_view($html);
	}

	


	// Отправка письма пользователю сайта
	public function send_mail() {
		
		// Если не переданы данные формы - функция вызвана по ошибке
		if ( !isset( $_POST['toUser'] ) or
		   !isset( $_POST['subject'] ) or
		   !isset( $_POST['message'] ) )
		{
			redirect('/');
		}
		// Если письмо пытается отправить незарегистрированный пользователь
		if ( !isset( $_SESSION['user'] ) ) {
			redirect('/');
		}

		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$toUser  = mb_substr( $_POST['toUser'], 0, 30 );
		$subject = mb_substr( $_POST['subject'], 0, 60 );
		$message = mb_substr( $_POST['message'], 0, Config::read('max_mail_lenght', 'users'));
		// Обрезаем лишние пробелы
		$toUser  = trim( $toUser );
		$subject = trim( $subject );
		$message = trim( $message );

		// Проверяем, заполнены ли обязательные поля
		$error = '';
		$valobj = new Validate;
		if ( empty( $toUser ) ) 				
			$error = $error.'<li>' . __('Empty field "for"') . '</li>'."\n";
		if ( empty( $subject ) ) 				
			$error = $error.'<li>' . __('Empty field "message title"') . '</li>'."\n";
		if ( empty( $message ) ) 				
			$error = $error.'<li>' . __('Empty field "text"') . '</li>'."\n";
		// Проверяем поля формы на недопустимые символы
		if (!empty($toUser) && !$valobj->cha_val($toUser, V_LOGIN))
			$error = $error.'<li>' . __('Wrong chars in field "to"') . '</li>'."\n";
		if (!empty($subject) and !$valobj->cha_val($subject, V_TEXT))
			$error = $error.'<li>' . __('Wrong chars in field "subject"') . '</li>'."\n";
			
			
		// Проверяем, есть ли такой пользователь
		if (!empty($toUser)) {
			$to = preg_replace( "#[^- _0-9a-zа-яА-Я]#i", '', $toUser );
			$res = $this->DB->select('users', DB_FIRST, array('cond' => array('name' => $to)));
			if (count($res) == 0)				
				$error = $error.'<li>' . sprintf(__('Not user with this name'), $to) . '</li>'."\n";
		}
		
		// Если были допущены ошибки при заполнении формы -
		// перенаправляем посетителя для исправления ошибок
		if (!empty( $error )) {
			$_SESSION['sendMailForm']            = array();
			$_SESSION['sendMailForm']['error']   = '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			$_SESSION['sendMailForm']['toUser']  = $toUser;
			$_SESSION['sendMailForm']['subject'] = $subject;
			$_SESSION['sendMailForm']['message'] = $message;
			redirect('/users/send_mail_form/' );
		}
		
		$toUser   = $res[0];
		$fromUser = $_SESSION['user']['name'];

		
		$message = 'ОТ: '.$fromUser."\n".'ТЕМА: '.$subject."\n\n".$message;
		/* clean DB cache */
		$this->DB->cleanSqlCache();
		// формируем заголовки письма
		$headers = "From: ".$_SERVER['SERVER_NAME']." <" . Config::read('admin_email') . ">\n";
		$headers = $headers."Content-type: text/plain; charset=\"utf-8\"\n";
		$headers = $headers."Return-path: <" . Config::read('admin_email') . ">\n";
		$subject = 'Письмо с форума '.$_SERVER['SERVER_NAME'].' от '.$fromUser;
		if ( mail( $toUser['email'], $subject, $message, $headers ) )
			return showInfoMessage(__('Operation is successful'), '/' );
		else
			return showInfoMessage(__('Some error occurred'), '/' );
	}




	// Функция возвращает html формы для авторизации на форуме
	public function login_form() {
		// For return to previos page(referer)
		if (!empty($_SERVER['HTTP_REFERER']) 
		&& preg_match('#^http://([^/]+)/(.+)#', $_SERVER['HTTP_REFERER'], $match)) {
			if (!empty($match[1]) && !empty($match[2]) && $match[1] == $_SERVER['SERVER_NAME']) {
				$ref_params = explode('/', $match[2]);
				if (empty($ref_params[0]) || empty($ref_params[1]) ||
				($ref_params[0] != 'users' && $ref_params[1] != 'login_form')) {
					$_SESSION['authorize_referer'] = $match[2];
				}
			}
		}
		

		$html = '';
		if ( isset( $_SESSION['loginForm']['error'] ) ) {
			$info = $this->Parser->getAndParse('infomessage.html' );
			$info = str_replace( '{INFO_MESSAGE}', $_SESSION['loginForm']['error'], $info );
			$html = $html . $info."\n";
			unset( $_SESSION['loginForm']['error'] );
		}
		
		if (Config::read('autorization_protected_key', 'secure') === 1) {
			$_SESSION['form_key_mine'] = rand(1000, 9999);
			$form_key = rand(1000, 9999);
			$_SESSION['form_hash'] = md5($form_key . $_SESSION['form_key_mine']);
			$form_salt = '<input type="hidden" name="form_key" value="' . $form_key . '" />';
		} else {
			$form_salt = '';
		}
		
		
		
		$tpl = $this->Parser->getAndParse('loginform.html' );
		$markets = array();
		$markets['{ACTION}'] = get_url('/users/login/');
		$markets['{NEW_PASSWORD}'] = get_link('Забыли пароль?', '/users/new_password_form/');
		$markets['{FORM_KEY}'] = $form_salt;
		$html = $html . $this->_replaceMarkets($markets, $tpl);
		
		
		// Navigation Panel
		$nav = array();
		$nav['{NAVIGATION}'] = get_link(__('Home'), '/') . __('Separator') 
			. get_link(h($this->module_title), '/users/') . __('Separator') . __('Authorize');
		$this->_globalize($nav);
		
		
		return $this->_view($html);
	}




	// Вход на форум - обработчик формы авторизации
	public function login() {
		
		// Если не переданы данные формы - значит функция была вызвана по ошибке
		if (!isset($_POST['username']) or !isset($_POST['password'])) {
			redirect('/');
		}
		$error = '';
		
		//pr($_SESSION['loginForm']['count']);
		if (Config::read('autorization_protected_key', 'secure') === 1) {
			if (empty($_SESSION['form_key_mine'])
			|| empty($_POST['form_key'])		
			|| md5(substr($_POST['form_key'], 0, 10) . $_SESSION['form_key_mine']) != $_SESSION['form_hash']) {
				showInfoMessage(__('Use authorize form'), '/');
			}
		}
		// Защита от перебора пароля - при каждой неудачной попытке время задержки увеличивается
		if (isset($_SESSION['loginForm']['count']) && $_SESSION['loginForm']['count'] > time()) {
			$error = '<li>' . sprintf(__('You must wait'), ($_SESSION['loginForm']['count'] - time())) . '</li>';
		}


		// Обрезаем переменные до длины, указанной в параметре maxlength тега input
		$name      = mb_substr( $_POST['id'], 0, 30 );
		$password  = mb_substr( $_POST['password'], 0, 30 );

		// Обрезаем лишние пробелы
		$name      = trim( $name );
		$password  = trim( $password );

		// Проверяем, заполнены ли обязательные поля
		$valobj = new Validate;
		if ( empty( $name ) )         		
			$error = $error.'<li>' . __('Empty field "name"') . '</li>'."\n";
		if ( empty( $password ) ) 			
			$error = $error.'<li>' . __('Empty field "pass"') . '</li>'."\n";

		// Проверяем поля формы на недопустимые символы
		if (!empty($name) && !$valobj->cha_val($name, V_LOGIN))
			$error = $error.'<li>' . __('Wrong chars in field "name"') . '</li>'."\n";
		if (!empty($password) && !$valobj->cha_val($password, V_LOGIN))
			$error = $error.'<li>' . __('Wrong chars in field "pass"') . '</li>'."\n";

		// Проверять существование такого пользователя есть смысл только в том
		// случае, если поля не пустые и не содержат недопустимых символов
		if (empty($error)) {
			// Выполняем запрос на получение данных пользователя из БД
			$res = $this->DB->query("SELECT *, UNIX_TIMESTAMP(last_visit) as unix_last_visit
				FROM `" . $this->DB->getFullTableName('users') . "`  WHERE name='"
				.mysql_real_escape_string( $name )."' AND passw='".mysql_real_escape_string( md5( $password ) )
				."' LIMIT 1");

			if (count($res) == 0) $error = $error.'<li>' . __('Wrong login or pass') . '</li>'."\n";
		}

		// Если были допущены ошибки при заполнении формы
		if (!empty($error)) {
			if (!isset($_SESSION['loginForm']['count'])) $_SESSION['loginForm']['count'] = 1;
			else if ($_SESSION['loginForm']['count'] < 10) $_SESSION['loginForm']['count']++;
  			else if ($_SESSION['loginForm']['count'] < time()) $_SESSION['loginForm']['count'] = time() + 10;
			else $_SESSION['loginForm']['count'] = $_SESSION['loginForm']['count'] + 10;
			
			$_SESSION['loginForm']['error'] = '<p class="errorMsg">' . __('Some error in form') . '</p>'.
			"\n".'<ul class="errorMsg">'."\n".$error.'</ul>'."\n";
			redirect('/users/login_form/');
		}

		// Все поля заполнены правильно и такой пользователь существует - продолжаем...
		unset( $_SESSION['loginForm'] );
		$user = $res[0];

		if (!empty($user['activation'])) return showInfoMessage(__('Your accaunt not activated'), '/' );

		// Если пользователь заблокирован
		if ($user['locked']) return redirect('/users/baned/');

		$_SESSION['user'] = $user;

		// Функция getNewThemes() помещает в массив $_SESSION['newThemes'] ID тем,
		// в которых были новые сообщения со времени последнего посещения пользователя
		getNewThemes();

		// Выставляем cookie, если пользователь хочет входить на форум автоматически
		if ( isset ( $_POST['autologin'] ) ) {
			$path = '/';
			setcookie( 'autologin', 'yes', time() + 3600 * 24 * Config::read('cookie_time'), $path );
			setcookie( 'id', $_SESSION['user']['id'], time() + 3600 * 24 * Config::read('cookie_time'), $path );
			setcookie( 'password', $_SESSION['user']['passw'], time() + 3600 * 24 * Config::read('cookie_time'), $path );
		}
		
		
		// Authorization complete. Redirect
		if (isset($_SESSION['authorize_referer'])) {
			redirect('/' . $_SESSION['authorize_referer']);
		} else if (!empty($_SERVER['HTTP_REFERER']) 
		&& preg_match('#^http://([^/]+)/(.+)#', $_SERVER['HTTP_REFERER'], $match)) {
			if (!empty($match[1]) && !empty($match[2]) && $match[1] == $_SERVER['SERVER_NAME']) {
				$ref_params = explode('/', $match[2]);
				if (empty($ref_params[0]) || empty($ref_params[1]) ||
				($ref_params[0] != 'users' && $ref_params[1] != 'login_form')) {
					redirect('/' . $match[2]);
				}
			}
		}
		redirect('/');
	}




	// Выход из системы
	public function logout() {
		if (isset($_SESSION['user'])) unset($_SESSION['user']);
		if (isset($_SESSION)) unset($_SESSION);

		$path = '/';
		if ( isset( $_COOKIE['autologin'] ) ) setcookie( 'autologin', '', time() - 1, $path );
		if ( isset( $_COOKIE['id'] ) ) setcookie( 'id', '', time() - 1, $path );
		if ( isset( $_COOKIE['password'] ) ) setcookie( 'password', '', time() - 1, $path );
		redirect( '/');
	}

	/**
	* @param int $id - user id
	* 
	* baned user
	*/
	public function onban($id) {
		//turn access
		$this->ACL->turn(array('users', 'ban_users'));
		$id = (int)$id;
		if ($id < 1) {
			redirect('/');
		}
		$this->DB->save('users', array('id' => $id, 'locked' => 1));
	}
	
	/**
	* @param int $id - user id
	* 
	* baned user
	*/
	public function offban($id) {
		//turn access
		$this->ACL->turn(array('users', 'ban_users'));
		$id = (int)$id;
		if ($id < 1) {
			redirect('/');
		}
		$this->DB->save('users', array('id' => $id, 'locked' => 0));
	}

	
	
	
	/**
	 * Change users rating
	 * This action take request from AJAX(recomented).
	 *
	 * @param int $to_id
	 * @param int $points
	 */
	public function rating($to_id = null, $points = null) {
		// Fps counter OFF
		$this->counter = false;
		$this->cached = false;
		

		// Check rules
		if (!isset($_SESSION['user'])) die(__('Permission denied'));
		if (!$this->ACL->turn(array('users', 'set_rating'), false)) die(__('Permission denied'));
		$from_id = intval($_SESSION['user']['id']);
		$to_id = intval($to_id);
		if ($to_id < 1) die(__('Can not find user'));
		if ($from_id == $to_id) die(__('No voting for yourself'));
		
		if ($points === null && !empty($_POST['points'])) $points = $_POST['points'];
		$points = intval($points);
		if ($points > 1) $points = 1;
		if ($points < -1) $points = -1;
		
		
		// Check user exists
		$to = $this->DB->select('users', DB_COUNT, array('cond' => array('id' => $to_id)));
		if ($to == 0) die(__('Can not find user'));
		
		
		// Comment
		$comment = '';
		if (isset($_POST['comment'])) {
			$comment = trim($_POST['comment']);
			if (mb_strlen($comment) > Config::read('rating_comment_lenght', 'users')) 
				die(sprintf(__('Very long comment', Config::read('rating_comment_lenght', 'users'))));
			$comment = substr($comment, 0, Config::read('rating_comment_lenght', 'users'));
		}
		
		

		$last_vote = $this->DB->select('users_votes', DB_FIRST, array(
			'cond' => array('to_user' => $to_id),
			'order' => 'date DESC',
		));
		if (count($last_vote) == 0 || ($last_vote[0]['from_user'] != $from_id)) {
			
			
			$this->DB->save('users', array(
				'id' => $to_id,
				'`rating` = `rating` + ' . $points,
			));
			$this->DB->save('users_votes', array(
				'from_user' => $from_id,
				'to_user' => $to_id,
				'comment' => $comment,
				'points' => $points,
				'date' => new Expr('NOW()'),
			));
			die('ok');
		}
		
		
		
		die(__('Some error occurred'));
	}
	
	
	/**
	 * View rating story
	 *
	 * @param int $user_id
	 */
	public function votes_story($user_id) {
		$this->counter = false;
		$this->cached = false;
		// Without wrapper we can use this for ajax requests
		$this->wrap = (!isset($_GET['wrapper'])) ? false : true;
		
		$user_id = intval($user_id);
		if ($user_id < 1) redirect('/');
		
		
		// Check user exists
		$to_user = $this->DB->select('users', DB_FIRST, array(
			'cond' => array('id' => $user_id),
		));
		if (count($to_user) < 1) redirect('/');
		$to_user = $this->Parser->quoteTags($to_user[0]);
		
	
		$data = $this->DB->select('users_votes', DB_ALL, array(
			'cond' => array('`a`.`to_user`' => $user_id),
			'order' => '`a`.`date` DESC',
			'joins' => array(
				array(
					'table' => 'users',
					'alias' => 'b',
					'type' => 'LEFT',
					'cond' => array('`b`.`id` = `a`.`from_user`'),
				),
			),
			'alias' => 'a',
			'fields' => array('a.*', 'b.`name` AS from_user_name'),
		));
		if (count($data) < 1) {
			return $this->_view(__('No votes for user'));
		}
		
		

		$info = '';
		foreach ($data as $rec) {
			$tpl = $this->Parser->getAndParse('rating_row.html');
			$rec = $this->Parser->quoteTags($rec);
			$markets = array();
			
			$markets['{VOTE_ID}'] = $rec['id'];
			
			// Admin buttons
			$markets['{MODER_PANEL}'] = '';
			if ($this->ACL->turn(array('users', 'delete_rating_comments'), false)) {
				$markets['{MODER_PANEL}'] = get_img('/sys/img/delete_16x16.png', 
					array(
						'onclick' => "deleteUserVote('" . $rec['id'] . "'); return false;", 
						'class' => 'aimg',
					)
				);
			}
			
			$markets['{FROM_USER}'] = h($rec['from_user_name']);
			$markets['{FROM_USER_ID}'] = h($rec['from_user']);
			$markets['{COMMENT}'] = (!empty($rec['comment'])) ? h($rec['comment']) : '-';
			$markets['{POINTS}'] = h($rec['points']);
			$markets['{DATE}'] = h($rec['date']);
			
			$info .= $this->_replaceMarkets($markets, $tpl);
		}
		
		
		$mk = array(
			'{COMMON_INFO}' => '',
			'{TO_USER}' => $to_user['name'],
			'{TO_USER_ID}' => $to_user['id'],
			'{VOTES_LIST}' => $info,
		);
		$maintpl = $this->Parser->getAndParse('rating_tb.html');
		$html = $this->_replaceMarkets($mk, $maintpl);
		
		return $this->_view($html);
	}
	
	
	/**
	 * Delete users votes
	 *
	 * @param int - vote ID
	 */
	public function delete_vote($voteID) {
		$this->counter = false;
		$this->cached = false;
		$voteID = intval($voteID);
		if ($voteID < 1) die('fail');
		
		
		if ($this->ACL->turn(array('users', 'delete_rating_comments'), false)) {
			$vote = $this->DB->select('users_votes', DB_FIRST, array('cond' => array('id' => $voteID)));
	
			if (count($vote) > 0) {
				$this->DB->query("DELETE FROM `" . $this->DB->getFullTableName('users_votes') . "` WHERE `id` = '" . $voteID . "'");
				$this->DB->save('users', array(
					'id' => $vote[0]['to_user'],
					'`rating` = `rating` - ' . $vote[0]['action'],
				));
				die('ok');
			}
		}
		
		die('fail');
	}
	
	
	
	/**
	* page for baned users
	*/
	public function baned() {
		$html = $this->Parser->getAndParse('baned.html');
		$this->_view($html);
	}
	
	
	
	/**
	* Creane warnings for bad users
	*/
	public function add_warning($uid = null) {
		if (!$this->ACL->turn(array('users', 'users_warnings'), false)) die(__('Permission denied'));
		$this->counter = false;
		$this->cached = false;
		
		$uid = intval($uid);
		if (empty($uid) && !empty($_POST['uid'])) $uid = intval($_POST['uid']);
		if (empty($uid)) die(__('Some error occurred'));
		
		
		$intruder = $this->DB->select('users', DB_FIRST, array('cond' => array('id' => $uid)));
		if (count($intruder) < 1) die(__('Can not find user'));

		
		// Action and cause
		$points = (!empty($_POST['points'])) ? intval($_POST['points']) : 1;
		if ((int)$points != 1 && (int)$points != -1) $points = 1;
		$cause = (!empty($_POST['cause'])) ? trim($_POST['cause']) : '';
		
		// Interval
		if (!empty($_POST['permanently'])) $timestamp = time() + 99999999;
		else if (!empty($_POST['mult']) && !empty($_POST['cnt'])) {
			switch (trim($_POST['mult'])) {
				case 'h':
					$timestamp = intval($_POST['cnt']) * 3600;
					break;
				case 'd':
					$timestamp = intval($_POST['cnt']) * 86400;
					break;
				case 'w':
					$timestamp = intval($_POST['cnt']) * 604800;
					break;
				case 'm':
					$timestamp = intval($_POST['cnt']) * 2419200;
					break;
				default:
					$timestamp = intval($_POST['cnt']) * 29030400;
					break;
			}
		}
		if (!empty($timestamp)) {
			$interval = date("Y-m-d H:i:s", time() + $timestamp);
			$ban = 1;
		} else {
			$interval = '0000-00-00 00:00:00';
			$ban = 0; 
		}
		
		
		$adm_id = (!empty($_SESSION['user']['id'])) ? intval($_SESSION['user']['id']) : 0;
		if ($adm_id < 1) die(__('Permission denied'));
		if ($adm_id == $uid) die(__('Some error occurred'));
		
		if (!$ban) {
			$max_warnings = Config::read('warnings_by_ban', 'users');
			if (!empty($intruder[0]['warnings']) && $intruder[0]['warnings'] + $points >= $max_warnings) {
				$ban = 1;
				$interval = Config::read('autoban_interval', 'users');
				$interval = time() + intval($interval);
				$interval = date("Y-m-d H:i:s", $interval);
				
				$clean_warnings = true;
			}
		}
		
		
		$usave_data = array(
			'id' => $uid,
			'ban_expire' => $interval,
			'locked' => $ban,
		);
		if (!empty($clean_warnings)) { 
			$usave_data['warnings'] = 0;
			$this->DB->query("DELETE FROM `" . $this->DB->getFullTableName('users_warnings') . "`
				WHERE `user_id` = '" . $uid . "'");
			
		} else {
			$usave_data[] = '`warnings` = `warnings` + ' . $points;
			$this->DB->save('users_warnings', array(
				'user_id' => $uid,
				'admin_id' => $adm_id,
				'points' => $points,
				'date' => new Expr('NOW()'),
				'cause' => $cause,
			));
		}
		$this->DB->save('users', $usave_data);

		if (!empty($_POST['noticepm'])) {
			$this->DB->save('messages', array(
				'to_user' => $uid,
				'from_user' => $adm_id,
				'subject' => __('You have new warnings'),
				'message' => __('Warnings cause').$cause,
				'sendtime' => new Expr('NOW()'),
				'id_rmv' => $adm_id,
			));
		}
		
		die('ok');
	}
	
	
	/**
	 * View warnings story
	 *
	 * @param int $uid
	 */
	public function warnings_story($uid) {
		$this->counter = false;
		$this->cached = false;
		// Without wrapper we can use this for ajax requests
		$this->wrap = (!isset($_GET['wrapper'])) ? false : true;
		
		$uid = intval($uid);
		if ($uid < 1) {
			if ($this->wrap) redirect('/');
			else die(__('Some error occurred'));
		}
		
		
		// Check user exists
		$to_user = $this->DB->select('users', DB_FIRST, array(
			'cond' => array('id' => $uid),
		));
		if (count($to_user) < 1) {
			if ($this->wrap) redirect('/');
			else die(__('Can not find user'));
		}
		$to_user = $this->Parser->quoteTags($to_user[0]);
		
	
		$data = $this->DB->select('users_warnings', DB_ALL, array(
			'cond' => array('`a`.`user_id`' => $uid),
			'order' => '`a`.`date` DESC',
			'joins' => array(
				array(
					'table' => 'users',
					'alias' => 'b',
					'type' => 'LEFT',
					'cond' => array('`b`.`id` = `a`.`admin_id`'),
				),
			),
			'alias' => 'a',
			'fields' => array('a.*', 'b.`name` AS from_user_name'),
		));
		if (count($data) < 1) {
			return $this->_view(__('No warnings for user'));
		}
		
		
		
		$max_warnings_by_ban = Config::read('warnings_by_ban', 'users');
		$user_procent_warnings = (100 / $max_warnings_by_ban) * $to_user['warnings'];
		$info = '';
		foreach ($data as $rec) {
			$tpl = $this->Parser->getAndParse('rating_row.html');
			$rec = $this->Parser->quoteTags($rec);
			$markets = array();
			
			$markets['{VOTE_ID}'] = 'w'.$rec['id'];
			$markets['{WARNING_ID}'] = $rec['id'];
			
			// Admin buttons
			$markets['{MODER_PANEL}'] = '';
			if ($this->ACL->turn(array('users', 'delete_warnings'), false)) {
				$markets['{MODER_PANEL}'] = get_img('/sys/img/delete_16x16.png', 
					array(
						'onclick' => "deleteUserWarning('" . $rec['id'] . "'); return false;", 
						'class' => 'aimg',
					)
				);
			}
			
			$markets['{FROM_USER}'] = h($rec['from_user_name']);
			$markets['{FROM_USER_ID}'] = h($rec['admin_id']);
			$markets['{COMMENT}'] = (!empty($rec['cause'])) ? h($rec['cause']) : '-';
			$markets['{POINTS}'] = h($rec['points']);
			$markets['{DATE}'] = h($rec['date']);
			
			$info .= $this->_replaceMarkets($markets, $tpl);
		}
		
		
		$mk = array(
			'{COMMON_INFO}' => __('How match warnings') 
			. $to_user['warnings'] . '(' . $user_procent_warnings . '%)',
			'{TO_USER}' => $to_user['name'],
			'{TO_USER_ID}' => $to_user['id'],
			'{VOTES_LIST}' => $info,
		);
		$maintpl = $this->Parser->getAndParse('rating_tb.html');
		$html = $this->_replaceMarkets($mk, $maintpl);
		
		return $this->_view($html);
	}
	
	
	/**
	 * Delete users warnings
	 *
	 * @param int - warning ID
	 */
	public function delete_warning($wID) {
		$this->counter = false;
		$this->cached = false;
		$wID = intval($wID);
		if ($wID < 1) die('fail');
		
		
		if ($this->ACL->turn(array('users', 'delete_warnings'), false)) {
			$warning = $this->DB->select('users_warnings', DB_FIRST, array('cond' => array('id' => $wID)));
	
			if (count($warning) > 0) {
				$this->DB->query("DELETE FROM `" . $this->DB->getFullTableName('users_warnings') . "` WHERE `id` = '" . $wID . "'");
				
				$ban = 1;
				$user_warnings = $this->DB->select('users', DB_FIRST, array('id' => $warning[0]['user_id']));
				if (count($user_warnings) > 0) {
					if ($user_warnings[0]['warnings'] < Config::read('warnings_by_ban', 'users')) {
						$ban = 0;
					}
				}
				$this->DB->save('users', array(
					'id' => $warning[0]['user_id'],
					'locked' => $ban,
					'`warnings` = `warnings` - ' . intval($warning[0]['points']),
				));
				die('ok');
			}
		}
		
		die('fail');
	}
}

?>